11 changed files with 1396 additions and 1 deletions
@ -0,0 +1,7 @@ |
|||||
|
<Project> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ExcludeFromTest Include="$(RepositoryRoot)test\OpenIddict.EntityFramework.Tests\*.csproj" Condition="'$(OS)' != 'Windows_NT'"/> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,24 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<Import Project="..\..\build\packages.props" /> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>net461</TargetFramework> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<Description>Entity Framework 6.x stores for OpenIddict.</Description> |
||||
|
<Authors>Kévin Chalet</Authors> |
||||
|
<PackageTags>aspnetcore;authentication;jwt;openidconnect;openiddict;security</PackageTags> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\OpenIddict.Core\OpenIddict.Core.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="EntityFramework" Version="$(EntityFrameworkVersion)" /> |
||||
|
<PackageReference Include="JetBrains.Annotations" Version="$(JetBrainsVersion)" PrivateAssets="All" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,264 @@ |
|||||
|
/* |
||||
|
* 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 System.ComponentModel.DataAnnotations.Schema; |
||||
|
using System.Data.Entity; |
||||
|
using System.Data.Entity.Infrastructure.Annotations; |
||||
|
using System.Diagnostics; |
||||
|
using System.Reflection; |
||||
|
using JetBrains.Annotations; |
||||
|
using Microsoft.Extensions.DependencyInjection.Extensions; |
||||
|
using OpenIddict.Core; |
||||
|
using OpenIddict.EntityFramework; |
||||
|
using OpenIddict.Models; |
||||
|
|
||||
|
namespace Microsoft.Extensions.DependencyInjection |
||||
|
{ |
||||
|
public static class OpenIddictExtensions |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Registers the Entity Framework 6.x stores. Note: when using the Entity Framework stores,
|
||||
|
/// the application <see cref="DbContext"/> MUST be manually registered in the DI container and
|
||||
|
/// the entities MUST be derived from the models contained in the OpenIddict.Models package.
|
||||
|
/// </summary>
|
||||
|
/// <param name="builder">The services builder used by OpenIddict to register new services.</param>
|
||||
|
/// <returns>The <see cref="OpenIddictBuilder"/>.</returns>
|
||||
|
public static OpenIddictBuilder AddEntityFrameworkStores<TContext>([NotNull] this OpenIddictBuilder builder) |
||||
|
where TContext : DbContext |
||||
|
{ |
||||
|
if (builder == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException(nameof(builder)); |
||||
|
} |
||||
|
|
||||
|
Debug.Assert(builder.ApplicationType != null && |
||||
|
builder.AuthorizationType != null && |
||||
|
builder.ScopeType != null && |
||||
|
builder.TokenType != null, "The entity types exposed by OpenIddictBuilder shouldn't be null."); |
||||
|
|
||||
|
var application = FindGenericBaseType(builder.ApplicationType, typeof(OpenIddictApplication<,,>)); |
||||
|
if (application == null) |
||||
|
{ |
||||
|
throw new InvalidOperationException("The Entity Framework stores can only be used " + |
||||
|
"with the built-in OpenIddictApplication entity."); |
||||
|
} |
||||
|
|
||||
|
var authorization = FindGenericBaseType(builder.AuthorizationType, typeof(OpenIddictAuthorization<,,>)); |
||||
|
if (authorization == null) |
||||
|
{ |
||||
|
throw new InvalidOperationException("The Entity Framework stores can only be used " + |
||||
|
"with the built-in OpenIddictAuthorization entity."); |
||||
|
} |
||||
|
|
||||
|
var scope = FindGenericBaseType(builder.ScopeType, typeof(OpenIddictScope<>)); |
||||
|
if (scope == null) |
||||
|
{ |
||||
|
throw new InvalidOperationException("The Entity Framework stores can only be used " + |
||||
|
"with the built-in OpenIddictScope entity."); |
||||
|
} |
||||
|
|
||||
|
var token = FindGenericBaseType(builder.TokenType, typeof(OpenIddictToken<,,>)); |
||||
|
if (token == null) |
||||
|
{ |
||||
|
throw new InvalidOperationException("The Entity Framework stores can only be used " + |
||||
|
"with the built-in OpenIddictToken entity."); |
||||
|
} |
||||
|
|
||||
|
var converter = TypeDescriptor.GetConverter(application.GenericTypeArguments[0]); |
||||
|
if (converter == null || !converter.CanConvertFrom(typeof(string)) || |
||||
|
!converter.CanConvertTo(typeof(string))) |
||||
|
{ |
||||
|
throw new InvalidOperationException("The specified entity key type is not supported."); |
||||
|
} |
||||
|
|
||||
|
// Register the application store in the DI container.
|
||||
|
builder.Services.TryAddScoped( |
||||
|
typeof(IOpenIddictApplicationStore<>).MakeGenericType(builder.ApplicationType), |
||||
|
typeof(OpenIddictApplicationStore<,,,,>).MakeGenericType( |
||||
|
/* TApplication: */ builder.ApplicationType, |
||||
|
/* TAuthorization: */ builder.AuthorizationType, |
||||
|
/* TToken: */ builder.TokenType, |
||||
|
/* TContext: */ typeof(TContext), |
||||
|
/* TKey: */ application.GenericTypeArguments[0])); |
||||
|
|
||||
|
// Register the authorization store in the DI container.
|
||||
|
builder.Services.TryAddScoped( |
||||
|
typeof(IOpenIddictAuthorizationStore<>).MakeGenericType(builder.AuthorizationType), |
||||
|
typeof(OpenIddictAuthorizationStore<,,,,>).MakeGenericType( |
||||
|
/* TAuthorization: */ builder.AuthorizationType, |
||||
|
/* TApplication: */ builder.ApplicationType, |
||||
|
/* TToken: */ builder.TokenType, |
||||
|
/* TContext: */ typeof(TContext), |
||||
|
/* TKey: */ authorization.GenericTypeArguments[0])); |
||||
|
|
||||
|
// Register the scope store in the DI container.
|
||||
|
builder.Services.TryAddScoped( |
||||
|
typeof(IOpenIddictScopeStore<>).MakeGenericType(builder.ScopeType), |
||||
|
typeof(OpenIddictScopeStore<,,>).MakeGenericType( |
||||
|
/* TScope: */ builder.ScopeType, |
||||
|
/* TContext: */ typeof(TContext), |
||||
|
/* TKey: */ scope.GenericTypeArguments[0])); |
||||
|
|
||||
|
// Register the token store in the DI container.
|
||||
|
builder.Services.TryAddScoped( |
||||
|
typeof(IOpenIddictTokenStore<>).MakeGenericType(builder.TokenType), |
||||
|
typeof(OpenIddictTokenStore<,,,,>).MakeGenericType( |
||||
|
/* TToken: */ builder.TokenType, |
||||
|
/* TApplication: */ builder.ApplicationType, |
||||
|
/* TAuthorization: */ builder.AuthorizationType, |
||||
|
/* TContext: */ typeof(TContext), |
||||
|
/* TKey: */ token.GenericTypeArguments[0])); |
||||
|
|
||||
|
return builder; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Registers the OpenIddict entity sets in the Entity Framework context
|
||||
|
/// using the default OpenIddict models and the default key type (string).
|
||||
|
/// </summary>
|
||||
|
/// <param name="builder">The builder used to configure the Entity Framework context.</param>
|
||||
|
/// <returns>The Entity Framework context builder.</returns>
|
||||
|
public static DbModelBuilder UseOpenIddict([NotNull] this DbModelBuilder builder) |
||||
|
{ |
||||
|
return builder.UseOpenIddict<OpenIddictApplication, |
||||
|
OpenIddictAuthorization, |
||||
|
OpenIddictScope, |
||||
|
OpenIddictToken, string>(); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Registers the OpenIddict entity sets in the Entity Framework context
|
||||
|
/// using the specified entities and the specified key type.
|
||||
|
/// Note: using this method requires creating non-generic derived classes
|
||||
|
/// for all the OpenIddict entities (application, authorization, scope, token).
|
||||
|
/// </summary>
|
||||
|
/// <param name="builder">The builder used to configure the Entity Framework context.</param>
|
||||
|
/// <returns>The Entity Framework context builder.</returns>
|
||||
|
public static DbModelBuilder UseOpenIddict<TApplication, TAuthorization, TScope, TToken, TKey>([NotNull] this DbModelBuilder builder) |
||||
|
where TApplication : OpenIddictApplication<TKey, TAuthorization, TToken>, new() |
||||
|
where TAuthorization : OpenIddictAuthorization<TKey, TApplication, TToken>, new() |
||||
|
where TScope : OpenIddictScope<TKey>, new() |
||||
|
where TToken : OpenIddictToken<TKey, TApplication, TAuthorization>, new() |
||||
|
where TKey : IEquatable<TKey> |
||||
|
{ |
||||
|
if (builder == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException(nameof(builder)); |
||||
|
} |
||||
|
|
||||
|
// Note: unlike Entity Framework Core 1.x/2.x, Entity Framework 6.x
|
||||
|
// always throws an exception when using generic types as entity types.
|
||||
|
// To ensure a better exception is thrown, a manual check is made here.
|
||||
|
if (typeof(TApplication).GetTypeInfo().IsGenericType) |
||||
|
{ |
||||
|
throw new InvalidOperationException("The application entity cannot be a generic type. " + |
||||
|
"Consider creating a non-generic derived class."); |
||||
|
} |
||||
|
|
||||
|
if (typeof(TAuthorization).GetTypeInfo().IsGenericType) |
||||
|
{ |
||||
|
throw new InvalidOperationException("The authorization entity cannot be a generic type. " + |
||||
|
"Consider creating a non-generic derived class."); |
||||
|
} |
||||
|
|
||||
|
if (typeof(TScope).GetTypeInfo().IsGenericType) |
||||
|
{ |
||||
|
throw new InvalidOperationException("The scope entity cannot be a generic type. " + |
||||
|
"Consider creating a non-generic derived class."); |
||||
|
} |
||||
|
|
||||
|
if (typeof(TToken).GetTypeInfo().IsGenericType) |
||||
|
{ |
||||
|
throw new InvalidOperationException("The scope entity cannot be a generic type. " + |
||||
|
"Consider creating a non-generic derived class."); |
||||
|
} |
||||
|
|
||||
|
// Warning: optional foreign keys MUST NOT be added as CLR properties because
|
||||
|
// Entity Framework would throw an exception due to the TKey generic parameter
|
||||
|
// being non-nullable when using value types like short, int, long or Guid.
|
||||
|
|
||||
|
// Configure the TApplication entity.
|
||||
|
builder.Entity<TApplication>() |
||||
|
.HasKey(application => application.Id); |
||||
|
|
||||
|
builder.Entity<TApplication>() |
||||
|
.Property(application => application.ClientId) |
||||
|
.HasMaxLength(450) |
||||
|
.HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute())); |
||||
|
|
||||
|
builder.Entity<TApplication>() |
||||
|
.HasMany(application => application.Authorizations) |
||||
|
.WithOptional(authorization => authorization.Application) |
||||
|
.Map(association => association.MapKey("ApplicationId")); |
||||
|
|
||||
|
builder.Entity<TApplication>() |
||||
|
.HasMany(application => application.Tokens) |
||||
|
.WithOptional(token => token.Application) |
||||
|
.Map(association => association.MapKey("ApplicationId")); |
||||
|
|
||||
|
builder.Entity<TApplication>() |
||||
|
.ToTable("OpenIddictApplications"); |
||||
|
|
||||
|
// Configure the TAuthorization entity.
|
||||
|
builder.Entity<TAuthorization>() |
||||
|
.HasKey(authorization => authorization.Id); |
||||
|
|
||||
|
builder.Entity<TAuthorization>() |
||||
|
.HasMany(application => application.Tokens) |
||||
|
.WithOptional(token => token.Authorization) |
||||
|
.Map(association => association.MapKey("AuthorizationId")); |
||||
|
|
||||
|
builder.Entity<TAuthorization>() |
||||
|
.ToTable("OpenIddictAuthorizations"); |
||||
|
|
||||
|
// Configure the TScope entity.
|
||||
|
builder.Entity<TScope>() |
||||
|
.HasKey(scope => scope.Id); |
||||
|
|
||||
|
builder.Entity<TScope>() |
||||
|
.ToTable("OpenIddictScopes"); |
||||
|
|
||||
|
// Configure the TToken entity.
|
||||
|
builder.Entity<TToken>() |
||||
|
.HasKey(token => token.Id); |
||||
|
|
||||
|
builder.Entity<TToken>() |
||||
|
.Property(token => token.Hash) |
||||
|
.HasMaxLength(450) |
||||
|
.HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute())); |
||||
|
|
||||
|
builder.Entity<TToken>() |
||||
|
.ToTable("OpenIddictTokens"); |
||||
|
|
||||
|
return builder; |
||||
|
} |
||||
|
|
||||
|
private static TypeInfo FindGenericBaseType(Type type, Type definition) |
||||
|
{ |
||||
|
if (type == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException(nameof(type)); |
||||
|
} |
||||
|
|
||||
|
if (definition == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException(nameof(definition)); |
||||
|
} |
||||
|
|
||||
|
for (var candidate = type.GetTypeInfo(); candidate != null; candidate = candidate.BaseType?.GetTypeInfo()) |
||||
|
{ |
||||
|
if (candidate.IsGenericType && candidate.GetGenericTypeDefinition() == definition) |
||||
|
{ |
||||
|
return candidate; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,225 @@ |
|||||
|
/* |
||||
|
* 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.Data.Entity; |
||||
|
using System.Data.Entity.Infrastructure; |
||||
|
using System.Linq; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using JetBrains.Annotations; |
||||
|
using OpenIddict.Core; |
||||
|
using OpenIddict.Models; |
||||
|
|
||||
|
namespace OpenIddict.EntityFramework |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Provides methods allowing to manage the applications stored in a database.
|
||||
|
/// Note: this class can only be used with the default OpenIddict entities.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TContext">The type of the Entity Framework database context.</typeparam>
|
||||
|
public class OpenIddictApplicationStore<TContext> : OpenIddictApplicationStore<OpenIddictApplication, |
||||
|
OpenIddictAuthorization, |
||||
|
OpenIddictToken, TContext, string> |
||||
|
where TContext : DbContext |
||||
|
{ |
||||
|
public OpenIddictApplicationStore([NotNull] TContext context) : base(context) { } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Provides methods allowing to manage the applications stored in a database.
|
||||
|
/// Note: this class can only be used with the default OpenIddict entities.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TContext">The type of the Entity Framework database context.</typeparam>
|
||||
|
/// <typeparam name="TKey">The type of the entity primary keys.</typeparam>
|
||||
|
public class OpenIddictApplicationStore<TContext, TKey> : OpenIddictApplicationStore<OpenIddictApplication<TKey>, |
||||
|
OpenIddictAuthorization<TKey>, |
||||
|
OpenIddictToken<TKey>, TContext, TKey> |
||||
|
where TContext : DbContext |
||||
|
where TKey : IEquatable<TKey> |
||||
|
{ |
||||
|
public OpenIddictApplicationStore([NotNull] TContext context) : base(context) { } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Provides methods allowing to manage the applications stored in a database.
|
||||
|
/// Note: this class can only be used with the default OpenIddict entities.
|
||||
|
/// </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="TContext">The type of the Entity Framework database context.</typeparam>
|
||||
|
/// <typeparam name="TKey">The type of the entity primary keys.</typeparam>
|
||||
|
public class OpenIddictApplicationStore<TApplication, TAuthorization, TToken, TContext, TKey> : |
||||
|
OpenIddictApplicationStore<TApplication, TAuthorization, TToken, TKey> |
||||
|
where TApplication : OpenIddictApplication<TKey, TAuthorization, TToken>, new() |
||||
|
where TAuthorization : OpenIddictAuthorization<TKey, TApplication, TToken>, new() |
||||
|
where TToken : OpenIddictToken<TKey, TApplication, TAuthorization>, new() |
||||
|
where TContext : DbContext |
||||
|
where TKey : IEquatable<TKey> |
||||
|
{ |
||||
|
public OpenIddictApplicationStore([NotNull] TContext context) |
||||
|
{ |
||||
|
if (context == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException(nameof(context)); |
||||
|
} |
||||
|
|
||||
|
Context = context; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the database context associated with the current store.
|
||||
|
/// </summary>
|
||||
|
protected virtual TContext Context { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the database set corresponding to the <typeparamref name="TApplication"/> entity.
|
||||
|
/// </summary>
|
||||
|
protected DbSet<TApplication> Applications => Context.Set<TApplication>(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Creates a new application.
|
||||
|
/// </summary>
|
||||
|
/// <param name="application">The application to create.</param>
|
||||
|
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
||||
|
/// <returns>
|
||||
|
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation, whose result returns the application.
|
||||
|
/// </returns>
|
||||
|
public override async Task<TApplication> CreateAsync([NotNull] TApplication application, CancellationToken cancellationToken) |
||||
|
{ |
||||
|
if (application == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException(nameof(application)); |
||||
|
} |
||||
|
|
||||
|
Applications.Add(application); |
||||
|
|
||||
|
await Context.SaveChangesAsync(cancellationToken); |
||||
|
|
||||
|
return application; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Creates a new application.
|
||||
|
/// </summary>
|
||||
|
/// <param name="descriptor">The application descriptor.</param>
|
||||
|
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
||||
|
/// <returns>
|
||||
|
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation, whose result returns the application.
|
||||
|
/// </returns>
|
||||
|
public override Task<TApplication> CreateAsync([NotNull] OpenIddictApplicationDescriptor descriptor, CancellationToken cancellationToken) |
||||
|
{ |
||||
|
if (descriptor == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException(nameof(descriptor)); |
||||
|
} |
||||
|
|
||||
|
var application = new TApplication |
||||
|
{ |
||||
|
ClientId = descriptor.ClientId, |
||||
|
ClientSecret = descriptor.ClientSecret, |
||||
|
DisplayName = descriptor.DisplayName, |
||||
|
LogoutRedirectUri = descriptor.LogoutRedirectUri, |
||||
|
RedirectUri = descriptor.RedirectUri, |
||||
|
Type = descriptor.Type |
||||
|
}; |
||||
|
|
||||
|
return CreateAsync(application, cancellationToken); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Removes an existing application.
|
||||
|
/// </summary>
|
||||
|
/// <param name="application">The application to delete.</param>
|
||||
|
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
||||
|
/// <returns>
|
||||
|
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
||||
|
/// </returns>
|
||||
|
public override async Task DeleteAsync([NotNull] TApplication application, CancellationToken cancellationToken) |
||||
|
{ |
||||
|
if (application == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException(nameof(application)); |
||||
|
} |
||||
|
|
||||
|
Applications.Remove(application); |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
await Context.SaveChangesAsync(cancellationToken); |
||||
|
} |
||||
|
|
||||
|
catch (DbUpdateConcurrencyException) { } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Executes 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="Task"/> that can be used to monitor the asynchronous operation,
|
||||
|
/// whose result returns the single element returned when executing the specified query.
|
||||
|
/// </returns>
|
||||
|
public override Task<TResult> GetAsync<TResult>([NotNull] Func<IQueryable<TApplication>, IQueryable<TResult>> query, CancellationToken cancellationToken) |
||||
|
{ |
||||
|
if (query == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException(nameof(query)); |
||||
|
} |
||||
|
|
||||
|
return query.Invoke(Applications).SingleOrDefaultAsync(cancellationToken); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Executes 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="Task"/> that can be used to monitor the asynchronous operation,
|
||||
|
/// whose result returns all the elements returned when executing the specified query.
|
||||
|
/// </returns>
|
||||
|
public override Task<TResult[]> ListAsync<TResult>([NotNull] Func<IQueryable<TApplication>, IQueryable<TResult>> query, CancellationToken cancellationToken) |
||||
|
{ |
||||
|
if (query == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException(nameof(query)); |
||||
|
} |
||||
|
|
||||
|
return query.Invoke(Applications).ToArrayAsync(cancellationToken); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Updates an existing application.
|
||||
|
/// </summary>
|
||||
|
/// <param name="application">The application to update.</param>
|
||||
|
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
||||
|
/// <returns>
|
||||
|
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
||||
|
/// </returns>
|
||||
|
public override async Task UpdateAsync([NotNull] TApplication application, CancellationToken cancellationToken) |
||||
|
{ |
||||
|
if (application == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException(nameof(application)); |
||||
|
} |
||||
|
|
||||
|
Applications.Attach(application); |
||||
|
Context.Entry(application).State = EntityState.Modified; |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
await Context.SaveChangesAsync(cancellationToken); |
||||
|
} |
||||
|
|
||||
|
catch (DbUpdateConcurrencyException) { } |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,211 @@ |
|||||
|
/* |
||||
|
* 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.Data.Entity; |
||||
|
using System.Data.Entity.Infrastructure; |
||||
|
using System.Linq; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using JetBrains.Annotations; |
||||
|
using OpenIddict.Core; |
||||
|
using OpenIddict.Models; |
||||
|
|
||||
|
namespace OpenIddict.EntityFramework |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Provides methods allowing to manage the authorizations stored in a database.
|
||||
|
/// Note: this class can only be used with the default OpenIddict entities.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TContext">The type of the Entity Framework database context.</typeparam>
|
||||
|
public class OpenIddictAuthorizationStore<TContext> : OpenIddictAuthorizationStore<OpenIddictAuthorization, |
||||
|
OpenIddictApplication, |
||||
|
OpenIddictToken, TContext, string> |
||||
|
where TContext : DbContext |
||||
|
{ |
||||
|
public OpenIddictAuthorizationStore([NotNull] TContext context) : base(context) { } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Provides methods allowing to manage the authorizations stored in a database.
|
||||
|
/// Note: this class can only be used with the default OpenIddict entities.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TContext">The type of the Entity Framework database context.</typeparam>
|
||||
|
/// <typeparam name="TKey">The type of the entity primary keys.</typeparam>
|
||||
|
public class OpenIddictAuthorizationStore<TContext, TKey> : OpenIddictAuthorizationStore<OpenIddictAuthorization<TKey>, |
||||
|
OpenIddictApplication<TKey>, |
||||
|
OpenIddictToken<TKey>, TContext, TKey> |
||||
|
where TContext : DbContext |
||||
|
where TKey : IEquatable<TKey> |
||||
|
{ |
||||
|
public OpenIddictAuthorizationStore([NotNull] TContext context) : base(context) { } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Provides methods allowing to manage the authorizations stored in a database.
|
||||
|
/// Note: this class can only be used with the default OpenIddict entities.
|
||||
|
/// </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="TContext">The type of the Entity Framework database context.</typeparam>
|
||||
|
/// <typeparam name="TKey">The type of the entity primary keys.</typeparam>
|
||||
|
public class OpenIddictAuthorizationStore<TAuthorization, TApplication, TToken, TContext, TKey> : |
||||
|
OpenIddictAuthorizationStore<TAuthorization, TApplication, TToken, TKey> |
||||
|
where TAuthorization : OpenIddictAuthorization<TKey, TApplication, TToken>, new() |
||||
|
where TApplication : OpenIddictApplication<TKey, TAuthorization, TToken>, new() |
||||
|
where TToken : OpenIddictToken<TKey, TApplication, TAuthorization>, new() |
||||
|
where TContext : DbContext |
||||
|
where TKey : IEquatable<TKey> |
||||
|
{ |
||||
|
public OpenIddictAuthorizationStore([NotNull] TContext context) |
||||
|
{ |
||||
|
if (context == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException(nameof(context)); |
||||
|
} |
||||
|
|
||||
|
Context = context; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the database context associated with the current store.
|
||||
|
/// </summary>
|
||||
|
protected virtual TContext Context { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the database set corresponding to the <typeparamref name="TApplication"/> entity.
|
||||
|
/// </summary>
|
||||
|
protected DbSet<TApplication> Applications => Context.Set<TApplication>(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the database set corresponding to the <typeparamref name="TAuthorization"/> entity.
|
||||
|
/// </summary>
|
||||
|
protected DbSet<TAuthorization> Authorizations => Context.Set<TAuthorization>(); |
||||
|
|
||||
|
/// <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="Task"/> that can be used to monitor the asynchronous operation, whose result returns the authorization.
|
||||
|
/// </returns>
|
||||
|
public override async Task<TAuthorization> CreateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) |
||||
|
{ |
||||
|
if (authorization == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException(nameof(authorization)); |
||||
|
} |
||||
|
|
||||
|
Authorizations.Add(authorization); |
||||
|
|
||||
|
await Context.SaveChangesAsync(cancellationToken); |
||||
|
|
||||
|
return authorization; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Creates a new authorization.
|
||||
|
/// </summary>
|
||||
|
/// <param name="descriptor">The authorization descriptor.</param>
|
||||
|
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
||||
|
/// <returns>
|
||||
|
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation, whose result returns the authorization.
|
||||
|
/// </returns>
|
||||
|
public override async Task<TAuthorization> CreateAsync([NotNull] OpenIddictAuthorizationDescriptor descriptor, CancellationToken cancellationToken) |
||||
|
{ |
||||
|
if (descriptor == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException(nameof(descriptor)); |
||||
|
} |
||||
|
|
||||
|
var authorization = new TAuthorization |
||||
|
{ |
||||
|
Scope = string.Join(" ", descriptor.Scopes), |
||||
|
Subject = descriptor.Subject |
||||
|
}; |
||||
|
|
||||
|
// Bind the authorization to the specified application, if applicable.
|
||||
|
if (!string.IsNullOrEmpty(descriptor.ApplicationId)) |
||||
|
{ |
||||
|
var key = ConvertIdentifierFromString(descriptor.ApplicationId); |
||||
|
|
||||
|
var application = await Applications.SingleOrDefaultAsync(entity => entity.Id.Equals(key)); |
||||
|
if (application == null) |
||||
|
{ |
||||
|
throw new InvalidOperationException("The application associated with the authorization cannot be found."); |
||||
|
} |
||||
|
|
||||
|
authorization.Application = application; |
||||
|
} |
||||
|
|
||||
|
return await CreateAsync(authorization, cancellationToken); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Executes 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="Task"/> that can be used to monitor the asynchronous operation,
|
||||
|
/// whose result returns the single element returned when executing the specified query.
|
||||
|
/// </returns>
|
||||
|
public override Task<TResult> GetAsync<TResult>([NotNull] Func<IQueryable<TAuthorization>, IQueryable<TResult>> query, CancellationToken cancellationToken) |
||||
|
{ |
||||
|
if (query == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException(nameof(query)); |
||||
|
} |
||||
|
|
||||
|
return query.Invoke(Authorizations).SingleOrDefaultAsync(cancellationToken); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Executes 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="Task"/> that can be used to monitor the asynchronous operation,
|
||||
|
/// whose result returns all the elements returned when executing the specified query.
|
||||
|
/// </returns>
|
||||
|
public override Task<TResult[]> ListAsync<TResult>([NotNull] Func<IQueryable<TAuthorization>, IQueryable<TResult>> query, CancellationToken cancellationToken) |
||||
|
{ |
||||
|
if (query == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException(nameof(query)); |
||||
|
} |
||||
|
|
||||
|
return query.Invoke(Authorizations).ToArrayAsync(cancellationToken); |
||||
|
} |
||||
|
|
||||
|
/// <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="Task"/> that can be used to monitor the asynchronous operation.
|
||||
|
/// </returns>
|
||||
|
public override async Task UpdateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) |
||||
|
{ |
||||
|
|
||||
|
Authorizations.Attach(authorization); |
||||
|
Context.Entry(authorization).State = EntityState.Modified; |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
await Context.SaveChangesAsync(cancellationToken); |
||||
|
} |
||||
|
|
||||
|
catch (DbUpdateConcurrencyException) { } |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,113 @@ |
|||||
|
/* |
||||
|
* 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.Data.Entity; |
||||
|
using System.Linq; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using JetBrains.Annotations; |
||||
|
using OpenIddict.Models; |
||||
|
|
||||
|
namespace OpenIddict.EntityFramework |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Provides methods allowing to manage the scopes stored in a database.
|
||||
|
/// Note: this class can only be used with the default OpenIddict entities.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TContext">The type of the Entity Framework database context.</typeparam>
|
||||
|
public class OpenIddictScopeStore<TContext> : OpenIddictScopeStore<OpenIddictScope, TContext, string> |
||||
|
where TContext : DbContext |
||||
|
{ |
||||
|
public OpenIddictScopeStore([NotNull] TContext context) : base(context) { } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Provides methods allowing to manage the scopes stored in a database.
|
||||
|
/// Note: this class can only be used with the default OpenIddict entities.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TContext">The type of the Entity Framework database context.</typeparam>
|
||||
|
/// <typeparam name="TKey">The type of the entity primary keys.</typeparam>
|
||||
|
public class OpenIddictScopeStore<TContext, TKey> : OpenIddictScopeStore<OpenIddictScope<TKey>, TContext, TKey> |
||||
|
where TContext : DbContext |
||||
|
where TKey : IEquatable<TKey> |
||||
|
{ |
||||
|
public OpenIddictScopeStore([NotNull] TContext context) : base(context) { } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Provides methods allowing to manage the scopes stored in a database.
|
||||
|
/// Note: this class can only be used with the default OpenIddict entities.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TScope">The type of the Scope entity.</typeparam>
|
||||
|
/// <typeparam name="TContext">The type of the Entity Framework database context.</typeparam>
|
||||
|
/// <typeparam name="TKey">The type of the entity primary keys.</typeparam>
|
||||
|
public class OpenIddictScopeStore<TScope, TContext, TKey> : Core.OpenIddictScopeStore<TScope, TKey> |
||||
|
where TScope : OpenIddictScope<TKey>, new() |
||||
|
where TContext : DbContext |
||||
|
where TKey : IEquatable<TKey> |
||||
|
{ |
||||
|
public OpenIddictScopeStore([NotNull] TContext context) |
||||
|
{ |
||||
|
if (context == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException(nameof(context)); |
||||
|
} |
||||
|
|
||||
|
Context = context; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the database context associated with the current store.
|
||||
|
/// </summary>
|
||||
|
protected virtual TContext Context { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the database set corresponding to the <typeparamref name="TScope"/> entity.
|
||||
|
/// </summary>
|
||||
|
protected DbSet<TScope> Scopes => Context.Set<TScope>(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Executes 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="Task"/> that can be used to monitor the asynchronous operation,
|
||||
|
/// whose result returns the single element returned when executing the specified query.
|
||||
|
/// </returns>
|
||||
|
public override Task<TResult> GetAsync<TResult>([NotNull] Func<IQueryable<TScope>, IQueryable<TResult>> query, CancellationToken cancellationToken) |
||||
|
{ |
||||
|
if (query == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException(nameof(query)); |
||||
|
} |
||||
|
|
||||
|
return query.Invoke(Scopes).SingleOrDefaultAsync(cancellationToken); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Executes 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="Task"/> that can be used to monitor the asynchronous operation,
|
||||
|
/// whose result returns all the elements returned when executing the specified query.
|
||||
|
/// </returns>
|
||||
|
public override Task<TResult[]> ListAsync<TResult>([NotNull] Func<IQueryable<TScope>, IQueryable<TResult>> query, CancellationToken cancellationToken) |
||||
|
{ |
||||
|
if (query == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException(nameof(query)); |
||||
|
} |
||||
|
|
||||
|
return query.Invoke(Scopes).ToArrayAsync(cancellationToken); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,347 @@ |
|||||
|
/* |
||||
|
* 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.Data.Entity; |
||||
|
using System.Data.Entity.Infrastructure; |
||||
|
using System.Linq; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using JetBrains.Annotations; |
||||
|
using OpenIddict.Core; |
||||
|
using OpenIddict.Models; |
||||
|
|
||||
|
namespace OpenIddict.EntityFramework |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Provides methods allowing to manage the tokens stored in a database.
|
||||
|
/// Note: this class can only be used with the default OpenIddict entities.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TContext">The type of the Entity Framework database context.</typeparam>
|
||||
|
public class OpenIddictTokenStore<TContext> : OpenIddictTokenStore<OpenIddictToken, |
||||
|
OpenIddictApplication, |
||||
|
OpenIddictAuthorization, TContext, string> |
||||
|
where TContext : DbContext |
||||
|
{ |
||||
|
public OpenIddictTokenStore([NotNull] TContext context) : base(context) { } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Provides methods allowing to manage the tokens stored in a database.
|
||||
|
/// Note: this class can only be used with the default OpenIddict entities.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TContext">The type of the Entity Framework database context.</typeparam>
|
||||
|
/// <typeparam name="TKey">The type of the entity primary keys.</typeparam>
|
||||
|
public class OpenIddictTokenStore<TContext, TKey> : OpenIddictTokenStore<OpenIddictToken<TKey>, |
||||
|
OpenIddictApplication<TKey>, |
||||
|
OpenIddictAuthorization<TKey>, TContext, TKey> |
||||
|
where TContext : DbContext |
||||
|
where TKey : IEquatable<TKey> |
||||
|
{ |
||||
|
public OpenIddictTokenStore([NotNull] TContext context) : base(context) { } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Provides methods allowing to manage the tokens stored in a database.
|
||||
|
/// Note: this class can only be used with the default OpenIddict entities.
|
||||
|
/// </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="TContext">The type of the Entity Framework database context.</typeparam>
|
||||
|
/// <typeparam name="TKey">The type of the entity primary keys.</typeparam>
|
||||
|
public class OpenIddictTokenStore<TToken, TApplication, TAuthorization, TContext, TKey> : |
||||
|
OpenIddictTokenStore<TToken, TApplication, TAuthorization, TKey> |
||||
|
where TToken : OpenIddictToken<TKey, TApplication, TAuthorization>, new() |
||||
|
where TApplication : OpenIddictApplication<TKey, TAuthorization, TToken>, new() |
||||
|
where TAuthorization : OpenIddictAuthorization<TKey, TApplication, TToken>, new() |
||||
|
where TContext : DbContext |
||||
|
where TKey : IEquatable<TKey> |
||||
|
{ |
||||
|
public OpenIddictTokenStore([NotNull] TContext context) |
||||
|
{ |
||||
|
if (context == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException(nameof(context)); |
||||
|
} |
||||
|
|
||||
|
Context = context; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the database context associated with the current store.
|
||||
|
/// </summary>
|
||||
|
protected virtual TContext Context { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the database set corresponding to the <typeparamref name="TApplication"/> entity.
|
||||
|
/// </summary>
|
||||
|
protected DbSet<TApplication> Applications => Context.Set<TApplication>(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the database set corresponding to the <typeparamref name="TAuthorization"/> entity.
|
||||
|
/// </summary>
|
||||
|
protected DbSet<TAuthorization> Authorizations => Context.Set<TAuthorization>(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the database set corresponding to the <typeparamref name="TToken"/> entity.
|
||||
|
/// </summary>
|
||||
|
protected DbSet<TToken> Tokens => Context.Set<TToken>(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Creates a new token.
|
||||
|
/// </summary>
|
||||
|
/// <param name="token">The token to create.</param>
|
||||
|
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
||||
|
/// <returns>
|
||||
|
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation, whose result returns the token.
|
||||
|
/// </returns>
|
||||
|
public override async Task<TToken> CreateAsync([NotNull] TToken token, CancellationToken cancellationToken) |
||||
|
{ |
||||
|
if (token == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException(nameof(token)); |
||||
|
} |
||||
|
|
||||
|
Tokens.Add(token); |
||||
|
|
||||
|
await Context.SaveChangesAsync(cancellationToken); |
||||
|
|
||||
|
return token; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Creates a new token, which is associated with a particular subject.
|
||||
|
/// </summary>
|
||||
|
/// <param name="descriptor">The token descriptor.</param>
|
||||
|
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
||||
|
/// <returns>
|
||||
|
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation, whose result returns the token.
|
||||
|
/// </returns>
|
||||
|
public override async Task<TToken> CreateAsync([NotNull] OpenIddictTokenDescriptor descriptor, CancellationToken cancellationToken) |
||||
|
{ |
||||
|
if (descriptor == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException(nameof(descriptor)); |
||||
|
} |
||||
|
|
||||
|
var token = new TToken |
||||
|
{ |
||||
|
Ciphertext = descriptor.Ciphertext, |
||||
|
CreationDate = descriptor.CreationDate, |
||||
|
ExpirationDate = descriptor.ExpirationDate, |
||||
|
Hash = descriptor.Hash, |
||||
|
Subject = descriptor.Subject, |
||||
|
Type = descriptor.Type |
||||
|
}; |
||||
|
|
||||
|
// Bind the token to the specified client application, if applicable.
|
||||
|
if (!string.IsNullOrEmpty(descriptor.ApplicationId)) |
||||
|
{ |
||||
|
var key = ConvertIdentifierFromString(descriptor.ApplicationId); |
||||
|
|
||||
|
var application = await Applications.SingleOrDefaultAsync(entity => entity.Id.Equals(key)); |
||||
|
if (application == null) |
||||
|
{ |
||||
|
throw new InvalidOperationException("The application associated with the token cannot be found."); |
||||
|
} |
||||
|
|
||||
|
token.Application = application; |
||||
|
} |
||||
|
|
||||
|
// Bind the token to the specified authorization, if applicable.
|
||||
|
if (!string.IsNullOrEmpty(descriptor.AuthorizationId)) |
||||
|
{ |
||||
|
var key = ConvertIdentifierFromString(descriptor.AuthorizationId); |
||||
|
|
||||
|
var authorization = await Authorizations.SingleOrDefaultAsync(entity => entity.Id.Equals(key)); |
||||
|
if (authorization == null) |
||||
|
{ |
||||
|
throw new InvalidOperationException("The authorization associated with the token cannot be found."); |
||||
|
} |
||||
|
|
||||
|
token.Authorization = authorization; |
||||
|
} |
||||
|
|
||||
|
return await CreateAsync(token, cancellationToken); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Removes a token.
|
||||
|
/// </summary>
|
||||
|
/// <param name="token">The token to delete.</param>
|
||||
|
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
||||
|
/// <returns>A <see cref="Task"/> that can be used to monitor the asynchronous operation.</returns>
|
||||
|
public override async Task DeleteAsync([NotNull] TToken token, CancellationToken cancellationToken) |
||||
|
{ |
||||
|
if (token == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException(nameof(token)); |
||||
|
} |
||||
|
|
||||
|
Tokens.Remove(token); |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
await Context.SaveChangesAsync(cancellationToken); |
||||
|
} |
||||
|
|
||||
|
catch (DbUpdateConcurrencyException) { } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Executes 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="Task"/> that can be used to monitor the asynchronous operation,
|
||||
|
/// whose result returns the single element returned when executing the specified query.
|
||||
|
/// </returns>
|
||||
|
public override Task<TResult> GetAsync<TResult>([NotNull] Func<IQueryable<TToken>, IQueryable<TResult>> query, CancellationToken cancellationToken) |
||||
|
{ |
||||
|
if (query == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException(nameof(query)); |
||||
|
} |
||||
|
|
||||
|
return query.Invoke(Tokens).SingleOrDefaultAsync(cancellationToken); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Executes 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="Task"/> that can be used to monitor the asynchronous operation,
|
||||
|
/// whose result returns all the elements returned when executing the specified query.
|
||||
|
/// </returns>
|
||||
|
public override Task<TResult[]> ListAsync<TResult>([NotNull] Func<IQueryable<TToken>, IQueryable<TResult>> query, CancellationToken cancellationToken) |
||||
|
{ |
||||
|
if (query == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException(nameof(query)); |
||||
|
} |
||||
|
|
||||
|
return query.Invoke(Tokens).ToArrayAsync(cancellationToken); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Sets the authorization associated with a token.
|
||||
|
/// </summary>
|
||||
|
/// <param name="token">The token.</param>
|
||||
|
/// <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="Task"/> that can be used to monitor the asynchronous operation.
|
||||
|
/// </returns>
|
||||
|
public override async Task SetAuthorizationAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken) |
||||
|
{ |
||||
|
if (token == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException(nameof(token)); |
||||
|
} |
||||
|
|
||||
|
if (!string.IsNullOrEmpty(identifier)) |
||||
|
{ |
||||
|
var key = ConvertIdentifierFromString(identifier); |
||||
|
|
||||
|
var authorization = await Authorizations.SingleOrDefaultAsync(element => element.Id.Equals(key)); |
||||
|
if (authorization == null) |
||||
|
{ |
||||
|
throw new InvalidOperationException("The authorization associated with the token cannot be found."); |
||||
|
} |
||||
|
|
||||
|
authorization.Tokens.Add(token); |
||||
|
} |
||||
|
|
||||
|
else |
||||
|
{ |
||||
|
var key = await GetIdAsync(token, cancellationToken); |
||||
|
|
||||
|
// Try to retrieve the authorization associated with the token.
|
||||
|
// If none can be found, assume that no authorization is attached.
|
||||
|
var authorization = await Authorizations.SingleOrDefaultAsync(element => element.Tokens.Any(t => t.Id.Equals(key))); |
||||
|
if (authorization != null) |
||||
|
{ |
||||
|
authorization.Tokens.Remove(token); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Sets the client application associated with a token.
|
||||
|
/// </summary>
|
||||
|
/// <param name="token">The token.</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="Task"/> that can be used to monitor the asynchronous operation.
|
||||
|
/// </returns>
|
||||
|
public override async Task SetClientAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken) |
||||
|
{ |
||||
|
if (token == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException(nameof(token)); |
||||
|
} |
||||
|
|
||||
|
if (!string.IsNullOrEmpty(identifier)) |
||||
|
{ |
||||
|
var key = ConvertIdentifierFromString(identifier); |
||||
|
|
||||
|
var application = await Applications.SingleOrDefaultAsync(element => element.Id.Equals(key)); |
||||
|
if (application == null) |
||||
|
{ |
||||
|
throw new InvalidOperationException("The application associated with the token cannot be found."); |
||||
|
} |
||||
|
|
||||
|
application.Tokens.Add(token); |
||||
|
} |
||||
|
|
||||
|
else |
||||
|
{ |
||||
|
var key = await GetIdAsync(token, cancellationToken); |
||||
|
|
||||
|
// Try to retrieve the application associated with the token.
|
||||
|
// If none can be found, assume that no application is attached.
|
||||
|
var application = await Applications.SingleOrDefaultAsync(element => element.Tokens.Any(t => t.Id.Equals(key))); |
||||
|
if (application != null) |
||||
|
{ |
||||
|
application.Tokens.Remove(token); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Updates an existing token.
|
||||
|
/// </summary>
|
||||
|
/// <param name="token">The token to update.</param>
|
||||
|
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
||||
|
/// <returns>
|
||||
|
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
|
||||
|
/// </returns>
|
||||
|
public override async Task UpdateAsync([NotNull] TToken token, CancellationToken cancellationToken) |
||||
|
{ |
||||
|
if (token == null) |
||||
|
{ |
||||
|
throw new ArgumentNullException(nameof(token)); |
||||
|
} |
||||
|
|
||||
|
Tokens.Attach(token); |
||||
|
Context.Entry(token).State = EntityState.Modified; |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
await Context.SaveChangesAsync(cancellationToken); |
||||
|
} |
||||
|
|
||||
|
catch (DbUpdateConcurrencyException) { } |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,25 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<Import Project="..\..\build\tests.props" /> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>net461</TargetFramework> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\..\src\OpenIddict.EntityFramework\OpenIddict.EntityFramework.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(AspNetCoreVersion)" /> |
||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(TestSdkVersion)" /> |
||||
|
<PackageReference Include="Moq" Version="$(MoqVersion)" /> |
||||
|
<PackageReference Include="xunit" Version="$(XunitVersion)" /> |
||||
|
<PackageReference Include="xunit.runner.visualstudio" Version="$(XunitVersion)" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,161 @@ |
|||||
|
using System; |
||||
|
using System.Data.Entity; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using OpenIddict.EntityFramework; |
||||
|
using OpenIddict.Models; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace OpenIddict.EntityFrameworkCore.Tests |
||||
|
{ |
||||
|
public class OpenIddictExtensionsTests |
||||
|
{ |
||||
|
[Fact] |
||||
|
public void AddEntityFrameworkStores_ThrowsAnExceptionForInvalidApplicationEntity() |
||||
|
{ |
||||
|
// Arrange
|
||||
|
var builder = new OpenIddictBuilder(new ServiceCollection()) |
||||
|
{ |
||||
|
ApplicationType = typeof(object) |
||||
|
}; |
||||
|
|
||||
|
// Act and assert
|
||||
|
var exception = Assert.Throws<InvalidOperationException>(delegate |
||||
|
{ |
||||
|
builder.AddEntityFrameworkStores<DbContext>(); |
||||
|
}); |
||||
|
|
||||
|
Assert.Equal("The Entity Framework stores can only be used " + |
||||
|
"with the built-in OpenIddictApplication entity.", exception.Message); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void AddEntityFrameworkStores_ThrowsAnExceptionForInvalidAuthorizationEntity() |
||||
|
{ |
||||
|
// Arrange
|
||||
|
var builder = new OpenIddictBuilder(new ServiceCollection()) |
||||
|
{ |
||||
|
AuthorizationType = typeof(object) |
||||
|
}; |
||||
|
|
||||
|
// Act and assert
|
||||
|
var exception = Assert.Throws<InvalidOperationException>(delegate |
||||
|
{ |
||||
|
builder.AddEntityFrameworkStores<DbContext>(); |
||||
|
}); |
||||
|
|
||||
|
Assert.Equal("The Entity Framework stores can only be used " + |
||||
|
"with the built-in OpenIddictAuthorization entity.", exception.Message); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void AddEntityFrameworkStores_ThrowsAnExceptionForInvalidScopeEntity() |
||||
|
{ |
||||
|
// Arrange
|
||||
|
var builder = new OpenIddictBuilder(new ServiceCollection()) |
||||
|
{ |
||||
|
ScopeType = typeof(object) |
||||
|
}; |
||||
|
|
||||
|
// Act and assert
|
||||
|
var exception = Assert.Throws<InvalidOperationException>(delegate |
||||
|
{ |
||||
|
builder.AddEntityFrameworkStores<DbContext>(); |
||||
|
}); |
||||
|
|
||||
|
Assert.Equal("The Entity Framework stores can only be used " + |
||||
|
"with the built-in OpenIddictScope entity.", exception.Message); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void AddEntityFrameworkStores_ThrowsAnExceptionForInvalidTokenEntity() |
||||
|
{ |
||||
|
// Arrange
|
||||
|
var builder = new OpenIddictBuilder(new ServiceCollection()) |
||||
|
{ |
||||
|
TokenType = typeof(object) |
||||
|
}; |
||||
|
|
||||
|
// Act and assert
|
||||
|
var exception = Assert.Throws<InvalidOperationException>(delegate |
||||
|
{ |
||||
|
builder.AddEntityFrameworkStores<DbContext>(); |
||||
|
}); |
||||
|
|
||||
|
Assert.Equal("The Entity Framework stores can only be used " + |
||||
|
"with the built-in OpenIddictToken entity.", exception.Message); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData(typeof(OpenIddictApplicationStore<OpenIddictApplication, OpenIddictAuthorization, OpenIddictToken, DbContext, string>))] |
||||
|
[InlineData(typeof(OpenIddictAuthorizationStore<OpenIddictAuthorization, OpenIddictApplication, OpenIddictToken, DbContext, string>))] |
||||
|
[InlineData(typeof(OpenIddictScopeStore<OpenIddictScope, DbContext, string>))] |
||||
|
[InlineData(typeof(OpenIddictTokenStore<OpenIddictToken, OpenIddictApplication, OpenIddictAuthorization, DbContext, string>))] |
||||
|
public void AddEntityFrameworkStores_RegistersEntityFrameworkStores(Type type) |
||||
|
{ |
||||
|
// Arrange
|
||||
|
var services = new ServiceCollection(); |
||||
|
var builder = new OpenIddictBuilder(services); |
||||
|
|
||||
|
// Act
|
||||
|
builder.AddEntityFrameworkStores<DbContext>(); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Contains(services, service => service.ImplementationType == type); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData(typeof(OpenIddictApplicationStore<OpenIddictApplication<Guid>, OpenIddictAuthorization<Guid>, OpenIddictToken<Guid>, DbContext, Guid>))] |
||||
|
[InlineData(typeof(OpenIddictAuthorizationStore<OpenIddictAuthorization<Guid>, OpenIddictApplication<Guid>, OpenIddictToken<Guid>, DbContext, Guid>))] |
||||
|
[InlineData(typeof(OpenIddictScopeStore<OpenIddictScope<Guid>, DbContext, Guid>))] |
||||
|
[InlineData(typeof(OpenIddictTokenStore<OpenIddictToken<Guid>, OpenIddictApplication<Guid>, OpenIddictAuthorization<Guid>, DbContext, Guid>))] |
||||
|
public void AddEntityFrameworkStores_KeyTypeIsInferredFromEntities(Type type) |
||||
|
{ |
||||
|
// Arrange
|
||||
|
var services = new ServiceCollection(); |
||||
|
|
||||
|
var builder = new OpenIddictBuilder(services) |
||||
|
{ |
||||
|
ApplicationType = typeof(OpenIddictApplication<Guid>), |
||||
|
AuthorizationType = typeof(OpenIddictAuthorization<Guid>), |
||||
|
ScopeType = typeof(OpenIddictScope<Guid>), |
||||
|
TokenType = typeof(OpenIddictToken<Guid>) |
||||
|
}; |
||||
|
|
||||
|
// Act
|
||||
|
builder.AddEntityFrameworkStores<DbContext>(); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Contains(services, service => service.ImplementationType == type); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData(typeof(OpenIddictApplicationStore<CustomApplication, CustomAuthorization, CustomToken, DbContext, long>))] |
||||
|
[InlineData(typeof(OpenIddictAuthorizationStore<CustomAuthorization, CustomApplication, CustomToken, DbContext, long>))] |
||||
|
[InlineData(typeof(OpenIddictScopeStore<CustomScope, DbContext, long>))] |
||||
|
[InlineData(typeof(OpenIddictTokenStore<CustomToken, CustomApplication, CustomAuthorization, DbContext, long>))] |
||||
|
public void AddEntityFrameworkStores_DefaultEntitiesCanBeReplaced(Type type) |
||||
|
{ |
||||
|
// Arrange
|
||||
|
var services = new ServiceCollection(); |
||||
|
|
||||
|
var builder = new OpenIddictBuilder(services) |
||||
|
{ |
||||
|
ApplicationType = typeof(CustomApplication), |
||||
|
AuthorizationType = typeof(CustomAuthorization), |
||||
|
ScopeType = typeof(CustomScope), |
||||
|
TokenType = typeof(CustomToken) |
||||
|
}; |
||||
|
|
||||
|
// Act
|
||||
|
builder.AddEntityFrameworkStores<DbContext>(); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Contains(services, service => service.ImplementationType == type); |
||||
|
} |
||||
|
|
||||
|
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> { } |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue