Browse Source

Remove the NHibernate stores

pull/902/head
Kévin Chalet 6 years ago
parent
commit
24ebea58f7
  1. 21
      OpenIddict.sln
  2. 1
      eng/Versions.props
  3. 12
      src/OpenIddict.NHibernate.Models/OpenIddict.NHibernate.Models.csproj
  4. 120
      src/OpenIddict.NHibernate.Models/OpenIddictApplication.cs
  5. 87
      src/OpenIddict.NHibernate.Models/OpenIddictAuthorization.cs
  6. 71
      src/OpenIddict.NHibernate.Models/OpenIddictScope.cs
  7. 107
      src/OpenIddict.NHibernate.Models/OpenIddictToken.cs
  8. 27
      src/OpenIddict.NHibernate/IOpenIddictNHibernateContext.cs
  9. 101
      src/OpenIddict.NHibernate/Mappings/OpenIddictApplicationMapping.cs
  10. 79
      src/OpenIddict.NHibernate/Mappings/OpenIddictAuthorizationMapping.cs
  11. 63
      src/OpenIddict.NHibernate/Mappings/OpenIddictScopeMapping.cs
  12. 80
      src/OpenIddict.NHibernate/Mappings/OpenIddictTokenMapping.cs
  13. 26
      src/OpenIddict.NHibernate/OpenIddict.NHibernate.csproj
  14. 102
      src/OpenIddict.NHibernate/OpenIddictNHibernateBuilder.cs
  15. 125
      src/OpenIddict.NHibernate/OpenIddictNHibernateContext.cs
  16. 91
      src/OpenIddict.NHibernate/OpenIddictNHibernateExtensions.cs
  17. 103
      src/OpenIddict.NHibernate/OpenIddictNHibernateHelpers.cs
  18. 22
      src/OpenIddict.NHibernate/OpenIddictNHibernateOptions.cs
  19. 77
      src/OpenIddict.NHibernate/Resolvers/OpenIddictApplicationStoreResolver.cs
  20. 77
      src/OpenIddict.NHibernate/Resolvers/OpenIddictAuthorizationStoreResolver.cs
  21. 75
      src/OpenIddict.NHibernate/Resolvers/OpenIddictScopeStoreResolver.cs
  22. 77
      src/OpenIddict.NHibernate/Resolvers/OpenIddictTokenStoreResolver.cs
  23. 1026
      src/OpenIddict.NHibernate/Stores/OpenIddictApplicationStore.cs
  24. 950
      src/OpenIddict.NHibernate/Stores/OpenIddictAuthorizationStore.cs
  25. 720
      src/OpenIddict.NHibernate/Stores/OpenIddictScopeStore.cs
  26. 1127
      src/OpenIddict.NHibernate/Stores/OpenIddictTokenStore.cs
  27. 16
      test/OpenIddict.NHibernate.Tests/OpenIddict.NHibernate.Tests.csproj
  28. 122
      test/OpenIddict.NHibernate.Tests/OpenIddictNHibernateBuilderTests.cs
  29. 240
      test/OpenIddict.NHibernate.Tests/OpenIddictNHibernateContextTests.cs
  30. 120
      test/OpenIddict.NHibernate.Tests/OpenIddictNHibernateExtensionsTests.cs
  31. 84
      test/OpenIddict.NHibernate.Tests/Resolvers/OpenIddictApplicationStoreResolverTests.cs
  32. 84
      test/OpenIddict.NHibernate.Tests/Resolvers/OpenIddictAuthorizationStoreResolverTests.cs
  33. 84
      test/OpenIddict.NHibernate.Tests/Resolvers/OpenIddictScopeStoreResolverTests.cs
  34. 84
      test/OpenIddict.NHibernate.Tests/Resolvers/OpenIddictTokenStoreResolverTests.cs

21
OpenIddict.sln

