diff --git a/OpenIddict.sln b/OpenIddict.sln index 786458c7..998f73a6 100644 --- a/OpenIddict.sln +++ b/OpenIddict.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26228.4 +VisualStudioVersion = 15.0.26730.12 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{D544447C-D701-46BB-9A5B-C76C612A596B}" EndProject @@ -31,6 +31,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenIddict.Tests", "test\Op EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenIddict.Models", "src\OpenIddict.Models\OpenIddict.Models.csproj", "{0102A6CC-41A6-4B34-B49E-65AFE95882BB}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenIddict.EntityFramework", "src\OpenIddict.EntityFramework\OpenIddict.EntityFramework.csproj", "{BF42CC6C-0B56-4F66-9866-18B8393F3C06}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenIddict.EntityFramework.Tests", "test\OpenIddict.EntityFramework.Tests\OpenIddict.EntityFramework.Tests.csproj", "{96325E37-9897-43AC-8408-7B17F58E8788}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -81,6 +85,14 @@ Global {0102A6CC-41A6-4B34-B49E-65AFE95882BB}.Debug|Any CPU.Build.0 = Debug|Any CPU {0102A6CC-41A6-4B34-B49E-65AFE95882BB}.Release|Any CPU.ActiveCfg = Release|Any CPU {0102A6CC-41A6-4B34-B49E-65AFE95882BB}.Release|Any CPU.Build.0 = Release|Any CPU + {BF42CC6C-0B56-4F66-9866-18B8393F3C06}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BF42CC6C-0B56-4F66-9866-18B8393F3C06}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BF42CC6C-0B56-4F66-9866-18B8393F3C06}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BF42CC6C-0B56-4F66-9866-18B8393F3C06}.Release|Any CPU.Build.0 = Release|Any CPU + {96325E37-9897-43AC-8408-7B17F58E8788}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {96325E37-9897-43AC-8408-7B17F58E8788}.Debug|Any CPU.Build.0 = Debug|Any CPU + {96325E37-9897-43AC-8408-7B17F58E8788}.Release|Any CPU.ActiveCfg = Release|Any CPU + {96325E37-9897-43AC-8408-7B17F58E8788}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -97,5 +109,10 @@ Global {8B4B0CCC-711B-4F9D-9DE6-DD32BDD3BCCA} = {5FC71D6A-A994-4F62-977F-88A7D25379D7} {3E2FBDB3-DC82-4E97-8EBC-CC8B279110FF} = {5FC71D6A-A994-4F62-977F-88A7D25379D7} {0102A6CC-41A6-4B34-B49E-65AFE95882BB} = {D544447C-D701-46BB-9A5B-C76C612A596B} + {BF42CC6C-0B56-4F66-9866-18B8393F3C06} = {D544447C-D701-46BB-9A5B-C76C612A596B} + {96325E37-9897-43AC-8408-7B17F58E8788} = {5FC71D6A-A994-4F62-977F-88A7D25379D7} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {A710059F-0466-4D48-9B3A-0EF4F840B616} EndGlobalSection EndGlobal diff --git a/build/dependencies.props b/build/dependencies.props index 303cd30f..7b443b02 100644 --- a/build/dependencies.props +++ b/build/dependencies.props @@ -5,6 +5,7 @@ 1.0.0 1.0.1 2.0.0 + 6.1.3 10.3.0 1.6.0 4.7.63 diff --git a/build/repo.props b/build/repo.props new file mode 100644 index 00000000..b8e3b559 --- /dev/null +++ b/build/repo.props @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/OpenIddict.EntityFramework/OpenIddict.EntityFramework.csproj b/src/OpenIddict.EntityFramework/OpenIddict.EntityFramework.csproj new file mode 100644 index 00000000..2d72dcc4 --- /dev/null +++ b/src/OpenIddict.EntityFramework/OpenIddict.EntityFramework.csproj @@ -0,0 +1,24 @@ + + + + + + net461 + + + + Entity Framework 6.x stores for OpenIddict. + Kévin Chalet + aspnetcore;authentication;jwt;openidconnect;openiddict;security + + + + + + + + + + + + diff --git a/src/OpenIddict.EntityFramework/OpenIddictExtensions.cs b/src/OpenIddict.EntityFramework/OpenIddictExtensions.cs new file mode 100644 index 00000000..da835d5f --- /dev/null +++ b/src/OpenIddict.EntityFramework/OpenIddictExtensions.cs @@ -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 + { + /// + /// Registers the Entity Framework 6.x stores. Note: when using the Entity Framework stores, + /// the application MUST be manually registered in the DI container and + /// the entities MUST be derived from the models contained in the OpenIddict.Models package. + /// + /// The services builder used by OpenIddict to register new services. + /// The . + public static OpenIddictBuilder AddEntityFrameworkStores([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; + } + + /// + /// Registers the OpenIddict entity sets in the Entity Framework context + /// using the default OpenIddict models and the default key type (string). + /// + /// The builder used to configure the Entity Framework context. + /// The Entity Framework context builder. + public static DbModelBuilder UseOpenIddict([NotNull] this DbModelBuilder builder) + { + return builder.UseOpenIddict(); + } + + /// + /// 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). + /// + /// The builder used to configure the Entity Framework context. + /// The Entity Framework context builder. + public static DbModelBuilder UseOpenIddict([NotNull] this DbModelBuilder builder) + where TApplication : OpenIddictApplication, new() + where TAuthorization : OpenIddictAuthorization, new() + where TScope : OpenIddictScope, new() + where TToken : OpenIddictToken, new() + where TKey : IEquatable + { + 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() + .HasKey(application => application.Id); + + builder.Entity() + .Property(application => application.ClientId) + .HasMaxLength(450) + .HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute())); + + builder.Entity() + .HasMany(application => application.Authorizations) + .WithOptional(authorization => authorization.Application) + .Map(association => association.MapKey("ApplicationId")); + + builder.Entity() + .HasMany(application => application.Tokens) + .WithOptional(token => token.Application) + .Map(association => association.MapKey("ApplicationId")); + + builder.Entity() + .ToTable("OpenIddictApplications"); + + // Configure the TAuthorization entity. + builder.Entity() + .HasKey(authorization => authorization.Id); + + builder.Entity() + .HasMany(application => application.Tokens) + .WithOptional(token => token.Authorization) + .Map(association => association.MapKey("AuthorizationId")); + + builder.Entity() + .ToTable("OpenIddictAuthorizations"); + + // Configure the TScope entity. + builder.Entity() + .HasKey(scope => scope.Id); + + builder.Entity() + .ToTable("OpenIddictScopes"); + + // Configure the TToken entity. + builder.Entity() + .HasKey(token => token.Id); + + builder.Entity() + .Property(token => token.Hash) + .HasMaxLength(450) + .HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute())); + + builder.Entity() + .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; + } + } +} \ No newline at end of file diff --git a/src/OpenIddict.EntityFramework/Stores/OpenIddictApplicationStore.cs b/src/OpenIddict.EntityFramework/Stores/OpenIddictApplicationStore.cs new file mode 100644 index 00000000..89629b5c --- /dev/null +++ b/src/OpenIddict.EntityFramework/Stores/OpenIddictApplicationStore.cs @@ -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 +{ + /// + /// Provides methods allowing to manage the applications stored in a database. + /// Note: this class can only be used with the default OpenIddict entities. + /// + /// The type of the Entity Framework database context. + public class OpenIddictApplicationStore : OpenIddictApplicationStore + where TContext : DbContext + { + public OpenIddictApplicationStore([NotNull] TContext context) : base(context) { } + } + + /// + /// Provides methods allowing to manage the applications stored in a database. + /// Note: this class can only be used with the default OpenIddict entities. + /// + /// The type of the Entity Framework database context. + /// The type of the entity primary keys. + public class OpenIddictApplicationStore : OpenIddictApplicationStore, + OpenIddictAuthorization, + OpenIddictToken, TContext, TKey> + where TContext : DbContext + where TKey : IEquatable + { + public OpenIddictApplicationStore([NotNull] TContext context) : base(context) { } + } + + /// + /// Provides methods allowing to manage the applications stored in a database. + /// Note: this class can only be used with the default OpenIddict entities. + /// + /// The type of the Application entity. + /// The type of the Authorization entity. + /// The type of the Token entity. + /// The type of the Entity Framework database context. + /// The type of the entity primary keys. + public class OpenIddictApplicationStore : + OpenIddictApplicationStore + where TApplication : OpenIddictApplication, new() + where TAuthorization : OpenIddictAuthorization, new() + where TToken : OpenIddictToken, new() + where TContext : DbContext + where TKey : IEquatable + { + public OpenIddictApplicationStore([NotNull] TContext context) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + Context = context; + } + + /// + /// Gets the database context associated with the current store. + /// + protected virtual TContext Context { get; } + + /// + /// Gets the database set corresponding to the entity. + /// + protected DbSet Applications => Context.Set(); + + /// + /// Creates a new application. + /// + /// The application to create. + /// The that can be used to abort the operation. + /// + /// A that can be used to monitor the asynchronous operation, whose result returns the application. + /// + public override async Task CreateAsync([NotNull] TApplication application, CancellationToken cancellationToken) + { + if (application == null) + { + throw new ArgumentNullException(nameof(application)); + } + + Applications.Add(application); + + await Context.SaveChangesAsync(cancellationToken); + + return application; + } + + /// + /// Creates a new application. + /// + /// The application descriptor. + /// The that can be used to abort the operation. + /// + /// A that can be used to monitor the asynchronous operation, whose result returns the application. + /// + public override Task 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); + } + + /// + /// Removes an existing application. + /// + /// The application to delete. + /// The that can be used to abort the operation. + /// + /// A that can be used to monitor the asynchronous operation. + /// + 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) { } + } + + /// + /// Executes the specified query. + /// + /// The result type. + /// The query to execute. + /// The that can be used to abort the operation. + /// + /// A that can be used to monitor the asynchronous operation, + /// whose result returns the single element returned when executing the specified query. + /// + public override Task GetAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken) + { + if (query == null) + { + throw new ArgumentNullException(nameof(query)); + } + + return query.Invoke(Applications).SingleOrDefaultAsync(cancellationToken); + } + + /// + /// Executes the specified query. + /// + /// The result type. + /// The query to execute. + /// The that can be used to abort the operation. + /// + /// A that can be used to monitor the asynchronous operation, + /// whose result returns all the elements returned when executing the specified query. + /// + public override Task ListAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken) + { + if (query == null) + { + throw new ArgumentNullException(nameof(query)); + } + + return query.Invoke(Applications).ToArrayAsync(cancellationToken); + } + + /// + /// Updates an existing application. + /// + /// The application to update. + /// The that can be used to abort the operation. + /// + /// A that can be used to monitor the asynchronous operation. + /// + 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) { } + } + } +} \ No newline at end of file diff --git a/src/OpenIddict.EntityFramework/Stores/OpenIddictAuthorizationStore.cs b/src/OpenIddict.EntityFramework/Stores/OpenIddictAuthorizationStore.cs new file mode 100644 index 00000000..af65eca1 --- /dev/null +++ b/src/OpenIddict.EntityFramework/Stores/OpenIddictAuthorizationStore.cs @@ -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 +{ + /// + /// Provides methods allowing to manage the authorizations stored in a database. + /// Note: this class can only be used with the default OpenIddict entities. + /// + /// The type of the Entity Framework database context. + public class OpenIddictAuthorizationStore : OpenIddictAuthorizationStore + where TContext : DbContext + { + public OpenIddictAuthorizationStore([NotNull] TContext context) : base(context) { } + } + + /// + /// Provides methods allowing to manage the authorizations stored in a database. + /// Note: this class can only be used with the default OpenIddict entities. + /// + /// The type of the Entity Framework database context. + /// The type of the entity primary keys. + public class OpenIddictAuthorizationStore : OpenIddictAuthorizationStore, + OpenIddictApplication, + OpenIddictToken, TContext, TKey> + where TContext : DbContext + where TKey : IEquatable + { + public OpenIddictAuthorizationStore([NotNull] TContext context) : base(context) { } + } + + /// + /// Provides methods allowing to manage the authorizations stored in a database. + /// Note: this class can only be used with the default OpenIddict entities. + /// + /// The type of the Authorization entity. + /// The type of the Application entity. + /// The type of the Token entity. + /// The type of the Entity Framework database context. + /// The type of the entity primary keys. + public class OpenIddictAuthorizationStore : + OpenIddictAuthorizationStore + where TAuthorization : OpenIddictAuthorization, new() + where TApplication : OpenIddictApplication, new() + where TToken : OpenIddictToken, new() + where TContext : DbContext + where TKey : IEquatable + { + public OpenIddictAuthorizationStore([NotNull] TContext context) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + Context = context; + } + + /// + /// Gets the database context associated with the current store. + /// + protected virtual TContext Context { get; } + + /// + /// Gets the database set corresponding to the entity. + /// + protected DbSet Applications => Context.Set(); + + /// + /// Gets the database set corresponding to the entity. + /// + protected DbSet Authorizations => Context.Set(); + + /// + /// Creates a new authorization. + /// + /// The authorization to create. + /// The that can be used to abort the operation. + /// + /// A that can be used to monitor the asynchronous operation, whose result returns the authorization. + /// + public override async Task CreateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) + { + if (authorization == null) + { + throw new ArgumentNullException(nameof(authorization)); + } + + Authorizations.Add(authorization); + + await Context.SaveChangesAsync(cancellationToken); + + return authorization; + } + + /// + /// Creates a new authorization. + /// + /// The authorization descriptor. + /// The that can be used to abort the operation. + /// + /// A that can be used to monitor the asynchronous operation, whose result returns the authorization. + /// + public override async Task 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); + } + + /// + /// Executes the specified query. + /// + /// The result type. + /// The query to execute. + /// The that can be used to abort the operation. + /// + /// A that can be used to monitor the asynchronous operation, + /// whose result returns the single element returned when executing the specified query. + /// + public override Task GetAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken) + { + if (query == null) + { + throw new ArgumentNullException(nameof(query)); + } + + return query.Invoke(Authorizations).SingleOrDefaultAsync(cancellationToken); + } + + /// + /// Executes the specified query. + /// + /// The result type. + /// The query to execute. + /// The that can be used to abort the operation. + /// + /// A that can be used to monitor the asynchronous operation, + /// whose result returns all the elements returned when executing the specified query. + /// + public override Task ListAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken) + { + if (query == null) + { + throw new ArgumentNullException(nameof(query)); + } + + return query.Invoke(Authorizations).ToArrayAsync(cancellationToken); + } + + /// + /// Updates an existing authorization. + /// + /// The authorization to update. + /// The that can be used to abort the operation. + /// + /// A that can be used to monitor the asynchronous operation. + /// + 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) { } + } + } +} \ No newline at end of file diff --git a/src/OpenIddict.EntityFramework/Stores/OpenIddictScopeStore.cs b/src/OpenIddict.EntityFramework/Stores/OpenIddictScopeStore.cs new file mode 100644 index 00000000..b960fba4 --- /dev/null +++ b/src/OpenIddict.EntityFramework/Stores/OpenIddictScopeStore.cs @@ -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 +{ + /// + /// Provides methods allowing to manage the scopes stored in a database. + /// Note: this class can only be used with the default OpenIddict entities. + /// + /// The type of the Entity Framework database context. + public class OpenIddictScopeStore : OpenIddictScopeStore + where TContext : DbContext + { + public OpenIddictScopeStore([NotNull] TContext context) : base(context) { } + } + + /// + /// Provides methods allowing to manage the scopes stored in a database. + /// Note: this class can only be used with the default OpenIddict entities. + /// + /// The type of the Entity Framework database context. + /// The type of the entity primary keys. + public class OpenIddictScopeStore : OpenIddictScopeStore, TContext, TKey> + where TContext : DbContext + where TKey : IEquatable + { + public OpenIddictScopeStore([NotNull] TContext context) : base(context) { } + } + + /// + /// Provides methods allowing to manage the scopes stored in a database. + /// Note: this class can only be used with the default OpenIddict entities. + /// + /// The type of the Scope entity. + /// The type of the Entity Framework database context. + /// The type of the entity primary keys. + public class OpenIddictScopeStore : Core.OpenIddictScopeStore + where TScope : OpenIddictScope, new() + where TContext : DbContext + where TKey : IEquatable + { + public OpenIddictScopeStore([NotNull] TContext context) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + Context = context; + } + + /// + /// Gets the database context associated with the current store. + /// + protected virtual TContext Context { get; } + + /// + /// Gets the database set corresponding to the entity. + /// + protected DbSet Scopes => Context.Set(); + + /// + /// Executes the specified query. + /// + /// The result type. + /// The query to execute. + /// The that can be used to abort the operation. + /// + /// A that can be used to monitor the asynchronous operation, + /// whose result returns the single element returned when executing the specified query. + /// + public override Task GetAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken) + { + if (query == null) + { + throw new ArgumentNullException(nameof(query)); + } + + return query.Invoke(Scopes).SingleOrDefaultAsync(cancellationToken); + } + + /// + /// Executes the specified query. + /// + /// The result type. + /// The query to execute. + /// The that can be used to abort the operation. + /// + /// A that can be used to monitor the asynchronous operation, + /// whose result returns all the elements returned when executing the specified query. + /// + public override Task ListAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken) + { + if (query == null) + { + throw new ArgumentNullException(nameof(query)); + } + + return query.Invoke(Scopes).ToArrayAsync(cancellationToken); + } + } +} \ No newline at end of file diff --git a/src/OpenIddict.EntityFramework/Stores/OpenIddictTokenStore.cs b/src/OpenIddict.EntityFramework/Stores/OpenIddictTokenStore.cs new file mode 100644 index 00000000..886200b7 --- /dev/null +++ b/src/OpenIddict.EntityFramework/Stores/OpenIddictTokenStore.cs @@ -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 +{ + /// + /// Provides methods allowing to manage the tokens stored in a database. + /// Note: this class can only be used with the default OpenIddict entities. + /// + /// The type of the Entity Framework database context. + public class OpenIddictTokenStore : OpenIddictTokenStore + where TContext : DbContext + { + public OpenIddictTokenStore([NotNull] TContext context) : base(context) { } + } + + /// + /// Provides methods allowing to manage the tokens stored in a database. + /// Note: this class can only be used with the default OpenIddict entities. + /// + /// The type of the Entity Framework database context. + /// The type of the entity primary keys. + public class OpenIddictTokenStore : OpenIddictTokenStore, + OpenIddictApplication, + OpenIddictAuthorization, TContext, TKey> + where TContext : DbContext + where TKey : IEquatable + { + public OpenIddictTokenStore([NotNull] TContext context) : base(context) { } + } + + /// + /// Provides methods allowing to manage the tokens stored in a database. + /// Note: this class can only be used with the default OpenIddict entities. + /// + /// The type of the Token entity. + /// The type of the Application entity. + /// The type of the Authorization entity. + /// The type of the Entity Framework database context. + /// The type of the entity primary keys. + public class OpenIddictTokenStore : + OpenIddictTokenStore + where TToken : OpenIddictToken, new() + where TApplication : OpenIddictApplication, new() + where TAuthorization : OpenIddictAuthorization, new() + where TContext : DbContext + where TKey : IEquatable + { + public OpenIddictTokenStore([NotNull] TContext context) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + Context = context; + } + + /// + /// Gets the database context associated with the current store. + /// + protected virtual TContext Context { get; } + + /// + /// Gets the database set corresponding to the entity. + /// + protected DbSet Applications => Context.Set(); + + /// + /// Gets the database set corresponding to the entity. + /// + protected DbSet Authorizations => Context.Set(); + + /// + /// Gets the database set corresponding to the entity. + /// + protected DbSet Tokens => Context.Set(); + + /// + /// Creates a new token. + /// + /// The token to create. + /// The that can be used to abort the operation. + /// + /// A that can be used to monitor the asynchronous operation, whose result returns the token. + /// + public override async Task CreateAsync([NotNull] TToken token, CancellationToken cancellationToken) + { + if (token == null) + { + throw new ArgumentNullException(nameof(token)); + } + + Tokens.Add(token); + + await Context.SaveChangesAsync(cancellationToken); + + return token; + } + + /// + /// Creates a new token, which is associated with a particular subject. + /// + /// The token descriptor. + /// The that can be used to abort the operation. + /// + /// A that can be used to monitor the asynchronous operation, whose result returns the token. + /// + public override async Task 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); + } + + /// + /// Removes a token. + /// + /// The token to delete. + /// The that can be used to abort the operation. + /// A that can be used to monitor the asynchronous operation. + 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) { } + } + + /// + /// Executes the specified query. + /// + /// The result type. + /// The query to execute. + /// The that can be used to abort the operation. + /// + /// A that can be used to monitor the asynchronous operation, + /// whose result returns the single element returned when executing the specified query. + /// + public override Task GetAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken) + { + if (query == null) + { + throw new ArgumentNullException(nameof(query)); + } + + return query.Invoke(Tokens).SingleOrDefaultAsync(cancellationToken); + } + + /// + /// Executes the specified query. + /// + /// The result type. + /// The query to execute. + /// The that can be used to abort the operation. + /// + /// A that can be used to monitor the asynchronous operation, + /// whose result returns all the elements returned when executing the specified query. + /// + public override Task ListAsync([NotNull] Func, IQueryable> query, CancellationToken cancellationToken) + { + if (query == null) + { + throw new ArgumentNullException(nameof(query)); + } + + return query.Invoke(Tokens).ToArrayAsync(cancellationToken); + } + + /// + /// Sets the authorization associated with a token. + /// + /// The token. + /// The unique identifier associated with the authorization. + /// The that can be used to abort the operation. + /// + /// A that can be used to monitor the asynchronous operation. + /// + 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); + } + } + } + + /// + /// Sets the client application associated with a token. + /// + /// The token. + /// The unique identifier associated with the client application. + /// The that can be used to abort the operation. + /// + /// A that can be used to monitor the asynchronous operation. + /// + 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); + } + } + } + + /// + /// Updates an existing token. + /// + /// The token to update. + /// The that can be used to abort the operation. + /// + /// A that can be used to monitor the asynchronous operation. + /// + 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) { } + } + } +} \ No newline at end of file diff --git a/test/OpenIddict.Core.Tests/OpenIddictBuilderTests.cs b/test/OpenIddict.Core.Tests/OpenIddictBuilderTests.cs index fd9fd1cb..6ef5830b 100644 --- a/test/OpenIddict.Core.Tests/OpenIddictBuilderTests.cs +++ b/test/OpenIddict.Core.Tests/OpenIddictBuilderTests.cs @@ -1,4 +1,10 @@ -using System; +/* + * 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.Logging; using Moq; diff --git a/test/OpenIddict.Core.Tests/OpenIddictExtensionsTests.cs b/test/OpenIddict.Core.Tests/OpenIddictExtensionsTests.cs index b740f2ff..edb2ccfa 100644 --- a/test/OpenIddict.Core.Tests/OpenIddictExtensionsTests.cs +++ b/test/OpenIddict.Core.Tests/OpenIddictExtensionsTests.cs @@ -1,4 +1,10 @@ -using System; +/* + * 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 OpenIddict.Models; using Xunit; diff --git a/test/OpenIddict.EntityFramework.Tests/OpenIddict.EntityFramework.Tests.csproj b/test/OpenIddict.EntityFramework.Tests/OpenIddict.EntityFramework.Tests.csproj new file mode 100644 index 00000000..34576c2c --- /dev/null +++ b/test/OpenIddict.EntityFramework.Tests/OpenIddict.EntityFramework.Tests.csproj @@ -0,0 +1,25 @@ + + + + + + net461 + + + + + + + + + + + + + + + + + + + diff --git a/test/OpenIddict.EntityFramework.Tests/OpenIddictExtensionsTests.cs b/test/OpenIddict.EntityFramework.Tests/OpenIddictExtensionsTests.cs new file mode 100644 index 00000000..45d9007c --- /dev/null +++ b/test/OpenIddict.EntityFramework.Tests/OpenIddictExtensionsTests.cs @@ -0,0 +1,167 @@ +/* + * 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 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(delegate + { + builder.AddEntityFrameworkStores(); + }); + + 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(delegate + { + builder.AddEntityFrameworkStores(); + }); + + 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(delegate + { + builder.AddEntityFrameworkStores(); + }); + + 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(delegate + { + builder.AddEntityFrameworkStores(); + }); + + Assert.Equal("The Entity Framework stores can only be used " + + "with the built-in OpenIddictToken entity.", exception.Message); + } + + [Theory] + [InlineData(typeof(OpenIddictApplicationStore))] + [InlineData(typeof(OpenIddictAuthorizationStore))] + [InlineData(typeof(OpenIddictScopeStore))] + [InlineData(typeof(OpenIddictTokenStore))] + public void AddEntityFrameworkStores_RegistersEntityFrameworkStores(Type type) + { + // Arrange + var services = new ServiceCollection(); + var builder = new OpenIddictBuilder(services); + + // Act + builder.AddEntityFrameworkStores(); + + // Assert + Assert.Contains(services, service => service.ImplementationType == type); + } + + [Theory] + [InlineData(typeof(OpenIddictApplicationStore, OpenIddictAuthorization, OpenIddictToken, DbContext, Guid>))] + [InlineData(typeof(OpenIddictAuthorizationStore, OpenIddictApplication, OpenIddictToken, DbContext, Guid>))] + [InlineData(typeof(OpenIddictScopeStore, DbContext, Guid>))] + [InlineData(typeof(OpenIddictTokenStore, OpenIddictApplication, OpenIddictAuthorization, DbContext, Guid>))] + public void AddEntityFrameworkStores_KeyTypeIsInferredFromEntities(Type type) + { + // Arrange + var services = new ServiceCollection(); + + var builder = new OpenIddictBuilder(services) + { + ApplicationType = typeof(OpenIddictApplication), + AuthorizationType = typeof(OpenIddictAuthorization), + ScopeType = typeof(OpenIddictScope), + TokenType = typeof(OpenIddictToken) + }; + + // Act + builder.AddEntityFrameworkStores(); + + // Assert + Assert.Contains(services, service => service.ImplementationType == type); + } + + [Theory] + [InlineData(typeof(OpenIddictApplicationStore))] + [InlineData(typeof(OpenIddictAuthorizationStore))] + [InlineData(typeof(OpenIddictScopeStore))] + [InlineData(typeof(OpenIddictTokenStore))] + 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(); + + // Assert + Assert.Contains(services, service => service.ImplementationType == type); + } + + public class CustomApplication : OpenIddictApplication { } + public class CustomAuthorization : OpenIddictAuthorization { } + public class CustomScope : OpenIddictScope { } + public class CustomToken : OpenIddictToken { } + } +} diff --git a/test/OpenIddict.EntityFrameworkCore.Tests/OpenIddictExtensionsTests.cs b/test/OpenIddict.EntityFrameworkCore.Tests/OpenIddictExtensionsTests.cs index 7a59825f..00b9f939 100644 --- a/test/OpenIddict.EntityFrameworkCore.Tests/OpenIddictExtensionsTests.cs +++ b/test/OpenIddict.EntityFrameworkCore.Tests/OpenIddictExtensionsTests.cs @@ -1,4 +1,10 @@ -using System; +/* + * 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.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using OpenIddict.Models; diff --git a/test/OpenIddict.Mvc.Tests/OpenIddictExtensionsTests.cs b/test/OpenIddict.Mvc.Tests/OpenIddictExtensionsTests.cs index 618711c2..e6674752 100644 --- a/test/OpenIddict.Mvc.Tests/OpenIddictExtensionsTests.cs +++ b/test/OpenIddict.Mvc.Tests/OpenIddictExtensionsTests.cs @@ -1,4 +1,10 @@ -using Microsoft.AspNetCore.Mvc; +/* + * 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 Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Xunit; diff --git a/test/OpenIddict.Mvc.Tests/OpenIddictModelBinderTests.cs b/test/OpenIddict.Mvc.Tests/OpenIddictModelBinderTests.cs index 77964abc..42f4182d 100644 --- a/test/OpenIddict.Mvc.Tests/OpenIddictModelBinderTests.cs +++ b/test/OpenIddict.Mvc.Tests/OpenIddictModelBinderTests.cs @@ -1,4 +1,10 @@ -using System; +/* + * 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.Threading.Tasks; using AspNet.Security.OpenIdConnect.Primitives; diff --git a/test/OpenIddict.Tests/OpenIddictExtensionsTests.cs b/test/OpenIddict.Tests/OpenIddictExtensionsTests.cs index df3e6319..d727beab 100644 --- a/test/OpenIddict.Tests/OpenIddictExtensionsTests.cs +++ b/test/OpenIddict.Tests/OpenIddictExtensionsTests.cs @@ -1,4 +1,10 @@ -using System; +/* + * 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.IdentityModel.Tokens.Jwt; using System.Reflection; using AspNet.Security.OpenIdConnect.Primitives; diff --git a/test/OpenIddict.Tests/OpenIddictProviderTests.Authentication.cs b/test/OpenIddict.Tests/OpenIddictProviderTests.Authentication.cs index a7cb5962..6424327d 100644 --- a/test/OpenIddict.Tests/OpenIddictProviderTests.Authentication.cs +++ b/test/OpenIddict.Tests/OpenIddictProviderTests.Authentication.cs @@ -1,6 +1,10 @@ -using System; +/* + * 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.IO; -using System.Linq; using System.Threading; using System.Threading.Tasks; using AspNet.Security.OpenIdConnect.Client; diff --git a/test/OpenIddict.Tests/OpenIddictProviderTests.Discovery.cs b/test/OpenIddict.Tests/OpenIddictProviderTests.Discovery.cs index 962bf052..9be4ff26 100644 --- a/test/OpenIddict.Tests/OpenIddictProviderTests.Discovery.cs +++ b/test/OpenIddict.Tests/OpenIddictProviderTests.Discovery.cs @@ -1,4 +1,9 @@ -using System.Linq; +/* + * 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.Tasks; using AspNet.Security.OpenIdConnect.Client; using AspNet.Security.OpenIdConnect.Primitives; @@ -6,6 +11,7 @@ using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.Facebook; using Microsoft.AspNetCore.Authentication.Google; using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json.Linq; using OpenIddict.Core; using Xunit; diff --git a/test/OpenIddict.Tests/OpenIddictProviderTests.Exchange.cs b/test/OpenIddict.Tests/OpenIddictProviderTests.Exchange.cs index dacb7958..ac1313cc 100644 --- a/test/OpenIddict.Tests/OpenIddictProviderTests.Exchange.cs +++ b/test/OpenIddict.Tests/OpenIddictProviderTests.Exchange.cs @@ -1,4 +1,9 @@ -using System.Collections.Generic; +/* + * 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.Security.Claims; using System.Threading; using System.Threading.Tasks; diff --git a/test/OpenIddict.Tests/OpenIddictProviderTests.Introspection.cs b/test/OpenIddict.Tests/OpenIddictProviderTests.Introspection.cs index dcc6374f..77de1e21 100644 --- a/test/OpenIddict.Tests/OpenIddictProviderTests.Introspection.cs +++ b/test/OpenIddict.Tests/OpenIddictProviderTests.Introspection.cs @@ -1,4 +1,9 @@ -using System.Linq; +/* + * 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.Security.Claims; using System.Threading; using System.Threading.Tasks; diff --git a/test/OpenIddict.Tests/OpenIddictProviderTests.Revocation.cs b/test/OpenIddict.Tests/OpenIddictProviderTests.Revocation.cs index da6a3d46..84846303 100644 --- a/test/OpenIddict.Tests/OpenIddictProviderTests.Revocation.cs +++ b/test/OpenIddict.Tests/OpenIddictProviderTests.Revocation.cs @@ -1,4 +1,10 @@ -using System; +/* + * 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.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Threading; diff --git a/test/OpenIddict.Tests/OpenIddictProviderTests.Serialization.cs b/test/OpenIddict.Tests/OpenIddictProviderTests.Serialization.cs index fba55324..76cfac78 100644 --- a/test/OpenIddict.Tests/OpenIddictProviderTests.Serialization.cs +++ b/test/OpenIddict.Tests/OpenIddictProviderTests.Serialization.cs @@ -1,6 +1,10 @@ -using System; -using System.Collections; -using System.Collections.Generic; +/* + * 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.Threading; using System.Threading.Tasks; using AspNet.Security.OpenIdConnect.Client; diff --git a/test/OpenIddict.Tests/OpenIddictProviderTests.Session.cs b/test/OpenIddict.Tests/OpenIddictProviderTests.Session.cs index c4c288d6..94972245 100644 --- a/test/OpenIddict.Tests/OpenIddictProviderTests.Session.cs +++ b/test/OpenIddict.Tests/OpenIddictProviderTests.Session.cs @@ -1,4 +1,9 @@ -using System.Linq; +/* + * 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 AspNet.Security.OpenIdConnect.Client; @@ -8,7 +13,6 @@ using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.DependencyInjection; using Moq; using OpenIddict.Core; -using OpenIddict.Models; using Xunit; namespace OpenIddict.Tests diff --git a/test/OpenIddict.Tests/OpenIddictProviderTests.Userinfo.cs b/test/OpenIddict.Tests/OpenIddictProviderTests.Userinfo.cs index be80b2a3..883f383d 100644 --- a/test/OpenIddict.Tests/OpenIddictProviderTests.Userinfo.cs +++ b/test/OpenIddict.Tests/OpenIddictProviderTests.Userinfo.cs @@ -1,4 +1,10 @@ -using System.Threading.Tasks; +/* + * 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.Tasks; using AspNet.Security.OpenIdConnect.Client; using AspNet.Security.OpenIdConnect.Primitives; using Xunit; diff --git a/test/OpenIddict.Tests/OpenIddictProviderTests.cs b/test/OpenIddict.Tests/OpenIddictProviderTests.cs index 153ca18a..38e0b7e7 100644 --- a/test/OpenIddict.Tests/OpenIddictProviderTests.cs +++ b/test/OpenIddict.Tests/OpenIddictProviderTests.cs @@ -1,4 +1,10 @@ -using System; +/* + * 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.Reflection; using System.Security.Claims; using System.Threading.Tasks;