Add support for store versioning + IUserActivityStore
This commit is contained in:
parent
f0a6eee30e
commit
be8232d3f9
|
|
@ -2,7 +2,11 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
|
||||
{
|
||||
|
|
@ -131,13 +135,35 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
|
|||
/// </summary>
|
||||
public DbSet<TRoleClaim> RoleClaims { get; set; }
|
||||
|
||||
private string _version;
|
||||
/// <summary>
|
||||
/// Configures the schema needed for the identity framework.
|
||||
/// Gets or sets the version to use during <see cref="OnModelCreating(ModelBuilder)"/>.
|
||||
/// If not set, defaults to the value in the <see cref="IdentityStoreOptions.Version"/>
|
||||
/// </summary>
|
||||
public string Version {
|
||||
get => _version ??
|
||||
this.GetService<IDbContextOptions>()
|
||||
.Extensions.OfType<CoreOptionsExtension>()
|
||||
.FirstOrDefault()?.ApplicationServiceProvider
|
||||
?.GetService<IOptions<IdentityStoreOptions>>()
|
||||
?.Value?.Version;
|
||||
set => _version = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the latest identity model.
|
||||
/// </summary>
|
||||
/// <param name="builder"></param>
|
||||
protected void OnModelCreatingLatest(ModelBuilder builder)
|
||||
=> OnModelCreatingV2(builder);
|
||||
|
||||
/// <summary>
|
||||
/// Configures the schema needed for the identity framework version <see cref="IdentityStoreOptions.Version2_0"/>.
|
||||
/// </summary>
|
||||
/// <param name="builder">
|
||||
/// The builder being used to construct the model for this context.
|
||||
/// </param>
|
||||
protected override void OnModelCreating(ModelBuilder builder)
|
||||
protected void OnModelCreatingV2(ModelBuilder builder)
|
||||
{
|
||||
builder.Entity<TUser>(b =>
|
||||
{
|
||||
|
|
@ -152,7 +178,6 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
|
|||
b.Property(u => u.Email).HasMaxLength(256);
|
||||
b.Property(u => u.NormalizedEmail).HasMaxLength(256);
|
||||
|
||||
// Replace with b.HasMany<IdentityUserClaim>().
|
||||
b.HasMany<TUserClaim>().WithOne().HasForeignKey(uc => uc.UserId).IsRequired();
|
||||
b.HasMany<TUserLogin>().WithOne().HasForeignKey(ul => ul.UserId).IsRequired();
|
||||
b.HasMany<TUserRole>().WithOne().HasForeignKey(ur => ur.UserId).IsRequired();
|
||||
|
|
@ -173,19 +198,19 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
|
|||
b.HasMany<TRoleClaim>().WithOne().HasForeignKey(rc => rc.RoleId).IsRequired();
|
||||
});
|
||||
|
||||
builder.Entity<TUserClaim>(b =>
|
||||
builder.Entity<TUserClaim>(b =>
|
||||
{
|
||||
b.HasKey(uc => uc.Id);
|
||||
b.ToTable("AspNetUserClaims");
|
||||
});
|
||||
|
||||
builder.Entity<TRoleClaim>(b =>
|
||||
builder.Entity<TRoleClaim>(b =>
|
||||
{
|
||||
b.HasKey(rc => rc.Id);
|
||||
b.ToTable("AspNetRoleClaims");
|
||||
});
|
||||
|
||||
builder.Entity<TUserRole>(b =>
|
||||
builder.Entity<TUserRole>(b =>
|
||||
{
|
||||
b.HasKey(r => new { r.UserId, r.RoleId });
|
||||
b.ToTable("AspNetUserRoles");
|
||||
|
|
@ -197,11 +222,53 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
|
|||
b.ToTable("AspNetUserLogins");
|
||||
});
|
||||
|
||||
builder.Entity<TUserToken>(b =>
|
||||
builder.Entity<TUserToken>(b =>
|
||||
{
|
||||
b.HasKey(l => new { l.UserId, l.LoginProvider, l.Name });
|
||||
b.ToTable("AspNetUserTokens");
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures the schema needed for the identity framework version <see cref="IdentityStoreOptions.Version1_0"/>.
|
||||
/// </summary>
|
||||
/// <param name="builder">
|
||||
/// The builder being used to construct the model for this context.
|
||||
/// </param>
|
||||
protected void OnModelCreatingV1(ModelBuilder builder)
|
||||
{
|
||||
|
||||
OnModelCreatingV2(builder);
|
||||
|
||||
// Ignore the additional 2.0 properties that were added
|
||||
builder.Entity<TUser>(b =>
|
||||
{
|
||||
b.Ignore(u => u.LastPasswordChangeDate);
|
||||
b.Ignore(u => u.LastSignInDate);
|
||||
b.Ignore(u => u.CreateDate);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures the schema needed for the identity framework.
|
||||
/// </summary>
|
||||
/// <param name="builder">
|
||||
/// The builder being used to construct the model for this context.
|
||||
/// </param>
|
||||
protected override void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
if (Version == IdentityStoreOptions.Version_Latest)
|
||||
{
|
||||
OnModelCreatingLatest(builder);
|
||||
}
|
||||
else if (Version == IdentityStoreOptions.Version2_0)
|
||||
{
|
||||
OnModelCreatingV2(builder);
|
||||
}
|
||||
else // Default is always the v1 schema
|
||||
{
|
||||
OnModelCreatingV1(builder);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -15,6 +15,52 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
/// </summary>
|
||||
public static class IdentityEntityFrameworkBuilderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds an Entity Framework implementation of identity information stores with the schema/features in
|
||||
/// <see cref="IdentityStoreOptions.Version1_0"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TContext">The Entity Framework database context to use.</typeparam>
|
||||
/// <param name="builder">The <see cref="IdentityBuilder"/> instance this method extends.</param>
|
||||
/// <returns>The <see cref="IdentityBuilder"/> instance this method extends.</returns>
|
||||
public static IdentityBuilder AddEntityFrameworkStoresV1<TContext>(this IdentityBuilder builder)
|
||||
where TContext : DbContext
|
||||
{
|
||||
builder.Services.Configure<IdentityStoreOptions>(o => o.Version = IdentityStoreOptions.Version1_0);
|
||||
AddStores(builder.Services, typeof(UserStoreV1<,,,,,,,,>), typeof(RoleStoreV1<,,,,>), builder.UserType, builder.RoleType, typeof(TContext));
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an Entity Framework implementation of identity information stores with the schema/features in
|
||||
/// <see cref="IdentityStoreOptions.Version2_0"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TContext">The Entity Framework database context to use.</typeparam>
|
||||
/// <param name="builder">The <see cref="IdentityBuilder"/> instance this method extends.</param>
|
||||
/// <returns>The <see cref="IdentityBuilder"/> instance this method extends.</returns>
|
||||
public static IdentityBuilder AddEntityFrameworkStoresV2<TContext>(this IdentityBuilder builder)
|
||||
where TContext : DbContext
|
||||
{
|
||||
builder.Services.Configure<IdentityStoreOptions>(o => o.Version = IdentityStoreOptions.Version2_0);
|
||||
// Note: RoleStore was not changed for V2.
|
||||
AddStores(builder.Services, typeof(UserStoreV2<,,,,,,,,>), typeof(RoleStoreV1<,,,,>), builder.UserType, builder.RoleType, typeof(TContext));
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an Entity Framework implementation of identity information stores with the latest schema/features in
|
||||
/// <see cref="IdentityStoreOptions.Version"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TContext">The Entity Framework database context to use.</typeparam>
|
||||
/// <param name="builder">The <see cref="IdentityBuilder"/> instance this method extends.</param>
|
||||
/// <returns>The <see cref="IdentityBuilder"/> instance this method extends.</returns>
|
||||
public static IdentityBuilder AddEntityFrameworkStoresLatest<TContext>(this IdentityBuilder builder)
|
||||
where TContext : DbContext
|
||||
{
|
||||
builder.Services.Configure<IdentityStoreOptions>(o => o.Version = IdentityStoreOptions.Version_Latest);
|
||||
AddStores(builder.Services, typeof(UserStore<,,,,,,,,>), typeof(RoleStore<,,,,>), builder.UserType, builder.RoleType, typeof(TContext));
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an Entity Framework implementation of identity information stores.
|
||||
/// </summary>
|
||||
|
|
@ -23,12 +69,9 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
/// <returns>The <see cref="IdentityBuilder"/> instance this method extends.</returns>
|
||||
public static IdentityBuilder AddEntityFrameworkStores<TContext>(this IdentityBuilder builder)
|
||||
where TContext : DbContext
|
||||
{
|
||||
AddStores(builder.Services, builder.UserType, builder.RoleType, typeof(TContext));
|
||||
return builder;
|
||||
}
|
||||
=> builder.AddEntityFrameworkStoresV1<TContext>();
|
||||
|
||||
private static void AddStores(IServiceCollection services, Type userType, Type roleType, Type contextType)
|
||||
private static void AddStores(IServiceCollection services, Type userStore, Type roleStore, Type userType, Type roleType, Type contextType)
|
||||
{
|
||||
var identityUserType = FindGenericBaseType(userType, typeof(IdentityUser<,,,,>));
|
||||
if (identityUserType == null)
|
||||
|
|
@ -43,7 +86,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
|
||||
services.TryAddScoped(
|
||||
typeof(IUserStore<>).MakeGenericType(userType),
|
||||
typeof(UserStore<,,,,,,,,>).MakeGenericType(userType, roleType, contextType,
|
||||
userStore.MakeGenericType(userType, roleType, contextType,
|
||||
identityUserType.GenericTypeArguments[0],
|
||||
identityUserType.GenericTypeArguments[1],
|
||||
identityUserType.GenericTypeArguments[2],
|
||||
|
|
@ -52,7 +95,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
identityRoleType.GenericTypeArguments[2]));
|
||||
services.TryAddScoped(
|
||||
typeof(IRoleStore<>).MakeGenericType(roleType),
|
||||
typeof(RoleStore<,,,,>).MakeGenericType(roleType, contextType,
|
||||
roleStore.MakeGenericType(roleType, contextType,
|
||||
identityRoleType.GenericTypeArguments[0],
|
||||
identityRoleType.GenericTypeArguments[1],
|
||||
identityRoleType.GenericTypeArguments[2]));
|
||||
|
|
|
|||
|
|
@ -2,70 +2,10 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Internal;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of a persistence store for roles.
|
||||
/// </summary>
|
||||
/// <typeparam name="TRole">The type of the class representing a role</typeparam>
|
||||
public class RoleStore<TRole> : RoleStore<TRole, DbContext, string>
|
||||
where TRole : IdentityRole<string>
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs a new instance of <see cref="RoleStore{TRole}"/>.
|
||||
/// </summary>
|
||||
/// <param name="context">The <see cref="DbContext"/>.</param>
|
||||
/// <param name="describer">The <see cref="IdentityErrorDescriber"/>.</param>
|
||||
public RoleStore(DbContext context, IdentityErrorDescriber describer = null) : base(context, describer) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of a persistence store for roles.
|
||||
/// </summary>
|
||||
/// <typeparam name="TRole">The type of the class representing a role.</typeparam>
|
||||
/// <typeparam name="TContext">The type of the data context class used to access the store.</typeparam>
|
||||
public class RoleStore<TRole, TContext> : RoleStore<TRole, TContext, string>
|
||||
where TRole : IdentityRole<string>
|
||||
where TContext : DbContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs a new instance of <see cref="RoleStore{TRole, TContext}"/>.
|
||||
/// </summary>
|
||||
/// <param name="context">The <see cref="DbContext"/>.</param>
|
||||
/// <param name="describer">The <see cref="IdentityErrorDescriber"/>.</param>
|
||||
public RoleStore(TContext context, IdentityErrorDescriber describer = null) : base(context, describer) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of a persistence store for roles.
|
||||
/// </summary>
|
||||
/// <typeparam name="TRole">The type of the class representing a role.</typeparam>
|
||||
/// <typeparam name="TContext">The type of the data context class used to access the store.</typeparam>
|
||||
/// <typeparam name="TKey">The type of the primary key for a role.</typeparam>
|
||||
public class RoleStore<TRole, TContext, TKey> : RoleStore<TRole, TContext, TKey, IdentityUserRole<TKey>, IdentityRoleClaim<TKey>>,
|
||||
IQueryableRoleStore<TRole>,
|
||||
IRoleClaimStore<TRole>
|
||||
where TRole : IdentityRole<TKey>
|
||||
where TKey : IEquatable<TKey>
|
||||
where TContext : DbContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs a new instance of <see cref="RoleStore{TRole, TContext, TKey}"/>.
|
||||
/// </summary>
|
||||
/// <param name="context">The <see cref="DbContext"/>.</param>
|
||||
/// <param name="describer">The <see cref="IdentityErrorDescriber"/>.</param>
|
||||
public RoleStore(TContext context, IdentityErrorDescriber describer = null) : base(context, describer) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of a persistence store for roles.
|
||||
/// </summary>
|
||||
|
|
@ -75,8 +15,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
|
|||
/// <typeparam name="TUserRole">The type of the class representing a user role.</typeparam>
|
||||
/// <typeparam name="TRoleClaim">The type of the class representing a role claim.</typeparam>
|
||||
public class RoleStore<TRole, TContext, TKey, TUserRole, TRoleClaim> :
|
||||
IQueryableRoleStore<TRole>,
|
||||
IRoleClaimStore<TRole>
|
||||
RoleStoreV1<TRole, TContext, TKey, TUserRole, TRoleClaim>
|
||||
where TRole : IdentityRole<TKey, TUserRole, TRoleClaim>
|
||||
where TKey : IEquatable<TKey>
|
||||
where TContext : DbContext
|
||||
|
|
@ -88,368 +27,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
|
|||
/// </summary>
|
||||
/// <param name="context">The <see cref="DbContext"/>.</param>
|
||||
/// <param name="describer">The <see cref="IdentityErrorDescriber"/>.</param>
|
||||
public RoleStore(TContext context, IdentityErrorDescriber describer = null)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
Context = context;
|
||||
ErrorDescriber = describer ?? new IdentityErrorDescriber();
|
||||
}
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the database context for this store.
|
||||
/// </summary>
|
||||
public TContext Context { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="IdentityErrorDescriber"/> for any error that occurred with the current operation.
|
||||
/// </summary>
|
||||
public IdentityErrorDescriber ErrorDescriber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a flag indicating if changes should be persisted after CreateAsync, UpdateAsync and DeleteAsync are called.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// True if changes should be automatically persisted, otherwise false.
|
||||
/// </value>
|
||||
public bool AutoSaveChanges { get; set; } = true;
|
||||
|
||||
/// <summary>Saves the current store.</summary>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
private async Task SaveChanges(CancellationToken cancellationToken)
|
||||
{
|
||||
if (AutoSaveChanges)
|
||||
{
|
||||
await Context.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new role in a store as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="role">The role to create in the store.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that represents the <see cref="IdentityResult"/> of the asynchronous query.</returns>
|
||||
public async virtual Task<IdentityResult> CreateAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (role == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(role));
|
||||
}
|
||||
Context.Add(role);
|
||||
await SaveChanges(cancellationToken);
|
||||
return IdentityResult.Success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates a role in a store as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="role">The role to update in the store.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that represents the <see cref="IdentityResult"/> of the asynchronous query.</returns>
|
||||
public async virtual Task<IdentityResult> UpdateAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (role == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(role));
|
||||
}
|
||||
Context.Attach(role);
|
||||
role.ConcurrencyStamp = Guid.NewGuid().ToString();
|
||||
Context.Update(role);
|
||||
try
|
||||
{
|
||||
await SaveChanges(cancellationToken);
|
||||
}
|
||||
catch (DbUpdateConcurrencyException)
|
||||
{
|
||||
return IdentityResult.Failed(ErrorDescriber.ConcurrencyFailure());
|
||||
}
|
||||
return IdentityResult.Success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a role from the store as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="role">The role to delete from the store.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that represents the <see cref="IdentityResult"/> of the asynchronous query.</returns>
|
||||
public async virtual Task<IdentityResult> DeleteAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (role == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(role));
|
||||
}
|
||||
Context.Remove(role);
|
||||
try
|
||||
{
|
||||
await SaveChanges(cancellationToken);
|
||||
}
|
||||
catch (DbUpdateConcurrencyException)
|
||||
{
|
||||
return IdentityResult.Failed(ErrorDescriber.ConcurrencyFailure());
|
||||
}
|
||||
return IdentityResult.Success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ID for a role from the store as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="role">The role whose ID should be returned.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that contains the ID of the role.</returns>
|
||||
public virtual Task<string> GetRoleIdAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (role == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(role));
|
||||
}
|
||||
return Task.FromResult(ConvertIdToString(role.Id));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of a role from the store as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="role">The role whose name should be returned.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that contains the name of the role.</returns>
|
||||
public virtual Task<string> GetRoleNameAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (role == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(role));
|
||||
}
|
||||
return Task.FromResult(role.Name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the name of a role in the store as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="role">The role whose name should be set.</param>
|
||||
/// <param name="roleName">The name of the role.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public virtual Task SetRoleNameAsync(TRole role, string roleName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (role == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(role));
|
||||
}
|
||||
role.Name = roleName;
|
||||
return TaskCache.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the provided <paramref name="id"/> to a strongly typed key object.
|
||||
/// </summary>
|
||||
/// <param name="id">The id to convert.</param>
|
||||
/// <returns>An instance of <typeparamref name="TKey"/> representing the provided <paramref name="id"/>.</returns>
|
||||
public virtual TKey ConvertIdFromString(string id)
|
||||
{
|
||||
if (id == null)
|
||||
{
|
||||
return default(TKey);
|
||||
}
|
||||
return (TKey)TypeDescriptor.GetConverter(typeof(TKey)).ConvertFromInvariantString(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the provided <paramref name="id"/> to its string representation.
|
||||
/// </summary>
|
||||
/// <param name="id">The id to convert.</param>
|
||||
/// <returns>An <see cref="string"/> representation of the provided <paramref name="id"/>.</returns>
|
||||
public virtual string ConvertIdToString(TKey id)
|
||||
{
|
||||
if (id.Equals(default(TKey)))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return id.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the role who has the specified ID as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="id">The role ID to look for.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that result of the look up.</returns>
|
||||
public virtual Task<TRole> FindByIdAsync(string id, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
var roleId = ConvertIdFromString(id);
|
||||
return Roles.FirstOrDefaultAsync(u => u.Id.Equals(roleId), cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the role who has the specified normalized name as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="normalizedName">The normalized role name to look for.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that result of the look up.</returns>
|
||||
public virtual Task<TRole> FindByNameAsync(string normalizedName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
return Roles.FirstOrDefaultAsync(r => r.NormalizedName == normalizedName, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a role's normalized name as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="role">The role whose normalized name should be retrieved.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that contains the name of the role.</returns>
|
||||
public virtual Task<string> GetNormalizedRoleNameAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (role == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(role));
|
||||
}
|
||||
return Task.FromResult(role.NormalizedName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set a role's normalized name as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="role">The role whose normalized name should be set.</param>
|
||||
/// <param name="normalizedName">The normalized name to set</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public virtual Task SetNormalizedRoleNameAsync(TRole role, string normalizedName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (role == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(role));
|
||||
}
|
||||
role.NormalizedName = normalizedName;
|
||||
return TaskCache.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws if this class has been disposed.
|
||||
/// </summary>
|
||||
protected void ThrowIfDisposed()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
throw new ObjectDisposedException(GetType().Name);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose the stores
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the claims associated with the specified <paramref name="role"/> as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="role">The role whose claims should be retrieved.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that contains the claims granted to a role.</returns>
|
||||
public async virtual Task<IList<Claim>> GetClaimsAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (role == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(role));
|
||||
}
|
||||
|
||||
return await RoleClaims.Where(rc => rc.RoleId.Equals(role.Id)).Select(c => new Claim(c.ClaimType, c.ClaimValue)).ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the <paramref name="claim"/> given to the specified <paramref name="role"/>.
|
||||
/// </summary>
|
||||
/// <param name="role">The role to add the claim to.</param>
|
||||
/// <param name="claim">The claim to add to the role.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public virtual Task AddClaimAsync(TRole role, Claim claim, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (role == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(role));
|
||||
}
|
||||
if (claim == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(claim));
|
||||
}
|
||||
|
||||
RoleClaims.Add(CreateRoleClaim(role, claim));
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the <paramref name="claim"/> given from the specified <paramref name="role"/>.
|
||||
/// </summary>
|
||||
/// <param name="role">The role to remove the claim from.</param>
|
||||
/// <param name="claim">The claim to remove from the role.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public async virtual Task RemoveClaimAsync(TRole role, Claim claim, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (role == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(role));
|
||||
}
|
||||
if (claim == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(claim));
|
||||
}
|
||||
var claims = await RoleClaims.Where(rc => rc.RoleId.Equals(role.Id) && rc.ClaimValue == claim.Value && rc.ClaimType == claim.Type).ToListAsync(cancellationToken);
|
||||
foreach (var c in claims)
|
||||
{
|
||||
RoleClaims.Remove(c);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A navigation property for the roles the store contains.
|
||||
/// </summary>
|
||||
public virtual IQueryable<TRole> Roles
|
||||
{
|
||||
get { return Context.Set<TRole>(); }
|
||||
}
|
||||
|
||||
private DbSet<TRoleClaim> RoleClaims { get { return Context.Set<TRoleClaim>(); } }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a entity representing a role claim.
|
||||
/// </summary>
|
||||
/// <param name="role">The associated role.</param>
|
||||
/// <param name="claim">The associated claim.</param>
|
||||
/// <returns>The role claim entity.</returns>
|
||||
protected virtual TRoleClaim CreateRoleClaim(TRole role, Claim claim)
|
||||
{
|
||||
return new TRoleClaim { RoleId = role.Id, ClaimType = claim.Type, ClaimValue = claim.Value };
|
||||
}
|
||||
public RoleStore(TContext context, IdentityErrorDescriber describer = null) : base(context, describer)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,254 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of a persistence store for roles.
|
||||
/// </summary>
|
||||
/// <typeparam name="TRole">The type of the class representing a role.</typeparam>
|
||||
/// <typeparam name="TContext">The type of the data context class used to access the store.</typeparam>
|
||||
/// <typeparam name="TKey">The type of the primary key for a role.</typeparam>
|
||||
/// <typeparam name="TUserRole">The type of the class representing a user role.</typeparam>
|
||||
/// <typeparam name="TRoleClaim">The type of the class representing a role claim.</typeparam>
|
||||
public class RoleStoreV1<TRole, TContext, TKey, TUserRole, TRoleClaim> :
|
||||
RoleStoreBaseV1<TRole, TKey, TUserRole, TRoleClaim>
|
||||
where TRole : IdentityRole<TKey, TUserRole, TRoleClaim>
|
||||
where TKey : IEquatable<TKey>
|
||||
where TContext : DbContext
|
||||
where TUserRole : IdentityUserRole<TKey>, new()
|
||||
where TRoleClaim : IdentityRoleClaim<TKey>, new()
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs a new instance of <see cref="RoleStore{TRole, TContext, TKey, TUserRole, TRoleClaim}"/>.
|
||||
/// </summary>
|
||||
/// <param name="context">The <see cref="DbContext"/>.</param>
|
||||
/// <param name="describer">The <see cref="IdentityErrorDescriber"/>.</param>
|
||||
public RoleStoreV1(TContext context, IdentityErrorDescriber describer = null) : base(describer ?? new IdentityErrorDescriber())
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
Context = context;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the database context for this store.
|
||||
/// </summary>
|
||||
public TContext Context { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a flag indicating if changes should be persisted after CreateAsync, UpdateAsync and DeleteAsync are called.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// True if changes should be automatically persisted, otherwise false.
|
||||
/// </value>
|
||||
public bool AutoSaveChanges { get; set; } = true;
|
||||
|
||||
/// <summary>Saves the current store.</summary>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
private async Task SaveChanges(CancellationToken cancellationToken)
|
||||
{
|
||||
if (AutoSaveChanges)
|
||||
{
|
||||
await Context.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new role in a store as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="role">The role to create in the store.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that represents the <see cref="IdentityResult"/> of the asynchronous query.</returns>
|
||||
public override async Task<IdentityResult> CreateAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (role == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(role));
|
||||
}
|
||||
Context.Add(role);
|
||||
await SaveChanges(cancellationToken);
|
||||
return IdentityResult.Success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates a role in a store as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="role">The role to update in the store.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that represents the <see cref="IdentityResult"/> of the asynchronous query.</returns>
|
||||
public async override Task<IdentityResult> UpdateAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (role == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(role));
|
||||
}
|
||||
Context.Attach(role);
|
||||
role.ConcurrencyStamp = Guid.NewGuid().ToString();
|
||||
Context.Update(role);
|
||||
try
|
||||
{
|
||||
await SaveChanges(cancellationToken);
|
||||
}
|
||||
catch (DbUpdateConcurrencyException)
|
||||
{
|
||||
return IdentityResult.Failed(ErrorDescriber.ConcurrencyFailure());
|
||||
}
|
||||
return IdentityResult.Success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a role from the store as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="role">The role to delete from the store.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that represents the <see cref="IdentityResult"/> of the asynchronous query.</returns>
|
||||
public async override Task<IdentityResult> DeleteAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (role == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(role));
|
||||
}
|
||||
Context.Remove(role);
|
||||
try
|
||||
{
|
||||
await SaveChanges(cancellationToken);
|
||||
}
|
||||
catch (DbUpdateConcurrencyException)
|
||||
{
|
||||
return IdentityResult.Failed(ErrorDescriber.ConcurrencyFailure());
|
||||
}
|
||||
return IdentityResult.Success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the role who has the specified ID as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="id">The role ID to look for.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that result of the look up.</returns>
|
||||
public override Task<TRole> FindByIdAsync(string id, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
var roleId = ConvertIdFromString(id);
|
||||
return Roles.FirstOrDefaultAsync(u => u.Id.Equals(roleId), cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the role who has the specified normalized name as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="normalizedName">The normalized role name to look for.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that result of the look up.</returns>
|
||||
public override Task<TRole> FindByNameAsync(string normalizedName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
return Roles.FirstOrDefaultAsync(r => r.NormalizedName == normalizedName, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the claims associated with the specified <paramref name="role"/> as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="role">The role whose claims should be retrieved.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that contains the claims granted to a role.</returns>
|
||||
public async override Task<IList<Claim>> GetClaimsAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (role == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(role));
|
||||
}
|
||||
|
||||
return await RoleClaims.Where(rc => rc.RoleId.Equals(role.Id)).Select(c => new Claim(c.ClaimType, c.ClaimValue)).ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the <paramref name="claim"/> given to the specified <paramref name="role"/>.
|
||||
/// </summary>
|
||||
/// <param name="role">The role to add the claim to.</param>
|
||||
/// <param name="claim">The claim to add to the role.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public override Task AddClaimAsync(TRole role, Claim claim, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (role == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(role));
|
||||
}
|
||||
if (claim == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(claim));
|
||||
}
|
||||
|
||||
RoleClaims.Add(CreateRoleClaim(role, claim));
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the <paramref name="claim"/> given from the specified <paramref name="role"/>.
|
||||
/// </summary>
|
||||
/// <param name="role">The role to remove the claim from.</param>
|
||||
/// <param name="claim">The claim to remove from the role.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public override async Task RemoveClaimAsync(TRole role, Claim claim, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (role == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(role));
|
||||
}
|
||||
if (claim == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(claim));
|
||||
}
|
||||
var claims = await RoleClaims.Where(rc => rc.RoleId.Equals(role.Id) && rc.ClaimValue == claim.Value && rc.ClaimType == claim.Type).ToListAsync(cancellationToken);
|
||||
foreach (var c in claims)
|
||||
{
|
||||
RoleClaims.Remove(c);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A navigation property for the roles the store contains.
|
||||
/// </summary>
|
||||
public override IQueryable<TRole> Roles
|
||||
{
|
||||
get { return Context.Set<TRole>(); }
|
||||
}
|
||||
|
||||
private DbSet<TRoleClaim> RoleClaims { get { return Context.Set<TRoleClaim>(); } }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a entity representing a role claim.
|
||||
/// </summary>
|
||||
/// <param name="role">The associated role.</param>
|
||||
/// <param name="claim">The associated claim.</param>
|
||||
/// <returns>The role claim entity.</returns>
|
||||
public override TRoleClaim CreateRoleClaim(TRole role, Claim claim)
|
||||
{
|
||||
return new TRoleClaim { RoleId = role.Id, ClaimType = claim.Type, ClaimValue = claim.Value };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,86 +2,10 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Internal;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a new instance of a persistence store for users, using the default implementation
|
||||
/// of <see cref="IdentityUser{TKey}"/> with a string as a primary key.
|
||||
/// </summary>
|
||||
public class UserStore : UserStore<IdentityUser<string>>
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs a new instance of <see cref="UserStore"/>.
|
||||
/// </summary>
|
||||
/// <param name="context">The <see cref="DbContext"/>.</param>
|
||||
/// <param name="describer">The <see cref="IdentityErrorDescriber"/>.</param>
|
||||
public UserStore(DbContext context, IdentityErrorDescriber describer = null) : base(context, describer) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of a persistence store for the specified user type.
|
||||
/// </summary>
|
||||
/// <typeparam name="TUser">The type representing a user.</typeparam>
|
||||
public class UserStore<TUser> : UserStore<TUser, IdentityRole, DbContext, string>
|
||||
where TUser : IdentityUser<string>, new()
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs a new instance of <see cref="UserStore{TUser}"/>.
|
||||
/// </summary>
|
||||
/// <param name="context">The <see cref="DbContext"/>.</param>
|
||||
/// <param name="describer">The <see cref="IdentityErrorDescriber"/>.</param>
|
||||
public UserStore(DbContext context, IdentityErrorDescriber describer = null) : base(context, describer) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a new instance of a persistence store for the specified user and role types.
|
||||
/// </summary>
|
||||
/// <typeparam name="TUser">The type representing a user.</typeparam>
|
||||
/// <typeparam name="TRole">The type representing a role.</typeparam>
|
||||
/// <typeparam name="TContext">The type of the data context class used to access the store.</typeparam>
|
||||
public class UserStore<TUser, TRole, TContext> : UserStore<TUser, TRole, TContext, string>
|
||||
where TUser : IdentityUser<string>
|
||||
where TRole : IdentityRole<string>
|
||||
where TContext : DbContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs a new instance of <see cref="UserStore{TUser, TRole, TContext}"/>.
|
||||
/// </summary>
|
||||
/// <param name="context">The <see cref="DbContext"/>.</param>
|
||||
/// <param name="describer">The <see cref="IdentityErrorDescriber"/>.</param>
|
||||
public UserStore(TContext context, IdentityErrorDescriber describer = null) : base(context, describer) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a new instance of a persistence store for the specified user and role types.
|
||||
/// </summary>
|
||||
/// <typeparam name="TUser">The type representing a user.</typeparam>
|
||||
/// <typeparam name="TRole">The type representing a role.</typeparam>
|
||||
/// <typeparam name="TContext">The type of the data context class used to access the store.</typeparam>
|
||||
/// <typeparam name="TKey">The type of the primary key for a role.</typeparam>
|
||||
public class UserStore<TUser, TRole, TContext, TKey> : UserStore<TUser, TRole, TContext, TKey, IdentityUserClaim<TKey>, IdentityUserRole<TKey>, IdentityUserLogin<TKey>, IdentityUserToken<TKey>, IdentityRoleClaim<TKey>>
|
||||
where TUser : IdentityUser<TKey>
|
||||
where TRole : IdentityRole<TKey>
|
||||
where TContext : DbContext
|
||||
where TKey : IEquatable<TKey>
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs a new instance of <see cref="UserStore{TUser, TRole, TContext, TKey}"/>.
|
||||
/// </summary>
|
||||
/// <param name="context">The <see cref="DbContext"/>.</param>
|
||||
/// <param name="describer">The <see cref="IdentityErrorDescriber"/>.</param>
|
||||
public UserStore(TContext context, IdentityErrorDescriber describer = null) : base(context, describer) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a new instance of a persistence store for the specified user and role types.
|
||||
/// </summary>
|
||||
|
|
@ -95,20 +19,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
|
|||
/// <typeparam name="TUserToken">The type representing a user token.</typeparam>
|
||||
/// <typeparam name="TRoleClaim">The type representing a role claim.</typeparam>
|
||||
public class UserStore<TUser, TRole, TContext, TKey, TUserClaim, TUserRole, TUserLogin, TUserToken, TRoleClaim> :
|
||||
UserStoreBase<TUser, TRole, TKey, TUserClaim, TUserRole, TUserLogin, TUserToken, TRoleClaim>,
|
||||
IUserLoginStore<TUser>,
|
||||
IUserRoleStore<TUser>,
|
||||
IUserClaimStore<TUser>,
|
||||
IUserPasswordStore<TUser>,
|
||||
IUserSecurityStampStore<TUser>,
|
||||
IUserEmailStore<TUser>,
|
||||
IUserLockoutStore<TUser>,
|
||||
IUserPhoneNumberStore<TUser>,
|
||||
IQueryableUserStore<TUser>,
|
||||
IUserTwoFactorStore<TUser>,
|
||||
IUserAuthenticationTokenStore<TUser>,
|
||||
IUserAuthenticatorKeyStore<TUser>,
|
||||
IUserTwoFactorRecoveryCodeStore<TUser>
|
||||
UserStoreV2<TUser, TRole, TContext, TKey, TUserClaim, TUserRole, TUserLogin, TUserToken, TRoleClaim>
|
||||
where TUser : IdentityUser<TKey, TUserClaim, TUserRole, TUserLogin, TUserToken>
|
||||
where TRole : IdentityRole<TKey, TUserRole, TRoleClaim>
|
||||
where TContext : DbContext
|
||||
|
|
@ -124,622 +35,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
|
|||
/// </summary>
|
||||
/// <param name="context">The context used to access the store.</param>
|
||||
/// <param name="describer">The <see cref="IdentityErrorDescriber"/> used to describe store errors.</param>
|
||||
public UserStore(TContext context, IdentityErrorDescriber describer = null) : base(describer ?? new IdentityErrorDescriber())
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
Context = context;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the database context for this store.
|
||||
/// </summary>
|
||||
public TContext Context { get; private set; }
|
||||
|
||||
private DbSet<TUser> UsersSet { get { return Context.Set<TUser>(); } }
|
||||
private DbSet<TRole> Roles { get { return Context.Set<TRole>(); } }
|
||||
private DbSet<TUserClaim> UserClaims { get { return Context.Set<TUserClaim>(); } }
|
||||
private DbSet<TUserRole> UserRoles { get { return Context.Set<TUserRole>(); } }
|
||||
private DbSet<TUserLogin> UserLogins { get { return Context.Set<TUserLogin>(); } }
|
||||
private DbSet<TUserToken> UserTokens { get { return Context.Set<TUserToken>(); } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a flag indicating if changes should be persisted after CreateAsync, UpdateAsync and DeleteAsync are called.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// True if changes should be automatically persisted, otherwise false.
|
||||
/// </value>
|
||||
public bool AutoSaveChanges { get; set; } = true;
|
||||
|
||||
/// <summary>Saves the current store.</summary>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
protected Task SaveChanges(CancellationToken cancellationToken)
|
||||
{
|
||||
return AutoSaveChanges ? Context.SaveChangesAsync(cancellationToken) : TaskCache.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the specified <paramref name="user"/> in the user store.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to create.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/> of the creation operation.</returns>
|
||||
public async override Task<IdentityResult> CreateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
Context.Add(user);
|
||||
await SaveChanges(cancellationToken);
|
||||
return IdentityResult.Success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the specified <paramref name="user"/> in the user store.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to update.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/> of the update operation.</returns>
|
||||
public async override Task<IdentityResult> UpdateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
|
||||
Context.Attach(user);
|
||||
user.ConcurrencyStamp = Guid.NewGuid().ToString();
|
||||
Context.Update(user);
|
||||
try
|
||||
{
|
||||
await SaveChanges(cancellationToken);
|
||||
}
|
||||
catch (DbUpdateConcurrencyException)
|
||||
{
|
||||
return IdentityResult.Failed(ErrorDescriber.ConcurrencyFailure());
|
||||
}
|
||||
return IdentityResult.Success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the specified <paramref name="user"/> from the user store.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to delete.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/> of the update operation.</returns>
|
||||
public async override Task<IdentityResult> DeleteAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
|
||||
Context.Remove(user);
|
||||
try
|
||||
{
|
||||
await SaveChanges(cancellationToken);
|
||||
}
|
||||
catch (DbUpdateConcurrencyException)
|
||||
{
|
||||
return IdentityResult.Failed(ErrorDescriber.ConcurrencyFailure());
|
||||
}
|
||||
return IdentityResult.Success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds and returns a user, if any, who has the specified <paramref name="userId"/>.
|
||||
/// </summary>
|
||||
/// <param name="userId">The user ID to search for.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>
|
||||
/// The <see cref="Task"/> that represents the asynchronous operation, containing the user matching the specified <paramref name="userId"/> if it exists.
|
||||
/// </returns>
|
||||
public override Task<TUser> FindByIdAsync(string userId, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
var id = ConvertIdFromString(userId);
|
||||
return UsersSet.FindAsync(new object[] { id }, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds and returns a user, if any, who has the specified normalized user name.
|
||||
/// </summary>
|
||||
/// <param name="normalizedUserName">The normalized user name to search for.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>
|
||||
/// The <see cref="Task"/> that represents the asynchronous operation, containing the user matching the specified <paramref name="normalizedUserName"/> if it exists.
|
||||
/// </returns>
|
||||
public override Task<TUser> FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
return Users.FirstOrDefaultAsync(u => u.NormalizedUserName == normalizedUserName, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A navigation property for the users the store contains.
|
||||
/// </summary>
|
||||
public override IQueryable<TUser> Users
|
||||
{
|
||||
get { return UsersSet; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a role with the normalized name if it exists.
|
||||
/// </summary>
|
||||
/// <param name="normalizedRoleName">The normalized role name.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The role if it exists.</returns>
|
||||
protected override Task<TRole> FindRoleAsync(string normalizedRoleName, CancellationToken cancellationToken)
|
||||
{
|
||||
return Roles.SingleOrDefaultAsync(r => r.NormalizedName == normalizedRoleName, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a user role for the userId and roleId if it exists.
|
||||
/// </summary>
|
||||
/// <param name="userId">The user's id.</param>
|
||||
/// <param name="roleId">The role's id.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The user role if it exists.</returns>
|
||||
protected override Task<TUserRole> FindUserRoleAsync(TKey userId, TKey roleId, CancellationToken cancellationToken)
|
||||
{
|
||||
return UserRoles.FindAsync(new object[] { userId, roleId }, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a user with the matching userId if it exists.
|
||||
/// </summary>
|
||||
/// <param name="userId">The user's id.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The user if it exists.</returns>
|
||||
protected override Task<TUser> FindUserAsync(TKey userId, CancellationToken cancellationToken)
|
||||
{
|
||||
return Users.SingleOrDefaultAsync(u => u.Id.Equals(userId), cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a user login with the matching userId, provider, providerKey if it exists.
|
||||
/// </summary>
|
||||
/// <param name="userId">The user's id.</param>
|
||||
/// <param name="loginProvider">The login provider name.</param>
|
||||
/// <param name="providerKey">The key provided by the <paramref name="loginProvider"/> to identify a user.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The user login if it exists.</returns>
|
||||
protected override Task<TUserLogin> FindUserLoginAsync(TKey userId, string loginProvider, string providerKey, CancellationToken cancellationToken)
|
||||
{
|
||||
return UserLogins.SingleOrDefaultAsync(userLogin => userLogin.UserId.Equals(userId) && userLogin.LoginProvider == loginProvider && userLogin.ProviderKey == providerKey, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a user login with provider, providerKey if it exists.
|
||||
/// </summary>
|
||||
/// <param name="loginProvider">The login provider name.</param>
|
||||
/// <param name="providerKey">The key provided by the <paramref name="loginProvider"/> to identify a user.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The user login if it exists.</returns>
|
||||
protected override Task<TUserLogin> FindUserLoginAsync(string loginProvider, string providerKey, CancellationToken cancellationToken)
|
||||
{
|
||||
return UserLogins.SingleOrDefaultAsync(userLogin => userLogin.LoginProvider == loginProvider && userLogin.ProviderKey == providerKey, cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Adds the given <paramref name="normalizedRoleName"/> to the specified <paramref name="user"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to add the role to.</param>
|
||||
/// <param name="normalizedRoleName">The role to add.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public async override Task AddToRoleAsync(TUser user, string normalizedRoleName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(normalizedRoleName))
|
||||
{
|
||||
throw new ArgumentException(Resources.ValueCannotBeNullOrEmpty, nameof(normalizedRoleName));
|
||||
}
|
||||
var roleEntity = await FindRoleAsync(normalizedRoleName, cancellationToken);
|
||||
if (roleEntity == null)
|
||||
{
|
||||
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resources.RoleNotFound, normalizedRoleName));
|
||||
}
|
||||
UserRoles.Add(CreateUserRole(user, roleEntity));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the given <paramref name="normalizedRoleName"/> from the specified <paramref name="user"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to remove the role from.</param>
|
||||
/// <param name="normalizedRoleName">The role to remove.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public async override Task RemoveFromRoleAsync(TUser user, string normalizedRoleName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(normalizedRoleName))
|
||||
{
|
||||
throw new ArgumentException(Resources.ValueCannotBeNullOrEmpty, nameof(normalizedRoleName));
|
||||
}
|
||||
var roleEntity = await FindRoleAsync(normalizedRoleName, cancellationToken);
|
||||
if (roleEntity != null)
|
||||
{
|
||||
var userRole = await FindUserRoleAsync(user.Id, roleEntity.Id, cancellationToken);
|
||||
if (userRole != null)
|
||||
{
|
||||
UserRoles.Remove(userRole);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the roles the specified <paramref name="user"/> is a member of.
|
||||
/// </summary>
|
||||
/// <param name="user">The user whose roles should be retrieved.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that contains the roles the user is a member of.</returns>
|
||||
public override async Task<IList<string>> GetRolesAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
var userId = user.Id;
|
||||
var query = from userRole in UserRoles
|
||||
join role in Roles on userRole.RoleId equals role.Id
|
||||
where userRole.UserId.Equals(userId)
|
||||
select role.Name;
|
||||
return await query.ToListAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a flag indicating if the specified user is a member of the give <paramref name="normalizedRoleName"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user whose role membership should be checked.</param>
|
||||
/// <param name="normalizedRoleName">The role to check membership of</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> containing a flag indicating if the specified user is a member of the given group. If the
|
||||
/// user is a member of the group the returned value with be true, otherwise it will be false.</returns>
|
||||
public override async Task<bool> IsInRoleAsync(TUser user, string normalizedRoleName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(normalizedRoleName))
|
||||
{
|
||||
throw new ArgumentException(Resources.ValueCannotBeNullOrEmpty, nameof(normalizedRoleName));
|
||||
}
|
||||
var role = await FindRoleAsync(normalizedRoleName, cancellationToken);
|
||||
if (role != null)
|
||||
{
|
||||
var userRole = await FindUserRoleAsync(user.Id, role.Id, cancellationToken);
|
||||
return userRole != null;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the claims associated with the specified <paramref name="user"/> as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="user">The user whose claims should be retrieved.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that contains the claims granted to a user.</returns>
|
||||
public async override Task<IList<Claim>> GetClaimsAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
|
||||
return await UserClaims.Where(uc => uc.UserId.Equals(user.Id)).Select(c => c.ToClaim()).ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the <paramref name="claims"/> given to the specified <paramref name="user"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to add the claim to.</param>
|
||||
/// <param name="claims">The claim to add to the user.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public override Task AddClaimsAsync(TUser user, IEnumerable<Claim> claims, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
if (claims == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(claims));
|
||||
}
|
||||
foreach (var claim in claims)
|
||||
{
|
||||
UserClaims.Add(CreateUserClaim(user, claim));
|
||||
}
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces the <paramref name="claim"/> on the specified <paramref name="user"/>, with the <paramref name="newClaim"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to replace the claim on.</param>
|
||||
/// <param name="claim">The claim replace.</param>
|
||||
/// <param name="newClaim">The new claim replacing the <paramref name="claim"/>.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public async override Task ReplaceClaimAsync(TUser user, Claim claim, Claim newClaim, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
if (claim == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(claim));
|
||||
}
|
||||
if (newClaim == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(newClaim));
|
||||
}
|
||||
|
||||
var matchedClaims = await UserClaims.Where(uc => uc.UserId.Equals(user.Id) && uc.ClaimValue == claim.Value && uc.ClaimType == claim.Type).ToListAsync(cancellationToken);
|
||||
foreach (var matchedClaim in matchedClaims)
|
||||
{
|
||||
matchedClaim.ClaimValue = newClaim.Value;
|
||||
matchedClaim.ClaimType = newClaim.Type;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the <paramref name="claims"/> given from the specified <paramref name="user"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to remove the claims from.</param>
|
||||
/// <param name="claims">The claim to remove.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public async override Task RemoveClaimsAsync(TUser user, IEnumerable<Claim> claims, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
if (claims == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(claims));
|
||||
}
|
||||
foreach (var claim in claims)
|
||||
{
|
||||
var matchedClaims = await UserClaims.Where(uc => uc.UserId.Equals(user.Id) && uc.ClaimValue == claim.Value && uc.ClaimType == claim.Type).ToListAsync(cancellationToken);
|
||||
foreach (var c in matchedClaims)
|
||||
{
|
||||
UserClaims.Remove(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the <paramref name="login"/> given to the specified <paramref name="user"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to add the login to.</param>
|
||||
/// <param name="login">The login to add to the user.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public override Task AddLoginAsync(TUser user, UserLoginInfo login,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
if (login == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(login));
|
||||
}
|
||||
UserLogins.Add(CreateUserLogin(user, login));
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the <paramref name="loginProvider"/> given from the specified <paramref name="user"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to remove the login from.</param>
|
||||
/// <param name="loginProvider">The login to remove from the user.</param>
|
||||
/// <param name="providerKey">The key provided by the <paramref name="loginProvider"/> to identify a user.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public override async Task RemoveLoginAsync(TUser user, string loginProvider, string providerKey,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
var entry = await FindUserLoginAsync(user.Id, loginProvider, providerKey, cancellationToken);
|
||||
if (entry != null)
|
||||
{
|
||||
UserLogins.Remove(entry);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the associated logins for the specified <param ref="user"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user whose associated logins to retrieve.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>
|
||||
/// The <see cref="Task"/> for the asynchronous operation, containing a list of <see cref="UserLoginInfo"/> for the specified <paramref name="user"/>, if any.
|
||||
/// </returns>
|
||||
public async override Task<IList<UserLoginInfo>> GetLoginsAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
var userId = user.Id;
|
||||
return await UserLogins.Where(l => l.UserId.Equals(userId))
|
||||
.Select(l => new UserLoginInfo(l.LoginProvider, l.ProviderKey, l.ProviderDisplayName)).ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the user associated with the specified login provider and login provider key.
|
||||
/// </summary>
|
||||
/// <param name="loginProvider">The login provider who provided the <paramref name="providerKey"/>.</param>
|
||||
/// <param name="providerKey">The key provided by the <paramref name="loginProvider"/> to identify a user.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>
|
||||
/// The <see cref="Task"/> for the asynchronous operation, containing the user, if any which matched the specified login provider and key.
|
||||
/// </returns>
|
||||
public async override Task<TUser> FindByLoginAsync(string loginProvider, string providerKey,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
var userLogin = await FindUserLoginAsync(loginProvider, providerKey, cancellationToken);
|
||||
if (userLogin != null)
|
||||
{
|
||||
return await FindUserAsync(userLogin.UserId, cancellationToken);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user, if any, associated with the specified, normalized email address.
|
||||
/// </summary>
|
||||
/// <param name="normalizedEmail">The normalized email address to return the user for.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>
|
||||
/// The task object containing the results of the asynchronous lookup operation, the user if any associated with the specified normalized email address.
|
||||
/// </returns>
|
||||
public override Task<TUser> FindByEmailAsync(string normalizedEmail, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
return Users.FirstOrDefaultAsync(u => u.NormalizedEmail == normalizedEmail, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all users with the specified claim.
|
||||
/// </summary>
|
||||
/// <param name="claim">The claim whose users should be retrieved.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>
|
||||
/// The <see cref="Task"/> contains a list of users, if any, that contain the specified claim.
|
||||
/// </returns>
|
||||
public async override Task<IList<TUser>> GetUsersForClaimAsync(Claim claim, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (claim == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(claim));
|
||||
}
|
||||
|
||||
var query = from userclaims in UserClaims
|
||||
join user in Users on userclaims.UserId equals user.Id
|
||||
where userclaims.ClaimValue == claim.Value
|
||||
&& userclaims.ClaimType == claim.Type
|
||||
select user;
|
||||
|
||||
return await query.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all users in the specified role.
|
||||
/// </summary>
|
||||
/// <param name="normalizedRoleName">The role whose users should be retrieved.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>
|
||||
/// The <see cref="Task"/> contains a list of users, if any, that are in the specified role.
|
||||
/// </returns>
|
||||
public async override Task<IList<TUser>> GetUsersInRoleAsync(string normalizedRoleName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (string.IsNullOrEmpty(normalizedRoleName))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(normalizedRoleName));
|
||||
}
|
||||
|
||||
var role = await FindRoleAsync(normalizedRoleName, cancellationToken);
|
||||
|
||||
if (role != null)
|
||||
{
|
||||
var query = from userrole in UserRoles
|
||||
join user in Users on userrole.UserId equals user.Id
|
||||
where userrole.RoleId.Equals(role.Id)
|
||||
select user;
|
||||
|
||||
return await query.ToListAsync(cancellationToken);
|
||||
}
|
||||
return new List<TUser>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find a user token if it exists.
|
||||
/// </summary>
|
||||
/// <param name="user">The token owner.</param>
|
||||
/// <param name="loginProvider">The login provider for the token.</param>
|
||||
/// <param name="name">The name of the token.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The user token if it exists.</returns>
|
||||
protected override Task<TUserToken> FindTokenAsync(TUser user, string loginProvider, string name, CancellationToken cancellationToken)
|
||||
=> UserTokens.FindAsync(new object[] { user.Id, loginProvider, name }, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Add a new user token.
|
||||
/// </summary>
|
||||
/// <param name="token">The token to be added.</param>
|
||||
/// <returns></returns>
|
||||
protected override Task AddUserTokenAsync(TUserToken token)
|
||||
{
|
||||
UserTokens.Add(token);
|
||||
return TaskCache.CompletedTask;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Remove a new user token.
|
||||
/// </summary>
|
||||
/// <param name="token">The token to be removed.</param>
|
||||
/// <returns></returns>
|
||||
protected override Task RemoveUserTokenAsync(TUserToken token)
|
||||
{
|
||||
UserTokens.Remove(token);
|
||||
return TaskCache.CompletedTask;
|
||||
}
|
||||
public UserStore(TContext context, IdentityErrorDescriber describer) : base(context, describer)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,650 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Internal;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
|
||||
{
|
||||
internal class UserStoreCore<TUser, TRole, TContext, TKey, TUserClaim, TUserRole, TUserLogin, TUserToken, TRoleClaim> :
|
||||
UserStoreBase<TUser, TRole, TKey, TUserClaim, TUserRole, TUserLogin, TUserToken, TRoleClaim>
|
||||
where TUser : IdentityUser<TKey, TUserClaim, TUserRole, TUserLogin, TUserToken>
|
||||
where TRole : IdentityRole<TKey, TUserRole, TRoleClaim>
|
||||
where TContext : DbContext
|
||||
where TKey : IEquatable<TKey>
|
||||
where TUserClaim : IdentityUserClaim<TKey>, new()
|
||||
where TUserRole : IdentityUserRole<TKey>, new()
|
||||
where TUserLogin : IdentityUserLogin<TKey>, new()
|
||||
where TUserToken : IdentityUserToken<TKey>, new()
|
||||
where TRoleClaim : IdentityRoleClaim<TKey>, new()
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of the store.
|
||||
/// </summary>
|
||||
/// <param name="context">The context used to access the store.</param>
|
||||
/// <param name="describer">The <see cref="IdentityErrorDescriber"/> used to describe store errors.</param>
|
||||
public UserStoreCore(TContext context, IdentityErrorDescriber describer) : base(describer)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
Context = context;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the database context for this store.
|
||||
/// </summary>
|
||||
public TContext Context { get; private set; }
|
||||
|
||||
public DbSet<TUser> UsersSet { get { return Context.Set<TUser>(); } }
|
||||
public DbSet<TRole> Roles { get { return Context.Set<TRole>(); } }
|
||||
public DbSet<TUserClaim> UserClaims { get { return Context.Set<TUserClaim>(); } }
|
||||
public DbSet<TUserRole> UserRoles { get { return Context.Set<TUserRole>(); } }
|
||||
public DbSet<TUserLogin> UserLogins { get { return Context.Set<TUserLogin>(); } }
|
||||
public DbSet<TUserToken> UserTokens { get { return Context.Set<TUserToken>(); } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a flag indicating if changes should be persisted after CreateAsync, UpdateAsync and DeleteAsync are called.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// True if changes should be automatically persisted, otherwise false.
|
||||
/// </value>
|
||||
public bool AutoSaveChanges { get; set; } = true;
|
||||
|
||||
/// <summary>Saves the current store.</summary>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public Task SaveChanges(CancellationToken cancellationToken)
|
||||
{
|
||||
return AutoSaveChanges ? Context.SaveChangesAsync(cancellationToken) : TaskCache.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the specified <paramref name="user"/> in the user store.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to create.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/> of the creation operation.</returns>
|
||||
public async override Task<IdentityResult> CreateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
Context.Add(user);
|
||||
await SaveChanges(cancellationToken);
|
||||
return IdentityResult.Success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the specified <paramref name="user"/> in the user store.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to update.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/> of the update operation.</returns>
|
||||
public async override Task<IdentityResult> UpdateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
|
||||
Context.Attach(user);
|
||||
user.ConcurrencyStamp = Guid.NewGuid().ToString();
|
||||
Context.Update(user);
|
||||
try
|
||||
{
|
||||
await SaveChanges(cancellationToken);
|
||||
}
|
||||
catch (DbUpdateConcurrencyException)
|
||||
{
|
||||
return IdentityResult.Failed(ErrorDescriber.ConcurrencyFailure());
|
||||
}
|
||||
return IdentityResult.Success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the specified <paramref name="user"/> from the user store.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to delete.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/> of the update operation.</returns>
|
||||
public async override Task<IdentityResult> DeleteAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
|
||||
Context.Remove(user);
|
||||
try
|
||||
{
|
||||
await SaveChanges(cancellationToken);
|
||||
}
|
||||
catch (DbUpdateConcurrencyException)
|
||||
{
|
||||
return IdentityResult.Failed(ErrorDescriber.ConcurrencyFailure());
|
||||
}
|
||||
return IdentityResult.Success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds and returns a user, if any, who has the specified <paramref name="userId"/>.
|
||||
/// </summary>
|
||||
/// <param name="userId">The user ID to search for.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>
|
||||
/// The <see cref="Task"/> that represents the asynchronous operation, containing the user matching the specified <paramref name="userId"/> if it exists.
|
||||
/// </returns>
|
||||
public override Task<TUser> FindByIdAsync(string userId, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
var id = ConvertIdFromString(userId);
|
||||
return UsersSet.FindAsync(new object[] { id }, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds and returns a user, if any, who has the specified normalized user name.
|
||||
/// </summary>
|
||||
/// <param name="normalizedUserName">The normalized user name to search for.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>
|
||||
/// The <see cref="Task"/> that represents the asynchronous operation, containing the user matching the specified <paramref name="normalizedUserName"/> if it exists.
|
||||
/// </returns>
|
||||
public override Task<TUser> FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
return Users.FirstOrDefaultAsync(u => u.NormalizedUserName == normalizedUserName, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A navigation property for the users the store contains.
|
||||
/// </summary>
|
||||
public override IQueryable<TUser> Users
|
||||
{
|
||||
get { return UsersSet; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a role with the normalized name if it exists.
|
||||
/// </summary>
|
||||
/// <param name="normalizedRoleName">The normalized role name.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The role if it exists.</returns>
|
||||
public override Task<TRole> FindRoleAsync(string normalizedRoleName, CancellationToken cancellationToken)
|
||||
{
|
||||
return Roles.SingleOrDefaultAsync(r => r.NormalizedName == normalizedRoleName, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a user role for the userId and roleId if it exists.
|
||||
/// </summary>
|
||||
/// <param name="userId">The user's id.</param>
|
||||
/// <param name="roleId">The role's id.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The user role if it exists.</returns>
|
||||
public override Task<TUserRole> FindUserRoleAsync(TKey userId, TKey roleId, CancellationToken cancellationToken)
|
||||
{
|
||||
return UserRoles.FindAsync(new object[] { userId, roleId }, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a user with the matching userId if it exists.
|
||||
/// </summary>
|
||||
/// <param name="userId">The user's id.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The user if it exists.</returns>
|
||||
public override Task<TUser> FindUserAsync(TKey userId, CancellationToken cancellationToken)
|
||||
{
|
||||
return Users.SingleOrDefaultAsync(u => u.Id.Equals(userId), cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a user login with the matching userId, provider, providerKey if it exists.
|
||||
/// </summary>
|
||||
/// <param name="userId">The user's id.</param>
|
||||
/// <param name="loginProvider">The login provider name.</param>
|
||||
/// <param name="providerKey">The key provided by the <paramref name="loginProvider"/> to identify a user.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The user login if it exists.</returns>
|
||||
public override Task<TUserLogin> FindUserLoginAsync(TKey userId, string loginProvider, string providerKey, CancellationToken cancellationToken)
|
||||
{
|
||||
return UserLogins.SingleOrDefaultAsync(userLogin => userLogin.UserId.Equals(userId) && userLogin.LoginProvider == loginProvider && userLogin.ProviderKey == providerKey, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a user login with provider, providerKey if it exists.
|
||||
/// </summary>
|
||||
/// <param name="loginProvider">The login provider name.</param>
|
||||
/// <param name="providerKey">The key provided by the <paramref name="loginProvider"/> to identify a user.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The user login if it exists.</returns>
|
||||
public override Task<TUserLogin> FindUserLoginAsync(string loginProvider, string providerKey, CancellationToken cancellationToken)
|
||||
{
|
||||
return UserLogins.SingleOrDefaultAsync(userLogin => userLogin.LoginProvider == loginProvider && userLogin.ProviderKey == providerKey, cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Adds the given <paramref name="normalizedRoleName"/> to the specified <paramref name="user"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to add the role to.</param>
|
||||
/// <param name="normalizedRoleName">The role to add.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public async override Task AddToRoleAsync(TUser user, string normalizedRoleName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(normalizedRoleName))
|
||||
{
|
||||
throw new ArgumentException(Resources.ValueCannotBeNullOrEmpty, nameof(normalizedRoleName));
|
||||
}
|
||||
var roleEntity = await FindRoleAsync(normalizedRoleName, cancellationToken);
|
||||
if (roleEntity == null)
|
||||
{
|
||||
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resources.RoleNotFound, normalizedRoleName));
|
||||
}
|
||||
UserRoles.Add(CreateUserRole(user, roleEntity));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the given <paramref name="normalizedRoleName"/> from the specified <paramref name="user"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to remove the role from.</param>
|
||||
/// <param name="normalizedRoleName">The role to remove.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public async override Task RemoveFromRoleAsync(TUser user, string normalizedRoleName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(normalizedRoleName))
|
||||
{
|
||||
throw new ArgumentException(Resources.ValueCannotBeNullOrEmpty, nameof(normalizedRoleName));
|
||||
}
|
||||
var roleEntity = await FindRoleAsync(normalizedRoleName, cancellationToken);
|
||||
if (roleEntity != null)
|
||||
{
|
||||
var userRole = await FindUserRoleAsync(user.Id, roleEntity.Id, cancellationToken);
|
||||
if (userRole != null)
|
||||
{
|
||||
UserRoles.Remove(userRole);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the roles the specified <paramref name="user"/> is a member of.
|
||||
/// </summary>
|
||||
/// <param name="user">The user whose roles should be retrieved.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that contains the roles the user is a member of.</returns>
|
||||
public override async Task<IList<string>> GetRolesAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
var userId = user.Id;
|
||||
var query = from userRole in UserRoles
|
||||
join role in Roles on userRole.RoleId equals role.Id
|
||||
where userRole.UserId.Equals(userId)
|
||||
select role.Name;
|
||||
return await query.ToListAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a flag indicating if the specified user is a member of the give <paramref name="normalizedRoleName"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user whose role membership should be checked.</param>
|
||||
/// <param name="normalizedRoleName">The role to check membership of</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> containing a flag indicating if the specified user is a member of the given group. If the
|
||||
/// user is a member of the group the returned value with be true, otherwise it will be false.</returns>
|
||||
public override async Task<bool> IsInRoleAsync(TUser user, string normalizedRoleName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(normalizedRoleName))
|
||||
{
|
||||
throw new ArgumentException(Resources.ValueCannotBeNullOrEmpty, nameof(normalizedRoleName));
|
||||
}
|
||||
var role = await FindRoleAsync(normalizedRoleName, cancellationToken);
|
||||
if (role != null)
|
||||
{
|
||||
var userRole = await FindUserRoleAsync(user.Id, role.Id, cancellationToken);
|
||||
return userRole != null;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the claims associated with the specified <paramref name="user"/> as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="user">The user whose claims should be retrieved.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that contains the claims granted to a user.</returns>
|
||||
public async override Task<IList<Claim>> GetClaimsAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
|
||||
return await UserClaims.Where(uc => uc.UserId.Equals(user.Id)).Select(c => c.ToClaim()).ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the <paramref name="claims"/> given to the specified <paramref name="user"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to add the claim to.</param>
|
||||
/// <param name="claims">The claim to add to the user.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public override Task AddClaimsAsync(TUser user, IEnumerable<Claim> claims, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
if (claims == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(claims));
|
||||
}
|
||||
foreach (var claim in claims)
|
||||
{
|
||||
UserClaims.Add(CreateUserClaim(user, claim));
|
||||
}
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces the <paramref name="claim"/> on the specified <paramref name="user"/>, with the <paramref name="newClaim"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to replace the claim on.</param>
|
||||
/// <param name="claim">The claim replace.</param>
|
||||
/// <param name="newClaim">The new claim replacing the <paramref name="claim"/>.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public async override Task ReplaceClaimAsync(TUser user, Claim claim, Claim newClaim, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
if (claim == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(claim));
|
||||
}
|
||||
if (newClaim == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(newClaim));
|
||||
}
|
||||
|
||||
var matchedClaims = await UserClaims.Where(uc => uc.UserId.Equals(user.Id) && uc.ClaimValue == claim.Value && uc.ClaimType == claim.Type).ToListAsync(cancellationToken);
|
||||
foreach (var matchedClaim in matchedClaims)
|
||||
{
|
||||
matchedClaim.ClaimValue = newClaim.Value;
|
||||
matchedClaim.ClaimType = newClaim.Type;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the <paramref name="claims"/> given from the specified <paramref name="user"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to remove the claims from.</param>
|
||||
/// <param name="claims">The claim to remove.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public async override Task RemoveClaimsAsync(TUser user, IEnumerable<Claim> claims, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
if (claims == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(claims));
|
||||
}
|
||||
foreach (var claim in claims)
|
||||
{
|
||||
var matchedClaims = await UserClaims.Where(uc => uc.UserId.Equals(user.Id) && uc.ClaimValue == claim.Value && uc.ClaimType == claim.Type).ToListAsync(cancellationToken);
|
||||
foreach (var c in matchedClaims)
|
||||
{
|
||||
UserClaims.Remove(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the <paramref name="login"/> given to the specified <paramref name="user"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to add the login to.</param>
|
||||
/// <param name="login">The login to add to the user.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public override Task AddLoginAsync(TUser user, UserLoginInfo login,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
if (login == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(login));
|
||||
}
|
||||
UserLogins.Add(CreateUserLogin(user, login));
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the <paramref name="loginProvider"/> given from the specified <paramref name="user"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to remove the login from.</param>
|
||||
/// <param name="loginProvider">The login to remove from the user.</param>
|
||||
/// <param name="providerKey">The key provided by the <paramref name="loginProvider"/> to identify a user.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public override async Task RemoveLoginAsync(TUser user, string loginProvider, string providerKey,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
var entry = await FindUserLoginAsync(user.Id, loginProvider, providerKey, cancellationToken);
|
||||
if (entry != null)
|
||||
{
|
||||
UserLogins.Remove(entry);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the associated logins for the specified <param ref="user"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user whose associated logins to retrieve.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>
|
||||
/// The <see cref="Task"/> for the asynchronous operation, containing a list of <see cref="UserLoginInfo"/> for the specified <paramref name="user"/>, if any.
|
||||
/// </returns>
|
||||
public async override Task<IList<UserLoginInfo>> GetLoginsAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
var userId = user.Id;
|
||||
return await UserLogins.Where(l => l.UserId.Equals(userId))
|
||||
.Select(l => new UserLoginInfo(l.LoginProvider, l.ProviderKey, l.ProviderDisplayName)).ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the user associated with the specified login provider and login provider key.
|
||||
/// </summary>
|
||||
/// <param name="loginProvider">The login provider who provided the <paramref name="providerKey"/>.</param>
|
||||
/// <param name="providerKey">The key provided by the <paramref name="loginProvider"/> to identify a user.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>
|
||||
/// The <see cref="Task"/> for the asynchronous operation, containing the user, if any which matched the specified login provider and key.
|
||||
/// </returns>
|
||||
public async override Task<TUser> FindByLoginAsync(string loginProvider, string providerKey,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
var userLogin = await FindUserLoginAsync(loginProvider, providerKey, cancellationToken);
|
||||
if (userLogin != null)
|
||||
{
|
||||
return await FindUserAsync(userLogin.UserId, cancellationToken);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user, if any, associated with the specified, normalized email address.
|
||||
/// </summary>
|
||||
/// <param name="normalizedEmail">The normalized email address to return the user for.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>
|
||||
/// The task object containing the results of the asynchronous lookup operation, the user if any associated with the specified normalized email address.
|
||||
/// </returns>
|
||||
public override Task<TUser> FindByEmailAsync(string normalizedEmail, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
return Users.FirstOrDefaultAsync(u => u.NormalizedEmail == normalizedEmail, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all users with the specified claim.
|
||||
/// </summary>
|
||||
/// <param name="claim">The claim whose users should be retrieved.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>
|
||||
/// The <see cref="Task"/> contains a list of users, if any, that contain the specified claim.
|
||||
/// </returns>
|
||||
public async override Task<IList<TUser>> GetUsersForClaimAsync(Claim claim, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (claim == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(claim));
|
||||
}
|
||||
|
||||
var query = from userclaims in UserClaims
|
||||
join user in Users on userclaims.UserId equals user.Id
|
||||
where userclaims.ClaimValue == claim.Value
|
||||
&& userclaims.ClaimType == claim.Type
|
||||
select user;
|
||||
|
||||
return await query.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all users in the specified role.
|
||||
/// </summary>
|
||||
/// <param name="normalizedRoleName">The role whose users should be retrieved.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>
|
||||
/// The <see cref="Task"/> contains a list of users, if any, that are in the specified role.
|
||||
/// </returns>
|
||||
public async override Task<IList<TUser>> GetUsersInRoleAsync(string normalizedRoleName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (string.IsNullOrEmpty(normalizedRoleName))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(normalizedRoleName));
|
||||
}
|
||||
|
||||
var role = await FindRoleAsync(normalizedRoleName, cancellationToken);
|
||||
|
||||
if (role != null)
|
||||
{
|
||||
var query = from userrole in UserRoles
|
||||
join user in Users on userrole.UserId equals user.Id
|
||||
where userrole.RoleId.Equals(role.Id)
|
||||
select user;
|
||||
|
||||
return await query.ToListAsync(cancellationToken);
|
||||
}
|
||||
return new List<TUser>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find a user token if it exists.
|
||||
/// </summary>
|
||||
/// <param name="user">The token owner.</param>
|
||||
/// <param name="loginProvider">The login provider for the token.</param>
|
||||
/// <param name="name">The name of the token.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The user token if it exists.</returns>
|
||||
public override Task<TUserToken> FindTokenAsync(TUser user, string loginProvider, string name, CancellationToken cancellationToken)
|
||||
=> UserTokens.FindAsync(new object[] { user.Id, loginProvider, name }, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Add a new user token.
|
||||
/// </summary>
|
||||
/// <param name="token">The token to be added.</param>
|
||||
/// <returns></returns>
|
||||
public override Task AddUserTokenAsync(TUserToken token)
|
||||
{
|
||||
UserTokens.Add(token);
|
||||
return TaskCache.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a new user token.
|
||||
/// </summary>
|
||||
/// <param name="token">The token to be removed.</param>
|
||||
/// <returns></returns>
|
||||
public override Task RemoveUserTokenAsync(TUserToken token)
|
||||
{
|
||||
UserTokens.Remove(token);
|
||||
return TaskCache.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,373 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a new instance of a persistence store for the specified user and role types that supports
|
||||
/// <see cref="IdentityStoreOptions.Version1_0"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TUser">The type representing a user.</typeparam>
|
||||
/// <typeparam name="TRole">The type representing a role.</typeparam>
|
||||
/// <typeparam name="TContext">The type of the data context class used to access the store.</typeparam>
|
||||
/// <typeparam name="TKey">The type of the primary key for a role.</typeparam>
|
||||
/// <typeparam name="TUserClaim">The type representing a claim.</typeparam>
|
||||
/// <typeparam name="TUserRole">The type representing a user role.</typeparam>
|
||||
/// <typeparam name="TUserLogin">The type representing a user external login.</typeparam>
|
||||
/// <typeparam name="TUserToken">The type representing a user token.</typeparam>
|
||||
/// <typeparam name="TRoleClaim">The type representing a role claim.</typeparam>
|
||||
public class UserStoreV1<TUser, TRole, TContext, TKey, TUserClaim, TUserRole, TUserLogin, TUserToken, TRoleClaim> :
|
||||
UserStoreBaseV1<TUser, TRole, TKey, TUserClaim, TUserRole, TUserLogin, TUserToken, TRoleClaim>
|
||||
where TUser : IdentityUser<TKey, TUserClaim, TUserRole, TUserLogin, TUserToken>
|
||||
where TRole : IdentityRole<TKey, TUserRole, TRoleClaim>
|
||||
where TContext : DbContext
|
||||
where TKey : IEquatable<TKey>
|
||||
where TUserClaim : IdentityUserClaim<TKey>, new()
|
||||
where TUserRole : IdentityUserRole<TKey>, new()
|
||||
where TUserLogin : IdentityUserLogin<TKey>, new()
|
||||
where TUserToken : IdentityUserToken<TKey>, new()
|
||||
where TRoleClaim : IdentityRoleClaim<TKey>, new()
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of the store.
|
||||
/// </summary>
|
||||
/// <param name="context">The context used to access the store.</param>
|
||||
/// <param name="describer">The <see cref="IdentityErrorDescriber"/> used to describe store errors.</param>
|
||||
public UserStoreV1(TContext context, IdentityErrorDescriber describer = null) : base(describer ?? new IdentityErrorDescriber())
|
||||
{
|
||||
_core = new UserStoreCore<TUser, TRole, TContext, TKey, TUserClaim, TUserRole, TUserLogin, TUserToken, TRoleClaim>(context, describer);
|
||||
}
|
||||
|
||||
private readonly UserStoreCore<TUser, TRole, TContext, TKey, TUserClaim, TUserRole, TUserLogin, TUserToken, TRoleClaim> _core;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the database context for this store.
|
||||
/// </summary>
|
||||
public TContext Context => _core.Context;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a flag indicating if changes should be persisted after CreateAsync, UpdateAsync and DeleteAsync are called.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// True if changes should be automatically persisted, otherwise false.
|
||||
/// </value>
|
||||
public bool AutoSaveChanges
|
||||
{
|
||||
get => _core.AutoSaveChanges;
|
||||
set => _core.AutoSaveChanges = value;
|
||||
}
|
||||
|
||||
/// <summary>Saves the current store.</summary>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
protected Task SaveChanges(CancellationToken cancellationToken) => _core.SaveChanges(cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Creates the specified <paramref name="user"/> in the user store.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to create.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/> of the creation operation.</returns>
|
||||
public override Task<IdentityResult> CreateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
=> _core.CreateAsync(user, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the specified <paramref name="user"/> in the user store.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to update.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/> of the update operation.</returns>
|
||||
public override Task<IdentityResult> UpdateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
=> _core.UpdateAsync(user, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the specified <paramref name="user"/> from the user store.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to delete.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/> of the update operation.</returns>
|
||||
public override Task<IdentityResult> DeleteAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
=> _core.DeleteAsync(user, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Finds and returns a user, if any, who has the specified <paramref name="userId"/>.
|
||||
/// </summary>
|
||||
/// <param name="userId">The user ID to search for.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>
|
||||
/// The <see cref="Task"/> that represents the asynchronous operation, containing the user matching the specified <paramref name="userId"/> if it exists.
|
||||
/// </returns>
|
||||
public override Task<TUser> FindByIdAsync(string userId, CancellationToken cancellationToken = default(CancellationToken))
|
||||
=> _core.FindByIdAsync(userId, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Finds and returns a user, if any, who has the specified normalized user name.
|
||||
/// </summary>
|
||||
/// <param name="normalizedUserName">The normalized user name to search for.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>
|
||||
/// The <see cref="Task"/> that represents the asynchronous operation, containing the user matching the specified <paramref name="normalizedUserName"/> if it exists.
|
||||
/// </returns>
|
||||
public override Task<TUser> FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
=> _core.FindByNameAsync(normalizedUserName, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// A navigation property for the users the store contains.
|
||||
/// </summary>
|
||||
public override IQueryable<TUser> Users => _core.UsersSet;
|
||||
|
||||
/// <summary>
|
||||
/// Return a role with the normalized name if it exists.
|
||||
/// </summary>
|
||||
/// <param name="normalizedRoleName">The normalized role name.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The role if it exists.</returns>
|
||||
public override Task<TRole> FindRoleAsync(string normalizedRoleName, CancellationToken cancellationToken)
|
||||
=> _core.FindRoleAsync(normalizedRoleName, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Return a user role for the userId and roleId if it exists.
|
||||
/// </summary>
|
||||
/// <param name="userId">The user's id.</param>
|
||||
/// <param name="roleId">The role's id.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The user role if it exists.</returns>
|
||||
public override Task<TUserRole> FindUserRoleAsync(TKey userId, TKey roleId, CancellationToken cancellationToken)
|
||||
=> _core.FindUserRoleAsync(userId, roleId, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Return a user with the matching userId if it exists.
|
||||
/// </summary>
|
||||
/// <param name="userId">The user's id.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The user if it exists.</returns>
|
||||
public override Task<TUser> FindUserAsync(TKey userId, CancellationToken cancellationToken)
|
||||
=> _core.FindUserAsync(userId, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Return a user login with the matching userId, provider, providerKey if it exists.
|
||||
/// </summary>
|
||||
/// <param name="userId">The user's id.</param>
|
||||
/// <param name="loginProvider">The login provider name.</param>
|
||||
/// <param name="providerKey">The key provided by the <paramref name="loginProvider"/> to identify a user.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The user login if it exists.</returns>
|
||||
public override Task<TUserLogin> FindUserLoginAsync(TKey userId, string loginProvider, string providerKey, CancellationToken cancellationToken)
|
||||
=> _core.FindUserLoginAsync(userId, loginProvider, providerKey, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Return a user login with provider, providerKey if it exists.
|
||||
/// </summary>
|
||||
/// <param name="loginProvider">The login provider name.</param>
|
||||
/// <param name="providerKey">The key provided by the <paramref name="loginProvider"/> to identify a user.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The user login if it exists.</returns>
|
||||
public override Task<TUserLogin> FindUserLoginAsync(string loginProvider, string providerKey, CancellationToken cancellationToken)
|
||||
=> _core.FindUserLoginAsync(loginProvider, providerKey, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Adds the given <paramref name="normalizedRoleName"/> to the specified <paramref name="user"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to add the role to.</param>
|
||||
/// <param name="normalizedRoleName">The role to add.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public override Task AddToRoleAsync(TUser user, string normalizedRoleName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
=> _core.AddToRoleAsync(user, normalizedRoleName, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Removes the given <paramref name="normalizedRoleName"/> from the specified <paramref name="user"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to remove the role from.</param>
|
||||
/// <param name="normalizedRoleName">The role to remove.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public override Task RemoveFromRoleAsync(TUser user, string normalizedRoleName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
=> _core.RemoveFromRoleAsync(user, normalizedRoleName, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the roles the specified <paramref name="user"/> is a member of.
|
||||
/// </summary>
|
||||
/// <param name="user">The user whose roles should be retrieved.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that contains the roles the user is a member of.</returns>
|
||||
public override Task<IList<string>> GetRolesAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
=> _core.GetRolesAsync(user, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a flag indicating if the specified user is a member of the give <paramref name="normalizedRoleName"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user whose role membership should be checked.</param>
|
||||
/// <param name="normalizedRoleName">The role to check membership of</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> containing a flag indicating if the specified user is a member of the given group. If the
|
||||
/// user is a member of the group the returned value with be true, otherwise it will be false.</returns>
|
||||
public override Task<bool> IsInRoleAsync(TUser user, string normalizedRoleName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
=> _core.IsInRoleAsync(user, normalizedRoleName, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Get the claims associated with the specified <paramref name="user"/> as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="user">The user whose claims should be retrieved.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that contains the claims granted to a user.</returns>
|
||||
public override Task<IList<Claim>> GetClaimsAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
=> _core.GetClaimsAsync(user, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Adds the <paramref name="claims"/> given to the specified <paramref name="user"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to add the claim to.</param>
|
||||
/// <param name="claims">The claim to add to the user.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public override Task AddClaimsAsync(TUser user, IEnumerable<Claim> claims, CancellationToken cancellationToken = default(CancellationToken))
|
||||
=> _core.AddClaimsAsync(user, claims, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Replaces the <paramref name="claim"/> on the specified <paramref name="user"/>, with the <paramref name="newClaim"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to replace the claim on.</param>
|
||||
/// <param name="claim">The claim replace.</param>
|
||||
/// <param name="newClaim">The new claim replacing the <paramref name="claim"/>.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public override Task ReplaceClaimAsync(TUser user, Claim claim, Claim newClaim, CancellationToken cancellationToken = default(CancellationToken))
|
||||
=> _core.ReplaceClaimAsync(user, claim, newClaim, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Removes the <paramref name="claims"/> given from the specified <paramref name="user"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to remove the claims from.</param>
|
||||
/// <param name="claims">The claim to remove.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public override Task RemoveClaimsAsync(TUser user, IEnumerable<Claim> claims, CancellationToken cancellationToken = default(CancellationToken))
|
||||
=> _core.RemoveClaimsAsync(user, claims, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Adds the <paramref name="login"/> given to the specified <paramref name="user"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to add the login to.</param>
|
||||
/// <param name="login">The login to add to the user.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public override Task AddLoginAsync(TUser user, UserLoginInfo login, CancellationToken cancellationToken = default(CancellationToken))
|
||||
=> _core.AddLoginAsync(user, login, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Removes the <paramref name="loginProvider"/> given from the specified <paramref name="user"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to remove the login from.</param>
|
||||
/// <param name="loginProvider">The login to remove from the user.</param>
|
||||
/// <param name="providerKey">The key provided by the <paramref name="loginProvider"/> to identify a user.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public override Task RemoveLoginAsync(TUser user, string loginProvider, string providerKey, CancellationToken cancellationToken = default(CancellationToken))
|
||||
=> _core.RemoveLoginAsync(user, loginProvider, providerKey, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the associated logins for the specified <param ref="user"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user whose associated logins to retrieve.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>
|
||||
/// The <see cref="Task"/> for the asynchronous operation, containing a list of <see cref="UserLoginInfo"/> for the specified <paramref name="user"/>, if any.
|
||||
/// </returns>
|
||||
public override Task<IList<UserLoginInfo>> GetLoginsAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
=> _core.GetLoginsAsync(user, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the user associated with the specified login provider and login provider key.
|
||||
/// </summary>
|
||||
/// <param name="loginProvider">The login provider who provided the <paramref name="providerKey"/>.</param>
|
||||
/// <param name="providerKey">The key provided by the <paramref name="loginProvider"/> to identify a user.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>
|
||||
/// The <see cref="Task"/> for the asynchronous operation, containing the user, if any which matched the specified login provider and key.
|
||||
/// </returns>
|
||||
public override Task<TUser> FindByLoginAsync(string loginProvider, string providerKey, CancellationToken cancellationToken = default(CancellationToken))
|
||||
=> _core.FindByLoginAsync(loginProvider, providerKey, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user, if any, associated with the specified, normalized email address.
|
||||
/// </summary>
|
||||
/// <param name="normalizedEmail">The normalized email address to return the user for.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>
|
||||
/// The task object containing the results of the asynchronous lookup operation, the user if any associated with the specified normalized email address.
|
||||
/// </returns>
|
||||
public override Task<TUser> FindByEmailAsync(string normalizedEmail, CancellationToken cancellationToken = default(CancellationToken))
|
||||
=> _core.FindByEmailAsync(normalizedEmail, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all users with the specified claim.
|
||||
/// </summary>
|
||||
/// <param name="claim">The claim whose users should be retrieved.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>
|
||||
/// The <see cref="Task"/> contains a list of users, if any, that contain the specified claim.
|
||||
/// </returns>
|
||||
public override Task<IList<TUser>> GetUsersForClaimAsync(Claim claim, CancellationToken cancellationToken = default(CancellationToken))
|
||||
=> _core.GetUsersForClaimAsync(claim, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all users in the specified role.
|
||||
/// </summary>
|
||||
/// <param name="normalizedRoleName">The role whose users should be retrieved.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>
|
||||
/// The <see cref="Task"/> contains a list of users, if any, that are in the specified role.
|
||||
/// </returns>
|
||||
public override Task<IList<TUser>> GetUsersInRoleAsync(string normalizedRoleName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
=> _core.GetUsersInRoleAsync(normalizedRoleName, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Find a user token if it exists.
|
||||
/// </summary>
|
||||
/// <param name="user">The token owner.</param>
|
||||
/// <param name="loginProvider">The login provider for the token.</param>
|
||||
/// <param name="name">The name of the token.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The user token if it exists.</returns>
|
||||
public override Task<TUserToken> FindTokenAsync(TUser user, string loginProvider, string name, CancellationToken cancellationToken)
|
||||
=> _core.FindTokenAsync(user, loginProvider, name, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Add a new user token.
|
||||
/// </summary>
|
||||
/// <param name="token">The token to be added.</param>
|
||||
/// <returns></returns>
|
||||
public override Task AddUserTokenAsync(TUserToken token)
|
||||
=> _core.AddUserTokenAsync(token);
|
||||
|
||||
/// <summary>
|
||||
/// Remove a new user token.
|
||||
/// </summary>
|
||||
/// <param name="token">The token to be removed.</param>
|
||||
/// <returns></returns>
|
||||
public override Task RemoveUserTokenAsync(TUserToken token)
|
||||
=> _core.RemoveUserTokenAsync(token);
|
||||
|
||||
/// <summary>
|
||||
/// Dispose the store
|
||||
/// </summary>
|
||||
/// <param name="disposing"></param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
if (disposing)
|
||||
{
|
||||
_core.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,373 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a new instance of a persistence store for the specified user and role types that supports
|
||||
/// <see cref="IdentityStoreOptions.Version2_0"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TUser">The type representing a user.</typeparam>
|
||||
/// <typeparam name="TRole">The type representing a role.</typeparam>
|
||||
/// <typeparam name="TContext">The type of the data context class used to access the store.</typeparam>
|
||||
/// <typeparam name="TKey">The type of the primary key for a role.</typeparam>
|
||||
/// <typeparam name="TUserClaim">The type representing a claim.</typeparam>
|
||||
/// <typeparam name="TUserRole">The type representing a user role.</typeparam>
|
||||
/// <typeparam name="TUserLogin">The type representing a user external login.</typeparam>
|
||||
/// <typeparam name="TUserToken">The type representing a user token.</typeparam>
|
||||
/// <typeparam name="TRoleClaim">The type representing a role claim.</typeparam>
|
||||
public class UserStoreV2<TUser, TRole, TContext, TKey, TUserClaim, TUserRole, TUserLogin, TUserToken, TRoleClaim> :
|
||||
UserStoreBaseV2<TUser, TRole, TKey, TUserClaim, TUserRole, TUserLogin, TUserToken, TRoleClaim>
|
||||
where TUser : IdentityUser<TKey, TUserClaim, TUserRole, TUserLogin, TUserToken>
|
||||
where TRole : IdentityRole<TKey, TUserRole, TRoleClaim>
|
||||
where TContext : DbContext
|
||||
where TKey : IEquatable<TKey>
|
||||
where TUserClaim : IdentityUserClaim<TKey>, new()
|
||||
where TUserRole : IdentityUserRole<TKey>, new()
|
||||
where TUserLogin : IdentityUserLogin<TKey>, new()
|
||||
where TUserToken : IdentityUserToken<TKey>, new()
|
||||
where TRoleClaim : IdentityRoleClaim<TKey>, new()
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of the store.
|
||||
/// </summary>
|
||||
/// <param name="context">The context used to access the store.</param>
|
||||
/// <param name="describer">The <see cref="IdentityErrorDescriber"/> used to describe store errors.</param>
|
||||
public UserStoreV2(TContext context, IdentityErrorDescriber describer = null) : base(describer ?? new IdentityErrorDescriber())
|
||||
{
|
||||
_core = new UserStoreCore<TUser, TRole, TContext, TKey, TUserClaim, TUserRole, TUserLogin, TUserToken, TRoleClaim>(context, describer);
|
||||
}
|
||||
|
||||
private readonly UserStoreCore<TUser, TRole, TContext, TKey, TUserClaim, TUserRole, TUserLogin, TUserToken, TRoleClaim> _core;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the database context for this store.
|
||||
/// </summary>
|
||||
public TContext Context => _core.Context;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a flag indicating if changes should be persisted after CreateAsync, UpdateAsync and DeleteAsync are called.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// True if changes should be automatically persisted, otherwise false.
|
||||
/// </value>
|
||||
public bool AutoSaveChanges
|
||||
{
|
||||
get => _core.AutoSaveChanges;
|
||||
set => _core.AutoSaveChanges = value;
|
||||
}
|
||||
|
||||
/// <summary>Saves the current store.</summary>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
protected Task SaveChanges(CancellationToken cancellationToken) => _core.SaveChanges(cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Creates the specified <paramref name="user"/> in the user store.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to create.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/> of the creation operation.</returns>
|
||||
public override Task<IdentityResult> CreateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
=> _core.CreateAsync(user, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the specified <paramref name="user"/> in the user store.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to update.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/> of the update operation.</returns>
|
||||
public override Task<IdentityResult> UpdateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
=> _core.UpdateAsync(user, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the specified <paramref name="user"/> from the user store.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to delete.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/> of the update operation.</returns>
|
||||
public override Task<IdentityResult> DeleteAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
=> _core.DeleteAsync(user, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Finds and returns a user, if any, who has the specified <paramref name="userId"/>.
|
||||
/// </summary>
|
||||
/// <param name="userId">The user ID to search for.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>
|
||||
/// The <see cref="Task"/> that represents the asynchronous operation, containing the user matching the specified <paramref name="userId"/> if it exists.
|
||||
/// </returns>
|
||||
public override Task<TUser> FindByIdAsync(string userId, CancellationToken cancellationToken = default(CancellationToken))
|
||||
=> _core.FindByIdAsync(userId, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Finds and returns a user, if any, who has the specified normalized user name.
|
||||
/// </summary>
|
||||
/// <param name="normalizedUserName">The normalized user name to search for.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>
|
||||
/// The <see cref="Task"/> that represents the asynchronous operation, containing the user matching the specified <paramref name="normalizedUserName"/> if it exists.
|
||||
/// </returns>
|
||||
public override Task<TUser> FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
=> _core.FindByNameAsync(normalizedUserName, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// A navigation property for the users the store contains.
|
||||
/// </summary>
|
||||
public override IQueryable<TUser> Users => _core.UsersSet;
|
||||
|
||||
/// <summary>
|
||||
/// Return a role with the normalized name if it exists.
|
||||
/// </summary>
|
||||
/// <param name="normalizedRoleName">The normalized role name.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The role if it exists.</returns>
|
||||
public override Task<TRole> FindRoleAsync(string normalizedRoleName, CancellationToken cancellationToken)
|
||||
=> _core.FindRoleAsync(normalizedRoleName, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Return a user role for the userId and roleId if it exists.
|
||||
/// </summary>
|
||||
/// <param name="userId">The user's id.</param>
|
||||
/// <param name="roleId">The role's id.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The user role if it exists.</returns>
|
||||
public override Task<TUserRole> FindUserRoleAsync(TKey userId, TKey roleId, CancellationToken cancellationToken)
|
||||
=> _core.FindUserRoleAsync(userId, roleId, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Return a user with the matching userId if it exists.
|
||||
/// </summary>
|
||||
/// <param name="userId">The user's id.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The user if it exists.</returns>
|
||||
public override Task<TUser> FindUserAsync(TKey userId, CancellationToken cancellationToken)
|
||||
=> _core.FindUserAsync(userId, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Return a user login with the matching userId, provider, providerKey if it exists.
|
||||
/// </summary>
|
||||
/// <param name="userId">The user's id.</param>
|
||||
/// <param name="loginProvider">The login provider name.</param>
|
||||
/// <param name="providerKey">The key provided by the <paramref name="loginProvider"/> to identify a user.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The user login if it exists.</returns>
|
||||
public override Task<TUserLogin> FindUserLoginAsync(TKey userId, string loginProvider, string providerKey, CancellationToken cancellationToken)
|
||||
=> _core.FindUserLoginAsync(userId, loginProvider, providerKey, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Return a user login with provider, providerKey if it exists.
|
||||
/// </summary>
|
||||
/// <param name="loginProvider">The login provider name.</param>
|
||||
/// <param name="providerKey">The key provided by the <paramref name="loginProvider"/> to identify a user.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The user login if it exists.</returns>
|
||||
public override Task<TUserLogin> FindUserLoginAsync(string loginProvider, string providerKey, CancellationToken cancellationToken)
|
||||
=> _core.FindUserLoginAsync(loginProvider, providerKey, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Adds the given <paramref name="normalizedRoleName"/> to the specified <paramref name="user"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to add the role to.</param>
|
||||
/// <param name="normalizedRoleName">The role to add.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public override Task AddToRoleAsync(TUser user, string normalizedRoleName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
=> _core.AddToRoleAsync(user, normalizedRoleName, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Removes the given <paramref name="normalizedRoleName"/> from the specified <paramref name="user"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to remove the role from.</param>
|
||||
/// <param name="normalizedRoleName">The role to remove.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public override Task RemoveFromRoleAsync(TUser user, string normalizedRoleName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
=> _core.RemoveFromRoleAsync(user, normalizedRoleName, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the roles the specified <paramref name="user"/> is a member of.
|
||||
/// </summary>
|
||||
/// <param name="user">The user whose roles should be retrieved.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that contains the roles the user is a member of.</returns>
|
||||
public override Task<IList<string>> GetRolesAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
=> _core.GetRolesAsync(user, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a flag indicating if the specified user is a member of the give <paramref name="normalizedRoleName"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user whose role membership should be checked.</param>
|
||||
/// <param name="normalizedRoleName">The role to check membership of</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> containing a flag indicating if the specified user is a member of the given group. If the
|
||||
/// user is a member of the group the returned value with be true, otherwise it will be false.</returns>
|
||||
public override Task<bool> IsInRoleAsync(TUser user, string normalizedRoleName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
=> _core.IsInRoleAsync(user, normalizedRoleName, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Get the claims associated with the specified <paramref name="user"/> as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="user">The user whose claims should be retrieved.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that contains the claims granted to a user.</returns>
|
||||
public override Task<IList<Claim>> GetClaimsAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
=> _core.GetClaimsAsync(user, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Adds the <paramref name="claims"/> given to the specified <paramref name="user"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to add the claim to.</param>
|
||||
/// <param name="claims">The claim to add to the user.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public override Task AddClaimsAsync(TUser user, IEnumerable<Claim> claims, CancellationToken cancellationToken = default(CancellationToken))
|
||||
=> _core.AddClaimsAsync(user, claims, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Replaces the <paramref name="claim"/> on the specified <paramref name="user"/>, with the <paramref name="newClaim"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to replace the claim on.</param>
|
||||
/// <param name="claim">The claim replace.</param>
|
||||
/// <param name="newClaim">The new claim replacing the <paramref name="claim"/>.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public override Task ReplaceClaimAsync(TUser user, Claim claim, Claim newClaim, CancellationToken cancellationToken = default(CancellationToken))
|
||||
=> _core.ReplaceClaimAsync(user, claim, newClaim, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Removes the <paramref name="claims"/> given from the specified <paramref name="user"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to remove the claims from.</param>
|
||||
/// <param name="claims">The claim to remove.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public override Task RemoveClaimsAsync(TUser user, IEnumerable<Claim> claims, CancellationToken cancellationToken = default(CancellationToken))
|
||||
=> _core.RemoveClaimsAsync(user, claims, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Adds the <paramref name="login"/> given to the specified <paramref name="user"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to add the login to.</param>
|
||||
/// <param name="login">The login to add to the user.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public override Task AddLoginAsync(TUser user, UserLoginInfo login, CancellationToken cancellationToken = default(CancellationToken))
|
||||
=> _core.AddLoginAsync(user, login, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Removes the <paramref name="loginProvider"/> given from the specified <paramref name="user"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to remove the login from.</param>
|
||||
/// <param name="loginProvider">The login to remove from the user.</param>
|
||||
/// <param name="providerKey">The key provided by the <paramref name="loginProvider"/> to identify a user.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public override Task RemoveLoginAsync(TUser user, string loginProvider, string providerKey, CancellationToken cancellationToken = default(CancellationToken))
|
||||
=> _core.RemoveLoginAsync(user, loginProvider, providerKey, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the associated logins for the specified <param ref="user"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user whose associated logins to retrieve.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>
|
||||
/// The <see cref="Task"/> for the asynchronous operation, containing a list of <see cref="UserLoginInfo"/> for the specified <paramref name="user"/>, if any.
|
||||
/// </returns>
|
||||
public override Task<IList<UserLoginInfo>> GetLoginsAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
||||
=> _core.GetLoginsAsync(user, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the user associated with the specified login provider and login provider key.
|
||||
/// </summary>
|
||||
/// <param name="loginProvider">The login provider who provided the <paramref name="providerKey"/>.</param>
|
||||
/// <param name="providerKey">The key provided by the <paramref name="loginProvider"/> to identify a user.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>
|
||||
/// The <see cref="Task"/> for the asynchronous operation, containing the user, if any which matched the specified login provider and key.
|
||||
/// </returns>
|
||||
public override Task<TUser> FindByLoginAsync(string loginProvider, string providerKey, CancellationToken cancellationToken = default(CancellationToken))
|
||||
=> _core.FindByLoginAsync(loginProvider, providerKey, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user, if any, associated with the specified, normalized email address.
|
||||
/// </summary>
|
||||
/// <param name="normalizedEmail">The normalized email address to return the user for.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>
|
||||
/// The task object containing the results of the asynchronous lookup operation, the user if any associated with the specified normalized email address.
|
||||
/// </returns>
|
||||
public override Task<TUser> FindByEmailAsync(string normalizedEmail, CancellationToken cancellationToken = default(CancellationToken))
|
||||
=> _core.FindByEmailAsync(normalizedEmail, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all users with the specified claim.
|
||||
/// </summary>
|
||||
/// <param name="claim">The claim whose users should be retrieved.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>
|
||||
/// The <see cref="Task"/> contains a list of users, if any, that contain the specified claim.
|
||||
/// </returns>
|
||||
public override Task<IList<TUser>> GetUsersForClaimAsync(Claim claim, CancellationToken cancellationToken = default(CancellationToken))
|
||||
=> _core.GetUsersForClaimAsync(claim, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all users in the specified role.
|
||||
/// </summary>
|
||||
/// <param name="normalizedRoleName">The role whose users should be retrieved.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>
|
||||
/// The <see cref="Task"/> contains a list of users, if any, that are in the specified role.
|
||||
/// </returns>
|
||||
public override Task<IList<TUser>> GetUsersInRoleAsync(string normalizedRoleName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
=> _core.GetUsersInRoleAsync(normalizedRoleName, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Find a user token if it exists.
|
||||
/// </summary>
|
||||
/// <param name="user">The token owner.</param>
|
||||
/// <param name="loginProvider">The login provider for the token.</param>
|
||||
/// <param name="name">The name of the token.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The user token if it exists.</returns>
|
||||
public override Task<TUserToken> FindTokenAsync(TUser user, string loginProvider, string name, CancellationToken cancellationToken)
|
||||
=> _core.FindTokenAsync(user, loginProvider, name, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Add a new user token.
|
||||
/// </summary>
|
||||
/// <param name="token">The token to be added.</param>
|
||||
/// <returns></returns>
|
||||
public override Task AddUserTokenAsync(TUserToken token)
|
||||
=> _core.AddUserTokenAsync(token);
|
||||
|
||||
/// <summary>
|
||||
/// Remove a new user token.
|
||||
/// </summary>
|
||||
/// <param name="token">The token to be removed.</param>
|
||||
/// <returns></returns>
|
||||
public override Task RemoveUserTokenAsync(TUserToken token)
|
||||
=> _core.RemoveUserTokenAsync(token);
|
||||
|
||||
/// <summary>
|
||||
/// Dispose the store
|
||||
/// </summary>
|
||||
/// <param name="disposing"></param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
if (disposing)
|
||||
{
|
||||
_core.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -55,7 +55,7 @@ namespace Microsoft.AspNetCore.Identity.Test
|
|||
/// </summary>
|
||||
/// <param name="services"></param>
|
||||
/// <param name="context"></param>
|
||||
protected virtual void SetupIdentityServices(IServiceCollection services, object context = null)
|
||||
protected virtual void SetupIdentityServices(IServiceCollection services, object context)
|
||||
{
|
||||
services.AddSingleton<IConfiguration>(new ConfigurationBuilder().Build());
|
||||
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
||||
|
|
@ -192,6 +192,31 @@ namespace Microsoft.AspNetCore.Identity.Test
|
|||
/// <returns>The query to use.</returns>
|
||||
protected abstract Expression<Func<TRole, bool>> RoleNameStartsWithPredicate(string roleName);
|
||||
|
||||
/// <summary>
|
||||
/// Test.
|
||||
/// </summary>
|
||||
/// <returns>Task</returns>
|
||||
[Fact]
|
||||
public async Task CreateUserWillSetCreateDateOnlyIfSupported()
|
||||
{
|
||||
if (ShouldSkipDbTests())
|
||||
{
|
||||
return;
|
||||
}
|
||||
var manager = CreateManager();
|
||||
var user = CreateTestUser();
|
||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
||||
var userId = await manager.GetUserIdAsync(user);
|
||||
if (manager.SupportsUserActivity)
|
||||
{
|
||||
Assert.NotNull(await manager.GetCreateDateAsync(user));
|
||||
}
|
||||
else
|
||||
{
|
||||
await Assert.ThrowsAsync<NotSupportedException>(() => manager.GetCreateDateAsync(user));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test.
|
||||
/// </summary>
|
||||
|
|
@ -490,9 +515,17 @@ namespace Microsoft.AspNetCore.Identity.Test
|
|||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
||||
manager.PasswordValidators.Clear();
|
||||
manager.PasswordValidators.Add(new AlwaysBadValidator());
|
||||
if (manager.SupportsUserActivity)
|
||||
{
|
||||
Assert.Null(await manager.GetLastPasswordChangeDateAsync(user));
|
||||
}
|
||||
IdentityResultAssert.IsFailure(await manager.AddPasswordAsync(user, "password"),
|
||||
AlwaysBadValidator.ErrorMessage);
|
||||
IdentityResultAssert.VerifyLogMessage(manager.Logger, $"User {await manager.GetUserIdAsync(user)} password validation failed: {AlwaysBadValidator.ErrorMessage.Code}.");
|
||||
if (manager.SupportsUserActivity)
|
||||
{
|
||||
Assert.Null(await manager.GetLastPasswordChangeDateAsync(user));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -578,6 +611,12 @@ namespace Microsoft.AspNetCore.Identity.Test
|
|||
var logins = await manager.GetLoginsAsync(user);
|
||||
Assert.NotNull(logins);
|
||||
Assert.Equal(0, logins.Count());
|
||||
if (manager.SupportsUserActivity)
|
||||
{
|
||||
Assert.NotNull(await manager.GetCreateDateAsync(user));
|
||||
Assert.Null(await manager.GetLastPasswordChangeDateAsync(user));
|
||||
Assert.Null(await manager.GetLastSignInDateAsync(user));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -604,6 +643,12 @@ namespace Microsoft.AspNetCore.Identity.Test
|
|||
Assert.Equal(provider, logins.First().LoginProvider);
|
||||
Assert.Equal(providerKey, logins.First().ProviderKey);
|
||||
Assert.Equal(display, logins.First().ProviderDisplayName);
|
||||
if (manager.SupportsUserActivity)
|
||||
{
|
||||
Assert.NotNull(await manager.GetCreateDateAsync(user));
|
||||
Assert.Null(await manager.GetLastPasswordChangeDateAsync(user));
|
||||
Assert.Null(await manager.GetLastSignInDateAsync(user));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -624,13 +669,27 @@ namespace Microsoft.AspNetCore.Identity.Test
|
|||
var login = new UserLoginInfo("Provider", userId, "display");
|
||||
IdentityResultAssert.IsSuccess(await manager.AddLoginAsync(user, login));
|
||||
Assert.False(await manager.HasPasswordAsync(user));
|
||||
if (manager.SupportsUserActivity)
|
||||
{
|
||||
Assert.Null(await manager.GetLastPasswordChangeDateAsync(user));
|
||||
}
|
||||
IdentityResultAssert.IsSuccess(await manager.AddPasswordAsync(user, "password"));
|
||||
if (manager.SupportsUserActivity)
|
||||
{
|
||||
Assert.NotNull(await manager.GetLastPasswordChangeDateAsync(user));
|
||||
}
|
||||
Assert.True(await manager.HasPasswordAsync(user));
|
||||
var logins = await manager.GetLoginsAsync(user);
|
||||
Assert.NotNull(logins);
|
||||
Assert.Equal(1, logins.Count());
|
||||
Assert.Equal(user, await manager.FindByLoginAsync(login.LoginProvider, login.ProviderKey));
|
||||
Assert.True(await manager.CheckPasswordAsync(user, "password"));
|
||||
|
||||
if (manager.SupportsUserActivity)
|
||||
{
|
||||
Assert.NotNull(await manager.GetCreateDateAsync(user));
|
||||
Assert.Null(await manager.GetLastSignInDateAsync(user));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -728,12 +787,57 @@ namespace Microsoft.AspNetCore.Identity.Test
|
|||
const string password = "password";
|
||||
const string newPassword = "newpassword";
|
||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password));
|
||||
DateTimeOffset? createChange = null;
|
||||
if (manager.SupportsUserActivity)
|
||||
{
|
||||
createChange = await manager.GetLastPasswordChangeDateAsync(user);
|
||||
Assert.NotNull(createChange);
|
||||
Assert.Null(await manager.GetLastSignInDateAsync(user));
|
||||
}
|
||||
else
|
||||
{
|
||||
await Assert.ThrowsAsync<NotSupportedException>(() => manager.GetLastPasswordChangeDateAsync(user));
|
||||
await Assert.ThrowsAsync<NotSupportedException>(() => manager.GetLastSignInDateAsync(user));
|
||||
}
|
||||
var stamp = await manager.GetSecurityStampAsync(user);
|
||||
Assert.NotNull(stamp);
|
||||
IdentityResultAssert.IsSuccess(await manager.ChangePasswordAsync(user, password, newPassword));
|
||||
Assert.False(await manager.CheckPasswordAsync(user, password));
|
||||
Assert.True(await manager.CheckPasswordAsync(user, newPassword));
|
||||
Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user));
|
||||
|
||||
if (manager.SupportsUserActivity)
|
||||
{
|
||||
var changeDate = await manager.GetLastPasswordChangeDateAsync(user);
|
||||
Assert.NotNull(changeDate);
|
||||
Assert.True(createChange < changeDate);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test.
|
||||
/// </summary>
|
||||
/// <returns>Task</returns>
|
||||
[Fact]
|
||||
public async Task CanUpdateLastSignInTime()
|
||||
{
|
||||
if (ShouldSkipDbTests())
|
||||
{
|
||||
return;
|
||||
}
|
||||
var manager = CreateManager();
|
||||
var user = CreateTestUser();
|
||||
if (manager.SupportsUserActivity)
|
||||
{
|
||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
||||
Assert.Null(await manager.GetLastSignInDateAsync(user));
|
||||
IdentityResultAssert.IsSuccess(await manager.UpdateLastSignInDateAsync(user));
|
||||
Assert.NotNull(await manager.GetLastSignInDateAsync(user));
|
||||
}
|
||||
else
|
||||
{
|
||||
await Assert.ThrowsAsync<NotSupportedException>(() => manager.UpdateLastSignInDateAsync(user));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -884,9 +988,20 @@ namespace Microsoft.AspNetCore.Identity.Test
|
|||
var manager = CreateManager();
|
||||
var user = CreateTestUser();
|
||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "password"));
|
||||
DateTimeOffset? createChange = null;
|
||||
if (manager.SupportsUserActivity)
|
||||
{
|
||||
createChange = await manager.GetLastPasswordChangeDateAsync(user);
|
||||
Assert.NotNull(createChange);
|
||||
}
|
||||
var result = await manager.ChangePasswordAsync(user, "bogus", "newpassword");
|
||||
IdentityResultAssert.IsFailure(result, "Incorrect password.");
|
||||
IdentityResultAssert.VerifyLogMessage(manager.Logger, $"Change password failed for user {await manager.GetUserIdAsync(user)}.");
|
||||
|
||||
if (manager.SupportsUserActivity)
|
||||
{
|
||||
Assert.Equal(createChange, await manager.GetLastPasswordChangeDateAsync(user));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ namespace Microsoft.AspNetCore.Identity
|
|||
{
|
||||
var auth = await Context.AuthenticateAsync(IdentityConstants.ApplicationScheme);
|
||||
var authenticationMethod = auth?.Principal?.FindFirstValue(ClaimTypes.AuthenticationMethod);
|
||||
await SignInAsync(user, auth?.Properties, authenticationMethod);
|
||||
await SignInAsync(user, auth?.Properties, authenticationMethod, updateLastSignIn: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -172,8 +172,9 @@ namespace Microsoft.AspNetCore.Identity
|
|||
/// <param name="user">The user to sign-in.</param>
|
||||
/// <param name="isPersistent">Flag indicating whether the sign-in cookie should persist after the browser is closed.</param>
|
||||
/// <param name="authenticationMethod">Name of the method used to authenticate the user.</param>
|
||||
/// <param name="updateLastSignIn">If true, will update the last sign for the user if supported.</param>
|
||||
/// <returns>The task object representing the asynchronous operation.</returns>
|
||||
public virtual Task SignInAsync(TUser user, bool isPersistent, string authenticationMethod = null)
|
||||
public virtual Task SignInAsync(TUser user, bool isPersistent, string authenticationMethod = null, bool updateLastSignIn = true)
|
||||
{
|
||||
return SignInAsync(user, new AuthenticationProperties { IsPersistent = isPersistent }, authenticationMethod);
|
||||
}
|
||||
|
|
@ -184,8 +185,9 @@ namespace Microsoft.AspNetCore.Identity
|
|||
/// <param name="user">The user to sign-in.</param>
|
||||
/// <param name="authenticationProperties">Properties applied to the login and authentication cookie.</param>
|
||||
/// <param name="authenticationMethod">Name of the method used to authenticate the user.</param>
|
||||
/// <param name="updateLastSignIn">If true, will update the last sign for the user if supported.</param>
|
||||
/// <returns>The task object representing the asynchronous operation.</returns>
|
||||
public virtual async Task SignInAsync(TUser user, AuthenticationProperties authenticationProperties, string authenticationMethod = null)
|
||||
public virtual async Task SignInAsync(TUser user, AuthenticationProperties authenticationProperties, string authenticationMethod = null, bool updateLastSignIn = true)
|
||||
{
|
||||
var userPrincipal = await CreateUserPrincipalAsync(user);
|
||||
// Review: should we guard against CreateUserPrincipal returning null?
|
||||
|
|
@ -196,6 +198,10 @@ namespace Microsoft.AspNetCore.Identity
|
|||
await Context.SignInAsync(IdentityConstants.ApplicationScheme,
|
||||
userPrincipal,
|
||||
authenticationProperties ?? new AuthenticationProperties());
|
||||
if (updateLastSignIn && UserManager.SupportsUserActivity)
|
||||
{
|
||||
await UserManager.UpdateLastSignInDateAsync(user);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,67 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides an abstraction for a store containing users' password expiration data.
|
||||
/// </summary>
|
||||
/// <typeparam name="TUser">The type encapsulating a user.</typeparam>
|
||||
public interface IUserActivityStore<TUser> : IUserStore<TUser> where TUser : class
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets the last password change date for the specified <paramref name="user"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <param name="changeDate">The last password change date.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
Task SetLastPasswordChangeDateAsync(TUser user, DateTimeOffset? changeDate, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the last password change date for the specified <paramref name="user"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation, returning the password hash for the specified <paramref name="user"/>.</returns>
|
||||
Task<DateTimeOffset?> GetLastPasswordChangeDateAsync(TUser user, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the creation date for the specified <paramref name="user"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <param name="creationDate">The date the user was created.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
Task SetCreateDateAsync(TUser user, DateTimeOffset? creationDate, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the creation date for the specified <paramref name="user"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation, returning the password hash for the specified <paramref name="user"/>.</returns>
|
||||
Task<DateTimeOffset?> GetCreateDateAsync(TUser user, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the last signin date for the specified <paramref name="user"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <param name="lastSignIn">The date the user last signed in.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
Task SetLastSignInDateAsync(TUser user, DateTimeOffset? lastSignIn, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the last signin date for the specified <paramref name="user"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation, returning the password hash for the specified <paramref name="user"/>.</returns>
|
||||
Task<DateTimeOffset?> GetLastSignInDateAsync(TUser user, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
|
|
@ -762,6 +762,22 @@ namespace Microsoft.AspNetCore.Identity
|
|||
return string.Format(CultureInfo.CurrentCulture, GetString("PasswordRequiresUniqueChars"), p0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Store does not implement IUserActivityStore<User>.
|
||||
/// </summary>
|
||||
internal static string StoreNotIUserActivityStore
|
||||
{
|
||||
get { return GetString("StoreNotIUserActivityStore"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Store does not implement IUserActivityStore<User>.
|
||||
/// </summary>
|
||||
internal static string FormatStoreNotIUserActivityStore()
|
||||
{
|
||||
return GetString("StoreNotIUserActivityStore");
|
||||
}
|
||||
|
||||
private static string GetString(string name, params string[] formatterNames)
|
||||
{
|
||||
var value = _resourceManager.GetString(name);
|
||||
|
|
|
|||
|
|
@ -305,4 +305,8 @@
|
|||
<value>Passwords must use at least {0} different characters.</value>
|
||||
<comment>Error message for passwords that are based on similar characters</comment>
|
||||
</data>
|
||||
<data name="StoreNotIUserActivityStore" xml:space="preserve">
|
||||
<value>Store does not implement IUserActivityStore<User>.</value>
|
||||
<comment>Error when the store does not implement this interface</comment>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity
|
||||
{
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -38,6 +38,11 @@ namespace Microsoft.AspNetCore.Identity
|
|||
/// <value>True if the user attempting to sign-in requires two factor authentication, otherwise false.</value>
|
||||
public bool RequiresTwoFactor { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns a flag indication whether the user attempting to sign-in requires a password change.
|
||||
/// </summary>
|
||||
public bool RequiresPasswordChange { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="SignInResult"/> that represents a successful sign-in.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -339,6 +339,21 @@ namespace Microsoft.AspNetCore.Identity
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a flag indicating whether the backing user store supports tracking user activity like creation date.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// true if the backing user store supports user activity tracking, otherwise false.
|
||||
/// </value>
|
||||
public virtual bool SupportsUserActivity
|
||||
{
|
||||
get
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
return Store is IUserActivityStore<TUser>;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a flag indicating whether the backing user store supports returning
|
||||
/// <see cref="IQueryable"/> collections of information.
|
||||
|
|
@ -466,6 +481,11 @@ namespace Microsoft.AspNetCore.Identity
|
|||
await UpdateNormalizedUserNameAsync(user);
|
||||
await UpdateNormalizedEmailAsync(user);
|
||||
|
||||
if (SupportsUserActivity)
|
||||
{
|
||||
await GetUserActivityStore().SetCreateDateAsync(user, DateTimeOffset.UtcNow, CancellationToken);
|
||||
}
|
||||
|
||||
return await Store.CreateAsync(user, CancellationToken);
|
||||
}
|
||||
|
||||
|
|
@ -2225,6 +2245,79 @@ namespace Microsoft.AspNetCore.Identity
|
|||
return IdentityResult.Failed(ErrorDescriber.RecoveryCodeRedemptionFailed());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a date representing when a user was created.
|
||||
/// </summary>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <returns>
|
||||
/// A <see cref="Task{TResult}"/> that represents the lookup.
|
||||
/// </returns>
|
||||
public virtual Task<DateTimeOffset?> GetCreateDateAsync(TUser user)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
var store = GetUserActivityStore();
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
return store.GetCreateDateAsync(user, CancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a date representing when the user last changed their password.
|
||||
/// </summary>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <returns>
|
||||
/// A <see cref="Task{TResult}"/> that represents the lookup.
|
||||
/// </returns>
|
||||
public virtual Task<DateTimeOffset?> GetLastPasswordChangeDateAsync(TUser user)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
var store = GetUserActivityStore();
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
return store.GetLastPasswordChangeDateAsync(user, CancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a date representing when the user last signed in.
|
||||
/// </summary>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <returns>
|
||||
/// A <see cref="Task{TResult}"/> that represents the lookup.
|
||||
/// </returns>
|
||||
public virtual Task<DateTimeOffset?> GetLastSignInDateAsync(TUser user)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
var store = GetUserActivityStore();
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
return store.GetLastSignInDateAsync(user, CancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the last sign in date to DateTimeOffest.UtcNow.
|
||||
/// </summary>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <returns>
|
||||
/// A <see cref="Task{TResult}"/> that represents the lookup.
|
||||
/// </returns>
|
||||
public virtual async Task<IdentityResult> UpdateLastSignInDateAsync(TUser user)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
var store = GetUserActivityStore();
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
await store.SetLastSignInDateAsync(user, DateTimeOffset.UtcNow, CancellationToken);
|
||||
return await UpdateAsync(user);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases the unmanaged resources used by the role manager and optionally releases the managed resources.
|
||||
/// </summary>
|
||||
|
|
@ -2248,6 +2341,17 @@ namespace Microsoft.AspNetCore.Identity
|
|||
return cast;
|
||||
}
|
||||
|
||||
internal IUserActivityStore<TUser> GetUserActivityStore()
|
||||
{
|
||||
var cast = Store as IUserActivityStore<TUser>;
|
||||
if (cast == null)
|
||||
{
|
||||
throw new NotSupportedException(Resources.StoreNotIUserActivityStore);
|
||||
}
|
||||
return cast;
|
||||
}
|
||||
|
||||
|
||||
internal IUserLockoutStore<TUser> GetUserLockoutStore()
|
||||
{
|
||||
var cast = Store as IUserLockoutStore<TUser>;
|
||||
|
|
@ -2310,6 +2414,10 @@ namespace Microsoft.AspNetCore.Identity
|
|||
}
|
||||
var hash = newPassword != null ? PasswordHasher.HashPassword(user, newPassword) : null;
|
||||
await passwordStore.SetPasswordHashAsync(user, hash, CancellationToken);
|
||||
if (SupportsUserActivity)
|
||||
{
|
||||
await GetUserActivityStore().SetLastPasswordChangeDateAsync(user, DateTimeOffset.UtcNow, CancellationToken);
|
||||
}
|
||||
await UpdateSecurityStampInternal(user);
|
||||
return IdentityResult.Success;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity
|
||||
{
|
||||
/// <summary>
|
||||
/// Used for store schema versioning.
|
||||
/// </summary>
|
||||
public class IdentityStoreOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Matches version 1.x.x
|
||||
/// </summary>
|
||||
public const string Version1_0 = "v1.0";
|
||||
|
||||
/// <summary>
|
||||
/// Matches version 2.0.0
|
||||
/// </summary>
|
||||
public const string Version2_0 = "v2.0";
|
||||
|
||||
/// <summary>
|
||||
/// Used to represent the most current version.
|
||||
/// </summary>
|
||||
public const string Version_Latest = "latest";
|
||||
|
||||
/// <summary>
|
||||
/// Used to determine what features/schema are supported in the store.
|
||||
/// </summary>
|
||||
public string Version { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -149,6 +149,21 @@ namespace Microsoft.AspNetCore.Identity
|
|||
/// </summary>
|
||||
public virtual int AccessFailedCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the date and time, in UTC, when the user was created.
|
||||
/// </summary>
|
||||
public virtual DateTimeOffset? CreateDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the date and time, in UTC, when the user last signed in.
|
||||
/// </summary>
|
||||
public virtual DateTimeOffset? LastSignInDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the date and time, in UTC, when the user last changed his password.
|
||||
/// </summary>
|
||||
public virtual DateTimeOffset? LastPasswordChangeDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the username for this user.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -2,26 +2,18 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Internal;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of a persistence store for roles.
|
||||
/// Creates a new instance of a persistence store for roles that always will match the latest /// Creates a new instance of a persistence store for roles that matches <see cref="IdentityStoreOptions.Version"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TRole">The type of the class representing a role.</typeparam>
|
||||
/// <typeparam name="TKey">The type of the primary key for a role.</typeparam>
|
||||
/// <typeparam name="TUserRole">The type of the class representing a user role.</typeparam>
|
||||
/// <typeparam name="TRoleClaim">The type of the class representing a role claim.</typeparam>
|
||||
public abstract class RoleStoreBase<TRole, TKey, TUserRole, TRoleClaim> :
|
||||
IQueryableRoleStore<TRole>,
|
||||
IRoleClaimStore<TRole>
|
||||
RoleStoreBaseV1<TRole, TKey, TUserRole, TRoleClaim>
|
||||
where TRole : IdentityRole<TKey, TUserRole, TRoleClaim>
|
||||
where TKey : IEquatable<TKey>
|
||||
where TUserRole : IdentityUserRole<TKey>, new()
|
||||
|
|
@ -31,242 +23,6 @@ namespace Microsoft.AspNetCore.Identity
|
|||
/// Constructs a new instance of <see cref="RoleStoreBase{TRole, TKey, TUserRole, TRoleClaim}"/>.
|
||||
/// </summary>
|
||||
/// <param name="describer">The <see cref="IdentityErrorDescriber"/>.</param>
|
||||
public RoleStoreBase(IdentityErrorDescriber describer)
|
||||
{
|
||||
if (describer == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(describer));
|
||||
}
|
||||
|
||||
ErrorDescriber = describer;
|
||||
}
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="IdentityErrorDescriber"/> for any error that occurred with the current operation.
|
||||
/// </summary>
|
||||
public IdentityErrorDescriber ErrorDescriber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new role in a store as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="role">The role to create in the store.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that represents the <see cref="IdentityResult"/> of the asynchronous query.</returns>
|
||||
public abstract Task<IdentityResult> CreateAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// Updates a role in a store as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="role">The role to update in the store.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that represents the <see cref="IdentityResult"/> of the asynchronous query.</returns>
|
||||
public abstract Task<IdentityResult> UpdateAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a role from the store as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="role">The role to delete from the store.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that represents the <see cref="IdentityResult"/> of the asynchronous query.</returns>
|
||||
public abstract Task<IdentityResult> DeleteAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ID for a role from the store as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="role">The role whose ID should be returned.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that contains the ID of the role.</returns>
|
||||
public virtual Task<string> GetRoleIdAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (role == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(role));
|
||||
}
|
||||
return Task.FromResult(ConvertIdToString(role.Id));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of a role from the store as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="role">The role whose name should be returned.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that contains the name of the role.</returns>
|
||||
public virtual Task<string> GetRoleNameAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (role == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(role));
|
||||
}
|
||||
return Task.FromResult(role.Name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the name of a role in the store as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="role">The role whose name should be set.</param>
|
||||
/// <param name="roleName">The name of the role.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public virtual Task SetRoleNameAsync(TRole role, string roleName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (role == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(role));
|
||||
}
|
||||
role.Name = roleName;
|
||||
return TaskCache.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the provided <paramref name="id"/> to a strongly typed key object.
|
||||
/// </summary>
|
||||
/// <param name="id">The id to convert.</param>
|
||||
/// <returns>An instance of <typeparamref name="TKey"/> representing the provided <paramref name="id"/>.</returns>
|
||||
public virtual TKey ConvertIdFromString(string id)
|
||||
{
|
||||
if (id == null)
|
||||
{
|
||||
return default(TKey);
|
||||
}
|
||||
return (TKey)TypeDescriptor.GetConverter(typeof(TKey)).ConvertFromInvariantString(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the provided <paramref name="id"/> to its string representation.
|
||||
/// </summary>
|
||||
/// <param name="id">The id to convert.</param>
|
||||
/// <returns>An <see cref="string"/> representation of the provided <paramref name="id"/>.</returns>
|
||||
public virtual string ConvertIdToString(TKey id)
|
||||
{
|
||||
if (id.Equals(default(TKey)))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return id.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the role who has the specified ID as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="id">The role ID to look for.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that result of the look up.</returns>
|
||||
public abstract Task<TRole> FindByIdAsync(string id, CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// Finds the role who has the specified normalized name as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="normalizedName">The normalized role name to look for.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that result of the look up.</returns>
|
||||
public abstract Task<TRole> FindByNameAsync(string normalizedName, CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// Get a role's normalized name as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="role">The role whose normalized name should be retrieved.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that contains the name of the role.</returns>
|
||||
public virtual Task<string> GetNormalizedRoleNameAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (role == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(role));
|
||||
}
|
||||
return Task.FromResult(role.NormalizedName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set a role's normalized name as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="role">The role whose normalized name should be set.</param>
|
||||
/// <param name="normalizedName">The normalized name to set</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public virtual Task SetNormalizedRoleNameAsync(TRole role, string normalizedName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (role == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(role));
|
||||
}
|
||||
role.NormalizedName = normalizedName;
|
||||
return TaskCache.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws if this class has been disposed.
|
||||
/// </summary>
|
||||
protected void ThrowIfDisposed()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
throw new ObjectDisposedException(GetType().Name);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose the stores
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the claims associated with the specified <paramref name="role"/> as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="role">The role whose claims should be retrieved.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that contains the claims granted to a role.</returns>
|
||||
public abstract Task<IList<Claim>> GetClaimsAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// Adds the <paramref name="claim"/> given to the specified <paramref name="role"/>.
|
||||
/// </summary>
|
||||
/// <param name="role">The role to add the claim to.</param>
|
||||
/// <param name="claim">The claim to add to the role.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public abstract Task AddClaimAsync(TRole role, Claim claim, CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// Removes the <paramref name="claim"/> given from the specified <paramref name="role"/>.
|
||||
/// </summary>
|
||||
/// <param name="role">The role to remove the claim from.</param>
|
||||
/// <param name="claim">The claim to remove from the role.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public abstract Task RemoveClaimAsync(TRole role, Claim claim, CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// A navigation property for the roles the store contains.
|
||||
/// </summary>
|
||||
public abstract IQueryable<TRole> Roles
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a entity representing a role claim.
|
||||
/// </summary>
|
||||
/// <param name="role">The associated role.</param>
|
||||
/// <param name="claim">The associated claim.</param>
|
||||
/// <returns>The role claim entity.</returns>
|
||||
protected virtual TRoleClaim CreateRoleClaim(TRole role, Claim claim)
|
||||
{
|
||||
return new TRoleClaim { RoleId = role.Id, ClaimType = claim.Type, ClaimValue = claim.Value };
|
||||
}
|
||||
public RoleStoreBase(IdentityErrorDescriber describer) : base(describer) { }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,272 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Internal;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of a persistence store for roles that matches <see cref="IdentityStoreOptions.Version1_0"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TRole">The type of the class representing a role.</typeparam>
|
||||
/// <typeparam name="TKey">The type of the primary key for a role.</typeparam>
|
||||
/// <typeparam name="TUserRole">The type of the class representing a user role.</typeparam>
|
||||
/// <typeparam name="TRoleClaim">The type of the class representing a role claim.</typeparam>
|
||||
public abstract class RoleStoreBaseV1<TRole, TKey, TUserRole, TRoleClaim> :
|
||||
IQueryableRoleStore<TRole>,
|
||||
IRoleClaimStore<TRole>
|
||||
where TRole : IdentityRole<TKey, TUserRole, TRoleClaim>
|
||||
where TKey : IEquatable<TKey>
|
||||
where TUserRole : IdentityUserRole<TKey>, new()
|
||||
where TRoleClaim : IdentityRoleClaim<TKey>, new()
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs a new instance of <see cref="RoleStoreBase{TRole, TKey, TUserRole, TRoleClaim}"/>.
|
||||
/// </summary>
|
||||
/// <param name="describer">The <see cref="IdentityErrorDescriber"/>.</param>
|
||||
public RoleStoreBaseV1(IdentityErrorDescriber describer)
|
||||
{
|
||||
if (describer == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(describer));
|
||||
}
|
||||
|
||||
ErrorDescriber = describer;
|
||||
}
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="IdentityErrorDescriber"/> for any error that occurred with the current operation.
|
||||
/// </summary>
|
||||
public IdentityErrorDescriber ErrorDescriber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new role in a store as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="role">The role to create in the store.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that represents the <see cref="IdentityResult"/> of the asynchronous query.</returns>
|
||||
public abstract Task<IdentityResult> CreateAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// Updates a role in a store as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="role">The role to update in the store.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that represents the <see cref="IdentityResult"/> of the asynchronous query.</returns>
|
||||
public abstract Task<IdentityResult> UpdateAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a role from the store as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="role">The role to delete from the store.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that represents the <see cref="IdentityResult"/> of the asynchronous query.</returns>
|
||||
public abstract Task<IdentityResult> DeleteAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ID for a role from the store as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="role">The role whose ID should be returned.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that contains the ID of the role.</returns>
|
||||
public virtual Task<string> GetRoleIdAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (role == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(role));
|
||||
}
|
||||
return Task.FromResult(ConvertIdToString(role.Id));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of a role from the store as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="role">The role whose name should be returned.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that contains the name of the role.</returns>
|
||||
public virtual Task<string> GetRoleNameAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (role == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(role));
|
||||
}
|
||||
return Task.FromResult(role.Name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the name of a role in the store as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="role">The role whose name should be set.</param>
|
||||
/// <param name="roleName">The name of the role.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public virtual Task SetRoleNameAsync(TRole role, string roleName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (role == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(role));
|
||||
}
|
||||
role.Name = roleName;
|
||||
return TaskCache.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the provided <paramref name="id"/> to a strongly typed key object.
|
||||
/// </summary>
|
||||
/// <param name="id">The id to convert.</param>
|
||||
/// <returns>An instance of <typeparamref name="TKey"/> representing the provided <paramref name="id"/>.</returns>
|
||||
public virtual TKey ConvertIdFromString(string id)
|
||||
{
|
||||
if (id == null)
|
||||
{
|
||||
return default(TKey);
|
||||
}
|
||||
return (TKey)TypeDescriptor.GetConverter(typeof(TKey)).ConvertFromInvariantString(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the provided <paramref name="id"/> to its string representation.
|
||||
/// </summary>
|
||||
/// <param name="id">The id to convert.</param>
|
||||
/// <returns>An <see cref="string"/> representation of the provided <paramref name="id"/>.</returns>
|
||||
public virtual string ConvertIdToString(TKey id)
|
||||
{
|
||||
if (id.Equals(default(TKey)))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return id.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the role who has the specified ID as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="id">The role ID to look for.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that result of the look up.</returns>
|
||||
public abstract Task<TRole> FindByIdAsync(string id, CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// Finds the role who has the specified normalized name as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="normalizedName">The normalized role name to look for.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that result of the look up.</returns>
|
||||
public abstract Task<TRole> FindByNameAsync(string normalizedName, CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// Get a role's normalized name as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="role">The role whose normalized name should be retrieved.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that contains the name of the role.</returns>
|
||||
public virtual Task<string> GetNormalizedRoleNameAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (role == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(role));
|
||||
}
|
||||
return Task.FromResult(role.NormalizedName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set a role's normalized name as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="role">The role whose normalized name should be set.</param>
|
||||
/// <param name="normalizedName">The normalized name to set</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public virtual Task SetNormalizedRoleNameAsync(TRole role, string normalizedName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (role == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(role));
|
||||
}
|
||||
role.NormalizedName = normalizedName;
|
||||
return TaskCache.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws if this class has been disposed.
|
||||
/// </summary>
|
||||
protected void ThrowIfDisposed()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
throw new ObjectDisposedException(GetType().Name);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose the stores
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the claims associated with the specified <paramref name="role"/> as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="role">The role whose claims should be retrieved.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that contains the claims granted to a role.</returns>
|
||||
public abstract Task<IList<Claim>> GetClaimsAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// Adds the <paramref name="claim"/> given to the specified <paramref name="role"/>.
|
||||
/// </summary>
|
||||
/// <param name="role">The role to add the claim to.</param>
|
||||
/// <param name="claim">The claim to add to the role.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public abstract Task AddClaimAsync(TRole role, Claim claim, CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// Removes the <paramref name="claim"/> given from the specified <paramref name="role"/>.
|
||||
/// </summary>
|
||||
/// <param name="role">The role to remove the claim from.</param>
|
||||
/// <param name="claim">The claim to remove from the role.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public abstract Task RemoveClaimAsync(TRole role, Claim claim, CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// A navigation property for the roles the store contains.
|
||||
/// </summary>
|
||||
public abstract IQueryable<TRole> Roles
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a entity representing a role claim.
|
||||
/// </summary>
|
||||
/// <param name="role">The associated role.</param>
|
||||
/// <param name="claim">The associated claim.</param>
|
||||
/// <returns>The role claim entity.</returns>
|
||||
public virtual TRoleClaim CreateRoleClaim(TRole role, Claim claim)
|
||||
{
|
||||
return new TRoleClaim { RoleId = role.Id, ClaimType = claim.Type, ClaimValue = claim.Value };
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,152 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Internal;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a new instance of a persistence store for the specified user and role types.
|
||||
/// </summary>
|
||||
/// <typeparam name="TUser">The type representing a user.</typeparam>
|
||||
/// <typeparam name="TRole">The type representing a role.</typeparam>
|
||||
/// <typeparam name="TKey">The type of the primary key for a role.</typeparam>
|
||||
/// <typeparam name="TUserClaim">The type representing a claim.</typeparam>
|
||||
/// <typeparam name="TUserRole">The type representing a user role.</typeparam>
|
||||
/// <typeparam name="TUserLogin">The type representing a user external login.</typeparam>
|
||||
/// <typeparam name="TUserToken">The type representing a user token.</typeparam>
|
||||
/// <typeparam name="TRoleClaim">The type representing a role claim.</typeparam>
|
||||
public abstract class UserStoreBaseV2<TUser, TRole, TKey, TUserClaim, TUserRole, TUserLogin, TUserToken, TRoleClaim> :
|
||||
UserStoreBaseV1<TUser, TRole, TKey, TUserClaim, TUserRole, TUserLogin, TUserToken, TRoleClaim>,
|
||||
IUserActivityStore<TUser>
|
||||
where TUser : IdentityUser<TKey, TUserClaim, TUserRole, TUserLogin, TUserToken>
|
||||
where TRole : IdentityRole<TKey, TUserRole, TRoleClaim>
|
||||
where TKey : IEquatable<TKey>
|
||||
where TUserClaim : IdentityUserClaim<TKey>, new()
|
||||
where TUserRole : IdentityUserRole<TKey>, new()
|
||||
where TUserLogin : IdentityUserLogin<TKey>, new()
|
||||
where TUserToken : IdentityUserToken<TKey>, new()
|
||||
where TRoleClaim : IdentityRoleClaim<TKey>, new()
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance.
|
||||
/// </summary>
|
||||
/// <param name="describer">The <see cref="IdentityErrorDescriber"/> used to describe store errors.</param>
|
||||
public UserStoreBaseV2(IdentityErrorDescriber describer) : base(describer) { }
|
||||
|
||||
/// <summary>
|
||||
/// Sets the last password change date for the specified <paramref name="user"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <param name="changeDate">The last password change date.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public virtual Task SetLastPasswordChangeDateAsync(TUser user, DateTimeOffset? changeDate, CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
user.LastPasswordChangeDate = changeDate;
|
||||
return TaskCache.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the last password change date for the specified <paramref name="user"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation, returning the password hash for the specified <paramref name="user"/>.</returns>
|
||||
public virtual Task<DateTimeOffset?> GetLastPasswordChangeDateAsync(TUser user, CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
return Task.FromResult(user.LastPasswordChangeDate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the creation date for the specified <paramref name="user"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <param name="creationDate">The date the user was created.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public virtual Task SetCreateDateAsync(TUser user, DateTimeOffset? creationDate, CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
user.CreateDate = creationDate;
|
||||
return TaskCache.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the creation date for the specified <paramref name="user"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation, returning the password hash for the specified <paramref name="user"/>.</returns>
|
||||
public virtual Task<DateTimeOffset?> GetCreateDateAsync(TUser user, CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
return Task.FromResult(user.CreateDate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the last signin date for the specified <paramref name="user"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <param name="lastSignIn">The date the user last signed in.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
|
||||
public virtual Task SetLastSignInDateAsync(TUser user, DateTimeOffset? lastSignIn, CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
user.LastSignInDate = lastSignIn;
|
||||
return TaskCache.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the last signin date for the specified <paramref name="user"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
|
||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation, returning the password hash for the specified <paramref name="user"/>.</returns>
|
||||
public virtual Task<DateTimeOffset?> GetLastSignInDateAsync(TUser user, CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
return Task.FromResult(user.LastSignInDate);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -9,16 +9,18 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test
|
|||
public class InMemoryContext :
|
||||
InMemoryContext<IdentityUser, IdentityRole, string>
|
||||
{
|
||||
public InMemoryContext(DbContextOptions options) : base(options)
|
||||
{ }
|
||||
public InMemoryContext(DbContextOptions options) : base(options) { }
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
=> optionsBuilder.UseInMemoryDatabase("Scratch");
|
||||
}
|
||||
|
||||
public class InMemoryContext<TUser> :
|
||||
InMemoryContext<TUser, IdentityRole, string>
|
||||
where TUser : IdentityUser
|
||||
{
|
||||
public InMemoryContext(DbContextOptions options) : base(options)
|
||||
{ }
|
||||
public InMemoryContext(DbContextOptions options) : base(options) { }
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
=> optionsBuilder.UseInMemoryDatabase("Scratch");
|
||||
}
|
||||
|
||||
public class InMemoryContext<TUser, TRole, TKey> : IdentityDbContext<TUser, TRole, TKey>
|
||||
|
|
@ -26,13 +28,10 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test
|
|||
where TRole : IdentityRole<TKey>
|
||||
where TKey : IEquatable<TKey>
|
||||
{
|
||||
public InMemoryContext(DbContextOptions options) : base(options)
|
||||
{ }
|
||||
public InMemoryContext(DbContextOptions options) : base(options) { }
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
optionsBuilder.UseInMemoryDatabase("Scratch");
|
||||
}
|
||||
=> optionsBuilder.UseInMemoryDatabase("Scratch");
|
||||
}
|
||||
|
||||
public abstract class InMemoryContext<TUser, TRole, TKey, TUserClaim, TUserRole, TUserLogin, TRoleClaim, TUserToken> : IdentityDbContext<TUser, TRole, TKey, TUserClaim, TUserRole, TUserLogin, TRoleClaim, TUserToken>
|
||||
|
|
@ -45,12 +44,57 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test
|
|||
where TRoleClaim : IdentityRoleClaim<TKey>
|
||||
where TUserToken : IdentityUserToken<TKey>
|
||||
{
|
||||
public InMemoryContext(DbContextOptions options) : base(options)
|
||||
{ }
|
||||
public InMemoryContext(DbContextOptions options) : base(options) { }
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
optionsBuilder.UseInMemoryDatabase("Scratch");
|
||||
}
|
||||
=> optionsBuilder.UseInMemoryDatabase("Scratch");
|
||||
}
|
||||
|
||||
public class InMemoryContextV1 :
|
||||
InMemoryContextV1<IdentityUser, IdentityRole, string>
|
||||
{
|
||||
public InMemoryContextV1(DbContextOptions options) : base(options) { }
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
=> optionsBuilder.UseInMemoryDatabase("ScratchV1");
|
||||
}
|
||||
|
||||
public class InMemoryContextV1<TUser> :
|
||||
InMemoryContext<TUser, IdentityRole, string>
|
||||
where TUser : IdentityUser
|
||||
{
|
||||
public InMemoryContextV1(DbContextOptions options) : base(options) { }
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
=> optionsBuilder.UseInMemoryDatabase("ScratchV1");
|
||||
}
|
||||
|
||||
public class InMemoryContextV1<TUser, TRole, TKey> : IdentityDbContext<TUser, TRole, TKey>
|
||||
where TUser : IdentityUser<TKey>
|
||||
where TRole : IdentityRole<TKey>
|
||||
where TKey : IEquatable<TKey>
|
||||
{
|
||||
public InMemoryContextV1(DbContextOptions options) : base(options) { }
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
=> optionsBuilder.UseInMemoryDatabase("ScratchV1");
|
||||
}
|
||||
|
||||
public abstract class InMemoryContextV1<TUser, TRole, TKey, TUserClaim, TUserRole, TUserLogin, TRoleClaim, TUserToken> : IdentityDbContext<TUser, TRole, TKey, TUserClaim, TUserRole, TUserLogin, TRoleClaim, TUserToken>
|
||||
where TUser : IdentityUser<TKey, TUserClaim, TUserRole, TUserLogin, TUserToken>
|
||||
where TRole : IdentityRole<TKey, TUserRole, TRoleClaim>
|
||||
where TKey : IEquatable<TKey>
|
||||
where TUserClaim : IdentityUserClaim<TKey>
|
||||
where TUserRole : IdentityUserRole<TKey>
|
||||
where TUserLogin : IdentityUserLogin<TKey>
|
||||
where TRoleClaim : IdentityRoleClaim<TKey>
|
||||
where TUserToken : IdentityUserToken<TKey>
|
||||
{
|
||||
public InMemoryContextV1(DbContextOptions options) : base(options) { }
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
=> optionsBuilder.UseInMemoryDatabase("ScratchV1");
|
||||
|
||||
// TODO: remove once we fix the base
|
||||
protected override void OnModelCreating(ModelBuilder builder)
|
||||
=> base.OnModelCreatingV1(builder);
|
||||
}
|
||||
}
|
||||
|
|
@ -3,27 +3,25 @@
|
|||
|
||||
using System;
|
||||
using System.Linq.Expressions;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Identity.Test;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test
|
||||
{
|
||||
public class InMemoryEFUserStoreTest : IdentitySpecificationTestBase<IdentityUser, IdentityRole, string>
|
||||
{
|
||||
protected override object CreateTestContext()
|
||||
{
|
||||
return new InMemoryContext(new DbContextOptionsBuilder().Options);
|
||||
}
|
||||
=> new InMemoryContext(new DbContextOptionsBuilder().Options);
|
||||
|
||||
protected override void AddUserStore(IServiceCollection services, object context = null)
|
||||
{
|
||||
services.AddSingleton<IUserStore<IdentityUser>>(new UserStore<IdentityUser>((InMemoryContext)context));
|
||||
}
|
||||
=> services.AddSingleton<IUserStore<IdentityUser>>(new UserStore<IdentityUser, IdentityRole, DbContext, string, IdentityUserClaim<string>, IdentityUserRole<string>, IdentityUserLogin<string>, IdentityUserToken<string>, IdentityRoleClaim<string>>((InMemoryContext)context, new IdentityErrorDescriber()));
|
||||
|
||||
protected override void AddRoleStore(IServiceCollection services, object context = null)
|
||||
{
|
||||
var store = new RoleStore<IdentityRole, InMemoryContext>((InMemoryContext)context);
|
||||
var store = new RoleStore<IdentityRole, InMemoryContext, string, IdentityUserRole<string>, IdentityRoleClaim<string>>((InMemoryContext)context);
|
||||
services.AddSingleton<IRoleStore<IdentityRole>>(store);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,62 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Linq.Expressions;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Identity.Test;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test
|
||||
{
|
||||
public class InMemoryEFUserStoreV1Test : IdentitySpecificationTestBase<IdentityUser, IdentityRole, string>
|
||||
{
|
||||
protected override object CreateTestContext()
|
||||
=> new InMemoryContextV1(new DbContextOptionsBuilder().Options);
|
||||
|
||||
protected override void AddUserStore(IServiceCollection services, object context = null)
|
||||
{
|
||||
services.AddSingleton<IUserStore<IdentityUser>>(new UserStoreV1<IdentityUser, IdentityRole, DbContext, string, IdentityUserClaim<string>, IdentityUserRole<string>, IdentityUserLogin<string>, IdentityUserToken<string>, IdentityRoleClaim<string>>((InMemoryContextV1)context, new IdentityErrorDescriber()));
|
||||
}
|
||||
|
||||
protected override void AddRoleStore(IServiceCollection services, object context = null)
|
||||
{
|
||||
var store = new RoleStoreV1<IdentityRole, InMemoryContextV1, string, IdentityUserRole<string>, IdentityRoleClaim<string>>((InMemoryContextV1)context);
|
||||
services.AddSingleton<IRoleStore<IdentityRole>>(store);
|
||||
}
|
||||
|
||||
protected override IdentityUser CreateTestUser(string namePrefix = "", string email = "", string phoneNumber = "",
|
||||
bool lockoutEnabled = false, DateTimeOffset? lockoutEnd = default(DateTimeOffset?), bool useNamePrefixAsUserName = false)
|
||||
{
|
||||
return new IdentityUser
|
||||
{
|
||||
UserName = useNamePrefixAsUserName ? namePrefix : string.Format("{0}{1}", namePrefix, Guid.NewGuid()),
|
||||
Email = email,
|
||||
PhoneNumber = phoneNumber,
|
||||
LockoutEnabled = lockoutEnabled,
|
||||
LockoutEnd = lockoutEnd
|
||||
};
|
||||
}
|
||||
|
||||
protected override IdentityRole CreateTestRole(string roleNamePrefix = "", bool useRoleNamePrefixAsRoleName = false)
|
||||
{
|
||||
var roleName = useRoleNamePrefixAsRoleName ? roleNamePrefix : string.Format("{0}{1}", roleNamePrefix, Guid.NewGuid());
|
||||
return new IdentityRole(roleName);
|
||||
}
|
||||
|
||||
protected override void SetUserPasswordHash(IdentityUser user, string hashedPassword)
|
||||
{
|
||||
user.PasswordHash = hashedPassword;
|
||||
}
|
||||
|
||||
protected override Expression<Func<IdentityUser, bool>> UserNameEqualsPredicate(string userName) => u => u.UserName == userName;
|
||||
|
||||
protected override Expression<Func<IdentityRole, bool>> RoleNameEqualsPredicate(string roleName) => r => r.Name == roleName;
|
||||
|
||||
protected override Expression<Func<IdentityUser, bool>> UserNameStartsWithPredicate(string userName) => u => u.UserName.StartsWith(userName);
|
||||
|
||||
protected override Expression<Func<IdentityRole, bool>> RoleNameStartsWithPredicate(string roleName) => r => r.Name.StartsWith(roleName);
|
||||
}
|
||||
}
|
||||
|
|
@ -200,12 +200,12 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test
|
|||
{
|
||||
public string LoginContext { get; set; }
|
||||
|
||||
public UserStoreWithGenerics(InMemoryContextWithGenerics context, string loginContext) : base(context)
|
||||
public UserStoreWithGenerics(InMemoryContextWithGenerics context, string loginContext) : base(context, new IdentityErrorDescriber())
|
||||
{
|
||||
LoginContext = loginContext;
|
||||
}
|
||||
|
||||
protected override IdentityUserRoleWithDate CreateUserRole(IdentityUserWithGenerics user, MyIdentityRole role)
|
||||
public override IdentityUserRoleWithDate CreateUserRole(IdentityUserWithGenerics user, MyIdentityRole role)
|
||||
{
|
||||
return new IdentityUserRoleWithDate()
|
||||
{
|
||||
|
|
@ -215,12 +215,12 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test
|
|||
};
|
||||
}
|
||||
|
||||
protected override IdentityUserClaimWithIssuer CreateUserClaim(IdentityUserWithGenerics user, Claim claim)
|
||||
public override IdentityUserClaimWithIssuer CreateUserClaim(IdentityUserWithGenerics user, Claim claim)
|
||||
{
|
||||
return new IdentityUserClaimWithIssuer { UserId = user.Id, ClaimType = claim.Type, ClaimValue = claim.Value, Issuer = claim.Issuer };
|
||||
}
|
||||
|
||||
protected override IdentityUserLoginWithContext CreateUserLogin(IdentityUserWithGenerics user, UserLoginInfo login)
|
||||
public override IdentityUserLoginWithContext CreateUserLogin(IdentityUserWithGenerics user, UserLoginInfo login)
|
||||
{
|
||||
return new IdentityUserLoginWithContext
|
||||
{
|
||||
|
|
@ -232,7 +232,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test
|
|||
};
|
||||
}
|
||||
|
||||
protected override IdentityUserTokenWithStuff CreateUserToken(IdentityUserWithGenerics user, string loginProvider, string name, string value)
|
||||
public override IdentityUserTokenWithStuff CreateUserToken(IdentityUserWithGenerics user, string loginProvider, string name, string value)
|
||||
{
|
||||
return new IdentityUserTokenWithStuff
|
||||
{
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test
|
|||
var services = TestIdentityFactory.CreateTestServices();
|
||||
services.AddEntityFrameworkInMemoryDatabase();
|
||||
services.AddSingleton(new InMemoryContext(new DbContextOptionsBuilder().Options));
|
||||
services.AddTransient<IRoleStore<IdentityRole>, RoleStore<IdentityRole, InMemoryContext>>();
|
||||
services.AddTransient<IRoleStore<IdentityRole>, RoleStore<IdentityRole, InMemoryContext, string, IdentityUserRole<string>, IdentityRoleClaim<string>>>();
|
||||
services.AddSingleton<RoleManager<IdentityRole>>();
|
||||
var provider = services.BuildServiceProvider();
|
||||
var manager = provider.GetRequiredService<RoleManager<IdentityRole>>();
|
||||
|
|
@ -38,7 +38,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test
|
|||
[Fact]
|
||||
public async Task RoleStoreMethodsThrowWhenDisposedTest()
|
||||
{
|
||||
var store = new RoleStore<IdentityRole>(new InMemoryContext(new DbContextOptionsBuilder().Options));
|
||||
var store = new RoleStore<IdentityRole, InMemoryContext, string, IdentityUserRole<string>, IdentityRoleClaim<string>>(new InMemoryContext(new DbContextOptionsBuilder().Options));
|
||||
store.Dispose();
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.FindByIdAsync(null));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.FindByNameAsync(null));
|
||||
|
|
@ -53,8 +53,8 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test
|
|||
[Fact]
|
||||
public async Task RoleStorePublicNullCheckTest()
|
||||
{
|
||||
Assert.Throws<ArgumentNullException>("context", () => new RoleStore<IdentityRole>(null));
|
||||
var store = new RoleStore<IdentityRole>(new InMemoryContext(new DbContextOptionsBuilder().Options));
|
||||
Assert.Throws<ArgumentNullException>("context", () => new RoleStore<IdentityRole, InMemoryContext, string, IdentityUserRole<string>, IdentityRoleClaim<string>>(null));
|
||||
var store = new RoleStore<IdentityRole, InMemoryContext, string, IdentityUserRole<string>, IdentityRoleClaim<string>>(new InMemoryContext(new DbContextOptionsBuilder().Options));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("role", async () => await store.GetRoleIdAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("role", async () => await store.GetRoleNameAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("role", async () => await store.SetRoleNameAsync(null, null));
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test
|
|||
public static RoleManager<IdentityRole> CreateRoleManager(InMemoryContext context)
|
||||
{
|
||||
var services = CreateTestServices();
|
||||
services.AddSingleton<IRoleStore<IdentityRole>>(new RoleStore<IdentityRole>(context));
|
||||
services.AddSingleton<IRoleStore<IdentityRole>>(new RoleStore<IdentityRole, DbContext, string, IdentityUserRole<string>, IdentityRoleClaim<string>>(context));
|
||||
return services.BuildServiceProvider().GetRequiredService<RoleManager<IdentityRole>>();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ using Microsoft.AspNetCore.Testing;
|
|||
using Microsoft.AspNetCore.Testing.xunit;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test
|
||||
|
|
@ -21,7 +22,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test
|
|||
where TRole : IdentityRole<TKey>, new()
|
||||
where TKey : IEquatable<TKey>
|
||||
{
|
||||
private readonly ScratchDatabaseFixture _fixture;
|
||||
protected readonly ScratchDatabaseFixture _fixture;
|
||||
|
||||
protected SqlStoreTestBase(ScratchDatabaseFixture fixture)
|
||||
{
|
||||
|
|
@ -64,7 +65,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test
|
|||
|
||||
protected override Expression<Func<TUser, bool>> UserNameStartsWithPredicate(string userName) => u => u.UserName.StartsWith(userName);
|
||||
|
||||
public TestDbContext CreateContext()
|
||||
public virtual TestDbContext CreateContext()
|
||||
{
|
||||
var db = DbUtil.Create<TestDbContext>(_fixture.ConnectionString);
|
||||
db.Database.EnsureCreated();
|
||||
|
|
@ -78,12 +79,12 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test
|
|||
|
||||
protected override void AddUserStore(IServiceCollection services, object context = null)
|
||||
{
|
||||
services.AddSingleton<IUserStore<TUser>>(new UserStore<TUser, TRole, TestDbContext, TKey>((TestDbContext)context));
|
||||
services.AddSingleton<IUserStore<TUser>>(new UserStore<TUser, TRole, TestDbContext, TKey, IdentityUserClaim<TKey>, IdentityUserRole<TKey>, IdentityUserLogin<TKey>, IdentityUserToken<TKey>, IdentityRoleClaim<TKey>>((TestDbContext)context, new IdentityErrorDescriber()));
|
||||
}
|
||||
|
||||
protected override void AddRoleStore(IServiceCollection services, object context = null)
|
||||
{
|
||||
services.AddSingleton<IRoleStore<TRole>>(new RoleStore<TRole, TestDbContext, TKey>((TestDbContext)context));
|
||||
services.AddSingleton<IRoleStore<TRole>>(new RoleStore<TRole, TestDbContext, TKey, IdentityUserRole<TKey>, IdentityRoleClaim<TKey>>((TestDbContext)context));
|
||||
}
|
||||
|
||||
protected override void SetUserPasswordHash(TUser user, string hashedPassword)
|
||||
|
|
@ -95,26 +96,36 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test
|
|||
[FrameworkSkipCondition(RuntimeFrameworks.Mono)]
|
||||
[OSSkipCondition(OperatingSystems.Linux)]
|
||||
[OSSkipCondition(OperatingSystems.MacOSX)]
|
||||
public void EnsureDefaultSchema()
|
||||
public virtual void EnsureDefaultSchema()
|
||||
{
|
||||
VerifyDefaultSchema(CreateContext());
|
||||
var db = DbUtil.Create<TestDbContext>(_fixture.ConnectionString);
|
||||
var services = new ServiceCollection().AddSingleton(db);
|
||||
services.AddIdentity<TUser, TRole>().AddEntityFrameworkStoresLatest<TestDbContext>();
|
||||
var sp = services.BuildServiceProvider();
|
||||
var version = sp.GetRequiredService<IOptions<IdentityStoreOptions>>().Value.Version;
|
||||
Assert.Equal(IdentityStoreOptions.Version_Latest, version);
|
||||
db.Version = version;
|
||||
db.Database.EnsureDeleted();
|
||||
db.Database.EnsureCreated();
|
||||
VerifyDefaultSchema(db);
|
||||
}
|
||||
|
||||
internal static void VerifyDefaultSchema(TestDbContext dbContext)
|
||||
protected virtual void VerifyDefaultSchema(TestDbContext dbContext)
|
||||
{
|
||||
var sqlConn = dbContext.Database.GetDbConnection();
|
||||
|
||||
using (var db = new SqlConnection(sqlConn.ConnectionString))
|
||||
{
|
||||
db.Open();
|
||||
Assert.True(VerifyColumns(db, "AspNetUsers", "Id", "UserName", "Email", "PasswordHash", "SecurityStamp",
|
||||
VerifyColumns(db, "AspNetUsers", "Id", "UserName", "Email", "PasswordHash", "SecurityStamp",
|
||||
"EmailConfirmed", "PhoneNumber", "PhoneNumberConfirmed", "TwoFactorEnabled", "LockoutEnabled",
|
||||
"LockoutEnd", "AccessFailedCount", "ConcurrencyStamp", "NormalizedUserName", "NormalizedEmail"));
|
||||
Assert.True(VerifyColumns(db, "AspNetRoles", "Id", "Name", "NormalizedName", "ConcurrencyStamp"));
|
||||
Assert.True(VerifyColumns(db, "AspNetUserRoles", "UserId", "RoleId"));
|
||||
Assert.True(VerifyColumns(db, "AspNetUserClaims", "Id", "UserId", "ClaimType", "ClaimValue"));
|
||||
Assert.True(VerifyColumns(db, "AspNetUserLogins", "UserId", "ProviderKey", "LoginProvider", "ProviderDisplayName"));
|
||||
Assert.True(VerifyColumns(db, "AspNetUserTokens", "UserId", "LoginProvider", "Name", "Value"));
|
||||
"LockoutEnd", "AccessFailedCount", "ConcurrencyStamp", "NormalizedUserName", "NormalizedEmail",
|
||||
"CreateDate", "LastSignInDate", "LastPasswordChangeDate");
|
||||
VerifyColumns(db, "AspNetRoles", "Id", "Name", "NormalizedName", "ConcurrencyStamp");
|
||||
VerifyColumns(db, "AspNetUserRoles", "UserId", "RoleId");
|
||||
VerifyColumns(db, "AspNetUserClaims", "Id", "UserId", "ClaimType", "ClaimValue");
|
||||
VerifyColumns(db, "AspNetUserLogins", "UserId", "ProviderKey", "LoginProvider", "ProviderDisplayName");
|
||||
VerifyColumns(db, "AspNetUserTokens", "UserId", "LoginProvider", "Name", "Value");
|
||||
|
||||
VerifyIndex(db, "AspNetRoles", "RoleNameIndex", isUnique: true);
|
||||
VerifyIndex(db, "AspNetUsers", "UserNameIndex", isUnique: true);
|
||||
|
|
@ -123,7 +134,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test
|
|||
}
|
||||
}
|
||||
|
||||
internal static bool VerifyColumns(SqlConnection conn, string table, params string[] columns)
|
||||
internal static void VerifyColumns(SqlConnection conn, string table, params string[] columns)
|
||||
{
|
||||
var count = 0;
|
||||
using (
|
||||
|
|
@ -136,12 +147,10 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test
|
|||
while (reader.Read())
|
||||
{
|
||||
count++;
|
||||
if (!columns.Contains(reader.GetString(0)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Assert.True(columns.Contains(reader.GetString(0)),
|
||||
"Unexpected column: " + reader.GetString(0));
|
||||
}
|
||||
return count == columns.Length;
|
||||
Assert.Equal(count, columns.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -363,6 +372,67 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test
|
|||
Assert.Equal(1, (await manager.GetLoginsAsync(userByEmail)).Count);
|
||||
Assert.Equal(2, (await manager.GetRolesAsync(userByEmail)).Count);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class SqlStoreTestBaseV1<TUser, TRole, TKey> : SqlStoreTestBase<TUser, TRole, TKey>, IClassFixture<ScratchDatabaseFixture>
|
||||
where TUser : IdentityUser<TKey>, new()
|
||||
where TRole : IdentityRole<TKey>, new()
|
||||
where TKey : IEquatable<TKey>
|
||||
{
|
||||
protected SqlStoreTestBaseV1(ScratchDatabaseFixture fixture) : base(fixture) { }
|
||||
|
||||
public override TestDbContext CreateContext()
|
||||
{
|
||||
var db = base.CreateContext();
|
||||
db.Version = IdentityStoreOptions.Version1_0;
|
||||
return db;
|
||||
}
|
||||
|
||||
protected override void AddUserStore(IServiceCollection services, object context = null)
|
||||
=> services.AddSingleton<IUserStore<TUser>>(new UserStoreV1<TUser, TRole, TestDbContext, TKey, IdentityUserClaim<TKey>, IdentityUserRole<TKey>, IdentityUserLogin<TKey>, IdentityUserToken<TKey>, IdentityRoleClaim<TKey>>((TestDbContext)context, new IdentityErrorDescriber()));
|
||||
|
||||
protected override void AddRoleStore(IServiceCollection services, object context = null)
|
||||
=> services.AddSingleton<IRoleStore<TRole>>(new RoleStoreV1<TRole, TestDbContext, TKey, IdentityUserRole<TKey>, IdentityRoleClaim<TKey>>((TestDbContext)context));
|
||||
|
||||
[ConditionalFact]
|
||||
[FrameworkSkipCondition(RuntimeFrameworks.Mono)]
|
||||
[OSSkipCondition(OperatingSystems.Linux)]
|
||||
[OSSkipCondition(OperatingSystems.MacOSX)]
|
||||
public override void EnsureDefaultSchema()
|
||||
{
|
||||
var db = DbUtil.Create<TestDbContext>(_fixture.ConnectionString);
|
||||
var services = new ServiceCollection().AddSingleton(db);
|
||||
services.AddIdentity<TUser, TRole>().AddEntityFrameworkStoresV1<TestDbContext>();
|
||||
var sp = services.BuildServiceProvider();
|
||||
var version = sp.GetRequiredService<IOptions<IdentityStoreOptions>>().Value.Version;
|
||||
Assert.Equal(IdentityStoreOptions.Version1_0, version);
|
||||
db.Version = version;
|
||||
db.Database.EnsureDeleted();
|
||||
db.Database.EnsureCreated();
|
||||
VerifyDefaultSchema(db);
|
||||
}
|
||||
|
||||
protected override void VerifyDefaultSchema(TestDbContext dbContext)
|
||||
{
|
||||
var sqlConn = dbContext.Database.GetDbConnection();
|
||||
|
||||
using (var db = new SqlConnection(sqlConn.ConnectionString))
|
||||
{
|
||||
db.Open();
|
||||
VerifyColumns(db, "AspNetUsers", "Id", "UserName", "Email", "PasswordHash", "SecurityStamp",
|
||||
"EmailConfirmed", "PhoneNumber", "PhoneNumberConfirmed", "TwoFactorEnabled", "LockoutEnabled",
|
||||
"LockoutEnd", "AccessFailedCount", "ConcurrencyStamp", "NormalizedUserName", "NormalizedEmail");
|
||||
VerifyColumns(db, "AspNetRoles", "Id", "Name", "NormalizedName", "ConcurrencyStamp");
|
||||
VerifyColumns(db, "AspNetUserRoles", "UserId", "RoleId");
|
||||
VerifyColumns(db, "AspNetUserClaims", "Id", "UserId", "ClaimType", "ClaimValue");
|
||||
VerifyColumns(db, "AspNetUserLogins", "UserId", "ProviderKey", "LoginProvider", "ProviderDisplayName");
|
||||
VerifyColumns(db, "AspNetUserTokens", "UserId", "LoginProvider", "Name", "Value");
|
||||
|
||||
VerifyIndex(db, "AspNetRoles", "RoleNameIndex", isUnique: true);
|
||||
VerifyIndex(db, "AspNetUsers", "UserNameIndex", isUnique: true);
|
||||
VerifyIndex(db, "AspNetUsers", "EmailIndex");
|
||||
db.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -25,21 +25,20 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test
|
|||
}
|
||||
}
|
||||
|
||||
public class UserStoreGuidTest : SqlStoreTestBase<GuidUser, GuidRole, Guid>
|
||||
public class UserStoreGuidV1Test : SqlStoreTestBaseV1<GuidUser, GuidRole, Guid>
|
||||
{
|
||||
public UserStoreGuidTest(ScratchDatabaseFixture fixture)
|
||||
public UserStoreGuidV1Test(ScratchDatabaseFixture fixture)
|
||||
: base(fixture)
|
||||
{ }
|
||||
|
||||
public class ApplicationUserStore : UserStoreV1<GuidUser, GuidRole, TestDbContext, Guid, IdentityUserClaim<Guid>, IdentityUserRole<Guid>, IdentityUserLogin<Guid>, IdentityUserToken<Guid>, IdentityRoleClaim<Guid>>
|
||||
{
|
||||
public ApplicationUserStore(TestDbContext context) : base(context, new IdentityErrorDescriber()) { }
|
||||
}
|
||||
|
||||
public class ApplicationUserStore : UserStore<GuidUser, GuidRole, TestDbContext, Guid>
|
||||
public class ApplicationRoleStore : RoleStoreV1<GuidRole, TestDbContext, Guid, IdentityUserRole<Guid>, IdentityRoleClaim<Guid>>
|
||||
{
|
||||
public ApplicationUserStore(TestDbContext context) : base(context) { }
|
||||
}
|
||||
|
||||
public class ApplicationRoleStore : RoleStore<GuidRole, TestDbContext, Guid>
|
||||
{
|
||||
public ApplicationRoleStore(TestDbContext context) : base(context) { }
|
||||
public ApplicationRoleStore(TestDbContext context) : base(context, new IdentityErrorDescriber()) { }
|
||||
}
|
||||
|
||||
protected override void AddUserStore(IServiceCollection services, object context = null)
|
||||
|
|
@ -60,4 +59,40 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test
|
|||
var builder = services.AddIdentity<GuidUser, GuidRole>().AddEntityFrameworkStores<TestDbContext>();
|
||||
}
|
||||
}
|
||||
|
||||
public class UserStoreGuidTest : SqlStoreTestBase<GuidUser, GuidRole, Guid>
|
||||
{
|
||||
public UserStoreGuidTest(ScratchDatabaseFixture fixture)
|
||||
: base(fixture)
|
||||
{ }
|
||||
|
||||
public class ApplicationUserStore : UserStore<GuidUser, GuidRole, TestDbContext, Guid, IdentityUserClaim<Guid>, IdentityUserRole<Guid>, IdentityUserLogin<Guid>, IdentityUserToken<Guid>, IdentityRoleClaim<Guid>>
|
||||
{
|
||||
public ApplicationUserStore(TestDbContext context) : base(context, new IdentityErrorDescriber()) { }
|
||||
}
|
||||
|
||||
public class ApplicationRoleStore : RoleStore<GuidRole, TestDbContext, Guid, IdentityUserRole<Guid>, IdentityRoleClaim<Guid>>
|
||||
{
|
||||
public ApplicationRoleStore(TestDbContext context) : base(context, new IdentityErrorDescriber()) { }
|
||||
}
|
||||
|
||||
protected override void AddUserStore(IServiceCollection services, object context = null)
|
||||
{
|
||||
services.AddSingleton<IUserStore<GuidUser>>(new ApplicationUserStore((TestDbContext)context));
|
||||
}
|
||||
|
||||
protected override void AddRoleStore(IServiceCollection services, object context = null)
|
||||
{
|
||||
services.AddSingleton<IRoleStore<GuidRole>>(new ApplicationRoleStore((TestDbContext)context));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddEntityFrameworkStoresCanInferKey()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
// This used to throw
|
||||
var builder = services.AddIdentity<GuidUser, GuidRole>().AddEntityFrameworkStores<TestDbContext>();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test
|
||||
{
|
||||
public class IntUser : IdentityUser<int>
|
||||
{
|
||||
public IntUser()
|
||||
{
|
||||
UserName = Guid.NewGuid().ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public class IntRole : IdentityRole<int>
|
||||
{
|
||||
public IntRole()
|
||||
{
|
||||
Name = Guid.NewGuid().ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public class UserStoreIntTest : SqlStoreTestBase<IntUser, IntRole, int>
|
||||
{
|
||||
public UserStoreIntTest(ScratchDatabaseFixture fixture)
|
||||
: base(fixture)
|
||||
{
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddEntityFrameworkStoresCanInferKey()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
// This used to throw
|
||||
var builder = services.AddIdentity<IntUser, IntRole>().AddEntityFrameworkStores<TestDbContext>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Testing.xunit;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test
|
||||
{
|
||||
public class IntUser : IdentityUser<int>
|
||||
{
|
||||
public IntUser() => UserName = Guid.NewGuid().ToString();
|
||||
}
|
||||
|
||||
public class IntRole : IdentityRole<int>
|
||||
{
|
||||
public IntRole() => Name = Guid.NewGuid().ToString();
|
||||
}
|
||||
|
||||
public class UserStoreIntTest : SqlStoreTestBase<IntUser, IntRole, int>
|
||||
{
|
||||
public UserStoreIntTest(ScratchDatabaseFixture fixture) : base(fixture) { }
|
||||
|
||||
[Fact]
|
||||
public void AddEntityFrameworkStoresCanInferKey()
|
||||
{
|
||||
// This used to throw
|
||||
var builder = new ServiceCollection().AddIdentity<IntUser, IntRole>().AddEntityFrameworkStores<TestDbContext>();
|
||||
}
|
||||
}
|
||||
|
||||
public class UserStoreIntV1Test : SqlStoreTestBaseV1<IntUser, IntRole, int>
|
||||
{
|
||||
public UserStoreIntV1Test(ScratchDatabaseFixture fixture) : base(fixture) { }
|
||||
|
||||
[Fact]
|
||||
public void AddEntityFrameworkStoresCanInferKey()
|
||||
{
|
||||
// This used to throw
|
||||
var builder = new ServiceCollection().AddIdentity<IntUser, IntRole>().AddEntityFrameworkStores<TestDbContext>();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -14,6 +14,11 @@ using Xunit;
|
|||
|
||||
namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test
|
||||
{
|
||||
internal class UserStore : UserStore<IdentityUser, IdentityRole, IdentityDbContext, string, IdentityUserClaim<string>, IdentityUserRole<string>, IdentityUserLogin<string>, IdentityUserToken<string>, IdentityRoleClaim<string>>
|
||||
{
|
||||
public UserStore(IdentityDbContext context, IdentityErrorDescriber describer = null) : base(context, describer ?? new IdentityErrorDescriber()) { }
|
||||
}
|
||||
|
||||
public class UserStoreTest : IdentitySpecificationTestBase<IdentityUser, IdentityRole>, IClassFixture<ScratchDatabaseFixture>
|
||||
{
|
||||
private readonly ScratchDatabaseFixture _fixture;
|
||||
|
|
@ -26,12 +31,6 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test
|
|||
protected override bool ShouldSkipDbTests()
|
||||
=> TestPlatformHelper.IsMono || !TestPlatformHelper.IsWindows;
|
||||
|
||||
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
|
||||
{
|
||||
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
|
||||
{ }
|
||||
}
|
||||
|
||||
[ConditionalFact]
|
||||
[FrameworkSkipCondition(RuntimeFrameworks.Mono)]
|
||||
[OSSkipCondition(OperatingSystems.Linux)]
|
||||
|
|
@ -64,21 +63,14 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test
|
|||
return CreateContext();
|
||||
}
|
||||
|
||||
public ApplicationDbContext CreateAppContext()
|
||||
{
|
||||
var db = DbUtil.Create<ApplicationDbContext>(_fixture.ConnectionString);
|
||||
db.Database.EnsureCreated();
|
||||
return db;
|
||||
}
|
||||
|
||||
protected override void AddUserStore(IServiceCollection services, object context = null)
|
||||
{
|
||||
services.AddSingleton<IUserStore<IdentityUser>>(new UserStore<IdentityUser, IdentityRole, IdentityDbContext>((IdentityDbContext)context));
|
||||
services.AddSingleton<IUserStore<IdentityUser>>(new UserStore((IdentityDbContext)context));
|
||||
}
|
||||
|
||||
protected override void AddRoleStore(IServiceCollection services, object context = null)
|
||||
{
|
||||
services.AddSingleton<IRoleStore<IdentityRole>>(new RoleStore<IdentityRole, IdentityDbContext>((IdentityDbContext)context));
|
||||
services.AddSingleton<IRoleStore<IdentityRole>>(new RoleStore<IdentityRole, IdentityDbContext, string, IdentityUserRole<string>, IdentityRoleClaim<string>>((IdentityDbContext)context));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -193,6 +185,20 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test
|
|||
IdentityResultAssert.IsSuccess(await manager.DeleteAsync(user));
|
||||
}
|
||||
|
||||
[ConditionalFact]
|
||||
[FrameworkSkipCondition(RuntimeFrameworks.Mono)]
|
||||
[OSSkipCondition(OperatingSystems.Linux)]
|
||||
[OSSkipCondition(OperatingSystems.MacOSX)]
|
||||
public async Task CreateUserSetsCreateDate()
|
||||
{
|
||||
var manager = CreateManager();
|
||||
var guid = Guid.NewGuid().ToString();
|
||||
var user = new IdentityUser { UserName = "New" + guid };
|
||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
||||
Assert.NotNull(await manager.GetCreateDateAsync(user));
|
||||
IdentityResultAssert.IsSuccess(await manager.DeleteAsync(user));
|
||||
}
|
||||
|
||||
[ConditionalFact]
|
||||
[FrameworkSkipCondition(RuntimeFrameworks.Mono)]
|
||||
[OSSkipCondition(OperatingSystems.Linux)]
|
||||
|
|
@ -422,6 +428,4 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test
|
|||
|
||||
protected override Expression<Func<IdentityUser, bool>> UserNameStartsWithPredicate(string userName) => u => u.UserName.StartsWith(userName);
|
||||
}
|
||||
|
||||
public class ApplicationUser : IdentityUser { }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,418 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Identity.Test;
|
||||
using Microsoft.AspNetCore.Testing.xunit;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test
|
||||
{
|
||||
internal class UserStoreV1 : UserStoreV1<IdentityUser, IdentityRole, IdentityDbContext, string, IdentityUserClaim<string>, IdentityUserRole<string>, IdentityUserLogin<string>, IdentityUserToken<string>, IdentityRoleClaim<string>>
|
||||
{
|
||||
public UserStoreV1(IdentityDbContext context, IdentityErrorDescriber describer = null) : base(context, describer ?? new IdentityErrorDescriber()) { }
|
||||
}
|
||||
|
||||
public class UserStoreV1Test : IdentitySpecificationTestBase<IdentityUser, IdentityRole>, IClassFixture<ScratchDatabaseFixture>
|
||||
{
|
||||
private readonly ScratchDatabaseFixture _fixture;
|
||||
|
||||
public UserStoreV1Test(ScratchDatabaseFixture fixture)
|
||||
{
|
||||
_fixture = fixture;
|
||||
}
|
||||
|
||||
protected override bool ShouldSkipDbTests()
|
||||
=> TestPlatformHelper.IsMono || !TestPlatformHelper.IsWindows;
|
||||
|
||||
[ConditionalFact]
|
||||
[FrameworkSkipCondition(RuntimeFrameworks.Mono)]
|
||||
[OSSkipCondition(OperatingSystems.Linux)]
|
||||
[OSSkipCondition(OperatingSystems.MacOSX)]
|
||||
public void CanCreateUserUsingEF()
|
||||
{
|
||||
using (var db = CreateContext())
|
||||
{
|
||||
var guid = Guid.NewGuid().ToString();
|
||||
db.Users.Add(new IdentityUser { Id = guid, UserName = guid });
|
||||
db.SaveChanges();
|
||||
Assert.True(db.Users.Any(u => u.UserName == guid));
|
||||
Assert.NotNull(db.Users.FirstOrDefault(u => u.UserName == guid));
|
||||
}
|
||||
}
|
||||
|
||||
public IdentityDbContext CreateContext(bool delete = false)
|
||||
{
|
||||
var db = DbUtil.Create<IdentityDbContext>(_fixture.ConnectionString);
|
||||
db.Version = IdentityStoreOptions.Version1_0;
|
||||
if (delete)
|
||||
{
|
||||
db.Database.EnsureDeleted();
|
||||
}
|
||||
db.Database.EnsureCreated();
|
||||
return db;
|
||||
}
|
||||
|
||||
protected override object CreateTestContext()
|
||||
{
|
||||
return CreateContext();
|
||||
}
|
||||
|
||||
protected override void AddUserStore(IServiceCollection services, object context = null)
|
||||
{
|
||||
services.AddSingleton<IUserStore<IdentityUser>>(new UserStoreV1((IdentityDbContext)context));
|
||||
}
|
||||
|
||||
protected override void AddRoleStore(IServiceCollection services, object context = null)
|
||||
{
|
||||
services.AddSingleton<IRoleStore<IdentityRole>>(new RoleStoreV1<IdentityRole, IdentityDbContext, string, IdentityUserRole<string>, IdentityRoleClaim<string>>((IdentityDbContext)context));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SqlUserStoreMethodsThrowWhenDisposedTest()
|
||||
{
|
||||
var store = new UserStore(new IdentityDbContext(new DbContextOptionsBuilder<IdentityDbContext>().Options));
|
||||
store.Dispose();
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.AddClaimsAsync(null, null));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.AddLoginAsync(null, null));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.AddToRoleAsync(null, null));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.GetClaimsAsync(null));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.GetLoginsAsync(null));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.GetRolesAsync(null));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.IsInRoleAsync(null, null));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.RemoveClaimsAsync(null, null));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.RemoveLoginAsync(null, null, null));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(
|
||||
async () => await store.RemoveFromRoleAsync(null, null));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.RemoveClaimsAsync(null, null));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.ReplaceClaimAsync(null, null, null));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.FindByLoginAsync(null, null));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.FindByIdAsync(null));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.FindByNameAsync(null));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.CreateAsync(null));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.UpdateAsync(null));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.DeleteAsync(null));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(
|
||||
async () => await store.SetEmailConfirmedAsync(null, true));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.GetEmailConfirmedAsync(null));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(
|
||||
async () => await store.SetPhoneNumberConfirmedAsync(null, true));
|
||||
await Assert.ThrowsAsync<ObjectDisposedException>(
|
||||
async () => await store.GetPhoneNumberConfirmedAsync(null));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UserStorePublicNullCheckTest()
|
||||
{
|
||||
Assert.Throws<ArgumentNullException>("context", () => new UserStore(null));
|
||||
var store = new UserStore(new IdentityDbContext(new DbContextOptionsBuilder<IdentityDbContext>().Options));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.GetUserIdAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.GetUserNameAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.SetUserNameAsync(null, null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.CreateAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.UpdateAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.DeleteAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.AddClaimsAsync(null, null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.ReplaceClaimAsync(null, null, null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.RemoveClaimsAsync(null, null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.GetClaimsAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.GetLoginsAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.GetRolesAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.AddLoginAsync(null, null));
|
||||
await
|
||||
Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.RemoveLoginAsync(null, null, null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.AddToRoleAsync(null, null));
|
||||
await
|
||||
Assert.ThrowsAsync<ArgumentNullException>("user",
|
||||
async () => await store.RemoveFromRoleAsync(null, null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.IsInRoleAsync(null, null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.GetPasswordHashAsync(null));
|
||||
await
|
||||
Assert.ThrowsAsync<ArgumentNullException>("user",
|
||||
async () => await store.SetPasswordHashAsync(null, null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.GetSecurityStampAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user",
|
||||
async () => await store.SetSecurityStampAsync(null, null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("login", async () => await store.AddLoginAsync(new IdentityUser("fake"), null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("claims",
|
||||
async () => await store.AddClaimsAsync(new IdentityUser("fake"), null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("claims",
|
||||
async () => await store.RemoveClaimsAsync(new IdentityUser("fake"), null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.GetEmailConfirmedAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user",
|
||||
async () => await store.SetEmailConfirmedAsync(null, true));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.GetEmailAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.SetEmailAsync(null, null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.GetPhoneNumberAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.SetPhoneNumberAsync(null, null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user",
|
||||
async () => await store.GetPhoneNumberConfirmedAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user",
|
||||
async () => await store.SetPhoneNumberConfirmedAsync(null, true));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.GetTwoFactorEnabledAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user",
|
||||
async () => await store.SetTwoFactorEnabledAsync(null, true));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.GetAccessFailedCountAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.GetLockoutEnabledAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.SetLockoutEnabledAsync(null, false));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.GetLockoutEndDateAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.SetLockoutEndDateAsync(null, new DateTimeOffset()));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.ResetAccessFailedCountAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.IncrementAccessFailedCountAsync(null));
|
||||
await Assert.ThrowsAsync<ArgumentException>("normalizedRoleName", async () => await store.AddToRoleAsync(new IdentityUser("fake"), null));
|
||||
await Assert.ThrowsAsync<ArgumentException>("normalizedRoleName", async () => await store.RemoveFromRoleAsync(new IdentityUser("fake"), null));
|
||||
await Assert.ThrowsAsync<ArgumentException>("normalizedRoleName", async () => await store.IsInRoleAsync(new IdentityUser("fake"), null));
|
||||
await Assert.ThrowsAsync<ArgumentException>("normalizedRoleName", async () => await store.AddToRoleAsync(new IdentityUser("fake"), ""));
|
||||
await Assert.ThrowsAsync<ArgumentException>("normalizedRoleName", async () => await store.RemoveFromRoleAsync(new IdentityUser("fake"), ""));
|
||||
await Assert.ThrowsAsync<ArgumentException>("normalizedRoleName", async () => await store.IsInRoleAsync(new IdentityUser("fake"), ""));
|
||||
}
|
||||
|
||||
[ConditionalFact]
|
||||
[FrameworkSkipCondition(RuntimeFrameworks.Mono)]
|
||||
[OSSkipCondition(OperatingSystems.Linux)]
|
||||
[OSSkipCondition(OperatingSystems.MacOSX)]
|
||||
public async Task CanCreateUsingManager()
|
||||
{
|
||||
var manager = CreateManager();
|
||||
var guid = Guid.NewGuid().ToString();
|
||||
var user = new IdentityUser { UserName = "New" + guid };
|
||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
||||
IdentityResultAssert.IsSuccess(await manager.DeleteAsync(user));
|
||||
}
|
||||
|
||||
[ConditionalFact]
|
||||
[FrameworkSkipCondition(RuntimeFrameworks.Mono)]
|
||||
[OSSkipCondition(OperatingSystems.Linux)]
|
||||
[OSSkipCondition(OperatingSystems.MacOSX)]
|
||||
public async Task TwoUsersSamePasswordDifferentHash()
|
||||
{
|
||||
var manager = CreateManager();
|
||||
var userA = new IdentityUser(Guid.NewGuid().ToString());
|
||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(userA, "password"));
|
||||
var userB = new IdentityUser(Guid.NewGuid().ToString());
|
||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(userB, "password"));
|
||||
|
||||
Assert.NotEqual(userA.PasswordHash, userB.PasswordHash);
|
||||
}
|
||||
|
||||
[ConditionalFact]
|
||||
[FrameworkSkipCondition(RuntimeFrameworks.Mono)]
|
||||
[OSSkipCondition(OperatingSystems.Linux)]
|
||||
[OSSkipCondition(OperatingSystems.MacOSX)]
|
||||
public async Task AddUserToUnknownRoleFails()
|
||||
{
|
||||
var manager = CreateManager();
|
||||
var u = CreateTestUser();
|
||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(u));
|
||||
await Assert.ThrowsAsync<InvalidOperationException>(
|
||||
async () => await manager.AddToRoleAsync(u, "bogus"));
|
||||
}
|
||||
|
||||
[ConditionalFact]
|
||||
[FrameworkSkipCondition(RuntimeFrameworks.Mono)]
|
||||
[OSSkipCondition(OperatingSystems.Linux)]
|
||||
[OSSkipCondition(OperatingSystems.MacOSX)]
|
||||
public async Task ConcurrentUpdatesWillFail()
|
||||
{
|
||||
var user = CreateTestUser();
|
||||
using (var db = CreateContext())
|
||||
{
|
||||
var manager = CreateManager(db);
|
||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
||||
}
|
||||
using (var db = CreateContext())
|
||||
using (var db2 = CreateContext())
|
||||
{
|
||||
var manager1 = CreateManager(db);
|
||||
var manager2 = CreateManager(db2);
|
||||
var user1 = await manager1.FindByIdAsync(user.Id);
|
||||
var user2 = await manager2.FindByIdAsync(user.Id);
|
||||
Assert.NotNull(user1);
|
||||
Assert.NotNull(user2);
|
||||
Assert.NotSame(user1, user2);
|
||||
user1.UserName = Guid.NewGuid().ToString();
|
||||
user2.UserName = Guid.NewGuid().ToString();
|
||||
IdentityResultAssert.IsSuccess(await manager1.UpdateAsync(user1));
|
||||
IdentityResultAssert.IsFailure(await manager2.UpdateAsync(user2), new IdentityErrorDescriber().ConcurrencyFailure());
|
||||
}
|
||||
}
|
||||
|
||||
[ConditionalFact]
|
||||
[FrameworkSkipCondition(RuntimeFrameworks.Mono)]
|
||||
[OSSkipCondition(OperatingSystems.Linux)]
|
||||
[OSSkipCondition(OperatingSystems.MacOSX)]
|
||||
public async Task ConcurrentUpdatesWillFailWithDetachedUser()
|
||||
{
|
||||
var user = CreateTestUser();
|
||||
using (var db = CreateContext())
|
||||
{
|
||||
var manager = CreateManager(db);
|
||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
||||
}
|
||||
using (var db = CreateContext())
|
||||
using (var db2 = CreateContext())
|
||||
{
|
||||
var manager1 = CreateManager(db);
|
||||
var manager2 = CreateManager(db2);
|
||||
var user2 = await manager2.FindByIdAsync(user.Id);
|
||||
Assert.NotNull(user2);
|
||||
Assert.NotSame(user, user2);
|
||||
user.UserName = Guid.NewGuid().ToString();
|
||||
user2.UserName = Guid.NewGuid().ToString();
|
||||
IdentityResultAssert.IsSuccess(await manager1.UpdateAsync(user));
|
||||
IdentityResultAssert.IsFailure(await manager2.UpdateAsync(user2), new IdentityErrorDescriber().ConcurrencyFailure());
|
||||
}
|
||||
}
|
||||
|
||||
[ConditionalFact]
|
||||
[FrameworkSkipCondition(RuntimeFrameworks.Mono)]
|
||||
[OSSkipCondition(OperatingSystems.Linux)]
|
||||
[OSSkipCondition(OperatingSystems.MacOSX)]
|
||||
public async Task DeleteAModifiedUserWillFail()
|
||||
{
|
||||
var user = CreateTestUser();
|
||||
using (var db = CreateContext())
|
||||
{
|
||||
var manager = CreateManager(db);
|
||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
||||
}
|
||||
using (var db = CreateContext())
|
||||
using (var db2 = CreateContext())
|
||||
{
|
||||
var manager1 = CreateManager(db);
|
||||
var manager2 = CreateManager(db2);
|
||||
var user1 = await manager1.FindByIdAsync(user.Id);
|
||||
var user2 = await manager2.FindByIdAsync(user.Id);
|
||||
Assert.NotNull(user1);
|
||||
Assert.NotNull(user2);
|
||||
Assert.NotSame(user1, user2);
|
||||
user1.UserName = Guid.NewGuid().ToString();
|
||||
IdentityResultAssert.IsSuccess(await manager1.UpdateAsync(user1));
|
||||
IdentityResultAssert.IsFailure(await manager2.DeleteAsync(user2), new IdentityErrorDescriber().ConcurrencyFailure());
|
||||
}
|
||||
}
|
||||
|
||||
[ConditionalFact]
|
||||
[FrameworkSkipCondition(RuntimeFrameworks.Mono)]
|
||||
[OSSkipCondition(OperatingSystems.Linux)]
|
||||
[OSSkipCondition(OperatingSystems.MacOSX)]
|
||||
public async Task ConcurrentRoleUpdatesWillFail()
|
||||
{
|
||||
var role = new IdentityRole(Guid.NewGuid().ToString());
|
||||
using (var db = CreateContext())
|
||||
{
|
||||
var manager = CreateRoleManager(db);
|
||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(role));
|
||||
}
|
||||
using (var db = CreateContext())
|
||||
using (var db2 = CreateContext())
|
||||
{
|
||||
var manager1 = CreateRoleManager(db);
|
||||
var manager2 = CreateRoleManager(db2);
|
||||
var role1 = await manager1.FindByIdAsync(role.Id);
|
||||
var role2 = await manager2.FindByIdAsync(role.Id);
|
||||
Assert.NotNull(role1);
|
||||
Assert.NotNull(role2);
|
||||
Assert.NotSame(role1, role2);
|
||||
role1.Name = Guid.NewGuid().ToString();
|
||||
role2.Name = Guid.NewGuid().ToString();
|
||||
IdentityResultAssert.IsSuccess(await manager1.UpdateAsync(role1));
|
||||
IdentityResultAssert.IsFailure(await manager2.UpdateAsync(role2), new IdentityErrorDescriber().ConcurrencyFailure());
|
||||
}
|
||||
}
|
||||
|
||||
[ConditionalFact]
|
||||
[FrameworkSkipCondition(RuntimeFrameworks.Mono)]
|
||||
[OSSkipCondition(OperatingSystems.Linux)]
|
||||
[OSSkipCondition(OperatingSystems.MacOSX)]
|
||||
public async Task ConcurrentRoleUpdatesWillFailWithDetachedRole()
|
||||
{
|
||||
var role = new IdentityRole(Guid.NewGuid().ToString());
|
||||
using (var db = CreateContext())
|
||||
{
|
||||
var manager = CreateRoleManager(db);
|
||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(role));
|
||||
}
|
||||
using (var db = CreateContext())
|
||||
using (var db2 = CreateContext())
|
||||
{
|
||||
var manager1 = CreateRoleManager(db);
|
||||
var manager2 = CreateRoleManager(db2);
|
||||
var role2 = await manager2.FindByIdAsync(role.Id);
|
||||
Assert.NotNull(role);
|
||||
Assert.NotNull(role2);
|
||||
Assert.NotSame(role, role2);
|
||||
role.Name = Guid.NewGuid().ToString();
|
||||
role2.Name = Guid.NewGuid().ToString();
|
||||
IdentityResultAssert.IsSuccess(await manager1.UpdateAsync(role));
|
||||
IdentityResultAssert.IsFailure(await manager2.UpdateAsync(role2), new IdentityErrorDescriber().ConcurrencyFailure());
|
||||
}
|
||||
}
|
||||
|
||||
[ConditionalFact]
|
||||
[FrameworkSkipCondition(RuntimeFrameworks.Mono)]
|
||||
[OSSkipCondition(OperatingSystems.Linux)]
|
||||
[OSSkipCondition(OperatingSystems.MacOSX)]
|
||||
public async Task DeleteAModifiedRoleWillFail()
|
||||
{
|
||||
var role = new IdentityRole(Guid.NewGuid().ToString());
|
||||
using (var db = CreateContext())
|
||||
{
|
||||
var manager = CreateRoleManager(db);
|
||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(role));
|
||||
}
|
||||
using (var db = CreateContext())
|
||||
using (var db2 = CreateContext())
|
||||
{
|
||||
var manager1 = CreateRoleManager(db);
|
||||
var manager2 = CreateRoleManager(db2);
|
||||
var role1 = await manager1.FindByIdAsync(role.Id);
|
||||
var role2 = await manager2.FindByIdAsync(role.Id);
|
||||
Assert.NotNull(role1);
|
||||
Assert.NotNull(role2);
|
||||
Assert.NotSame(role1, role2);
|
||||
role1.Name = Guid.NewGuid().ToString();
|
||||
IdentityResultAssert.IsSuccess(await manager1.UpdateAsync(role1));
|
||||
IdentityResultAssert.IsFailure(await manager2.DeleteAsync(role2), new IdentityErrorDescriber().ConcurrencyFailure());
|
||||
}
|
||||
}
|
||||
|
||||
protected override IdentityUser CreateTestUser(string namePrefix = "", string email = "", string phoneNumber = "",
|
||||
bool lockoutEnabled = false, DateTimeOffset? lockoutEnd = default(DateTimeOffset?), bool useNamePrefixAsUserName = false)
|
||||
{
|
||||
return new IdentityUser
|
||||
{
|
||||
UserName = useNamePrefixAsUserName ? namePrefix : string.Format("{0}{1}", namePrefix, Guid.NewGuid()),
|
||||
Email = email,
|
||||
PhoneNumber = phoneNumber,
|
||||
LockoutEnabled = lockoutEnabled,
|
||||
LockoutEnd = lockoutEnd
|
||||
};
|
||||
}
|
||||
|
||||
protected override IdentityRole CreateTestRole(string roleNamePrefix = "", bool useRoleNamePrefixAsRoleName = false)
|
||||
{
|
||||
var roleName = useRoleNamePrefixAsRoleName ? roleNamePrefix : string.Format("{0}{1}", roleNamePrefix, Guid.NewGuid());
|
||||
return new IdentityRole(roleName);
|
||||
}
|
||||
|
||||
protected override void SetUserPasswordHash(IdentityUser user, string hashedPassword)
|
||||
{
|
||||
user.PasswordHash = hashedPassword;
|
||||
}
|
||||
|
||||
protected override Expression<Func<IdentityUser, bool>> UserNameEqualsPredicate(string userName) => u => u.UserName == userName;
|
||||
|
||||
protected override Expression<Func<IdentityRole, bool>> RoleNameEqualsPredicate(string roleName) => r => r.Name == roleName;
|
||||
|
||||
protected override Expression<Func<IdentityRole, bool>> RoleNameStartsWithPredicate(string roleName) => r => r.Name.StartsWith(roleName);
|
||||
|
||||
protected override Expression<Func<IdentityUser, bool>> UserNameStartsWithPredicate(string userName) => u => u.UserName.StartsWith(userName);
|
||||
}
|
||||
}
|
||||
|
|
@ -230,12 +230,12 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test
|
|||
{
|
||||
public string LoginContext { get; set; }
|
||||
|
||||
public UserStoreWithGenerics(ContextWithGenerics context, string loginContext) : base(context)
|
||||
public UserStoreWithGenerics(ContextWithGenerics context, string loginContext) : base(context, new IdentityErrorDescriber())
|
||||
{
|
||||
LoginContext = loginContext;
|
||||
}
|
||||
|
||||
protected override IdentityUserRoleWithDate CreateUserRole(IdentityUserWithGenerics user, MyIdentityRole role)
|
||||
public override IdentityUserRoleWithDate CreateUserRole(IdentityUserWithGenerics user, MyIdentityRole role)
|
||||
{
|
||||
return new IdentityUserRoleWithDate()
|
||||
{
|
||||
|
|
@ -245,12 +245,12 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test
|
|||
};
|
||||
}
|
||||
|
||||
protected override IdentityUserClaimWithIssuer CreateUserClaim(IdentityUserWithGenerics user, Claim claim)
|
||||
public override IdentityUserClaimWithIssuer CreateUserClaim(IdentityUserWithGenerics user, Claim claim)
|
||||
{
|
||||
return new IdentityUserClaimWithIssuer { UserId = user.Id, ClaimType = claim.Type, ClaimValue = claim.Value, Issuer = claim.Issuer };
|
||||
}
|
||||
|
||||
protected override IdentityUserLoginWithContext CreateUserLogin(IdentityUserWithGenerics user, UserLoginInfo login)
|
||||
public override IdentityUserLoginWithContext CreateUserLogin(IdentityUserWithGenerics user, UserLoginInfo login)
|
||||
{
|
||||
return new IdentityUserLoginWithContext
|
||||
{
|
||||
|
|
@ -262,7 +262,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test
|
|||
};
|
||||
}
|
||||
|
||||
protected override IdentityUserTokenWithStuff CreateUserToken(IdentityUserWithGenerics user, string loginProvider, string name, string value)
|
||||
public override IdentityUserTokenWithStuff CreateUserToken(IdentityUserWithGenerics user, string loginProvider, string name, string value)
|
||||
{
|
||||
return new IdentityUserTokenWithStuff
|
||||
{
|
||||
|
|
@ -283,7 +283,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test
|
|||
_loginContext = loginContext;
|
||||
}
|
||||
|
||||
protected override IdentityRoleClaimWithIssuer CreateRoleClaim(MyIdentityRole role, Claim claim)
|
||||
public override IdentityRoleClaimWithIssuer CreateRoleClaim(MyIdentityRole role, Claim claim)
|
||||
{
|
||||
return new IdentityRoleClaimWithIssuer { RoleId = role.Id, ClaimType = claim.Type, ClaimValue = claim.Value, Issuer = claim.Issuer };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,8 @@ namespace Microsoft.AspNetCore.Identity.InMemory
|
|||
IRoleClaimStore<TRole>,
|
||||
IUserAuthenticationTokenStore<TUser>,
|
||||
IUserAuthenticatorKeyStore<TUser>,
|
||||
IUserTwoFactorRecoveryCodeStore<TUser>
|
||||
IUserTwoFactorRecoveryCodeStore<TUser>,
|
||||
IUserActivityStore<TUser>
|
||||
where TRole : TestRole
|
||||
where TUser : TestUser
|
||||
{
|
||||
|
|
@ -583,6 +584,39 @@ namespace Microsoft.AspNetCore.Identity.InMemory
|
|||
return false;
|
||||
}
|
||||
|
||||
public Task SetLastPasswordChangeDateAsync(TUser user, DateTimeOffset? changeDate, CancellationToken cancellationToken)
|
||||
{
|
||||
user.LastPasswordChangeDate = changeDate;
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task<DateTimeOffset?> GetLastPasswordChangeDateAsync(TUser user, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(user.LastPasswordChangeDate);
|
||||
}
|
||||
|
||||
public Task SetCreateDateAsync(TUser user, DateTimeOffset? creationDate, CancellationToken cancellationToken)
|
||||
{
|
||||
user.CreateDate = creationDate;
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task<DateTimeOffset?> GetCreateDateAsync(TUser user, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(user.CreateDate);
|
||||
}
|
||||
|
||||
public Task SetLastSignInDateAsync(TUser user, DateTimeOffset? lastSignIn, CancellationToken cancellationToken)
|
||||
{
|
||||
user.LastSignInDate = lastSignIn;
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task<DateTimeOffset?> GetLastSignInDateAsync(TUser user, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(user.LastSignInDate);
|
||||
}
|
||||
|
||||
public IQueryable<TRole> Roles
|
||||
{
|
||||
get { return _roles.Values.AsQueryable(); }
|
||||
|
|
|
|||
|
|
@ -27,4 +27,7 @@
|
|||
<PackageReference Include="xunit.runner.visualstudio" Version="$(XunitVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -197,7 +197,7 @@ namespace Microsoft.AspNetCore.Identity.Test
|
|||
var signInManager = new Mock<SignInManager<TestUser>>(userManager.Object,
|
||||
contextAccessor.Object, claimsManager.Object, identityOptions.Object, null, new Mock<IAuthenticationSchemeProvider>().Object);
|
||||
signInManager.Setup(s => s.ValidateSecurityStampAsync(It.IsAny<ClaimsPrincipal>())).Throws(new Exception("Shouldn't be called"));
|
||||
signInManager.Setup(s => s.SignInAsync(user, false, null)).Throws(new Exception("Shouldn't be called"));
|
||||
signInManager.Setup(s => s.SignInAsync(user, false, null, It.IsAny<bool>())).Throws(new Exception("Shouldn't be called"));
|
||||
var services = new ServiceCollection();
|
||||
services.AddSingleton(options.Object);
|
||||
services.AddSingleton(signInManager.Object);
|
||||
|
|
|
|||
|
|
@ -202,6 +202,8 @@ namespace Microsoft.AspNetCore.Identity.Test
|
|||
manager.Setup(m => m.SupportsUserLockout).Returns(true).Verifiable();
|
||||
manager.Setup(m => m.IsLockedOutAsync(user)).ReturnsAsync(false).Verifiable();
|
||||
manager.Setup(m => m.CheckPasswordAsync(user, "password")).ReturnsAsync(true).Verifiable();
|
||||
manager.Setup(m => m.SupportsUserActivity).Returns(true).Verifiable();
|
||||
manager.Setup(m => m.UpdateLastSignInDateAsync(user)).ReturnsAsync(IdentityResult.Success).Verifiable();
|
||||
|
||||
var context = new DefaultHttpContext();
|
||||
var auth = MockAuth(context);
|
||||
|
|
@ -226,6 +228,8 @@ namespace Microsoft.AspNetCore.Identity.Test
|
|||
manager.Setup(m => m.SupportsUserLockout).Returns(true).Verifiable();
|
||||
manager.Setup(m => m.IsLockedOutAsync(user)).ReturnsAsync(false).Verifiable();
|
||||
manager.Setup(m => m.CheckPasswordAsync(user, "password")).ReturnsAsync(true).Verifiable();
|
||||
manager.Setup(m => m.SupportsUserActivity).Returns(true).Verifiable();
|
||||
manager.Setup(m => m.UpdateLastSignInDateAsync(user)).ReturnsAsync(IdentityResult.Success).Verifiable();
|
||||
|
||||
var context = new DefaultHttpContext();
|
||||
var auth = MockAuth(context);
|
||||
|
|
@ -252,6 +256,8 @@ namespace Microsoft.AspNetCore.Identity.Test
|
|||
manager.Setup(m => m.IsLockedOutAsync(user)).ReturnsAsync(false).Verifiable();
|
||||
manager.Setup(m => m.CheckPasswordAsync(user, "password")).ReturnsAsync(true).Verifiable();
|
||||
manager.Setup(m => m.ResetAccessFailedCountAsync(user)).ReturnsAsync(IdentityResult.Success).Verifiable();
|
||||
manager.Setup(m => m.SupportsUserActivity).Returns(true).Verifiable();
|
||||
manager.Setup(m => m.UpdateLastSignInDateAsync(user)).ReturnsAsync(IdentityResult.Success).Verifiable();
|
||||
|
||||
var context = new DefaultHttpContext();
|
||||
var auth = MockAuth(context);
|
||||
|
|
@ -375,6 +381,8 @@ namespace Microsoft.AspNetCore.Identity.Test
|
|||
manager.Setup(m => m.SupportsUserLockout).Returns(true).Verifiable();
|
||||
manager.Setup(m => m.VerifyTwoFactorTokenAsync(user, providerName ?? TokenOptions.DefaultAuthenticatorProvider, code)).ReturnsAsync(true).Verifiable();
|
||||
manager.Setup(m => m.ResetAccessFailedCountAsync(user)).ReturnsAsync(IdentityResult.Success).Verifiable();
|
||||
manager.Setup(m => m.SupportsUserActivity).Returns(true).Verifiable();
|
||||
manager.Setup(m => m.UpdateLastSignInDateAsync(user)).ReturnsAsync(IdentityResult.Success).Verifiable();
|
||||
|
||||
var context = new DefaultHttpContext();
|
||||
var auth = MockAuth(context);
|
||||
|
|
@ -419,6 +427,8 @@ namespace Microsoft.AspNetCore.Identity.Test
|
|||
var manager = SetupUserManager(user);
|
||||
manager.Setup(m => m.SupportsUserLockout).Returns(supportsLockout).Verifiable();
|
||||
manager.Setup(m => m.RedeemTwoFactorRecoveryCodeAsync(user, bypassCode)).ReturnsAsync(IdentityResult.Success).Verifiable();
|
||||
manager.Setup(m => m.SupportsUserActivity).Returns(true).Verifiable();
|
||||
manager.Setup(m => m.UpdateLastSignInDateAsync(user)).ReturnsAsync(IdentityResult.Success).Verifiable();
|
||||
if (supportsLockout)
|
||||
{
|
||||
manager.Setup(m => m.ResetAccessFailedCountAsync(user)).ReturnsAsync(IdentityResult.Success).Verifiable();
|
||||
|
|
@ -456,11 +466,15 @@ namespace Microsoft.AspNetCore.Identity.Test
|
|||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true, true)]
|
||||
[InlineData(true, false)]
|
||||
[InlineData(false, true)]
|
||||
[InlineData(false, false)]
|
||||
public async Task CanExternalSignIn(bool isPersistent, bool supportsLockout)
|
||||
[InlineData(true, true, true)]
|
||||
[InlineData(true, false, true)]
|
||||
[InlineData(false, true, true)]
|
||||
[InlineData(false, false, true)]
|
||||
[InlineData(true, true, false)]
|
||||
[InlineData(true, false, false)]
|
||||
[InlineData(false, true, false)]
|
||||
[InlineData(false, false, false)]
|
||||
public async Task CanExternalSignIn(bool isPersistent, bool supportsLockout, bool supportsActivity)
|
||||
{
|
||||
// Setup
|
||||
var user = new TestUser { UserName = "Foo" };
|
||||
|
|
@ -473,6 +487,11 @@ namespace Microsoft.AspNetCore.Identity.Test
|
|||
manager.Setup(m => m.IsLockedOutAsync(user)).ReturnsAsync(false).Verifiable();
|
||||
}
|
||||
manager.Setup(m => m.FindByLoginAsync(loginProvider, providerKey)).ReturnsAsync(user).Verifiable();
|
||||
if (supportsActivity)
|
||||
{
|
||||
manager.Setup(m => m.SupportsUserActivity).Returns(true).Verifiable();
|
||||
manager.Setup(m => m.UpdateLastSignInDateAsync(user)).ReturnsAsync(IdentityResult.Success).Verifiable();
|
||||
}
|
||||
|
||||
var context = new DefaultHttpContext();
|
||||
var auth = MockAuth(context);
|
||||
|
|
@ -518,8 +537,7 @@ namespace Microsoft.AspNetCore.Identity.Test
|
|||
{ CallBase = true };
|
||||
//signInManager.Setup(s => s.SignInAsync(user, It.Is<AuthenticationProperties>(p => p.IsPersistent == isPersistent),
|
||||
//externalLogin? loginProvider : null)).Returns(Task.FromResult(0)).Verifiable();
|
||||
signInManager.Setup(s => s.SignInAsync(user, It.IsAny<AuthenticationProperties>(), null)).Returns(Task.FromResult(0)).Verifiable();
|
||||
signInManager.Object.Context = context;
|
||||
signInManager.Setup(s => s.SignInAsync(user, It.IsAny<AuthenticationProperties>(), /*authmethod*/null, /*updateLastSignIn*/false)).Returns(Task.FromResult(0)).Verifiable();
|
||||
|
||||
// Act
|
||||
await signInManager.Object.RefreshSignInAsync(user);
|
||||
|
|
@ -530,23 +548,39 @@ namespace Microsoft.AspNetCore.Identity.Test
|
|||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true, true, true, true)]
|
||||
[InlineData(true, true, false, true)]
|
||||
[InlineData(true, false, true, true)]
|
||||
[InlineData(true, false, false, true)]
|
||||
[InlineData(false, true, true, true)]
|
||||
[InlineData(false, true, false, true)]
|
||||
[InlineData(false, false, true, true)]
|
||||
[InlineData(false, false, false, true)]
|
||||
[InlineData(true, true, true, false)]
|
||||
[InlineData(true, true, false, false)]
|
||||
[InlineData(true, false, true, false)]
|
||||
[InlineData(true, false, false, false)]
|
||||
[InlineData(false, true, true, false)]
|
||||
[InlineData(false, true, false, false)]
|
||||
[InlineData(false, false, true, false)]
|
||||
[InlineData(false, false, false, false)]
|
||||
public async Task CanTwoFactorSignIn(bool isPersistent, bool supportsLockout, bool externalLogin, bool rememberClient)
|
||||
[InlineData(true, true, true, true, true)]
|
||||
[InlineData(true, true, false, true, true)]
|
||||
[InlineData(true, false, true, true, true)]
|
||||
[InlineData(true, false, false, true, true)]
|
||||
[InlineData(false, true, true, true, true)]
|
||||
[InlineData(false, true, false, true, true)]
|
||||
[InlineData(false, false, true, true, true)]
|
||||
[InlineData(false, false, false, true, true)]
|
||||
[InlineData(true, true, true, false, true)]
|
||||
[InlineData(true, true, false, false, true)]
|
||||
[InlineData(true, false, true, false, true)]
|
||||
[InlineData(true, false, false, false, true)]
|
||||
[InlineData(false, true, true, false, true)]
|
||||
[InlineData(false, true, false, false, true)]
|
||||
[InlineData(false, false, true, false, true)]
|
||||
[InlineData(false, false, false, false, true)]
|
||||
[InlineData(true, true, true, true, false)]
|
||||
[InlineData(true, true, false, true, false)]
|
||||
[InlineData(true, false, true, true, false)]
|
||||
[InlineData(true, false, false, true, false)]
|
||||
[InlineData(false, true, true, true, false)]
|
||||
[InlineData(false, true, false, true, false)]
|
||||
[InlineData(false, false, true, true, false)]
|
||||
[InlineData(false, false, false, true, false)]
|
||||
[InlineData(true, true, true, false, false)]
|
||||
[InlineData(true, true, false, false, false)]
|
||||
[InlineData(true, false, true, false, false)]
|
||||
[InlineData(true, false, false, false, false)]
|
||||
[InlineData(false, true, true, false, false)]
|
||||
[InlineData(false, true, false, false, false)]
|
||||
[InlineData(false, false, true, false, false)]
|
||||
[InlineData(false, false, false, false, false)]
|
||||
public async Task CanTwoFactorSignIn(bool isPersistent, bool supportsLockout, bool externalLogin, bool rememberClient, bool supportsUserActivity)
|
||||
{
|
||||
// Setup
|
||||
var user = new TestUser { UserName = "Foo" };
|
||||
|
|
@ -559,6 +593,11 @@ namespace Microsoft.AspNetCore.Identity.Test
|
|||
manager.Setup(m => m.IsLockedOutAsync(user)).ReturnsAsync(false).Verifiable();
|
||||
manager.Setup(m => m.ResetAccessFailedCountAsync(user)).ReturnsAsync(IdentityResult.Success).Verifiable();
|
||||
}
|
||||
if (supportsUserActivity)
|
||||
{
|
||||
manager.Setup(m => m.SupportsUserActivity).Returns(true).Verifiable();
|
||||
manager.Setup(m => m.UpdateLastSignInDateAsync(user)).ReturnsAsync(IdentityResult.Success).Verifiable();
|
||||
}
|
||||
manager.Setup(m => m.VerifyTwoFactorTokenAsync(user, provider, code)).ReturnsAsync(true).Verifiable();
|
||||
var context = new DefaultHttpContext();
|
||||
var auth = MockAuth(context);
|
||||
|
|
|
|||
|
|
@ -580,6 +580,17 @@ namespace Microsoft.AspNetCore.Identity.Test
|
|||
Assert.Throws<NotSupportedException>(() => manager.Users.Count());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UsersActivityMethodsFailWhenStoreNotImplemented()
|
||||
{
|
||||
var manager = MockHelpers.TestUserManager(new NoopUserStore());
|
||||
Assert.False(manager.SupportsUserActivity);
|
||||
await Assert.ThrowsAsync<NotSupportedException>(() => manager.UpdateLastSignInDateAsync(null));
|
||||
await Assert.ThrowsAsync<NotSupportedException>(() => manager.GetLastSignInDateAsync(null));
|
||||
await Assert.ThrowsAsync<NotSupportedException>(() => manager.GetLastPasswordChangeDateAsync(null));
|
||||
await Assert.ThrowsAsync<NotSupportedException>(() => manager.GetCreateDateAsync(null));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UsersEmailMethodsFailWhenStoreNotImplemented()
|
||||
{
|
||||
|
|
@ -698,146 +709,6 @@ namespace Microsoft.AspNetCore.Identity.Test
|
|||
Assert.ThrowsAsync<NotImplementedException>(() => manager.GenerateUserTokenAsync(new TestUser(), "A", "purpose"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TOTPTest()
|
||||
{
|
||||
//var verify = new TotpAuthenticatorVerification();
|
||||
//var secret = "abcdefghij";
|
||||
//var secret = Base32.FromBase32(authKey);
|
||||
|
||||
// Assert.Equal(bytes, secret);
|
||||
|
||||
//var code = verify.VerifyCode(secret, -1);
|
||||
//Assert.Equal(code, 287004);
|
||||
|
||||
|
||||
//var bytes = new byte[] { (byte)'H', (byte)'e', (byte)'l', (byte)'l', (byte)'o', (byte)'!', (byte)0xDE, (byte)0xAD, (byte)0xBE, (byte)0xEF };
|
||||
//var base32 = Base32.ToBase32(bytes);
|
||||
// var code = Rfc6238AuthenticationService.GenerateCode(bytes);
|
||||
// Assert.Equal(Rfc6238AuthenticationService.GenerateCode(bytes), Rfc6238AuthenticationService.CalculateOneTimePassword(new HMACSHA1(bytes)));
|
||||
//Assert.True(Rfc6238AuthenticationService.ValidateCode(bytes, code));
|
||||
}
|
||||
|
||||
public static byte[] ToBytes(string input)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input))
|
||||
{
|
||||
throw new ArgumentNullException("input");
|
||||
}
|
||||
|
||||
input = input.TrimEnd('='); //remove padding characters
|
||||
int byteCount = input.Length * 5 / 8; //this must be TRUNCATED
|
||||
byte[] returnArray = new byte[byteCount];
|
||||
|
||||
byte curByte = 0, bitsRemaining = 8;
|
||||
int mask = 0, arrayIndex = 0;
|
||||
|
||||
foreach (char c in input)
|
||||
{
|
||||
int cValue = CharToValue(c);
|
||||
|
||||
if (bitsRemaining > 5)
|
||||
{
|
||||
mask = cValue << (bitsRemaining - 5);
|
||||
curByte = (byte)(curByte | mask);
|
||||
bitsRemaining -= 5;
|
||||
}
|
||||
else
|
||||
{
|
||||
mask = cValue >> (5 - bitsRemaining);
|
||||
curByte = (byte)(curByte | mask);
|
||||
returnArray[arrayIndex++] = curByte;
|
||||
curByte = (byte)(cValue << (3 + bitsRemaining));
|
||||
bitsRemaining += 3;
|
||||
}
|
||||
}
|
||||
|
||||
//if we didn't end with a full byte
|
||||
if (arrayIndex != byteCount)
|
||||
{
|
||||
returnArray[arrayIndex] = curByte;
|
||||
}
|
||||
|
||||
return returnArray;
|
||||
}
|
||||
|
||||
public static string ToString(byte[] input)
|
||||
{
|
||||
if (input == null || input.Length == 0)
|
||||
{
|
||||
throw new ArgumentNullException("input");
|
||||
}
|
||||
|
||||
int charCount = (int)Math.Ceiling(input.Length / 5d) * 8;
|
||||
char[] returnArray = new char[charCount];
|
||||
|
||||
byte nextChar = 0, bitsRemaining = 5;
|
||||
int arrayIndex = 0;
|
||||
|
||||
foreach (byte b in input)
|
||||
{
|
||||
nextChar = (byte)(nextChar | (b >> (8 - bitsRemaining)));
|
||||
returnArray[arrayIndex++] = ValueToChar(nextChar);
|
||||
|
||||
if (bitsRemaining < 4)
|
||||
{
|
||||
nextChar = (byte)((b >> (3 - bitsRemaining)) & 31);
|
||||
returnArray[arrayIndex++] = ValueToChar(nextChar);
|
||||
bitsRemaining += 5;
|
||||
}
|
||||
|
||||
bitsRemaining -= 3;
|
||||
nextChar = (byte)((b << bitsRemaining) & 31);
|
||||
}
|
||||
|
||||
//if we didn't end with a full char
|
||||
if (arrayIndex != charCount)
|
||||
{
|
||||
returnArray[arrayIndex++] = ValueToChar(nextChar);
|
||||
while (arrayIndex != charCount) returnArray[arrayIndex++] = '='; //padding
|
||||
}
|
||||
|
||||
return new string(returnArray);
|
||||
}
|
||||
|
||||
private static int CharToValue(char c)
|
||||
{
|
||||
var value = (int)c;
|
||||
|
||||
//65-90 == uppercase letters
|
||||
if (value < 91 && value > 64)
|
||||
{
|
||||
return value - 65;
|
||||
}
|
||||
//50-55 == numbers 2-7
|
||||
if (value < 56 && value > 49)
|
||||
{
|
||||
return value - 24;
|
||||
}
|
||||
//97-122 == lowercase letters
|
||||
if (value < 123 && value > 96)
|
||||
{
|
||||
return value - 97;
|
||||
}
|
||||
|
||||
throw new ArgumentException("Character is not a Base32 character.", "c");
|
||||
}
|
||||
|
||||
private static char ValueToChar(byte b)
|
||||
{
|
||||
if (b < 26)
|
||||
{
|
||||
return (char)(b + 65);
|
||||
}
|
||||
|
||||
if (b < 32)
|
||||
{
|
||||
return (char)(b + 24);
|
||||
}
|
||||
|
||||
throw new ArgumentException("Byte is not a value Base32 value.", "b");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UserManagerWillUseTokenProviderInstanceOverDefaults()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -124,6 +124,10 @@ namespace Microsoft.AspNetCore.Identity.Test
|
|||
/// </summary>
|
||||
public virtual int AccessFailedCount { get; set; }
|
||||
|
||||
public virtual DateTimeOffset? CreateDate { get; set; }
|
||||
public virtual DateTimeOffset? LastSignInDate { get; set; }
|
||||
public virtual DateTimeOffset? LastPasswordChangeDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Navigation property
|
||||
/// </summary>
|
||||
|
|
|
|||
Loading…
Reference in New Issue