@ -47,12 +47,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "shared", "shared", "{D8075F
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenIddict.Extensions", "shared\OpenIddict.Extensions\OpenIddict.Extensions.csproj", "{B90761B9-7582-44CB-AB0D-3C4058693227}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenIddict.NHibernate", "src\OpenIddict.NHibernate\OpenIddict.NHibernate.csproj", "{17BFF448-F11F-40D6-B658-BD81B306D2CA}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenIddict.NHibernate.Models", "src\OpenIddict.NHibernate.Models\OpenIddict.NHibernate.Models.csproj", "{22882DA6-6A5F-4E48-8BDC-7248B1DE5D14}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenIddict.NHibernate.Tests", "test\OpenIddict.NHibernate.Tests\OpenIddict.NHibernate.Tests.csproj", "{B99BCBEC-9771-4C68-96E2-1A54E9BC432D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenIddict.Server.AspNetCore", "src\OpenIddict.Server.AspNetCore\OpenIddict.Server.AspNetCore.csproj", "{9C86897B-DB77-4D85-AC74-A6768F97098C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenIddict.Server.Owin", "src\OpenIddict.Server.Owin\OpenIddict.Server.Owin.csproj", "{93FFD5DC-EC92-48AD-B638-613205D89BD7}"
@ -176,18 +170,6 @@ Global
{B90761B9-7582-44CB-AB0D-3C4058693227}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B90761B9-7582-44CB-AB0D-3C4058693227}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B90761B9-7582-44CB-AB0D-3C4058693227}.Release|Any CPU.Build.0 = Release|Any CPU
{17BFF448-F11F-40D6-B658-BD81B306D2CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{17BFF448-F11F-40D6-B658-BD81B306D2CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{17BFF448-F11F-40D6-B658-BD81B306D2CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{17BFF448-F11F-40D6-B658-BD81B306D2CA}.Release|Any CPU.Build.0 = Release|Any CPU
{22882DA6-6A5F-4E48-8BDC-7248B1DE5D14}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{22882DA6-6A5F-4E48-8BDC-7248B1DE5D14}.Debug|Any CPU.Build.0 = Debug|Any CPU
{22882DA6-6A5F-4E48-8BDC-7248B1DE5D14}.Release|Any CPU.ActiveCfg = Release|Any CPU
{22882DA6-6A5F-4E48-8BDC-7248B1DE5D14}.Release|Any CPU.Build.0 = Release|Any CPU
{B99BCBEC-9771-4C68-96E2-1A54E9BC432D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B99BCBEC-9771-4C68-96E2-1A54E9BC432D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B99BCBEC-9771-4C68-96E2-1A54E9BC432D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B99BCBEC-9771-4C68-96E2-1A54E9BC432D}.Release|Any CPU.Build.0 = Release|Any CPU
{9C86897B-DB77-4D85-AC74-A6768F97098C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9C86897B-DB77-4D85-AC74-A6768F97098C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9C86897B-DB77-4D85-AC74-A6768F97098C}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -267,9 +249,6 @@ Global
{8FACE85E-EF8F-4AB1-85DD-4010D5E2165D} = {5FC71D6A-A994-4F62-977F-88A7D25379D7}
{27F603EF-D335-445B-9443-6B5A6CA3C110} = {5FC71D6A-A994-4F62-977F-88A7D25379D7}
{B90761B9-7582-44CB-AB0D-3C4058693227} = {D8075F1F-6257-463B-B481-BDC7C5ABA292}
{17BFF448-F11F-40D6-B658-BD81B306D2CA} = {D544447C-D701-46BB-9A5B-C76C612A596B}
{22882DA6-6A5F-4E48-8BDC-7248B1DE5D14} = {D544447C-D701-46BB-9A5B-C76C612A596B}
{B99BCBEC-9771-4C68-96E2-1A54E9BC432D} = {5FC71D6A-A994-4F62-977F-88A7D25379D7}
{9C86897B-DB77-4D85-AC74-A6768F97098C} = {D544447C-D701-46BB-9A5B-C76C612A596B}
{93FFD5DC-EC92-48AD-B638-613205D89BD7} = {D544447C-D701-46BB-9A5B-C76C612A596B}
{97A59757-A249-4FCF-B042-BF425E117706} = {D544447C-D701-46BB-9A5B-C76C612A596B}

1
eng/Versions.props

@ -41,7 +41,6 @@
<LinqAsyncVersion>4.0.0</LinqAsyncVersion>
<MongoDbVersion>2.9.0</MongoDbVersion>
<MoqVersion>4.13.1</MoqVersion>
<NHibernateVersion>5.2.2</NHibernateVersion>
<OwinVersion>4.0.0</OwinVersion>
<SystemTextJsonVersion>4.6.0</SystemTextJsonVersion>
<TasksExtensionsVersion>4.5.3</TasksExtensionsVersion>

12
src/OpenIddict.NHibernate.Models/OpenIddict.NHibernate.Models.csproj

@ -1,12 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<Description>Relational entities for the NHibernate 5.x stores.</Description>
<PackageTags>$(PackageTags);nhibernate;models</PackageTags>
</PropertyGroup>
</Project>

120
src/OpenIddict.NHibernate.Models/OpenIddictApplication.cs

@ -1,120 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace OpenIddict.NHibernate.Models
{
/// <summary>
/// Represents an OpenIddict application.
/// </summary>
public class OpenIddictApplication : OpenIddictApplication<string, OpenIddictAuthorization, OpenIddictToken>
{
public OpenIddictApplication()
{
// Generate a new string identifier.
Id = Guid.NewGuid().ToString();
}
}
/// <summary>
/// Represents an OpenIddict application.
/// </summary>
public class OpenIddictApplication<TKey> : OpenIddictApplication<TKey, OpenIddictAuthorization<TKey>, OpenIddictToken<TKey>>
where TKey : IEquatable<TKey>
{ }
/// <summary>
/// Represents an OpenIddict application.
/// </summary>
[DebuggerDisplay("Id = {Id.ToString(),nq} ; ClientId = {ClientId,nq} ; Type = {Type,nq}")]
public class OpenIddictApplication<TKey, TAuthorization, TToken> where TKey : IEquatable<TKey>
{
/// <summary>
/// Gets or sets the list of the authorizations associated with this application.
/// </summary>
public virtual IList<TAuthorization> Authorizations { get; set; } = new List<TAuthorization>();
/// <summary>
/// Gets or sets the client identifier
/// associated with the current application.
/// </summary>
public virtual string ClientId { get; set; }
/// <summary>
/// Gets or sets the client secret associated with the current application.
/// Note: depending on the application manager used to create this instance,
/// this property may be hashed or encrypted for security reasons.
/// </summary>
public virtual string ClientSecret { get; set; }
/// <summary>
/// Gets or sets the consent type
/// associated with the current application.
/// </summary>
public virtual string ConsentType { get; set; }
/// <summary>
/// Gets or sets the display name
/// associated with the current application.
/// </summary>
public virtual string DisplayName { get; set; }
/// <summary>
/// Gets or sets the unique identifier
/// associated with the current application.
/// </summary>
public virtual TKey Id { get; set; }
/// <summary>
/// Gets or sets the permissions associated with the
/// current application, serialized as a JSON array.
/// </summary>
public virtual string Permissions { get; set; }
/// <summary>
/// Gets or sets the logout callback URLs associated with
/// the current application, serialized as a JSON array.
/// </summary>
public virtual string PostLogoutRedirectUris { get; set; }
/// <summary>
/// Gets or sets the additional properties serialized as a JSON object,
/// or <c>null</c> if no bag was associated with the current application.
/// </summary>
public virtual string Properties { get; set; }
/// <summary>
/// Gets or sets the callback URLs associated with the
/// current application, serialized as a JSON array.
/// </summary>
public virtual string RedirectUris { get; set; }
/// <summary>
/// Gets or sets the requirements associated with the
/// current application, serialized as a JSON array.
/// </summary>
public virtual string Requirements { get; set; }
/// <summary>
/// Gets or sets the list of the tokens associated with this application.
/// </summary>
public virtual IList<TToken> Tokens { get; set; } = new List<TToken>();
/// <summary>
/// Gets or sets the application type
/// associated with the current application.
/// </summary>
public virtual string Type { get; set; }
/// <summary>
/// Gets or sets the entity version, used as a concurrency token.
/// </summary>
public virtual int Version { get; set; }
}
}

87
src/OpenIddict.NHibernate.Models/OpenIddictAuthorization.cs

@ -1,87 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace OpenIddict.NHibernate.Models
{
/// <summary>
/// Represents an OpenIddict authorization.
/// </summary>
public class OpenIddictAuthorization : OpenIddictAuthorization<string, OpenIddictApplication, OpenIddictToken>
{
public OpenIddictAuthorization()
{
// Generate a new string identifier.
Id = Guid.NewGuid().ToString();
}
}
/// <summary>
/// Represents an OpenIddict authorization.
/// </summary>
public class OpenIddictAuthorization<TKey> : OpenIddictAuthorization<TKey, OpenIddictApplication<TKey>, OpenIddictToken<TKey>>
where TKey : IEquatable<TKey>
{ }
/// <summary>
/// Represents an OpenIddict authorization.
/// </summary>
[DebuggerDisplay("Id = {Id.ToString(),nq} ; Subject = {Subject,nq} ; Type = {Type,nq} ; Status = {Status,nq}")]
public class OpenIddictAuthorization<TKey, TApplication, TToken> where TKey : IEquatable<TKey>
{
/// <summary>
/// Gets or sets the application associated with the current authorization.
/// </summary>
public virtual TApplication Application { get; set; }
/// <summary>
/// Gets or sets the unique identifier
/// associated with the current authorization.
/// </summary>
public virtual TKey Id { get; set; }
/// <summary>
/// Gets or sets the additional properties serialized as a JSON object,
/// or <c>null</c> if no bag was associated with the current authorization.
/// </summary>
public virtual string Properties { get; set; }
/// <summary>
/// Gets or sets the scopes associated with the current
/// authorization, serialized as a JSON array.
/// </summary>
public virtual string Scopes { get; set; }
/// <summary>
/// Gets or sets the status of the current authorization.
/// </summary>
public virtual string Status { get; set; }
/// <summary>
/// Gets or sets the subject associated with the current authorization.
/// </summary>
public virtual string Subject { get; set; }
/// <summary>
/// Gets or sets the list of tokens
/// associated with the current authorization.
/// </summary>
public virtual IList<TToken> Tokens { get; set; } = new List<TToken>();
/// <summary>
/// Gets or sets the type of the current authorization.
/// </summary>
public virtual string Type { get; set; }
/// <summary>
/// Gets or sets the entity version, used as a concurrency token.
/// </summary>
public virtual int Version { get; set; }
}
}

71
src/OpenIddict.NHibernate.Models/OpenIddictScope.cs

@ -1,71 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.Diagnostics;
namespace OpenIddict.NHibernate.Models
{
/// <summary>
/// Represents an OpenIddict scope.
/// </summary>
public class OpenIddictScope : OpenIddictScope<string>
{
public OpenIddictScope()
{
// Generate a new string identifier.
Id = Guid.NewGuid().ToString();
}
}
/// <summary>
/// Represents an OpenIddict scope.
/// </summary>
[DebuggerDisplay("Id = {Id.ToString(),nq} ; Name = {Name,nq}")]
public class OpenIddictScope<TKey> where TKey : IEquatable<TKey>
{
/// <summary>
/// Gets or sets the public description
/// associated with the current scope.
/// </summary>
public virtual string Description { get; set; }
/// <summary>
/// Gets or sets the display name
/// associated with the current scope.
/// </summary>
public virtual string DisplayName { get; set; }
/// <summary>
/// Gets or sets the unique identifier
/// associated with the current scope.
/// </summary>
public virtual TKey Id { get; set; }
/// <summary>
/// Gets or sets the unique name
/// associated with the current scope.
/// </summary>
public virtual string Name { get; set; }
/// <summary>
/// Gets or sets the additional properties serialized as a JSON object,
/// or <c>null</c> if no bag was associated with the current scope.
/// </summary>
public virtual string Properties { get; set; }
/// <summary>
/// Gets or sets the resources associated with the
/// current scope, serialized as a JSON array.
/// </summary>
public virtual string Resources { get; set; }
/// <summary>
/// Gets or sets the entity version, used as a concurrency token.
/// </summary>
public virtual int Version { get; set; }
}
}

107
src/OpenIddict.NHibernate.Models/OpenIddictToken.cs

@ -1,107 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.Diagnostics;
namespace OpenIddict.NHibernate.Models
{
/// <summary>
/// Represents an OpenIddict token.
/// </summary>
public class OpenIddictToken : OpenIddictToken<string, OpenIddictApplication, OpenIddictAuthorization>
{
public OpenIddictToken()
{
// Generate a new string identifier.
Id = Guid.NewGuid().ToString();
}
}
/// <summary>
/// Represents an OpenIddict token.
/// </summary>
public class OpenIddictToken<TKey> : OpenIddictToken<TKey, OpenIddictApplication<TKey>, OpenIddictAuthorization<TKey>>
where TKey : IEquatable<TKey>
{
}
/// <summary>
/// Represents an OpenIddict token.
/// </summary>
[DebuggerDisplay("Id = {Id.ToString(),nq} ; Subject = {Subject,nq} ; Type = {Type,nq} ; Status = {Status,nq}")]
public class OpenIddictToken<TKey, TApplication, TAuthorization> where TKey : IEquatable<TKey>
{
/// <summary>
/// Gets or sets the application associated with the current token.
/// </summary>
public virtual TApplication Application { get; set; }
/// <summary>
/// Gets or sets the authorization associated with the current token.
/// </summary>
public virtual TAuthorization Authorization { get; set; }
/// <summary>
/// Gets or sets the date on which the token
/// will start to be considered valid.
/// </summary>
public virtual DateTimeOffset? CreationDate { get; set; }
/// <summary>
/// Gets or sets the date on which the token
/// will no longer be considered valid.
/// </summary>
public virtual DateTimeOffset? ExpirationDate { get; set; }
/// <summary>
/// Gets or sets the unique identifier
/// associated with the current token.
/// </summary>
public virtual TKey Id { get; set; }
/// <summary>
/// Gets or sets the payload of the current token, if applicable.
/// Note: this property is only used for reference tokens
/// and may be encrypted for security reasons.
/// </summary>
public virtual string Payload { get; set; }
/// <summary>
/// Gets or sets the additional properties serialized as a JSON object,
/// or <c>null</c> if no bag was associated with the current token.
/// </summary>
public virtual string Properties { get; set; }
/// <summary>
/// Gets or sets the reference identifier associated
/// with the current token, if applicable.
/// Note: this property is only used for reference tokens
/// and may be hashed or encrypted for security reasons.
/// </summary>
public virtual string ReferenceId { get; set; }
/// <summary>
/// Gets or sets the status of the current token.
/// </summary>
public virtual string Status { get; set; }
/// <summary>
/// Gets or sets the subject associated with the current token.
/// </summary>
public virtual string Subject { get; set; }
/// <summary>
/// Gets or sets the type of the current token.
/// </summary>
public virtual string Type { get; set; }
/// <summary>
/// Gets or sets the entity version, used as a concurrency token.
/// </summary>
public virtual int Version { get; set; }
}
}

27
src/OpenIddict.NHibernate/IOpenIddictNHibernateContext.cs

@ -1,27 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System.Threading;
using System.Threading.Tasks;
using NHibernate;
namespace OpenIddict.NHibernate
{
/// <summary>
/// Exposes the NHibernate session used by the OpenIddict stores.
/// </summary>
public interface IOpenIddictNHibernateContext
{
/// <summary>
/// Gets the <see cref="ISession"/>.
/// </summary>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the
/// asynchronous operation, whose result returns the NHibernate session.
/// </returns>
ValueTask<ISession> GetSessionAsync(CancellationToken cancellationToken);
}
}

101
src/OpenIddict.NHibernate/Mappings/OpenIddictApplicationMapping.cs

@ -1,101 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.ComponentModel;
using NHibernate.Mapping.ByCode;
using NHibernate.Mapping.ByCode.Conformist;
using OpenIddict.NHibernate.Models;
namespace OpenIddict.NHibernate
{
/// <summary>
/// Defines a relational mapping for the Application entity.
/// </summary>
/// <typeparam name="TApplication">The type of the Application entity.</typeparam>
/// <typeparam name="TAuthorization">The type of the Authorization entity.</typeparam>
/// <typeparam name="TToken">The type of the Token entity.</typeparam>
/// <typeparam name="TKey">The type of the Key entity.</typeparam>
[EditorBrowsable(EditorBrowsableState.Never)]
public class OpenIddictApplicationMapping<TApplication, TAuthorization, TToken, TKey> : ClassMapping<TApplication>
where TApplication : OpenIddictApplication<TKey, TAuthorization, TToken>
where TAuthorization : OpenIddictAuthorization<TKey, TApplication, TToken>
where TToken : OpenIddictToken<TKey, TApplication, TAuthorization>
where TKey : IEquatable<TKey>
{
public OpenIddictApplicationMapping()
{
Id(application => application.Id, map =>
{
map.Generator(Generators.Identity);
});
Version(application => application.Version, map =>
{
map.Insert(true);
});
Property(application => application.ClientId, map =>
{
map.NotNullable(true);
map.Unique(true);
});
Property(application => application.ClientSecret);
Property(application => application.ConsentType);
Property(application => application.DisplayName);
Property(application => application.Permissions, map =>
{
map.Length(10000);
});
Property(application => application.PostLogoutRedirectUris, map =>
{
map.Length(10000);
});
Property(application => application.Properties, map =>
{
map.Length(10000);
});
Property(application => application.RedirectUris, map =>
{
map.Length(10000);
});
Property(application => application.Type, map =>
{
map.NotNullable(true);
});
Bag(application => application.Authorizations,
map =>
{
map.Key(key => key.Column("ApplicationId"));
},
map =>
{
map.OneToMany();
});
Bag(application => application.Tokens,
map =>
{
map.Key(key => key.Column("ApplicationId"));
},
map =>
{
map.OneToMany();
});
Table("OpenIddictApplications");
}
}
}

79
src/OpenIddict.NHibernate/Mappings/OpenIddictAuthorizationMapping.cs

@ -1,79 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.ComponentModel;
using NHibernate.Mapping.ByCode;
using NHibernate.Mapping.ByCode.Conformist;
using OpenIddict.NHibernate.Models;
namespace OpenIddict.NHibernate
{
/// <summary>
/// Defines a relational mapping for the Authorization entity.
/// </summary>
/// <typeparam name="TAuthorization">The type of the Authorization entity.</typeparam>
/// <typeparam name="TApplication">The type of the Application entity.</typeparam>
/// <typeparam name="TToken">The type of the Token entity.</typeparam>
/// <typeparam name="TKey">The type of the Key entity.</typeparam>
[EditorBrowsable(EditorBrowsableState.Never)]
public class OpenIddictAuthorizationMapping<TAuthorization, TApplication, TToken, TKey> : ClassMapping<TAuthorization>
where TAuthorization : OpenIddictAuthorization<TKey, TApplication, TToken>
where TApplication : OpenIddictApplication<TKey, TAuthorization, TToken>
where TToken : OpenIddictToken<TKey, TApplication, TAuthorization>
where TKey : IEquatable<TKey>
{
public OpenIddictAuthorizationMapping()
{
Id(authorization => authorization.Id, map =>
{
map.Generator(Generators.Identity);
});
Version(authorization => authorization.Version, map =>
{
map.Insert(true);
});
Property(authorization => authorization.Properties, map =>
{
map.Length(10000);
});
Property(authorization => authorization.Scopes, map =>
{
map.Length(10000);
});
Property(authorization => authorization.Status, map =>
{
map.NotNullable(true);
});
Property(authorization => authorization.Type, map =>
{
map.NotNullable(true);
});
ManyToOne(authorization => authorization.Application, map =>
{
map.ForeignKey("ApplicationId");
});
Bag(authorization => authorization.Tokens,
map =>
{
map.Key(key => key.Column("AuthorizationId"));
},
map =>
{
map.OneToMany();
});
Table("OpenIddictAuthorizations");
}
}
}

63
src/OpenIddict.NHibernate/Mappings/OpenIddictScopeMapping.cs

@ -1,63 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.ComponentModel;
using NHibernate.Mapping.ByCode;
using NHibernate.Mapping.ByCode.Conformist;
using OpenIddict.NHibernate.Models;
namespace OpenIddict.NHibernate
{
/// <summary>
/// Defines a relational mapping for the Scope entity.
/// </summary>
/// <typeparam name="TScope">The type of the Scope entity.</typeparam>
/// <typeparam name="TKey">The type of the Key entity.</typeparam>
[EditorBrowsable(EditorBrowsableState.Never)]
public class OpenIddictScopeMapping<TScope, TKey> : ClassMapping<TScope>
where TScope : OpenIddictScope<TKey>
where TKey : IEquatable<TKey>
{
public OpenIddictScopeMapping()
{
Id(scope => scope.Id, map =>
{
map.Generator(Generators.Identity);
});
Version(scope => scope.Version, map =>
{
map.Insert(true);
});
Property(scope => scope.Description, map =>
{
map.Length(10000);
});
Property(scope => scope.DisplayName);
Property(scope => scope.Name, map =>
{
map.NotNullable(true);
map.Unique(true);
});
Property(scope => scope.Properties, map =>
{
map.Length(10000);
});
Property(scope => scope.Resources, map =>
{
map.Length(10000);
});
Table("OpenIddictScopes");
}
}
}

80
src/OpenIddict.NHibernate/Mappings/OpenIddictTokenMapping.cs

@ -1,80 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.ComponentModel;
using NHibernate.Mapping.ByCode;
using NHibernate.Mapping.ByCode.Conformist;
using OpenIddict.NHibernate.Models;
namespace OpenIddict.NHibernate
{
/// <summary>
/// Defines a relational mapping for the Token entity.
/// </summary>
/// <typeparam name="TToken">The type of the Token entity.</typeparam>
/// <typeparam name="TApplication">The type of the Application entity.</typeparam>
/// <typeparam name="TAuthorization">The type of the Authorization entity.</typeparam>
/// <typeparam name="TKey">The type of the Key entity.</typeparam>
[EditorBrowsable(EditorBrowsableState.Never)]
public class OpenIddictTokenMapping<TToken, TApplication, TAuthorization, TKey> : ClassMapping<TToken>
where TToken : OpenIddictToken<TKey, TApplication, TAuthorization>
where TApplication : OpenIddictApplication<TKey, TAuthorization, TToken>
where TAuthorization : OpenIddictAuthorization<TKey, TApplication, TToken>
where TKey : IEquatable<TKey>
{
public OpenIddictTokenMapping()
{
Id(token => token.Id, map =>
{
map.Generator(Generators.Identity);
});
Version(token => token.Version, map =>
{
map.Insert(true);
});
Property(token => token.CreationDate);
Property(token => token.ExpirationDate);
Property(token => token.Payload, map =>
{
map.Length(10000);
});
Property(token => token.Properties, map =>
{
map.Length(10000);
});
Property(token => token.ReferenceId);
Property(token => token.Status, map =>
{
map.NotNullable(true);
});
Property(token => token.Type, map =>
{
map.NotNullable(true);
});
ManyToOne(token => token.Application, map =>
{
map.Column("ApplicationId");
});
ManyToOne(token => token.Authorization, map =>
{
map.Column("AuthorizationId");
});
Table("OpenIddictTokens");
}
}
}

26
src/OpenIddict.NHibernate/OpenIddict.NHibernate.csproj

@ -1,26 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;netstandard2.1</TargetFrameworks>
</PropertyGroup>
<PropertyGroup>
<Description>NHibernate 5.x stores for OpenIddict.</Description>
<PackageTags>$(PackageTags);nhibernate</PackageTags>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\OpenIddict.Core\OpenIddict.Core.csproj" />
<ProjectReference Include="..\OpenIddict.NHibernate.Models\OpenIddict.NHibernate.Models.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="$(JetBrainsVersion)" PrivateAssets="All" />
<PackageReference Include="NHibernate" Version="$(NHibernateVersion)" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\shared\OpenIddict.Extensions\*\*.cs" />
</ItemGroup>
</Project>

102
src/OpenIddict.NHibernate/OpenIddictNHibernateBuilder.cs

@ -1,102 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.ComponentModel;
using JetBrains.Annotations;
using NHibernate;
using OpenIddict.Core;
using OpenIddict.NHibernate;
using OpenIddict.NHibernate.Models;
namespace Microsoft.Extensions.DependencyInjection
{
/// <summary>
/// Exposes the necessary methods required to configure the OpenIddict NHibernate services.
/// </summary>
public class OpenIddictNHibernateBuilder
{
/// <summary>
/// Initializes a new instance of <see cref="OpenIddictNHibernateBuilder"/>.
/// </summary>
/// <param name="services">The services collection.</param>
public OpenIddictNHibernateBuilder([NotNull] IServiceCollection services)
=> Services = services ?? throw new ArgumentNullException(nameof(services));
/// <summary>
/// Gets the services collection.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public IServiceCollection Services { get; }
/// <summary>
/// Amends the default OpenIddict NHibernate configuration.
/// </summary>
/// <param name="configuration">The delegate used to configure the OpenIddict options.</param>
/// <remarks>This extension can be safely called multiple times.</remarks>
/// <returns>The <see cref="OpenIddictNHibernateBuilder"/>.</returns>
public OpenIddictNHibernateBuilder Configure([NotNull] Action<OpenIddictNHibernateOptions> configuration)
{
if (configuration == null)
{
throw new ArgumentNullException(nameof(configuration));
}
Services.Configure(configuration);
return this;
}
/// <summary>
/// Configures the NHibernate stores to use the specified session factory
/// instead of retrieving it from the dependency injection container.
/// </summary>
/// <param name="factory">The <see cref="ISessionFactory"/>.</param>
/// <returns>The <see cref="OpenIddictNHibernateBuilder"/>.</returns>
public OpenIddictNHibernateBuilder UseSessionFactory([NotNull] ISessionFactory factory)
{
if (factory == null)
{
throw new ArgumentNullException(nameof(factory));
}
return Configure(options => options.SessionFactory = factory);
}
/// <summary>
/// Configures OpenIddict to use the default OpenIddict Entity Framework entities, with the specified key type.
/// </summary>
/// <returns>The <see cref="OpenIddictNHibernateBuilder"/>.</returns>
public OpenIddictNHibernateBuilder ReplaceDefaultEntities<TKey>()
where TKey : IEquatable<TKey>
=> ReplaceDefaultEntities<OpenIddictApplication<TKey>,
OpenIddictAuthorization<TKey>,
OpenIddictScope<TKey>,
OpenIddictToken<TKey>, TKey>();
/// <summary>
/// Configures OpenIddict to use the specified entities, derived from the default OpenIddict Entity Framework entities.
/// </summary>
/// <returns>The <see cref="OpenIddictNHibernateBuilder"/>.</returns>
public OpenIddictNHibernateBuilder ReplaceDefaultEntities<TApplication, TAuthorization, TScope, TToken, TKey>()
where TApplication : OpenIddictApplication<TKey, TAuthorization, TToken>
where TAuthorization : OpenIddictAuthorization<TKey, TApplication, TToken>
where TScope : OpenIddictScope<TKey>
where TToken : OpenIddictToken<TKey, TApplication, TAuthorization>
where TKey : IEquatable<TKey>
{
Services.Configure<OpenIddictCoreOptions>(options =>
{
options.DefaultApplicationType = typeof(TApplication);
options.DefaultAuthorizationType = typeof(TAuthorization);
options.DefaultScopeType = typeof(TScope);
options.DefaultTokenType = typeof(TToken);
});
return this;
}
}
}

125
src/OpenIddict.NHibernate/OpenIddictNHibernateContext.cs

@ -1,125 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using NHibernate;
namespace OpenIddict.NHibernate
{
/// <summary>
/// Exposes the NHibernate session used by the OpenIddict stores.
/// </summary>
public class OpenIddictNHibernateContext : IOpenIddictNHibernateContext, IDisposable
{
private readonly IOptionsMonitor<OpenIddictNHibernateOptions> _options;
private readonly IServiceProvider _provider;
private ISession _session;
public OpenIddictNHibernateContext(
[NotNull] IOptionsMonitor<OpenIddictNHibernateOptions> options,
[NotNull] IServiceProvider provider)
{
_options = options;
_provider = provider;
}
/// <summary>
/// Disposes the session held by this instance, if applicable.
/// </summary>
public void Dispose() => _session?.Dispose();
/// <summary>
/// Gets the <see cref="ISession"/>.
/// </summary>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the
/// asynchronous operation, whose result returns the NHibernate session.
/// </returns>
/// <remarks>
/// If a session factory was explicitly set in the OpenIddict NHibernate options,
/// a new session, specific to the OpenIddict stores is automatically opened
/// and disposed when the ambient scope is collected. If no session factory
/// was set, the session is retrieved from the dependency injection container
/// and a derived instance disabling automatic flush is managed by the context.
/// </remarks>
public ValueTask<ISession> GetSessionAsync(CancellationToken cancellationToken)
{
if (_session != null)
{
return new ValueTask<ISession>(_session);
}
if (cancellationToken.IsCancellationRequested)
{
return new ValueTask<ISession>(Task.FromCanceled<ISession>(cancellationToken));
}
var options = _options.CurrentValue;
if (options == null)
{
throw new InvalidOperationException("The OpenIddict NHibernate options cannot be retrieved.");
}
// Note: by default, NHibernate is natively configured to perform automatic flushes
// on queries when it determines stale data may be returned during their execution.
// Combined with implicit entity updates, this feature is inconvenient for OpenIddict
// as it may result in updated entities being persisted before they are explicitly
// validated by the core managers and marked as updated by the NHibernate stores.
// To ensure this doesn't interfere with OpenIddict, automatic flush is disabled.
var factory = options.SessionFactory;
if (factory == null)
{
var session = _provider.GetService<ISession>();
if (session != null)
{
// If the flush mode is already set to manual, avoid creating a sub-session.
// If the session must be derived, all the parameters are inherited from
// the original session (except the flush mode, explicitly set to manual).
if (session.FlushMode != FlushMode.Manual)
{
session = _session = session.SessionWithOptions()
.AutoClose()
.AutoJoinTransaction()
.Connection()
.ConnectionReleaseMode()
.FlushMode(FlushMode.Manual)
.Interceptor()
.OpenSession();
}
return new ValueTask<ISession>(session);
}
factory = _provider.GetService<ISessionFactory>();
}
if (factory == null)
{
throw new InvalidOperationException(new StringBuilder()
.AppendLine("No suitable NHibernate session or session factory can be found.")
.Append("To configure the OpenIddict NHibernate stores to use a specific factory, use ")
.Append("'services.AddOpenIddict().AddCore().UseNHibernate().UseSessionFactory()' or register an ")
.Append("'ISession'/'ISessionFactory' in the dependency injection container in 'ConfigureServices()'.")
.ToString());
}
else
{
var session = factory.OpenSession();
session.FlushMode = FlushMode.Manual;
return new ValueTask<ISession>(_session = session);
}
}
}
}

91
src/OpenIddict.NHibernate/OpenIddictNHibernateExtensions.cs

@ -1,91 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection.Extensions;
using OpenIddict.NHibernate;
using OpenIddict.NHibernate.Models;
namespace Microsoft.Extensions.DependencyInjection
{
/// <summary>
/// Exposes extensions allowing to register the OpenIddict NHibernate services.
/// </summary>
public static class OpenIddictNHibernateExtensions
{
/// <summary>
/// Registers the NHibernate stores services in the DI container and
/// configures OpenIddict to use the NHibernate entities by default.
/// </summary>
/// <param name="builder">The services builder used by OpenIddict to register new services.</param>
/// <remarks>This extension can be safely called multiple times.</remarks>
/// <returns>The <see cref="OpenIddictNHibernateBuilder"/>.</returns>
public static OpenIddictNHibernateBuilder UseNHibernate([NotNull] this OpenIddictCoreBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
// Since NHibernate may be used with databases performing case-insensitive or
// culture-sensitive comparisons, ensure the additional filtering logic is enforced
// in case case-sensitive stores were registered before this extension was called.
builder.Configure(options => options.DisableAdditionalFiltering = false);
builder.SetDefaultApplicationEntity<OpenIddictApplication>()
.SetDefaultAuthorizationEntity<OpenIddictAuthorization>()
.SetDefaultScopeEntity<OpenIddictScope>()
.SetDefaultTokenEntity<OpenIddictToken>();
builder.ReplaceApplicationStoreResolver<OpenIddictApplicationStoreResolver>()
.ReplaceAuthorizationStoreResolver<OpenIddictAuthorizationStoreResolver>()
.ReplaceScopeStoreResolver<OpenIddictScopeStoreResolver>()
.ReplaceTokenStoreResolver<OpenIddictTokenStoreResolver>();
builder.Services.TryAddSingleton<OpenIddictApplicationStoreResolver.TypeResolutionCache>();
builder.Services.TryAddSingleton<OpenIddictAuthorizationStoreResolver.TypeResolutionCache>();
builder.Services.TryAddSingleton<OpenIddictScopeStoreResolver.TypeResolutionCache>();
builder.Services.TryAddSingleton<OpenIddictTokenStoreResolver.TypeResolutionCache>();
builder.Services.TryAddScoped(typeof(OpenIddictApplicationStore<,,,>));
builder.Services.TryAddScoped(typeof(OpenIddictAuthorizationStore<,,,>));
builder.Services.TryAddScoped(typeof(OpenIddictScopeStore<,>));
builder.Services.TryAddScoped(typeof(OpenIddictTokenStore<,,,>));
builder.Services.TryAddScoped<IOpenIddictNHibernateContext, OpenIddictNHibernateContext>();
return new OpenIddictNHibernateBuilder(builder.Services);
}
/// <summary>
/// Registers the NHibernate stores services in the DI container and
/// configures OpenIddict to use the NHibernate entities by default.
/// </summary>
/// <param name="builder">The services builder used by OpenIddict to register new services.</param>
/// <param name="configuration">The configuration delegate used to configure the NHibernate services.</param>
/// <remarks>This extension can be safely called multiple times.</remarks>
/// <returns>The <see cref="OpenIddictCoreBuilder"/>.</returns>
public static OpenIddictCoreBuilder UseNHibernate(
[NotNull] this OpenIddictCoreBuilder builder,
[NotNull] Action<OpenIddictNHibernateBuilder> configuration)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (configuration == null)
{
throw new ArgumentNullException(nameof(configuration));
}
configuration(builder.UseNHibernate());
return builder;
}
}
}

103
src/OpenIddict.NHibernate/OpenIddictNHibernateHelpers.cs

@ -1,103 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using JetBrains.Annotations;
using NHibernate.Linq;
using NHibernate.Mapping.ByCode;
using OpenIddict.NHibernate;
using OpenIddict.NHibernate.Models;
namespace NHibernate.Cfg
{
/// <summary>
/// Exposes extensions simplifying the integration between OpenIddict and NHibernate.
/// </summary>
public static class OpenIddictNHibernateHelpers
{
/// <summary>
/// Registers the OpenIddict entity mappings in the NHibernate
/// configuration using the default entities and the default key type.
/// </summary>
/// <param name="configuration">The NHibernate configuration builder.</param>
/// <returns>The <see cref="Configuration"/>.</returns>
public static Configuration UseOpenIddict([NotNull] this Configuration configuration)
=> configuration.UseOpenIddict<OpenIddictApplication,
OpenIddictAuthorization,
OpenIddictScope,
OpenIddictToken, string>();
/// <summary>
/// Registers the OpenIddict entity mappings in the NHibernate
/// configuration using the default entities and the specified key type.
/// </summary>
/// <param name="configuration">The NHibernate configuration builder.</param>
/// <returns>The <see cref="Configuration"/>.</returns>
public static Configuration UseOpenIddict<TKey>([NotNull] this Configuration configuration)
where TKey : IEquatable<TKey>
=> configuration.UseOpenIddict<OpenIddictApplication<TKey>,
OpenIddictAuthorization<TKey>,
OpenIddictScope<TKey>,
OpenIddictToken<TKey>, TKey>();
/// <summary>
/// Registers the OpenIddict entity mappings in the NHibernate
/// configuration using the specified entities and the specified key type.
/// </summary>
/// <param name="configuration">The NHibernate configuration builder.</param>
/// <returns>The <see cref="Configuration"/>.</returns>
public static Configuration UseOpenIddict<TApplication, TAuthorization, TScope, TToken, TKey>([NotNull] this Configuration configuration)
where TApplication : OpenIddictApplication<TKey, TAuthorization, TToken>
where TAuthorization : OpenIddictAuthorization<TKey, TApplication, TToken>
where TScope : OpenIddictScope<TKey>
where TToken : OpenIddictToken<TKey, TApplication, TAuthorization>
where TKey : IEquatable<TKey>
{
if (configuration == null)
{
throw new ArgumentNullException(nameof(configuration));
}
var mapper = new ModelMapper();
mapper.AddMapping<OpenIddictApplicationMapping<TApplication, TAuthorization, TToken, TKey>>();
mapper.AddMapping<OpenIddictAuthorizationMapping<TAuthorization, TApplication, TToken, TKey>>();
mapper.AddMapping<OpenIddictScopeMapping<TScope, TKey>>();
mapper.AddMapping<OpenIddictTokenMapping<TToken, TApplication, TAuthorization, TKey>>();
configuration.AddMapping(mapper.CompileMappingForAllExplicitlyAddedEntities());
return configuration;
}
/// <summary>
/// Executes the query and returns the results as a non-streamed async enumeration.
/// </summary>
/// <typeparam name="T">The type of the returned entities.</typeparam>
/// <param name="source">The query source.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The non-streamed async enumeration containing the results.</returns>
internal static IAsyncEnumerable<T> AsAsyncEnumerable<T>([NotNull] this IQueryable<T> source, CancellationToken cancellationToken)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
return ExecuteAsync(cancellationToken);
async IAsyncEnumerable<T> ExecuteAsync(CancellationToken cancellationToken)
{
foreach (var element in await source.ToListAsync(cancellationToken))
{
yield return element;
}
}
}
}
}

22
src/OpenIddict.NHibernate/OpenIddictNHibernateOptions.cs

@ -1,22 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using NHibernate;
namespace OpenIddict.NHibernate
{
/// <summary>
/// Provides various settings needed to configure the OpenIddict NHibernate integration.
/// </summary>
public class OpenIddictNHibernateOptions
{
/// <summary>
/// Gets or sets the session factory used by the OpenIddict NHibernate stores.
/// If none is explicitly set, the session factory is resolved from the DI container.
/// </summary>
public ISessionFactory SessionFactory { get; set; }
}
}

77
src/OpenIddict.NHibernate/Resolvers/OpenIddictApplicationStoreResolver.cs

@ -1,77 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.Collections.Concurrent;
using System.Text;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
using OpenIddict.Abstractions;
using OpenIddict.Extensions;
using OpenIddict.NHibernate.Models;
namespace OpenIddict.NHibernate
{
/// <summary>
/// Exposes a method allowing to resolve an application store.
/// </summary>
public class OpenIddictApplicationStoreResolver : IOpenIddictApplicationStoreResolver
{
private readonly TypeResolutionCache _cache;
private readonly IServiceProvider _provider;
public OpenIddictApplicationStoreResolver(
[NotNull] TypeResolutionCache cache,
[NotNull] IServiceProvider provider)
{
_cache = cache;
_provider = provider;
}
/// <summary>
/// Returns an application store compatible with the specified application type or throws an
/// <see cref="InvalidOperationException"/> if no store can be built using the specified type.
/// </summary>
/// <typeparam name="TApplication">The type of the Application entity.</typeparam>
/// <returns>An <see cref="IOpenIddictApplicationStore{TApplication}"/>.</returns>
public IOpenIddictApplicationStore<TApplication> Get<TApplication>() where TApplication : class
{
var store = _provider.GetService<IOpenIddictApplicationStore<TApplication>>();
if (store != null)
{
return store;
}
var type = _cache.GetOrAdd(typeof(TApplication), key =>
{
var root = OpenIddictHelpers.FindGenericBaseType(key, typeof(OpenIddictApplication<,,>));
if (root == null)
{
throw new InvalidOperationException(new StringBuilder()
.AppendLine("The specified application type is not compatible with the NHibernate stores.")
.Append("When enabling the NHibernate stores, make sure you use the built-in ")
.Append("'OpenIddictApplication' entity (from the 'OpenIddict.NHibernate.Models' package) ")
.Append("or a custom entity that inherits from the generic 'OpenIddictApplication' entity.")
.ToString());
}
return typeof(OpenIddictApplicationStore<,,,>).MakeGenericType(
/* TApplication: */ key,
/* TAuthorization: */ root.GenericTypeArguments[1],
/* TToken: */ root.GenericTypeArguments[2],
/* TKey: */ root.GenericTypeArguments[0]);
});
return (IOpenIddictApplicationStore<TApplication>) _provider.GetRequiredService(type);
}
// Note: NHibernate resolvers are registered as scoped dependencies as their inner
// service provider must be able to resolve scoped services (typically, the store they return).
// To avoid having to declare a static type resolution cache, a special cache service is used
// here and registered as a singleton dependency so that its content persists beyond the scope.
public class TypeResolutionCache : ConcurrentDictionary<Type, Type> { }
}
}

77
src/OpenIddict.NHibernate/Resolvers/OpenIddictAuthorizationStoreResolver.cs

@ -1,77 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.Collections.Concurrent;
using System.Text;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
using OpenIddict.Abstractions;
using OpenIddict.Extensions;
using OpenIddict.NHibernate.Models;
namespace OpenIddict.NHibernate
{
/// <summary>
/// Exposes a method allowing to resolve an authorization store.
/// </summary>
public class OpenIddictAuthorizationStoreResolver : IOpenIddictAuthorizationStoreResolver
{
private readonly TypeResolutionCache _cache;
private readonly IServiceProvider _provider;
public OpenIddictAuthorizationStoreResolver(
[NotNull] TypeResolutionCache cache,
[NotNull] IServiceProvider provider)
{
_cache = cache;
_provider = provider;
}
/// <summary>
/// Returns an authorization store compatible with the specified authorization type or throws an
/// <see cref="InvalidOperationException"/> if no store can be built using the specified type.
/// </summary>
/// <typeparam name="TAuthorization">The type of the Authorization entity.</typeparam>
/// <returns>An <see cref="IOpenIddictAuthorizationStore{TAuthorization}"/>.</returns>
public IOpenIddictAuthorizationStore<TAuthorization> Get<TAuthorization>() where TAuthorization : class
{
var store = _provider.GetService<IOpenIddictAuthorizationStore<TAuthorization>>();
if (store != null)
{
return store;
}
var type = _cache.GetOrAdd(typeof(TAuthorization), key =>
{
var root = OpenIddictHelpers.FindGenericBaseType(key, typeof(OpenIddictAuthorization<,,>));
if (root == null)
{
throw new InvalidOperationException(new StringBuilder()
.AppendLine("The specified authorization type is not compatible with the NHibernate stores.")
.Append("When enabling the NHibernate stores, make sure you use the built-in ")
.Append("'OpenIddictAuthorization' entity (from the 'OpenIddict.NHibernate.Models' package) ")
.Append("or a custom entity that inherits from the generic 'OpenIddictAuthorization' entity.")
.ToString());
}
return typeof(OpenIddictAuthorizationStore<,,,>).MakeGenericType(
/* TAuthorization: */ key,
/* TApplication: */ root.GenericTypeArguments[1],
/* TToken: */ root.GenericTypeArguments[2],
/* TKey: */ root.GenericTypeArguments[0]);
});
return (IOpenIddictAuthorizationStore<TAuthorization>) _provider.GetRequiredService(type);
}
// Note: NHibernate resolvers are registered as scoped dependencies as their inner
// service provider must be able to resolve scoped services (typically, the store they return).
// To avoid having to declare a static type resolution cache, a special cache service is used
// here and registered as a singleton dependency so that its content persists beyond the scope.
public class TypeResolutionCache : ConcurrentDictionary<Type, Type> { }
}
}

75
src/OpenIddict.NHibernate/Resolvers/OpenIddictScopeStoreResolver.cs

@ -1,75 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.Collections.Concurrent;
using System.Text;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
using OpenIddict.Abstractions;
using OpenIddict.Extensions;
using OpenIddict.NHibernate.Models;
namespace OpenIddict.NHibernate
{
/// <summary>
/// Exposes a method allowing to resolve a scope store.
/// </summary>
public class OpenIddictScopeStoreResolver : IOpenIddictScopeStoreResolver
{
private readonly TypeResolutionCache _cache;
private readonly IServiceProvider _provider;
public OpenIddictScopeStoreResolver(
[NotNull] TypeResolutionCache cache,
[NotNull] IServiceProvider provider)
{
_cache = cache;
_provider = provider;
}
/// <summary>
/// Returns a scope store compatible with the specified scope type or throws an
/// <see cref="InvalidOperationException"/> if no store can be built using the specified type.
/// </summary>
/// <typeparam name="TScope">The type of the Scope entity.</typeparam>
/// <returns>An <see cref="IOpenIddictScopeStore{TScope}"/>.</returns>
public IOpenIddictScopeStore<TScope> Get<TScope>() where TScope : class
{
var store = _provider.GetService<IOpenIddictScopeStore<TScope>>();
if (store != null)
{
return store;
}
var type = _cache.GetOrAdd(typeof(TScope), key =>
{
var root = OpenIddictHelpers.FindGenericBaseType(key, typeof(OpenIddictScope<>));
if (root == null)
{
throw new InvalidOperationException(new StringBuilder()
.AppendLine("The specified scope type is not compatible with the NHibernate stores.")
.Append("When enabling the NHibernate stores, make sure you use the built-in ")
.Append("'OpenIddictScope' entity (from the 'OpenIddict.NHibernate.Models' package) ")
.Append("or a custom entity that inherits from the generic 'OpenIddictScope' entity.")
.ToString());
}
return typeof(OpenIddictScopeStore<,>).MakeGenericType(
/* TScope: */ key,
/* TKey: */ root.GenericTypeArguments[0]);
});
return (IOpenIddictScopeStore<TScope>) _provider.GetRequiredService(type);
}
// Note: NHibernate resolvers are registered as scoped dependencies as their inner
// service provider must be able to resolve scoped services (typically, the store they return).
// To avoid having to declare a static type resolution cache, a special cache service is used
// here and registered as a singleton dependency so that its content persists beyond the scope.
public class TypeResolutionCache : ConcurrentDictionary<Type, Type> { }
}
}

77
src/OpenIddict.NHibernate/Resolvers/OpenIddictTokenStoreResolver.cs

@ -1,77 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.Collections.Concurrent;
using System.Text;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
using OpenIddict.Abstractions;
using OpenIddict.Extensions;
using OpenIddict.NHibernate.Models;
namespace OpenIddict.NHibernate
{
/// <summary>
/// Exposes a method allowing to resolve a token store.
/// </summary>
public class OpenIddictTokenStoreResolver : IOpenIddictTokenStoreResolver
{
private readonly TypeResolutionCache _cache;
private readonly IServiceProvider _provider;
public OpenIddictTokenStoreResolver(
[NotNull] TypeResolutionCache cache,
[NotNull] IServiceProvider provider)
{
_cache = cache;
_provider = provider;
}
/// <summary>
/// Returns a token store compatible with the specified token type or throws an
/// <see cref="InvalidOperationException"/> if no store can be built using the specified type.
/// </summary>
/// <typeparam name="TToken">The type of the Token entity.</typeparam>
/// <returns>An <see cref="IOpenIddictTokenStore{TToken}"/>.</returns>
public IOpenIddictTokenStore<TToken> Get<TToken>() where TToken : class
{
var store = _provider.GetService<IOpenIddictTokenStore<TToken>>();
if (store != null)
{
return store;
}
var type = _cache.GetOrAdd(typeof(TToken), key =>
{
var root = OpenIddictHelpers.FindGenericBaseType(key, typeof(OpenIddictToken<,,>));
if (root == null)
{
throw new InvalidOperationException(new StringBuilder()
.AppendLine("The specified token type is not compatible with the NHibernate stores.")
.Append("When enabling the NHibernate stores, make sure you use the built-in ")
.Append("'OpenIddictToken' entity (from the 'OpenIddict.NHibernate.Models' package) ")
.Append("or a custom entity that inherits from the generic 'OpenIddictToken' entity.")
.ToString());
}
return typeof(OpenIddictTokenStore<,,,>).MakeGenericType(
/* TToken: */ key,
/* TApplication: */ root.GenericTypeArguments[1],
/* TAuthorization: */ root.GenericTypeArguments[2],
/* TKey: */ root.GenericTypeArguments[0]);
});
return (IOpenIddictTokenStore<TToken>) _provider.GetRequiredService(type);
}
// Note: NHibernate resolvers are registered as scoped dependencies as their inner
// service provider must be able to resolve scoped services (typically, the store they return).
// To avoid having to declare a static type resolution cache, a special cache service is used
// here and registered as a singleton dependency so that its content persists beyond the scope.
public class TypeResolutionCache : ConcurrentDictionary<Type, Type> { }
}
}

1026
src/OpenIddict.NHibernate/Stores/OpenIddictApplicationStore.cs

File diff suppressed because it is too large

950
src/OpenIddict.NHibernate/Stores/OpenIddictAuthorizationStore.cs

@ -1,950 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Linq;
using OpenIddict.Abstractions;
using OpenIddict.NHibernate.Models;
namespace OpenIddict.NHibernate
{
/// <summary>
/// Provides methods allowing to manage the authorizations stored in a database.
/// </summary>
public class OpenIddictAuthorizationStore : OpenIddictAuthorizationStore<OpenIddictAuthorization,
OpenIddictApplication,
OpenIddictToken, string>
{
public OpenIddictAuthorizationStore(
[NotNull] IMemoryCache cache,
[NotNull] IOpenIddictNHibernateContext context,
[NotNull] IOptionsMonitor<OpenIddictNHibernateOptions> options)
: base(cache, context, options)
{
}
}
/// <summary>
/// Provides methods allowing to manage the authorizations stored in a database.
/// </summary>
/// <typeparam name="TKey">The type of the entity primary keys.</typeparam>
public class OpenIddictAuthorizationStore<TKey> : OpenIddictAuthorizationStore<OpenIddictAuthorization<TKey>,
OpenIddictApplication<TKey>,
OpenIddictToken<TKey>, TKey>
where TKey : IEquatable<TKey>
{
public OpenIddictAuthorizationStore(
[NotNull] IMemoryCache cache,
[NotNull] IOpenIddictNHibernateContext context,
[NotNull] IOptionsMonitor<OpenIddictNHibernateOptions> options)
: base(cache, context, options)
{
}
}
/// <summary>
/// Provides methods allowing to manage the authorizations stored in a database.
/// </summary>
/// <typeparam name="TAuthorization">The type of the Authorization entity.</typeparam>
/// <typeparam name="TApplication">The type of the Application entity.</typeparam>
/// <typeparam name="TToken">The type of the Token entity.</typeparam>
/// <typeparam name="TKey">The type of the entity primary keys.</typeparam>
public class OpenIddictAuthorizationStore<TAuthorization, TApplication, TToken, TKey> : IOpenIddictAuthorizationStore<TAuthorization>
where TAuthorization : OpenIddictAuthorization<TKey, TApplication, TToken>
where TApplication : OpenIddictApplication<TKey, TAuthorization, TToken>
where TToken : OpenIddictToken<TKey, TApplication, TAuthorization>
where TKey : IEquatable<TKey>
{
public OpenIddictAuthorizationStore(
[NotNull] IMemoryCache cache,
[NotNull] IOpenIddictNHibernateContext context,
[NotNull] IOptionsMonitor<OpenIddictNHibernateOptions> options)
{
Cache = cache;
Context = context;
Options = options;
}
/// <summary>
/// Gets the memory cache associated with the current store.
/// </summary>
protected IMemoryCache Cache { get; }
/// <summary>
/// Gets the database context associated with the current store.
/// </summary>
protected IOpenIddictNHibernateContext Context { get; }
/// <summary>
/// Gets the options associated with the current store.
/// </summary>
protected IOptionsMonitor<OpenIddictNHibernateOptions> Options { get; }
/// <summary>
/// Determines the number of authorizations that exist in the database.
/// </summary>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the number of authorizations in the database.
/// </returns>
public virtual async ValueTask<long> CountAsync(CancellationToken cancellationToken)
{
var session = await Context.GetSessionAsync(cancellationToken);
return await session.Query<TAuthorization>().LongCountAsync(cancellationToken);
}
/// <summary>
/// Determines the number of authorizations that match the specified query.
/// </summary>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the number of authorizations that match the specified query.
/// </returns>
public virtual async ValueTask<long> CountAsync<TResult>([NotNull] Func<IQueryable<TAuthorization>, IQueryable<TResult>> query, CancellationToken cancellationToken)
{
if (query == null)
{
throw new ArgumentNullException(nameof(query));
}
var session = await Context.GetSessionAsync(cancellationToken);
return await query(session.Query<TAuthorization>()).LongCountAsync(cancellationToken);
}
/// <summary>
/// Creates a new authorization.
/// </summary>
/// <param name="authorization">The authorization to create.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
public virtual async ValueTask CreateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken)
{
if (authorization == null)
{
throw new ArgumentNullException(nameof(authorization));
}
var session = await Context.GetSessionAsync(cancellationToken);
await session.SaveAsync(authorization, cancellationToken);
await session.FlushAsync(cancellationToken);
}
/// <summary>
/// Removes an existing authorization.
/// </summary>
/// <param name="authorization">The authorization to delete.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
public virtual async ValueTask DeleteAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken)
{
if (authorization == null)
{
throw new ArgumentNullException(nameof(authorization));
}
var session = await Context.GetSessionAsync(cancellationToken);
try
{
// Delete all the tokens associated with the authorization.
await (from token in session.Query<TToken>()
where token.Authorization.Id.Equals(authorization.Id)
select token).DeleteAsync(cancellationToken);
await session.DeleteAsync(authorization, cancellationToken);
await session.FlushAsync(cancellationToken);
}
catch (StaleObjectStateException exception)
{
throw new OpenIddictExceptions.ConcurrencyException(new StringBuilder()
.AppendLine("The authorization was concurrently updated and cannot be persisted in its current state.")
.Append("Reload the authorization from the database and retry the operation.")
.ToString(), exception);
}
}
/// <summary>
/// Retrieves the authorizations corresponding to the specified
/// subject and associated with the application identifier.
/// </summary>
/// <param name="subject">The subject associated with the authorization.</param>
/// <param name="client">The client associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The authorizations corresponding to the subject/client.</returns>
public virtual IAsyncEnumerable<TAuthorization> FindAsync(
[NotNull] string subject, [NotNull] string client, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(subject))
{
throw new ArgumentException("The subject cannot be null or empty.", nameof(subject));
}
if (string.IsNullOrEmpty(client))
{
throw new ArgumentException("The client cannot be null or empty.", nameof(client));
}
return ExecuteAsync(cancellationToken);
async IAsyncEnumerable<TAuthorization> ExecuteAsync(CancellationToken cancellationToken)
{
var session = await Context.GetSessionAsync(cancellationToken);
var key = ConvertIdentifierFromString(client);
await foreach (var authorization in
(from authorization in session.Query<TAuthorization>().Fetch(authorization => authorization.Application)
where authorization.Application != null &&
authorization.Application.Id.Equals(key) &&
authorization.Subject == subject
select authorization).AsAsyncEnumerable(cancellationToken))
{
yield return authorization;
}
}
}
/// <summary>
/// Retrieves the authorizations matching the specified parameters.
/// </summary>
/// <param name="subject">The subject associated with the authorization.</param>
/// <param name="client">The client associated with the authorization.</param>
/// <param name="status">The authorization status.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The authorizations corresponding to the criteria.</returns>
public virtual IAsyncEnumerable<TAuthorization> FindAsync(
[NotNull] string subject, [NotNull] string client,
[NotNull] string status, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(subject))
{
throw new ArgumentException("The subject cannot be null or empty.", nameof(subject));
}
if (string.IsNullOrEmpty(client))
{
throw new ArgumentException("The client cannot be null or empty.", nameof(client));
}
if (string.IsNullOrEmpty(status))
{
throw new ArgumentException("The status cannot be null or empty.", nameof(status));
}
return ExecuteAsync(cancellationToken);
async IAsyncEnumerable<TAuthorization> ExecuteAsync(CancellationToken cancellationToken)
{
var session = await Context.GetSessionAsync(cancellationToken);
var key = ConvertIdentifierFromString(client);
await foreach (var authorization in
(from authorization in session.Query<TAuthorization>().Fetch(authorization => authorization.Application)
where authorization.Application != null &&
authorization.Application.Id.Equals(key) &&
authorization.Subject == subject &&
authorization.Status == status
select authorization).AsAsyncEnumerable(cancellationToken))
{
yield return authorization;
}
}
}
/// <summary>
/// Retrieves the authorizations matching the specified parameters.
/// </summary>
/// <param name="subject">The subject associated with the authorization.</param>
/// <param name="client">The client associated with the authorization.</param>
/// <param name="status">The authorization status.</param>
/// <param name="type">The authorization type.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The authorizations corresponding to the criteria.</returns>
public virtual IAsyncEnumerable<TAuthorization> FindAsync(
[NotNull] string subject, [NotNull] string client,
[NotNull] string status, [NotNull] string type, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(subject))
{
throw new ArgumentException("The subject cannot be null or empty.", nameof(subject));
}
if (string.IsNullOrEmpty(client))
{
throw new ArgumentException("The client identifier cannot be null or empty.", nameof(client));
}
if (string.IsNullOrEmpty(status))
{
throw new ArgumentException("The status cannot be null or empty.", nameof(status));
}
if (string.IsNullOrEmpty(type))
{
throw new ArgumentException("The type cannot be null or empty.", nameof(type));
}
return ExecuteAsync(cancellationToken);
async IAsyncEnumerable<TAuthorization> ExecuteAsync(CancellationToken cancellationToken)
{
var session = await Context.GetSessionAsync(cancellationToken);
var key = ConvertIdentifierFromString(client);
await foreach (var authorization in
(from authorization in session.Query<TAuthorization>().Fetch(authorization => authorization.Application)
where authorization.Application != null &&
authorization.Application.Id.Equals(key) &&
authorization.Subject == subject &&
authorization.Status == status &&
authorization.Type == type
select authorization).AsAsyncEnumerable(cancellationToken))
{
yield return authorization;
}
}
}
/// <summary>
/// Retrieves the authorizations matching the specified parameters.
/// </summary>
/// <param name="subject">The subject associated with the authorization.</param>
/// <param name="client">The client associated with the authorization.</param>
/// <param name="status">The authorization status.</param>
/// <param name="type">The authorization type.</param>
/// <param name="scopes">The minimal scopes associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The authorizations corresponding to the criteria.</returns>
public virtual IAsyncEnumerable<TAuthorization> FindAsync(
[NotNull] string subject, [NotNull] string client,
[NotNull] string status, [NotNull] string type,
ImmutableArray<string> scopes, CancellationToken cancellationToken)
=> FindAsync(subject, client, status, type, cancellationToken)
.WhereAwait(async authorization => new HashSet<string>(
await GetScopesAsync(authorization, cancellationToken), StringComparer.Ordinal).IsSupersetOf(scopes));
/// <summary>
/// Retrieves the list of authorizations corresponding to the specified application identifier.
/// </summary>
/// <param name="identifier">The application identifier associated with the authorizations.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The authorizations corresponding to the specified application.</returns>
public virtual IAsyncEnumerable<TAuthorization> FindByApplicationIdAsync(
[NotNull] string identifier, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(identifier))
{
throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier));
}
return ExecuteAsync(cancellationToken);
async IAsyncEnumerable<TAuthorization> ExecuteAsync(CancellationToken cancellationToken)
{
var session = await Context.GetSessionAsync(cancellationToken);
var key = ConvertIdentifierFromString(identifier);
await foreach (var authorization in
(from authorization in session.Query<TAuthorization>().Fetch(authorization => authorization.Application)
where authorization.Application != null &&
authorization.Application.Id.Equals(key)
select authorization).AsAsyncEnumerable(cancellationToken))
{
yield return authorization;
}
}
}
/// <summary>
/// Retrieves an authorization using its unique identifier.
/// </summary>
/// <param name="identifier">The unique identifier associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the authorization corresponding to the identifier.
/// </returns>
public virtual async ValueTask<TAuthorization> FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(identifier))
{
throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier));
}
var session = await Context.GetSessionAsync(cancellationToken);
return await session.GetAsync<TAuthorization>(ConvertIdentifierFromString(identifier), cancellationToken);
}
/// <summary>
/// Retrieves all the authorizations corresponding to the specified subject.
/// </summary>
/// <param name="subject">The subject associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The authorizations corresponding to the specified subject.</returns>
public virtual IAsyncEnumerable<TAuthorization> FindBySubjectAsync(
[NotNull] string subject, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(subject))
{
throw new ArgumentException("The subject cannot be null or empty.", nameof(subject));
}
return ExecuteAsync(cancellationToken);
async IAsyncEnumerable<TAuthorization> ExecuteAsync(CancellationToken cancellationToken)
{
var session = await Context.GetSessionAsync(cancellationToken);
await foreach (var authorization in
(from authorization in session.Query<TAuthorization>().Fetch(authorization => authorization.Application)
where authorization.Subject == subject
select authorization).AsAsyncEnumerable(cancellationToken))
{
yield return authorization;
}
}
}
/// <summary>
/// Retrieves the optional application identifier associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the application identifier associated with the authorization.
/// </returns>
public virtual ValueTask<string> GetApplicationIdAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken)
{
if (authorization == null)
{
throw new ArgumentNullException(nameof(authorization));
}
if (authorization.Application == null)
{
return new ValueTask<string>(result: null);
}
return new ValueTask<string>(ConvertIdentifierToString(authorization.Application.Id));
}
/// <summary>
/// Executes the specified query and returns the first element.
/// </summary>
/// <typeparam name="TState">The state type.</typeparam>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="state">The optional state.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the first element returned when executing the query.
/// </returns>
public virtual async ValueTask<TResult> GetAsync<TState, TResult>(
[NotNull] Func<IQueryable<TAuthorization>, TState, IQueryable<TResult>> query,
[CanBeNull] TState state, CancellationToken cancellationToken)
{
if (query == null)
{
throw new ArgumentNullException(nameof(query));
}
var session = await Context.GetSessionAsync(cancellationToken);
return await query(session.Query<TAuthorization>()
.Fetch(authorization => authorization.Application), state).FirstOrDefaultAsync(cancellationToken);
}
/// <summary>
/// Retrieves the unique identifier associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the unique identifier associated with the authorization.
/// </returns>
public virtual ValueTask<string> GetIdAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken)
{
if (authorization == null)
{
throw new ArgumentNullException(nameof(authorization));
}
return new ValueTask<string>(ConvertIdentifierToString(authorization.Id));
}
/// <summary>
/// Retrieves the additional properties associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the additional properties associated with the authorization.
/// </returns>
public virtual ValueTask<ImmutableDictionary<string, JsonElement>> GetPropertiesAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken)
{
if (authorization == null)
{
throw new ArgumentNullException(nameof(authorization));
}
if (string.IsNullOrEmpty(authorization.Properties))
{
return new ValueTask<ImmutableDictionary<string, JsonElement>>(ImmutableDictionary.Create<string, JsonElement>());
}
// Note: parsing the stringified properties is an expensive operation.
// To mitigate that, the resulting object is stored in the memory cache.
var key = string.Concat("68056e1a-dbcf-412b-9a6a-d791c7dbe726", "\x1e", authorization.Properties);
var properties = Cache.GetOrCreate(key, entry =>
{
entry.SetPriority(CacheItemPriority.High)
.SetSlidingExpiration(TimeSpan.FromMinutes(1));
return JsonSerializer.Deserialize<ImmutableDictionary<string, JsonElement>>(authorization.Properties);
});
return new ValueTask<ImmutableDictionary<string, JsonElement>>(properties);
}
/// <summary>
/// Retrieves the scopes associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the scopes associated with the specified authorization.
/// </returns>
public virtual ValueTask<ImmutableArray<string>> GetScopesAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken)
{
if (authorization == null)
{
throw new ArgumentNullException(nameof(authorization));
}
if (string.IsNullOrEmpty(authorization.Scopes))
{
return new ValueTask<ImmutableArray<string>>(ImmutableArray.Create<string>());
}
// Note: parsing the stringified scopes is an expensive operation.
// To mitigate that, the resulting array is stored in the memory cache.
var key = string.Concat("2ba4ab0f-e2ec-4d48-b3bd-28e2bb660c75", "\x1e", authorization.Scopes);
var scopes = Cache.GetOrCreate(key, entry =>
{
entry.SetPriority(CacheItemPriority.High)
.SetSlidingExpiration(TimeSpan.FromMinutes(1));
return JsonSerializer.Deserialize<ImmutableArray<string>>(authorization.Scopes);
});
return new ValueTask<ImmutableArray<string>>(scopes);
}
/// <summary>
/// Retrieves the status associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the status associated with the specified authorization.
/// </returns>
public virtual ValueTask<string> GetStatusAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken)
{
if (authorization == null)
{
throw new ArgumentNullException(nameof(authorization));
}
return new ValueTask<string>(authorization.Status);
}
/// <summary>
/// Retrieves the subject associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the subject associated with the specified authorization.
/// </returns>
public virtual ValueTask<string> GetSubjectAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken)
{
if (authorization == null)
{
throw new ArgumentNullException(nameof(authorization));
}
return new ValueTask<string>(authorization.Subject);
}
/// <summary>
/// Retrieves the type associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the type associated with the specified authorization.
/// </returns>
public virtual ValueTask<string> GetTypeAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken)
{
if (authorization == null)
{
throw new ArgumentNullException(nameof(authorization));
}
return new ValueTask<string>(authorization.Type);
}
/// <summary>
/// Instantiates a new authorization.
/// </summary>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the instantiated authorization, that can be persisted in the database.
/// </returns>
public virtual ValueTask<TAuthorization> InstantiateAsync(CancellationToken cancellationToken)
{
try
{
return new ValueTask<TAuthorization>(Activator.CreateInstance<TAuthorization>());
}
catch (MemberAccessException exception)
{
return new ValueTask<TAuthorization>(Task.FromException<TAuthorization>(
new InvalidOperationException(new StringBuilder()
.AppendLine("An error occurred while trying to create a new authorization instance.")
.Append("Make sure that the authorization entity is not abstract and has a public parameterless constructor ")
.Append("or create a custom authorization store that overrides 'InstantiateAsync()' to use a custom factory.")
.ToString(), exception)));
}
}
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
/// <param name="count">The number of results to return.</param>
/// <param name="offset">The number of results to skip.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the elements returned when executing the specified query.</returns>
public virtual async IAsyncEnumerable<TAuthorization> ListAsync(
[CanBeNull] int? count, [CanBeNull] int? offset, [EnumeratorCancellation] CancellationToken cancellationToken)
{
var session = await Context.GetSessionAsync(cancellationToken);
var query = session.Query<TAuthorization>()
.Fetch(authorization => authorization.Application)
.OrderBy(authorization => authorization.Id)
.AsQueryable();
if (offset.HasValue)
{
query = query.Skip(offset.Value);
}
if (count.HasValue)
{
query = query.Take(count.Value);
}
await foreach (var authorization in query.AsAsyncEnumerable(cancellationToken))
{
yield return authorization;
}
}
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
/// <typeparam name="TState">The state type.</typeparam>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="state">The optional state.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the elements returned when executing the specified query.</returns>
public virtual IAsyncEnumerable<TResult> ListAsync<TState, TResult>(
[NotNull] Func<IQueryable<TAuthorization>, TState, IQueryable<TResult>> query,
[CanBeNull] TState state, CancellationToken cancellationToken)
{
if (query == null)
{
throw new ArgumentNullException(nameof(query));
}
return ExecuteAsync(cancellationToken);
async IAsyncEnumerable<TResult> ExecuteAsync(CancellationToken cancellationToken)
{
var session = await Context.GetSessionAsync(cancellationToken);
await foreach (var element in query(
session.Query<TAuthorization>()
.Fetch(authorization => authorization.Application), state).AsAsyncEnumerable(cancellationToken))
{
yield return element;
}
}
}
/// <summary>
/// Removes the authorizations that are marked as invalid and the ad-hoc ones that have no valid/nonexpired token attached.
/// </summary>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
public virtual async ValueTask PruneAsync(CancellationToken cancellationToken)
{
var session = await Context.GetSessionAsync(cancellationToken);
await (from token in session.Query<TToken>()
where token.Status != OpenIddictConstants.Statuses.Valid ||
token.ExpirationDate < DateTimeOffset.UtcNow
select token).DeleteAsync(cancellationToken);
await (from authorization in session.Query<TAuthorization>()
where authorization.Status != OpenIddictConstants.Statuses.Valid ||
(authorization.Type == OpenIddictConstants.AuthorizationTypes.AdHoc && !authorization.Tokens.Any())
select authorization).DeleteAsync(cancellationToken);
await session.FlushAsync(cancellationToken);
}
/// <summary>
/// Sets the application identifier associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="identifier">The unique identifier associated with the client application.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
public virtual async ValueTask SetApplicationIdAsync([NotNull] TAuthorization authorization,
[CanBeNull] string identifier, CancellationToken cancellationToken)
{
if (authorization == null)
{
throw new ArgumentNullException(nameof(authorization));
}
var session = await Context.GetSessionAsync(cancellationToken);
if (!string.IsNullOrEmpty(identifier))
{
authorization.Application = await session.LoadAsync<TApplication>(ConvertIdentifierFromString(identifier), cancellationToken);
}
else
{
authorization.Application = null;
}
}
/// <summary>
/// Sets the additional properties associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="properties">The additional properties associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
public virtual ValueTask SetPropertiesAsync([NotNull] TAuthorization authorization,
[CanBeNull] ImmutableDictionary<string, JsonElement> properties, CancellationToken cancellationToken)
{
if (authorization == null)
{
throw new ArgumentNullException(nameof(authorization));
}
if (properties == null || properties.IsEmpty)
{
authorization.Properties = null;
return default;
}
authorization.Properties = JsonSerializer.Serialize(properties, new JsonSerializerOptions
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
WriteIndented = false
});
return default;
}
/// <summary>
/// Sets the scopes associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="scopes">The scopes associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
public virtual ValueTask SetScopesAsync([NotNull] TAuthorization authorization,
ImmutableArray<string> scopes, CancellationToken cancellationToken)
{
if (authorization == null)
{
throw new ArgumentNullException(nameof(authorization));
}
if (scopes.IsDefaultOrEmpty)
{
authorization.Scopes = null;
return default;
}
authorization.Scopes = JsonSerializer.Serialize(scopes, new JsonSerializerOptions
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
WriteIndented = false
});
return default;
}
/// <summary>
/// Sets the status associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="status">The status associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
public virtual ValueTask SetStatusAsync([NotNull] TAuthorization authorization,
[CanBeNull] string status, CancellationToken cancellationToken)
{
if (authorization == null)
{
throw new ArgumentNullException(nameof(authorization));
}
authorization.Status = status;
return default;
}
/// <summary>
/// Sets the subject associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="subject">The subject associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
public virtual ValueTask SetSubjectAsync([NotNull] TAuthorization authorization,
[CanBeNull] string subject, CancellationToken cancellationToken)
{
if (authorization == null)
{
throw new ArgumentNullException(nameof(authorization));
}
authorization.Subject = subject;
return default;
}
/// <summary>
/// Sets the type associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="type">The type associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
public virtual ValueTask SetTypeAsync([NotNull] TAuthorization authorization,
[CanBeNull] string type, CancellationToken cancellationToken)
{
if (authorization == null)
{
throw new ArgumentNullException(nameof(authorization));
}
authorization.Type = type;
return default;
}
/// <summary>
/// Updates an existing authorization.
/// </summary>
/// <param name="authorization">The authorization to update.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
public virtual async ValueTask UpdateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken)
{
if (authorization == null)
{
throw new ArgumentNullException(nameof(authorization));
}
var session = await Context.GetSessionAsync(cancellationToken);
try
{
await session.UpdateAsync(authorization, cancellationToken);
await session.FlushAsync(cancellationToken);
}
catch (StaleObjectStateException exception)
{
throw new OpenIddictExceptions.ConcurrencyException(new StringBuilder()
.AppendLine("The authorization was concurrently updated and cannot be persisted in its current state.")
.Append("Reload the authorization from the database and retry the operation.")
.ToString(), exception);
}
}
/// <summary>
/// Converts the provided identifier to a strongly typed key object.
/// </summary>
/// <param name="identifier">The identifier to convert.</param>
/// <returns>An instance of <typeparamref name="TKey"/> representing the provided identifier.</returns>
public virtual TKey ConvertIdentifierFromString([CanBeNull] string identifier)
{
if (string.IsNullOrEmpty(identifier))
{
return default;
}
return (TKey) TypeDescriptor.GetConverter(typeof(TKey)).ConvertFromInvariantString(identifier);
}
/// <summary>
/// Converts the provided identifier to its string representation.
/// </summary>
/// <param name="identifier">The identifier to convert.</param>
/// <returns>A <see cref="string"/> representation of the provided identifier.</returns>
public virtual string ConvertIdentifierToString([CanBeNull] TKey identifier)
{
if (Equals(identifier, default(TKey)))
{
return null;
}
return TypeDescriptor.GetConverter(typeof(TKey)).ConvertToInvariantString(identifier);
}
}
}

