Browse Source

Introduce built-in Entity Framework 6.x stores for OpenIddict

pull/469/head
Kévin Chalet 8 years ago
parent
commit
3c17571919
  1. 19
      OpenIddict.sln
  2. 1
      build/dependencies.props
  3. 7
      build/repo.props
  4. 24
      src/OpenIddict.EntityFramework/OpenIddict.EntityFramework.csproj
  5. 264
      src/OpenIddict.EntityFramework/OpenIddictExtensions.cs
  6. 225
      src/OpenIddict.EntityFramework/Stores/OpenIddictApplicationStore.cs
  7. 211
      src/OpenIddict.EntityFramework/Stores/OpenIddictAuthorizationStore.cs
  8. 113
      src/OpenIddict.EntityFramework/Stores/OpenIddictScopeStore.cs
  9. 347
      src/OpenIddict.EntityFramework/Stores/OpenIddictTokenStore.cs
  10. 25
      test/OpenIddict.EntityFramework.Tests/OpenIddict.EntityFramework.Tests.csproj
  11. 161
      test/OpenIddict.EntityFramework.Tests/OpenIddictExtensionsTests.cs

19
OpenIddict.sln

@ -1,7 +1,7 @@
 
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15 # Visual Studio 15
VisualStudioVersion = 15.0.26228.4 VisualStudioVersion = 15.0.26730.12
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{D544447C-D701-46BB-9A5B-C76C612A596B}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{D544447C-D701-46BB-9A5B-C76C612A596B}"
EndProject EndProject
@ -31,6 +31,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenIddict.Tests", "test\Op
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenIddict.Models", "src\OpenIddict.Models\OpenIddict.Models.csproj", "{0102A6CC-41A6-4B34-B49E-65AFE95882BB}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenIddict.Models", "src\OpenIddict.Models\OpenIddict.Models.csproj", "{0102A6CC-41A6-4B34-B49E-65AFE95882BB}"
EndProject 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 Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU 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}.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.ActiveCfg = Release|Any CPU
{0102A6CC-41A6-4B34-B49E-65AFE95882BB}.Release|Any CPU.Build.0 = 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 EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -97,5 +109,10 @@ Global
{8B4B0CCC-711B-4F9D-9DE6-DD32BDD3BCCA} = {5FC71D6A-A994-4F62-977F-88A7D25379D7} {8B4B0CCC-711B-4F9D-9DE6-DD32BDD3BCCA} = {5FC71D6A-A994-4F62-977F-88A7D25379D7}
{3E2FBDB3-DC82-4E97-8EBC-CC8B279110FF} = {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} {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 EndGlobalSection
EndGlobal EndGlobal

1
build/dependencies.props

@ -6,6 +6,7 @@
<AspNetCoreVersion>2.0.0</AspNetCoreVersion> <AspNetCoreVersion>2.0.0</AspNetCoreVersion>
<CoreFxVersion>4.4.0</CoreFxVersion> <CoreFxVersion>4.4.0</CoreFxVersion>
<CryptoHelperVersion>3.0.0</CryptoHelperVersion> <CryptoHelperVersion>3.0.0</CryptoHelperVersion>
<EntityFrameworkVersion>6.1.3</EntityFrameworkVersion>
<JetBrainsVersion>10.3.0</JetBrainsVersion> <JetBrainsVersion>10.3.0</JetBrainsVersion>
<JsonNetBsonVersion>1.0.1</JsonNetBsonVersion> <JsonNetBsonVersion>1.0.1</JsonNetBsonVersion>
<MoqVersion>4.7.63</MoqVersion> <MoqVersion>4.7.63</MoqVersion>

7
build/repo.props

@ -0,0 +1,7 @@
<Project>
<ItemGroup>
<ExcludeFromTest Include="$(RepositoryRoot)test\OpenIddict.EntityFramework.Tests\*.csproj" Condition="'$(OS)' != 'Windows_NT'"/>
</ItemGroup>
</Project>

24
src/OpenIddict.EntityFramework/OpenIddict.EntityFramework.csproj

@ -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>

264
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
{
/// <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;
}
}
}

225
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
{
/// <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) { }
}
}
}

211
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
{
/// <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) { }
}
}
}

113
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
{
/// <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);
}
}
}

347
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
{
/// <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) { }
}
}
}

25
test/OpenIddict.EntityFramework.Tests/OpenIddict.EntityFramework.Tests.csproj

@ -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>

161
test/OpenIddict.EntityFramework.Tests/OpenIddictExtensionsTests.cs

@ -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…
Cancel
Save