720
src/OpenIddict.NHibernate/Stores/OpenIddictScopeStore.cs

@ -1,720 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Linq;
using OpenIddict.Abstractions;
using OpenIddict.NHibernate.Models;
namespace OpenIddict.NHibernate
{
/// <summary>
/// Provides methods allowing to manage the scopes stored in a database.
/// </summary>
public class OpenIddictScopeStore : OpenIddictScopeStore<OpenIddictScope, string>
{
public OpenIddictScopeStore(
[NotNull] IMemoryCache cache,
[NotNull] IOpenIddictNHibernateContext context,
[NotNull] IOptionsMonitor<OpenIddictNHibernateOptions> options)
: base(cache, context, options)
{
}
}
/// <summary>
/// Provides methods allowing to manage the scopes stored in a database.
/// </summary>
/// <typeparam name="TKey">The type of the entity primary keys.</typeparam>
public class OpenIddictScopeStore<TKey> : OpenIddictScopeStore<OpenIddictScope<TKey>, TKey>
where TKey : IEquatable<TKey>
{
public OpenIddictScopeStore(
[NotNull] IMemoryCache cache,
[NotNull] IOpenIddictNHibernateContext context,
[NotNull] IOptionsMonitor<OpenIddictNHibernateOptions> options)
: base(cache, context, options)
{
}
}
/// <summary>
/// Provides methods allowing to manage the scopes stored in a database.
/// </summary>
/// <typeparam name="TScope">The type of the Scope entity.</typeparam>
/// <typeparam name="TKey">The type of the entity primary keys.</typeparam>
public class OpenIddictScopeStore<TScope, TKey> : IOpenIddictScopeStore<TScope>
where TScope : OpenIddictScope<TKey>
where TKey : IEquatable<TKey>
{
public OpenIddictScopeStore(
[NotNull] IMemoryCache cache,
[NotNull] IOpenIddictNHibernateContext context,
[NotNull] IOptionsMonitor<OpenIddictNHibernateOptions> options)
{
Cache = cache;
Context = context;
Options = options;
}
/// <summary>
/// Gets the memory cache associated with the current store.
/// </summary>
protected IMemoryCache Cache { get; }
/// <summary>
/// Gets the database context associated with the current store.
/// </summary>
protected IOpenIddictNHibernateContext Context { get; }
/// <summary>
/// Gets the options associated with the current store.
/// </summary>
protected IOptionsMonitor<OpenIddictNHibernateOptions> Options { get; }
/// <summary>
/// Determines the number of scopes that exist in the database.
/// </summary>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the number of scopes in the database.
/// </returns>
public virtual async ValueTask<long> CountAsync(CancellationToken cancellationToken)
{
var session = await Context.GetSessionAsync(cancellationToken);
return await session.Query<TScope>().LongCountAsync(cancellationToken);
}
/// <summary>
/// Determines the number of scopes that match the specified query.
/// </summary>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the number of scopes that match the specified query.
/// </returns>
public virtual async ValueTask<long> CountAsync<TResult>([NotNull] Func<IQueryable<TScope>, IQueryable<TResult>> query, CancellationToken cancellationToken)
{
if (query == null)
{
throw new ArgumentNullException(nameof(query));
}
var session = await Context.GetSessionAsync(cancellationToken);
return await query(session.Query<TScope>()).LongCountAsync(cancellationToken);
}
/// <summary>
/// Creates a new scope.
/// </summary>
/// <param name="scope">The scope to create.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
public virtual async ValueTask CreateAsync([NotNull] TScope scope, CancellationToken cancellationToken)
{
if (scope == null)
{
throw new ArgumentNullException(nameof(scope));
}
var session = await Context.GetSessionAsync(cancellationToken);
await session.SaveAsync(scope, cancellationToken);
await session.FlushAsync(cancellationToken);
}
/// <summary>
/// Removes an existing scope.
/// </summary>
/// <param name="scope">The scope to delete.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
public virtual async ValueTask DeleteAsync([NotNull] TScope scope, CancellationToken cancellationToken)
{
if (scope == null)
{
throw new ArgumentNullException(nameof(scope));
}
var session = await Context.GetSessionAsync(cancellationToken);
try
{
await session.DeleteAsync(scope, cancellationToken);
await session.FlushAsync(cancellationToken);
}
catch (StaleObjectStateException exception)
{
throw new OpenIddictExceptions.ConcurrencyException(new StringBuilder()
.AppendLine("The scope was concurrently updated and cannot be persisted in its current state.")
.Append("Reload the scope from the database and retry the operation.")
.ToString(), exception);
}
}
/// <summary>
/// Retrieves a scope using its unique identifier.
/// </summary>
/// <param name="identifier">The unique identifier associated with the scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the scope corresponding to the identifier.
/// </returns>
public virtual async ValueTask<TScope> FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(identifier))
{
throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier));
}
var session = await Context.GetSessionAsync(cancellationToken);
return await session.GetAsync<TScope>(ConvertIdentifierFromString(identifier), cancellationToken);
}
/// <summary>
/// Retrieves a scope using its name.
/// </summary>
/// <param name="name">The name associated with the scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the scope corresponding to the specified name.
/// </returns>
public virtual async ValueTask<TScope> FindByNameAsync([NotNull] string name, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(name))
{
throw new ArgumentException("The scope name cannot be null or empty.", nameof(name));
}
var session = await Context.GetSessionAsync(cancellationToken);
return await (from scope in session.Query<TScope>()
where scope.Name == name
select scope).FirstOrDefaultAsync(cancellationToken);
}
/// <summary>
/// Retrieves a list of scopes using their name.
/// </summary>
/// <param name="names">The names associated with the scopes.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The scopes corresponding to the specified names.</returns>
public virtual IAsyncEnumerable<TScope> FindByNamesAsync(ImmutableArray<string> names, CancellationToken cancellationToken)
{
if (names.Any(name => string.IsNullOrEmpty(name)))
{
throw new ArgumentException("Scope names cannot be null or empty.", nameof(names));
}
return ExecuteAsync(cancellationToken);
async IAsyncEnumerable<TScope> ExecuteAsync(CancellationToken cancellationToken)
{
var session = await Context.GetSessionAsync(cancellationToken);
// Note: Enumerable.Contains() is deliberately used without the extension method syntax to ensure
// ImmutableArray.Contains() (which is not fully supported by NHibernate) is not used instead.
await foreach (var scope in (from scope in session.Query<TScope>()
where Enumerable.Contains(names, scope.Name)
select scope).AsAsyncEnumerable(cancellationToken))
{
yield return scope;
}
}
}
/// <summary>
/// Retrieves all the scopes that contain the specified resource.
/// </summary>
/// <param name="resource">The resource associated with the scopes.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>The scopes associated with the specified resource.</returns>
public virtual IAsyncEnumerable<TScope> FindByResourceAsync([NotNull] string resource, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(resource))
{
throw new ArgumentException("The resource cannot be null or empty.", nameof(resource));
}
return ExecuteAsync(cancellationToken);
async IAsyncEnumerable<TScope> ExecuteAsync(CancellationToken cancellationToken)
{
var session = await Context.GetSessionAsync(cancellationToken);
// To optimize the efficiency of the query a bit, only scopes whose stringified
// Resources column contains the specified resource are returned. Once the scopes
// are retrieved, a second pass is made to ensure only valid elements are returned.
// Implementers that use this method in a hot path may want to override this method
// to use SQL Server 2016 functions like JSON_VALUE to make the query more efficient.
await foreach (var scope in session.Query<TScope>()
.Where(scope => scope.Resources.Contains(resource))
.AsAsyncEnumerable(cancellationToken)
.WhereAwait(async scope => (await GetResourcesAsync(scope, cancellationToken)).Contains(resource, StringComparer.Ordinal)))
{
yield return scope;
}
}
}
/// <summary>
/// Executes the specified query and returns the first element.
/// </summary>
/// <typeparam name="TState">The state type.</typeparam>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="state">The optional state.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation,
/// whose result returns the first element returned when executing the query.
/// </returns>
public virtual async ValueTask<TResult> GetAsync<TState, TResult>(
[NotNull] Func<IQueryable<TScope>, TState, IQueryable<TResult>> query,
[CanBeNull] TState state, CancellationToken cancellationToken)
{
if (query == null)
{
throw new ArgumentNullException(nameof(query));
}
var session = await Context.GetSessionAsync(cancellationToken);
return await query(session.Query<TScope>(), state).FirstOrDefaultAsync(cancellationToken);
}
/// <summary>
/// Retrieves the description associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the description associated with the specified scope.
/// </returns>
public virtual ValueTask<string> GetDescriptionAsync([NotNull] TScope scope, CancellationToken cancellationToken)
{
if (scope == null)
{
throw new ArgumentNullException(nameof(scope));
}
return new ValueTask<string>(scope.Description);
}
/// <summary>
/// Retrieves the display name associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the display name associated with the scope.
/// </returns>
public virtual ValueTask<string> GetDisplayNameAsync([NotNull] TScope scope, CancellationToken cancellationToken)
{
if (scope == null)
{
throw new ArgumentNullException(nameof(scope));
}
return new ValueTask<string>(scope.DisplayName);
}
/// <summary>
/// Retrieves the unique identifier associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the unique identifier associated with the scope.
/// </returns>
public virtual ValueTask<string> GetIdAsync([NotNull] TScope scope, CancellationToken cancellationToken)
{
if (scope == null)
{
throw new ArgumentNullException(nameof(scope));
}
return new ValueTask<string>(ConvertIdentifierToString(scope.Id));
}
/// <summary>
/// Retrieves the name associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the name associated with the specified scope.
/// </returns>
public virtual ValueTask<string> GetNameAsync([NotNull] TScope scope, CancellationToken cancellationToken)
{
if (scope == null)
{
throw new ArgumentNullException(nameof(scope));
}
return new ValueTask<string>(scope.Name);
}
/// <summary>
/// Retrieves the additional properties associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the additional properties associated with the scope.
/// </returns>
public virtual ValueTask<ImmutableDictionary<string, JsonElement>> GetPropertiesAsync([NotNull] TScope scope, CancellationToken cancellationToken)
{
if (scope == null)
{
throw new ArgumentNullException(nameof(scope));
}
if (string.IsNullOrEmpty(scope.Properties))
{
return new ValueTask<ImmutableDictionary<string, JsonElement>>(ImmutableDictionary.Create<string, JsonElement>());
}
// Note: parsing the stringified properties is an expensive operation.
// To mitigate that, the resulting object is stored in the memory cache.
var key = string.Concat("78d8dfdd-3870-442e-b62e-dc9bf6eaeff7", "\x1e", scope.Properties);
var properties = Cache.GetOrCreate(key, entry =>
{
entry.SetPriority(CacheItemPriority.High)
.SetSlidingExpiration(TimeSpan.FromMinutes(1));
return JsonSerializer.Deserialize<ImmutableDictionary<string, JsonElement>>(scope.Properties);
});
return new ValueTask<ImmutableDictionary<string, JsonElement>>(properties);
}
/// <summary>
/// Retrieves the resources associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns all the resources associated with the scope.
/// </returns>
public virtual ValueTask<ImmutableArray<string>> GetResourcesAsync([NotNull] TScope scope, CancellationToken cancellationToken)
{
if (scope == null)
{
throw new ArgumentNullException(nameof(scope));
}
if (string.IsNullOrEmpty(scope.Resources))
{
return new ValueTask<ImmutableArray<string>>(ImmutableArray.Create<string>());
}
// Note: parsing the stringified resources is an expensive operation.
// To mitigate that, the resulting array is stored in the memory cache.
var key = string.Concat("b6148250-aede-4fb9-a621-07c9bcf238c3", "\x1e", scope.Resources);
var resources = Cache.GetOrCreate(key, entry =>
{
entry.SetPriority(CacheItemPriority.High)
.SetSlidingExpiration(TimeSpan.FromMinutes(1));
return JsonSerializer.Deserialize<ImmutableArray<string>>(scope.Resources);
});
return new ValueTask<ImmutableArray<string>>(resources);
}
/// <summary>
/// Instantiates a new scope.
/// </summary>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="ValueTask{TResult}"/> that can be used to monitor the asynchronous operation,
/// whose result returns the instantiated scope, that can be persisted in the database.
/// </returns>
public virtual ValueTask<TScope> InstantiateAsync(CancellationToken cancellationToken)
{
try
{
return new ValueTask<TScope>(Activator.CreateInstance<TScope>());
}
catch (MemberAccessException exception)
{
return new ValueTask<TScope>(Task.FromException<TScope>(
new InvalidOperationException(new StringBuilder()
.AppendLine("An error occurred while trying to create a new scope instance.")
.Append("Make sure that the scope entity is not abstract and has a public parameterless constructor ")
.Append("or create a custom scope store that overrides 'InstantiateAsync()' to use a custom factory.")
.ToString(), exception)));
}
}
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
/// <param name="count">The number of results to return.</param>
/// <param name="offset">The number of results to skip.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the elements returned when executing the specified query.</returns>
public virtual async IAsyncEnumerable<TScope> ListAsync(
[CanBeNull] int? count, [CanBeNull] int? offset, [EnumeratorCancellation] CancellationToken cancellationToken)
{
var session = await Context.GetSessionAsync(cancellationToken);
var query = session.Query<TScope>()
.OrderBy(scope => scope.Id)
.AsQueryable();
if (offset.HasValue)
{
query = query.Skip(offset.Value);
}
if (count.HasValue)
{
query = query.Take(count.Value);
}
await foreach (var scope in query.AsAsyncEnumerable(cancellationToken))
{
yield return scope;
}
}
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
/// <typeparam name="TState">The state type.</typeparam>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="query">The query to execute.</param>
/// <param name="state">The optional state.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>All the elements returned when executing the specified query.</returns>
public virtual IAsyncEnumerable<TResult> ListAsync<TState, TResult>(
[NotNull] Func<IQueryable<TScope>, TState, IQueryable<TResult>> query,
[CanBeNull] TState state, CancellationToken cancellationToken)
{
if (query == null)
{
throw new ArgumentNullException(nameof(query));
}
return ExecuteAsync(cancellationToken);
async IAsyncEnumerable<TResult> ExecuteAsync(CancellationToken cancellationToken)
{
var session = await Context.GetSessionAsync(cancellationToken);
await foreach (var element in query(session.Query<TScope>(), state).AsAsyncEnumerable(cancellationToken))
{
yield return element;
}
}
}
/// <summary>
/// Sets the description associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="description">The description associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
public virtual ValueTask SetDescriptionAsync([NotNull] TScope scope, [CanBeNull] string description, CancellationToken cancellationToken)
{
if (scope == null)
{
throw new ArgumentNullException(nameof(scope));
}
scope.Description = description;
return default;
}
/// <summary>
/// Sets the display name associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="name">The display name associated with the scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
public virtual ValueTask SetDisplayNameAsync([NotNull] TScope scope, [CanBeNull] string name, CancellationToken cancellationToken)
{
if (scope == null)
{
throw new ArgumentNullException(nameof(scope));
}
scope.DisplayName = name;
return default;
}
/// <summary>
/// Sets the name associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="name">The name associated with the authorization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
public virtual ValueTask SetNameAsync([NotNull] TScope scope, [CanBeNull] string name, CancellationToken cancellationToken)
{
if (scope == null)
{
throw new ArgumentNullException(nameof(scope));
}
scope.Name = name;
return default;
}
/// <summary>
/// Sets the additional properties associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="properties">The additional properties associated with the scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
public virtual ValueTask SetPropertiesAsync([NotNull] TScope scope,
[CanBeNull] ImmutableDictionary<string, JsonElement> properties, CancellationToken cancellationToken)
{
if (scope == null)
{
throw new ArgumentNullException(nameof(scope));
}
if (properties == null || properties.IsEmpty)
{
scope.Properties = null;
return default;
}
scope.Properties = JsonSerializer.Serialize(properties, new JsonSerializerOptions
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
WriteIndented = false
});
return default;
}
/// <summary>
/// Sets the resources associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="resources">The resources associated with the scope.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
public virtual ValueTask SetResourcesAsync([NotNull] TScope scope, ImmutableArray<string> resources, CancellationToken cancellationToken)
{
if (scope == null)
{
throw new ArgumentNullException(nameof(scope));
}
if (resources.IsDefaultOrEmpty)
{
scope.Resources = null;
return default;
}
scope.Resources = JsonSerializer.Serialize(resources, new JsonSerializerOptions
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
WriteIndented = false
});
return default;
}
/// <summary>
/// Updates an existing scope.
/// </summary>
/// <param name="scope">The scope to update.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.</returns>
public virtual async ValueTask UpdateAsync([NotNull] TScope scope, CancellationToken cancellationToken)
{
if (scope == null)
{
throw new ArgumentNullException(nameof(scope));
}
var session = await Context.GetSessionAsync(cancellationToken);
try
{
await session.UpdateAsync(scope, cancellationToken);
await session.FlushAsync(cancellationToken);
}
catch (StaleObjectStateException exception)
{
throw new OpenIddictExceptions.ConcurrencyException(new StringBuilder()
.AppendLine("The scope was concurrently updated and cannot be persisted in its current state.")
.Append("Reload the scope from the database and retry the operation.")
.ToString(), exception);
}
}
/// <summary>
/// Converts the provided identifier to a strongly typed key object.
/// </summary>
/// <param name="identifier">The identifier to convert.</param>
/// <returns>An instance of <typeparamref name="TKey"/> representing the provided identifier.</returns>
public virtual TKey ConvertIdentifierFromString([CanBeNull] string identifier)
{
if (string.IsNullOrEmpty(identifier))
{
return default;
}
return (TKey) TypeDescriptor.GetConverter(typeof(TKey)).ConvertFromInvariantString(identifier);
}
/// <summary>
/// Converts the provided identifier to its string representation.
/// </summary>
/// <param name="identifier">The identifier to convert.</param>
/// <returns>A <see cref="string"/> representation of the provided identifier.</returns>
public virtual string ConvertIdentifierToString([CanBeNull] TKey identifier)
{
if (Equals(identifier, default(TKey)))
{
return null;
}
return TypeDescriptor.GetConverter(typeof(TKey)).ConvertToInvariantString(identifier);
}
}
}

1127
src/OpenIddict.NHibernate/Stores/OpenIddictTokenStore.cs

File diff suppressed because it is too large

16
test/OpenIddict.NHibernate.Tests/OpenIddict.NHibernate.Tests.csproj

@ -1,16 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net461;netcoreapp2.1;netcoreapp3.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\OpenIddict.NHibernate\OpenIddict.NHibernate.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(ExtensionsVersion)" />
<PackageReference Include="Moq" Version="$(MoqVersion)" />
</ItemGroup>
</Project>

122
test/OpenIddict.NHibernate.Tests/OpenIddictNHibernateBuilderTests.cs

@ -1,122 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Moq;
using NHibernate;
using OpenIddict.Core;
using OpenIddict.NHibernate.Models;
using Xunit;
namespace OpenIddict.NHibernate.Tests
{
public class OpenIddictNHibernateBuilderTests
{
[Fact]
public void Constructor_ThrowsAnExceptionForNullServices()
{
// Arrange
var services = (IServiceCollection) null;
// Act and assert
var exception = Assert.Throws<ArgumentNullException>(() => new OpenIddictNHibernateBuilder(services));
Assert.Equal("services", exception.ParamName);
}
[Fact]
public void ReplaceDefaultEntities_EntitiesAreCorrectlyReplaced()
{
// Arrange
var services = CreateServices();
var builder = CreateBuilder(services);
// Act
builder.ReplaceDefaultEntities<CustomApplication, CustomAuthorization, CustomScope, CustomToken, long>();
// Assert
var provider = services.BuildServiceProvider();
var options = provider.GetRequiredService<IOptionsMonitor<OpenIddictCoreOptions>>().CurrentValue;
Assert.Equal(typeof(CustomApplication), options.DefaultApplicationType);
Assert.Equal(typeof(CustomAuthorization), options.DefaultAuthorizationType);
Assert.Equal(typeof(CustomScope), options.DefaultScopeType);
Assert.Equal(typeof(CustomToken), options.DefaultTokenType);
}
[Fact]
public void ReplaceDefaultEntities_AllowsSpecifyingCustomKeyType()
{
// Arrange
var services = CreateServices();
var builder = CreateBuilder(services);
// Act
builder.ReplaceDefaultEntities<long>();
// Assert
var provider = services.BuildServiceProvider();
var options = provider.GetRequiredService<IOptionsMonitor<OpenIddictCoreOptions>>().CurrentValue;
Assert.Equal(typeof(OpenIddictApplication<long>), options.DefaultApplicationType);
Assert.Equal(typeof(OpenIddictAuthorization<long>), options.DefaultAuthorizationType);
Assert.Equal(typeof(OpenIddictScope<long>), options.DefaultScopeType);
Assert.Equal(typeof(OpenIddictToken<long>), options.DefaultTokenType);
}
[Fact]
public void UseSessionFactory_ThrowsAnExceptionForNullFactory()
{
// Arrange
var services = CreateServices();
var builder = CreateBuilder(services);
// Act and assert
var exception = Assert.Throws<ArgumentNullException>(delegate
{
return builder.UseSessionFactory(factory: null);
});
Assert.Equal("factory", exception.ParamName);
}
[Fact]
public void UseSessionFactory_SetsDbContextTypeInOptions()
{
// Arrange
var services = CreateServices();
var builder = CreateBuilder(services);
var factory = Mock.Of<ISessionFactory>();
// Act
builder.UseSessionFactory(factory);
// Assert
var provider = services.BuildServiceProvider();
var options = provider.GetRequiredService<IOptionsMonitor<OpenIddictNHibernateOptions>>().CurrentValue;
Assert.Same(factory, options.SessionFactory);
}
private static OpenIddictNHibernateBuilder CreateBuilder(IServiceCollection services)
=> services.AddOpenIddict().AddCore().UseNHibernate();
private static IServiceCollection CreateServices()
{
var services = new ServiceCollection();
services.AddOptions();
return services;
}
public class CustomApplication : OpenIddictApplication<long, CustomAuthorization, CustomToken> { }
public class CustomAuthorization : OpenIddictAuthorization<long, CustomApplication, CustomToken> { }
public class CustomScope : OpenIddictScope<long> { }
public class CustomToken : OpenIddictToken<long, CustomApplication, CustomAuthorization> { }
}
}

240
test/OpenIddict.NHibernate.Tests/OpenIddictNHibernateContextTests.cs

@ -1,240 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Moq;
using NHibernate;
using Xunit;
namespace OpenIddict.NHibernate.Tests
{
public class OpenIddictNHibernateContextTests
{
[Fact]
public async Task GetSessionAsync_ThrowsAnExceptionForCanceledToken()
{
// Arrange
var services = new ServiceCollection();
var provider = services.BuildServiceProvider();
var options = Mock.Of<IOptionsMonitor<OpenIddictNHibernateOptions>>();
var token = new CancellationToken(canceled: true);
var context = new OpenIddictNHibernateContext(options, provider);
// Act and assert
var exception = await Assert.ThrowsAsync<TaskCanceledException>(async delegate
{
await context.GetSessionAsync(token);
});
Assert.Equal(token, exception.CancellationToken);
}
[Fact]
public async Task GetSessionAsync_UsesSessionRegisteredInDependencyInjectionContainer()
{
// Arrange
var services = new ServiceCollection();
var session = new Mock<ISession>();
var factory = new Mock<ISessionFactory>();
services.AddSingleton(session.Object);
services.AddSingleton(factory.Object);
var provider = services.BuildServiceProvider();
var options = Mock.Of<IOptionsMonitor<OpenIddictNHibernateOptions>>(
mock => mock.CurrentValue == new OpenIddictNHibernateOptions
{
SessionFactory = null
});
var context = new OpenIddictNHibernateContext(options, provider);
// Act and assert
Assert.Same(session.Object, await context.GetSessionAsync(CancellationToken.None));
factory.Verify(mock => mock.OpenSession(), Times.Never());
}
[Theory]
[InlineData(FlushMode.Always)]
[InlineData(FlushMode.Auto)]
[InlineData(FlushMode.Commit)]
[InlineData(FlushMode.Unspecified)]
public async Task GetSessionAsync_CreatesSubSessionWhenFlushModeIsNotManual(FlushMode mode)
{
// Arrange
var services = new ServiceCollection();
var session = new Mock<ISession>();
session.SetupProperty(mock => mock.FlushMode, mode);
var builder = new Mock<ISharedSessionBuilder>();
builder.Setup(mock => mock.AutoClose())
.Returns(builder.Object);
builder.Setup(mock => mock.AutoJoinTransaction())
.Returns(builder.Object);
builder.Setup(mock => mock.Connection())
.Returns(builder.Object);
builder.Setup(mock => mock.ConnectionReleaseMode())
.Returns(builder.Object);
builder.Setup(mock => mock.FlushMode(FlushMode.Manual))
.Returns(builder.Object);
builder.Setup(mock => mock.Interceptor())
.Returns(builder.Object);
builder.Setup(mock => mock.OpenSession())
.Returns(session.Object);
session.Setup(mock => mock.SessionWithOptions())
.Returns(builder.Object);
var factory = new Mock<ISessionFactory>();
services.AddSingleton(session.Object);
services.AddSingleton(factory.Object);
var provider = services.BuildServiceProvider();
var options = Mock.Of<IOptionsMonitor<OpenIddictNHibernateOptions>>(
mock => mock.CurrentValue == new OpenIddictNHibernateOptions
{
SessionFactory = null
});
var context = new OpenIddictNHibernateContext(options, provider);
// Act and assert
Assert.Same(session.Object, await context.GetSessionAsync(CancellationToken.None));
builder.Verify(mock => mock.AutoClose(), Times.Once());
builder.Verify(mock => mock.AutoJoinTransaction(), Times.Once());
builder.Verify(mock => mock.Connection(), Times.Once());
builder.Verify(mock => mock.ConnectionReleaseMode(), Times.Once());
builder.Verify(mock => mock.FlushMode(FlushMode.Manual), Times.Once());
builder.Verify(mock => mock.Interceptor(), Times.Once());
builder.Verify(mock => mock.OpenSession(), Times.Once());
factory.Verify(mock => mock.OpenSession(), Times.Never());
}
[Fact]
public async Task GetSessionAsync_UsesSessionFactoryRegisteredInDependencyInjectionContainer()
{
// Arrange
var services = new ServiceCollection();
services.AddSingleton(Mock.Of<ISessionFactory>());
var session = new Mock<ISession>();
var factory = new Mock<ISessionFactory>();
factory.Setup(mock => mock.OpenSession())
.Returns(session.Object);
var provider = services.BuildServiceProvider();
var options = Mock.Of<IOptionsMonitor<OpenIddictNHibernateOptions>>(
mock => mock.CurrentValue == new OpenIddictNHibernateOptions
{
SessionFactory = factory.Object
});
var context = new OpenIddictNHibernateContext(options, provider);
// Act and assert
Assert.Same(session.Object, await context.GetSessionAsync(CancellationToken.None));
factory.Verify(mock => mock.OpenSession(), Times.Once());
session.VerifySet(mock => mock.FlushMode = FlushMode.Manual, Times.Once());
}
[Fact]
public async Task GetSessionAsync_ThrowsAnExceptionWhenSessionFactoryCannotBeFound()
{
// Arrange
var services = new ServiceCollection();
var provider = services.BuildServiceProvider();
var options = Mock.Of<IOptionsMonitor<OpenIddictNHibernateOptions>>(
mock => mock.CurrentValue == new OpenIddictNHibernateOptions
{
SessionFactory = null
});
var context = new OpenIddictNHibernateContext(options, provider);
// Act and assert
var exception = await Assert.ThrowsAsync<InvalidOperationException>(async delegate
{
await context.GetSessionAsync(CancellationToken.None);
});
Assert.Equal(new StringBuilder()
.AppendLine("No suitable NHibernate session or session factory can be found.")
.Append("To configure the OpenIddict NHibernate stores to use a specific factory, use ")
.Append("'services.AddOpenIddict().AddCore().UseNHibernate().UseSessionFactory()' or register an ")
.Append("'ISession'/'ISessionFactory' in the dependency injection container in 'ConfigureServices()'.")
.ToString(), exception.Message);
}
[Fact]
public async Task GetSessionAsync_PrefersSessionFactoryRegisteredInOptionsToSessionRegisteredInDependencyInjectionContainer()
{
// Arrange
var services = new ServiceCollection();
services.AddSingleton(Mock.Of<ISessionFactory>());
var session = new Mock<ISession>();
var factory = new Mock<ISessionFactory>();
factory.Setup(mock => mock.OpenSession())
.Returns(session.Object);
var provider = services.BuildServiceProvider();
var options = Mock.Of<IOptionsMonitor<OpenIddictNHibernateOptions>>(
mock => mock.CurrentValue == new OpenIddictNHibernateOptions
{
SessionFactory = factory.Object
});
var context = new OpenIddictNHibernateContext(options, provider);
// Act and assert
Assert.Same(session.Object, await context.GetSessionAsync(CancellationToken.None));
factory.Verify(mock => mock.OpenSession(), Times.Once());
session.VerifySet(mock => mock.FlushMode = FlushMode.Manual, Times.Once());
}
[Fact]
public async Task GetSessionAsync_ReturnsCachedSession()
{
// Arrange
var services = new ServiceCollection();
var provider = services.BuildServiceProvider();
var factory = new Mock<ISessionFactory>();
factory.Setup(mock => mock.OpenSession())
.Returns(() => Mock.Of<ISession>());
var options = Mock.Of<IOptionsMonitor<OpenIddictNHibernateOptions>>(
mock => mock.CurrentValue == new OpenIddictNHibernateOptions
{
SessionFactory = factory.Object
});
var context = new OpenIddictNHibernateContext(options, provider);
// Act and assert
Assert.Same(
await context.GetSessionAsync(CancellationToken.None),
await context.GetSessionAsync(CancellationToken.None));
factory.Verify(mock => mock.OpenSession(), Times.Once());
}
}
}

120
test/OpenIddict.NHibernate.Tests/OpenIddictNHibernateExtensionsTests.cs

@ -1,120 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using OpenIddict.Abstractions;
using OpenIddict.Core;
using OpenIddict.NHibernate.Models;
using Xunit;
namespace OpenIddict.NHibernate.Tests
{
public class OpenIddictNHibernateExtensionsTests
{
[Fact]
public void UseNHibernate_ThrowsAnExceptionForNullBuilder()
{
// Arrange
var builder = (OpenIddictCoreBuilder) null;
// Act and assert
var exception = Assert.Throws<ArgumentNullException>(() => builder.UseNHibernate());
Assert.Equal("builder", exception.ParamName);
}
[Fact]
public void UseNHibernate_ThrowsAnExceptionForNullConfiguration()
{
// Arrange
var services = new ServiceCollection();
var builder = new OpenIddictCoreBuilder(services);
// Act and assert
var exception = Assert.Throws<ArgumentNullException>(() => builder.UseNHibernate(configuration: null));
Assert.Equal("configuration", exception.ParamName);
}
[Fact]
public void UseNHibernate_RegistersDefaultEntities()
{
// Arrange
var services = new ServiceCollection().AddOptions();
var builder = new OpenIddictCoreBuilder(services);
// Act
builder.UseNHibernate();
// Assert
var provider = services.BuildServiceProvider();
var options = provider.GetRequiredService<IOptionsMonitor<OpenIddictCoreOptions>>().CurrentValue;
Assert.Equal(typeof(OpenIddictApplication), options.DefaultApplicationType);
Assert.Equal(typeof(OpenIddictAuthorization), options.DefaultAuthorizationType);
Assert.Equal(typeof(OpenIddictScope), options.DefaultScopeType);
Assert.Equal(typeof(OpenIddictToken), options.DefaultTokenType);
}
[Theory]
[InlineData(typeof(IOpenIddictApplicationStoreResolver), typeof(OpenIddictApplicationStoreResolver))]
[InlineData(typeof(IOpenIddictAuthorizationStoreResolver), typeof(OpenIddictAuthorizationStoreResolver))]
[InlineData(typeof(IOpenIddictScopeStoreResolver), typeof(OpenIddictScopeStoreResolver))]
[InlineData(typeof(IOpenIddictTokenStoreResolver), typeof(OpenIddictTokenStoreResolver))]
public void UseNHibernate_RegistersNHibernateStoreResolvers(Type serviceType, Type implementationType)
{
// Arrange
var services = new ServiceCollection();
var builder = new OpenIddictCoreBuilder(services);
// Act
builder.UseNHibernate();
// Assert
Assert.Contains(services, service => service.ServiceType == serviceType &&
service.ImplementationType == implementationType);
}
[Theory]
[InlineData(typeof(OpenIddictApplicationStoreResolver.TypeResolutionCache))]
[InlineData(typeof(OpenIddictAuthorizationStoreResolver.TypeResolutionCache))]
[InlineData(typeof(OpenIddictScopeStoreResolver.TypeResolutionCache))]
[InlineData(typeof(OpenIddictTokenStoreResolver.TypeResolutionCache))]
public void UseEntityFramework_RegistersNHibernateStoreResolverCaches(Type type)
{
// Arrange
var services = new ServiceCollection();
var builder = new OpenIddictCoreBuilder(services);
// Act
builder.UseNHibernate();
// Assert
Assert.Contains(services, service => service.ServiceType == type &&
service.ImplementationType == type);
}
[Theory]
[InlineData(typeof(OpenIddictApplicationStore<,,,>))]
[InlineData(typeof(OpenIddictAuthorizationStore<,,,>))]
[InlineData(typeof(OpenIddictScopeStore<,>))]
[InlineData(typeof(OpenIddictTokenStore<,,,>))]
public void UseNHibernate_RegistersNHibernateStore(Type type)
{
// Arrange
var services = new ServiceCollection();
var builder = new OpenIddictCoreBuilder(services);
// Act
builder.UseNHibernate();
// Assert
Assert.Contains(services, service => service.ServiceType == type && service.ImplementationType == type);
}
}
}

84
test/OpenIddict.NHibernate.Tests/Resolvers/OpenIddictApplicationStoreResolverTests.cs

@ -1,84 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.Text;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Moq;
using OpenIddict.Abstractions;
using OpenIddict.NHibernate.Models;
using Xunit;
using static OpenIddict.NHibernate.OpenIddictApplicationStoreResolver;
namespace OpenIddict.NHibernate.Tests
{
public class OpenIddictApplicationStoreResolverTests
{
[Fact]
public void Get_ReturnsCustomStoreCorrespondingToTheSpecifiedTypeWhenAvailable()
{
// Arrange
var services = new ServiceCollection();
services.AddSingleton(Mock.Of<IOpenIddictApplicationStore<CustomApplication>>());
var provider = services.BuildServiceProvider();
var resolver = new OpenIddictApplicationStoreResolver(new TypeResolutionCache(), provider);
// Act and assert
Assert.NotNull(resolver.Get<CustomApplication>());
}
[Fact]
public void Get_ThrowsAnExceptionForInvalidEntityType()
{
// Arrange
var services = new ServiceCollection();
var provider = services.BuildServiceProvider();
var resolver = new OpenIddictApplicationStoreResolver(new TypeResolutionCache(), provider);
// Act and assert
var exception = Assert.Throws<InvalidOperationException>(() => resolver.Get<CustomApplication>());
Assert.Equal(new StringBuilder()
.AppendLine("The specified application type is not compatible with the NHibernate stores.")
.Append("When enabling the NHibernate stores, make sure you use the built-in ")
.Append("'OpenIddictApplication' entity (from the 'OpenIddict.NHibernate.Models' package) ")
.Append("or a custom entity that inherits from the generic 'OpenIddictApplication' entity.")
.ToString(), exception.Message);
}
[Fact]
public void Get_ReturnsDefaultStoreCorrespondingToTheSpecifiedTypeWhenAvailable()
{
// Arrange
var services = new ServiceCollection();
services.AddSingleton(Mock.Of<IOpenIddictApplicationStore<CustomApplication>>());
services.AddSingleton(CreateStore());
var provider = services.BuildServiceProvider();
var resolver = new OpenIddictApplicationStoreResolver(new TypeResolutionCache(), provider);
// Act and assert
Assert.NotNull(resolver.Get<MyApplication>());
}
private static OpenIddictApplicationStore<MyApplication, MyAuthorization, MyToken, long> CreateStore()
=> new Mock<OpenIddictApplicationStore<MyApplication, MyAuthorization, MyToken, long>>(
Mock.Of<IMemoryCache>(),
Mock.Of<IOpenIddictNHibernateContext>(),
Mock.Of<IOptionsMonitor<OpenIddictNHibernateOptions>>()).Object;
public class CustomApplication { }
public class MyApplication : OpenIddictApplication<long, MyAuthorization, MyToken> { }
public class MyAuthorization : OpenIddictAuthorization<long, MyApplication, MyToken> { }
public class MyScope : OpenIddictScope<long> { }
public class MyToken : OpenIddictToken<long, MyApplication, MyAuthorization> { }
}
}

84
test/OpenIddict.NHibernate.Tests/Resolvers/OpenIddictAuthorizationStoreResolverTests.cs

@ -1,84 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.Text;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Moq;
using OpenIddict.Abstractions;
using OpenIddict.NHibernate.Models;
using Xunit;
using static OpenIddict.NHibernate.OpenIddictAuthorizationStoreResolver;
namespace OpenIddict.NHibernate.Tests
{
public class OpenIddictAuthorizationStoreResolverTests
{
[Fact]
public void Get_ReturnsCustomStoreCorrespondingToTheSpecifiedTypeWhenAvailable()
{
// Arrange
var services = new ServiceCollection();
services.AddSingleton(Mock.Of<IOpenIddictAuthorizationStore<CustomAuthorization>>());
var provider = services.BuildServiceProvider();
var resolver = new OpenIddictAuthorizationStoreResolver(new TypeResolutionCache(), provider);
// Act and assert
Assert.NotNull(resolver.Get<CustomAuthorization>());
}
[Fact]
public void Get_ThrowsAnExceptionForInvalidEntityType()
{
// Arrange
var services = new ServiceCollection();
var provider = services.BuildServiceProvider();
var resolver = new OpenIddictAuthorizationStoreResolver(new TypeResolutionCache(), provider);
// Act and assert
var exception = Assert.Throws<InvalidOperationException>(() => resolver.Get<CustomAuthorization>());
Assert.Equal(new StringBuilder()
.AppendLine("The specified authorization type is not compatible with the NHibernate stores.")
.Append("When enabling the NHibernate stores, make sure you use the built-in ")
.Append("'OpenIddictAuthorization' entity (from the 'OpenIddict.NHibernate.Models' package) ")
.Append("or a custom entity that inherits from the generic 'OpenIddictAuthorization' entity.")
.ToString(), exception.Message);
}
[Fact]
public void Get_ReturnsDefaultStoreCorrespondingToTheSpecifiedTypeWhenAvailable()
{
// Arrange
var services = new ServiceCollection();
services.AddSingleton(Mock.Of<IOpenIddictAuthorizationStore<CustomAuthorization>>());
services.AddSingleton(CreateStore());
var provider = services.BuildServiceProvider();
var resolver = new OpenIddictAuthorizationStoreResolver(new TypeResolutionCache(), provider);
// Act and assert
Assert.NotNull(resolver.Get<MyAuthorization>());
}
private static OpenIddictAuthorizationStore<MyAuthorization, MyApplication, MyToken, long> CreateStore()
=> new Mock<OpenIddictAuthorizationStore<MyAuthorization, MyApplication, MyToken, long>>(
Mock.Of<IMemoryCache>(),
Mock.Of<IOpenIddictNHibernateContext>(),
Mock.Of<IOptionsMonitor<OpenIddictNHibernateOptions>>()).Object;
public class CustomAuthorization { }
public class MyApplication : OpenIddictApplication<long, MyAuthorization, MyToken> { }
public class MyAuthorization : OpenIddictAuthorization<long, MyApplication, MyToken> { }
public class MyScope : OpenIddictScope<long> { }
public class MyToken : OpenIddictToken<long, MyApplication, MyAuthorization> { }
}
}

84
test/OpenIddict.NHibernate.Tests/Resolvers/OpenIddictScopeStoreResolverTests.cs

@ -1,84 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.Text;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Moq;
using OpenIddict.Abstractions;
using OpenIddict.NHibernate.Models;
using Xunit;
using static OpenIddict.NHibernate.OpenIddictScopeStoreResolver;
namespace OpenIddict.NHibernate.Tests
{
public class OpenIddictScopeStoreResolverTests
{
[Fact]
public void Get_ReturnsCustomStoreCorrespondingToTheSpecifiedTypeWhenAvailable()
{
// Arrange
var services = new ServiceCollection();
services.AddSingleton(Mock.Of<IOpenIddictScopeStore<CustomScope>>());
var provider = services.BuildServiceProvider();
var resolver = new OpenIddictScopeStoreResolver(new TypeResolutionCache(), provider);
// Act and assert
Assert.NotNull(resolver.Get<CustomScope>());
}
[Fact]
public void Get_ThrowsAnExceptionForInvalidEntityType()
{
// Arrange
var services = new ServiceCollection();
var provider = services.BuildServiceProvider();
var resolver = new OpenIddictScopeStoreResolver(new TypeResolutionCache(), provider);
// Act and assert
var exception = Assert.Throws<InvalidOperationException>(() => resolver.Get<CustomScope>());
Assert.Equal(new StringBuilder()
.AppendLine("The specified scope type is not compatible with the NHibernate stores.")
.Append("When enabling the NHibernate stores, make sure you use the built-in ")
.Append("'OpenIddictScope' entity (from the 'OpenIddict.NHibernate.Models' package) ")
.Append("or a custom entity that inherits from the generic 'OpenIddictScope' entity.")
.ToString(), exception.Message);
}
[Fact]
public void Get_ReturnsDefaultStoreCorrespondingToTheSpecifiedTypeWhenAvailable()
{
// Arrange
var services = new ServiceCollection();
services.AddSingleton(Mock.Of<IOpenIddictScopeStore<CustomScope>>());
services.AddSingleton(CreateStore());
var provider = services.BuildServiceProvider();
var resolver = new OpenIddictScopeStoreResolver(new TypeResolutionCache(), provider);
// Act and assert
Assert.NotNull(resolver.Get<MyScope>());
}
private static OpenIddictScopeStore<MyScope, long> CreateStore()
=> new Mock<OpenIddictScopeStore<MyScope, long>>(
Mock.Of<IMemoryCache>(),
Mock.Of<IOpenIddictNHibernateContext>(),
Mock.Of<IOptionsMonitor<OpenIddictNHibernateOptions>>()).Object;
public class CustomScope { }
public class MyApplication : OpenIddictApplication<long, MyAuthorization, MyToken> { }
public class MyAuthorization : OpenIddictAuthorization<long, MyApplication, MyToken> { }
public class MyScope : OpenIddictScope<long> { }
public class MyToken : OpenIddictToken<long, MyApplication, MyAuthorization> { }
}
}

84
test/OpenIddict.NHibernate.Tests/Resolvers/OpenIddictTokenStoreResolverTests.cs

@ -1,84 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.Text;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Moq;
using OpenIddict.Abstractions;
using OpenIddict.NHibernate.Models;
using Xunit;
using static OpenIddict.NHibernate.OpenIddictTokenStoreResolver;
namespace OpenIddict.NHibernate.Tests
{
public class OpenIddictTokenStoreResolverTests
{
[Fact]
public void Get_ReturnsCustomStoreCorrespondingToTheSpecifiedTypeWhenAvailable()
{
// Arrange
var services = new ServiceCollection();
services.AddSingleton(Mock.Of<IOpenIddictTokenStore<CustomToken>>());
var provider = services.BuildServiceProvider();
var resolver = new OpenIddictTokenStoreResolver(new TypeResolutionCache(), provider);
// Act and assert
Assert.NotNull(resolver.Get<CustomToken>());
}
[Fact]
public void Get_ThrowsAnExceptionForInvalidEntityType()
{
// Arrange
var services = new ServiceCollection();
var provider = services.BuildServiceProvider();
var resolver = new OpenIddictTokenStoreResolver(new TypeResolutionCache(), provider);
// Act and assert
var exception = Assert.Throws<InvalidOperationException>(() => resolver.Get<CustomToken>());
Assert.Equal(new StringBuilder()
.AppendLine("The specified token type is not compatible with the NHibernate stores.")
.Append("When enabling the NHibernate stores, make sure you use the built-in ")
.Append("'OpenIddictToken' entity (from the 'OpenIddict.NHibernate.Models' package) ")
.Append("or a custom entity that inherits from the generic 'OpenIddictToken' entity.")
.ToString(), exception.Message);
}
[Fact]
public void Get_ReturnsDefaultStoreCorrespondingToTheSpecifiedTypeWhenAvailable()
{
// Arrange
var services = new ServiceCollection();
services.AddSingleton(Mock.Of<IOpenIddictTokenStore<CustomToken>>());
services.AddSingleton(CreateStore());
var provider = services.BuildServiceProvider();
var resolver = new OpenIddictTokenStoreResolver(new TypeResolutionCache(), provider);
// Act and assert
Assert.NotNull(resolver.Get<MyToken>());
}
private static OpenIddictTokenStore<MyToken, MyApplication, MyAuthorization, long> CreateStore()
=> new Mock<OpenIddictTokenStore<MyToken, MyApplication, MyAuthorization, long>>(
Mock.Of<IMemoryCache>(),
Mock.Of<IOpenIddictNHibernateContext>(),
Mock.Of<IOptionsMonitor<OpenIddictNHibernateOptions>>()).Object;
public class CustomToken { }
public class MyApplication : OpenIddictApplication<long, MyAuthorization, MyToken> { }
public class MyAuthorization : OpenIddictAuthorization<long, MyApplication, MyToken> { }
public class MyScope : OpenIddictScope<long> { }
public class MyToken : OpenIddictToken<long, MyApplication, MyAuthorization> { }
}
}
Loading…
Cancel
Save