diff --git a/samples/IdentitySample.Mvc/Properties/launchSettings.json b/samples/IdentitySample.Mvc/Properties/launchSettings.json
new file mode 100644
index 0000000000..cf11afc04f
--- /dev/null
+++ b/samples/IdentitySample.Mvc/Properties/launchSettings.json
@@ -0,0 +1,19 @@
+{
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:28248/",
+ "sslPort": 0
+ }
+ },
+ "profiles": {
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNET_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/IdentityDbContext.cs b/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/IdentityDbContext.cs
index 038587f329..af1b1b24fa 100644
--- a/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/IdentityDbContext.cs
+++ b/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/IdentityDbContext.cs
@@ -3,7 +3,6 @@
using System;
using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
{
@@ -52,7 +51,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
/// The type of user objects.
/// The type of role objects.
/// The type of the primary key for users and roles.
- public class IdentityDbContext : DbContext
+ public class IdentityDbContext : IdentityDbContext, IdentityUserRole, IdentityUserLogin, IdentityRoleClaim, IdentityUserToken>
where TUser : IdentityUser
where TRole : IdentityRole
where TKey : IEquatable
@@ -69,6 +68,41 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
///
protected IdentityDbContext()
{ }
+ }
+
+ ///
+ /// Base class for the Entity Framework database context used for identity.
+ ///
+ /// The type of user objects.
+ /// The type of role objects.
+ /// The type of the primary key for users and roles.
+ /// The type of the user claim object.
+ /// The type of the user role object.
+ /// The type of the user login object.
+ /// The type of the role claim object.
+ /// The type of the user token object.
+ public abstract class IdentityDbContext : DbContext
+ where TUser : IdentityUser
+ where TRole : IdentityRole
+ where TKey : IEquatable
+ where TUserClaim : IdentityUserClaim
+ where TUserRole : IdentityUserRole
+ where TUserLogin : IdentityUserLogin
+ where TRoleClaim : IdentityRoleClaim
+ where TUserToken : IdentityUserToken
+ {
+ ///
+ /// Initializes a new instance of .
+ ///
+ /// The options to be used by a .
+ public IdentityDbContext(DbContextOptions options) : base(options)
+ { }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ protected IdentityDbContext()
+ { }
///
/// Gets or sets the of Users.
@@ -78,22 +112,22 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
///
/// Gets or sets the of User claims.
///
- public DbSet> UserClaims { get; set; }
+ public DbSet UserClaims { get; set; }
///
/// Gets or sets the of User logins.
///
- public DbSet> UserLogins { get; set; }
+ public DbSet UserLogins { get; set; }
///
/// Gets or sets the of User roles.
///
- public DbSet> UserRoles { get; set; }
+ public DbSet UserRoles { get; set; }
///
/// Gets or sets the of User tokens.
///
- public DbSet> UserTokens { get; set; }
+ public DbSet UserTokens { get; set; }
///
/// Gets or sets the of roles.
@@ -103,7 +137,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
///
/// Gets or sets the of role claims.
///
- public DbSet> RoleClaims { get; set; }
+ public DbSet RoleClaims { get; set; }
///
/// Configures the schema needed for the identity framework.
@@ -144,31 +178,31 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
b.HasMany(r => r.Claims).WithOne().HasForeignKey(rc => rc.RoleId).IsRequired();
});
- builder.Entity>(b =>
+ builder.Entity(b =>
{
b.HasKey(uc => uc.Id);
b.ToTable("AspNetUserClaims");
});
- builder.Entity>(b =>
+ builder.Entity(b =>
{
b.HasKey(rc => rc.Id);
b.ToTable("AspNetRoleClaims");
});
- builder.Entity>(b =>
+ builder.Entity(b =>
{
b.HasKey(r => new { r.UserId, r.RoleId });
b.ToTable("AspNetUserRoles");
});
- builder.Entity>(b =>
+ builder.Entity(b =>
{
b.HasKey(l => new { l.LoginProvider, l.ProviderKey });
b.ToTable("AspNetUserLogins");
});
- builder.Entity>(b =>
+ builder.Entity(b =>
{
b.HasKey(l => new { l.UserId, l.LoginProvider, l.Name });
b.ToTable("AspNetUserTokens");
diff --git a/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/IdentityEntityFrameworkBuilderExtensions.cs b/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/IdentityEntityFrameworkBuilderExtensions.cs
index e97780fd31..8a5476d305 100644
--- a/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/IdentityEntityFrameworkBuilderExtensions.cs
+++ b/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/IdentityEntityFrameworkBuilderExtensions.cs
@@ -43,16 +43,9 @@ namespace Microsoft.Extensions.DependencyInjection
{
Type userStoreType;
Type roleStoreType;
- if (keyType != null)
- {
- userStoreType = typeof(UserStore<,,,>).MakeGenericType(userType, roleType, contextType, keyType);
- roleStoreType = typeof(RoleStore<,,>).MakeGenericType(roleType, contextType, keyType);
- }
- else
- {
- userStoreType = typeof(UserStore<,,>).MakeGenericType(userType, roleType, contextType);
- roleStoreType = typeof(RoleStore<,>).MakeGenericType(roleType, contextType);
- }
+ keyType = keyType ?? typeof(string);
+ userStoreType = typeof(UserStore<,,,>).MakeGenericType(userType, roleType, contextType, keyType);
+ roleStoreType = typeof(RoleStore<,,>).MakeGenericType(roleType, contextType, keyType);
var services = new ServiceCollection();
services.AddScoped(
diff --git a/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/IdentityRole.cs b/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/IdentityRole.cs
index 105a1d5419..7498e8ce4a 100644
--- a/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/IdentityRole.cs
+++ b/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/IdentityRole.cs
@@ -39,7 +39,21 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
/// Represents a role in the identity system
///
/// The type used for the primary key for the role.
- public class IdentityRole where TKey : IEquatable
+ public class IdentityRole : IdentityRole, IdentityRoleClaim>
+ where TKey : IEquatable
+ {
+ }
+
+ ///
+ /// Represents a role in the identity system
+ ///
+ /// The type used for the primary key for the role.
+ /// The type used for user roles.
+ /// The type used for role claims.
+ public class IdentityRole
+ where TKey : IEquatable
+ where TUserRole : IdentityUserRole
+ where TRoleClaim : IdentityRoleClaim
{
///
/// Initializes a new instance of .
@@ -58,12 +72,12 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
///
/// Navigation property for the users in this role.
///
- public virtual ICollection> Users { get; } = new List>();
+ public virtual ICollection Users { get; } = new List();
///
/// Navigation property for claims in this role.
///
- public virtual ICollection> Claims { get; } = new List>();
+ public virtual ICollection Claims { get; } = new List();
///
/// Gets or sets the primary key for this role.
diff --git a/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/IdentityRoleClaim.cs b/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/IdentityRoleClaim.cs
index 063abb1e9c..d9a6672b0b 100644
--- a/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/IdentityRoleClaim.cs
+++ b/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/IdentityRoleClaim.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
+using System.Security.Claims;
namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
{
@@ -30,5 +31,16 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
/// Gets or sets the claim value for this claim.
///
public virtual string ClaimValue { get; set; }
+
+ public virtual Claim ToClaim()
+ {
+ return new Claim(ClaimType, ClaimValue);
+ }
+
+ public virtual void InitializeFromClaim(Claim other)
+ {
+ ClaimType = other?.Type;
+ ClaimValue = other?.Value;
+ }
}
}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/IdentityUser.cs b/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/IdentityUser.cs
index 8dd5ee7123..2341b8a0cb 100644
--- a/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/IdentityUser.cs
+++ b/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/IdentityUser.cs
@@ -39,7 +39,18 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
/// Represents a user in the identity system
///
/// The type used for the primary key for the user.
- public class IdentityUser where TKey : IEquatable
+ public class IdentityUser : IdentityUser, IdentityUserRole, IdentityUserLogin>
+ where TKey : IEquatable
+ { }
+
+ ///
+ /// Represents a user in the identity system
+ ///
+ /// The type used for the primary key for the user.
+ /// The type representing a claim.
+ /// The type representing a user role.
+ /// The type representing a user external login.
+ public class IdentityUser where TKey : IEquatable
{
///
/// Initializes a new instance of .
@@ -140,17 +151,17 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
///
/// Navigation property for the roles this user belongs to.
///
- public virtual ICollection> Roles { get; } = new List>();
+ public virtual ICollection Roles { get; } = new List();
///
/// Navigation property for the claims this user possesses.
///
- public virtual ICollection> Claims { get; } = new List>();
+ public virtual ICollection Claims { get; } = new List();
///
/// Navigation property for this users login accounts.
///
- public virtual ICollection> Logins { get; } = new List>();
+ public virtual ICollection Logins { get; } = new List();
///
/// Returns the username for this user.
diff --git a/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/IdentityUserClaim.cs b/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/IdentityUserClaim.cs
index 26a7c7bbb6..2edeb6b33d 100644
--- a/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/IdentityUserClaim.cs
+++ b/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/IdentityUserClaim.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
+using System.Security.Claims;
namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
{
@@ -30,5 +31,24 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
/// Gets or sets the claim value for this claim.
///
public virtual string ClaimValue { get; set; }
+
+ ///
+ /// Converts the entity into a Claim instance.
+ ///
+ ///
+ public virtual Claim ToClaim()
+ {
+ return new Claim(ClaimType, ClaimValue);
+ }
+
+ ///
+ /// Reads the type and value from the Claim.
+ ///
+ ///
+ public virtual void InitializeFromClaim(Claim claim)
+ {
+ ClaimType = claim.Type;
+ ClaimValue = claim.Value;
+ }
}
}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/RoleStore.cs b/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/RoleStore.cs
index e69bb94fc7..60a40af476 100644
--- a/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/RoleStore.cs
+++ b/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/RoleStore.cs
@@ -40,12 +40,39 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
/// The type of the class representing a role.
/// The type of the data context class used to access the store.
/// The type of the primary key for a role.
- public class RoleStore :
+ public class RoleStore : RoleStore, IdentityRoleClaim>,
IQueryableRoleStore,
IRoleClaimStore
where TRole : IdentityRole
where TKey : IEquatable
where TContext : DbContext
+ {
+ public RoleStore(TContext context, IdentityErrorDescriber describer = null) : base(context, describer)
+ {
+ }
+
+ protected override IdentityRoleClaim CreateRoleClaim(TRole role, Claim claim)
+ {
+ return new IdentityRoleClaim { RoleId = role.Id, ClaimType = claim.Type, ClaimValue = claim.Value };
+ }
+ }
+
+ ///
+ /// Creates a new instance of a persistence store for roles.
+ ///
+ /// The type of the class representing a role.
+ /// The type of the data context class used to access the store.
+ /// The type of the primary key for a role.
+ /// The type of the class representing a user role.
+ /// The type of the class representing a role claim.
+ public abstract class RoleStore :
+ IQueryableRoleStore,
+ IRoleClaimStore
+ where TRole : IdentityRole
+ where TKey : IEquatable
+ where TContext : DbContext
+ where TUserRole : IdentityUserRole
+ where TRoleClaim : IdentityRoleClaim
{
public RoleStore(TContext context, IdentityErrorDescriber describer = null)
{
@@ -358,8 +385,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
throw new ArgumentNullException(nameof(claim));
}
- RoleClaims.Add(new IdentityRoleClaim { RoleId = role.Id, ClaimType = claim.Type, ClaimValue = claim.Value });
-
+ RoleClaims.Add(CreateRoleClaim(role, claim));
return Task.FromResult(false);
}
@@ -396,6 +422,14 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
get { return Context.Set(); }
}
- private DbSet> RoleClaims { get { return Context.Set>(); } }
+ private DbSet RoleClaims { get { return Context.Set(); } }
+
+ ///
+ /// Creates a entity representing a role claim.
+ ///
+ ///
+ ///
+ ///
+ protected abstract TRoleClaim CreateRoleClaim(TRole role, Claim claim);
}
}
diff --git a/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/UserStore.cs b/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/UserStore.cs
index 766ab658de..1300eac368 100644
--- a/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/UserStore.cs
+++ b/src/Microsoft.AspNetCore.Identity.EntityFrameworkCore/UserStore.cs
@@ -14,7 +14,7 @@ using Microsoft.EntityFrameworkCore;
namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
{
///
- /// Creates a new instance of a persistence store for users, using the default implementation
+ /// Represents a new instance of a persistence store for users, using the default implementation
/// of with a string as a primary key.
///
public class UserStore : UserStore>
@@ -26,25 +26,78 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
/// Creates a new instance of a persistence store for the specified user type.
///
/// The type representing a user.
- public class UserStore : UserStore
+ public class UserStore : UserStore
where TUser : IdentityUser, new()
{
public UserStore(DbContext context, IdentityErrorDescriber describer = null) : base(context, describer) { }
}
///
- /// Creates a new instance of a persistence store for the specified user and role types.
+ /// Represents a new instance of a persistence store for the specified user and role types.
///
/// The type representing a user.
/// The type representing a role.
/// The type of the data context class used to access the store.
public class UserStore : UserStore
- where TUser : IdentityUser, new()
- where TRole : IdentityRole, new()
+ where TUser : IdentityUser
+ where TRole : IdentityRole
where TContext : DbContext
{
public UserStore(TContext context, IdentityErrorDescriber describer = null) : base(context, describer) { }
}
+ ///
+ /// Represents a new instance of a persistence store for the specified user and role types.
+ ///
+ /// The type representing a user.
+ /// The type representing a role.
+ /// The type of the data context class used to access the store.
+ /// The type of the primary key for a role.
+ public class UserStore : UserStore, IdentityUserRole, IdentityUserLogin, IdentityUserToken>
+ where TUser : IdentityUser
+ where TRole : IdentityRole
+ where TContext : DbContext
+ where TKey : IEquatable
+ {
+ public UserStore(TContext context, IdentityErrorDescriber describer = null) : base(context, describer) { }
+
+ protected override IdentityUserRole CreateUserRole(TUser user, TRole role)
+ {
+ return new IdentityUserRole()
+ {
+ UserId = user.Id,
+ RoleId = role.Id
+ };
+ }
+
+ protected override IdentityUserClaim CreateUserClaim(TUser user, Claim claim)
+ {
+ var userClaim = new IdentityUserClaim { UserId = user.Id };
+ userClaim.InitializeFromClaim(claim);
+ return userClaim;
+ }
+
+ protected override IdentityUserLogin CreateUserLogin(TUser user, UserLoginInfo login)
+ {
+ return new IdentityUserLogin
+ {
+ UserId = user.Id,
+ ProviderKey = login.ProviderKey,
+ LoginProvider = login.LoginProvider,
+ ProviderDisplayName = login.ProviderDisplayName
+ };
+ }
+
+ protected override IdentityUserToken CreateUserToken(TUser user, string loginProvider, string name, string value)
+ {
+ return new IdentityUserToken
+ {
+ UserId = user.Id,
+ LoginProvider = loginProvider,
+ Name = name,
+ Value = value
+ };
+ }
+ }
///
/// Represents a new instance of a persistence store for the specified user and role types.
@@ -53,7 +106,11 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
/// The type representing a role.
/// The type of the data context class used to access the store.
/// The type of the primary key for a role.
- public class UserStore :
+ /// The type representing a claim.
+ /// The type representing a user role.
+ /// The type representing a user external login.
+ /// The type representing a user token.
+ public abstract class UserStore :
IUserLoginStore,
IUserRoleStore,
IUserClaimStore,
@@ -65,12 +122,15 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
IQueryableUserStore,
IUserTwoFactorStore,
IUserAuthenticationTokenStore
- where TUser : IdentityUser
- where TRole : IdentityRole
+ where TUser : IdentityUser
+ where TRole : IdentityRole>
where TContext : DbContext
where TKey : IEquatable
+ where TUserClaim : IdentityUserClaim
+ where TUserRole : IdentityUserRole
+ where TUserLogin : IdentityUserLogin
+ where TUserToken : IdentityUserToken
{
-
///
/// Creates a new instance of .
///
@@ -98,6 +158,46 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
///
public IdentityErrorDescriber ErrorDescriber { get; set; }
+ private DbSet Roles { get { return Context.Set(); } }
+ private DbSet UserClaims { get { return Context.Set(); } }
+ private DbSet UserRoles { get { return Context.Set(); } }
+ private DbSet UserLogins { get { return Context.Set(); } }
+ private DbSet UserTokens { get { return Context.Set(); } }
+
+ ///
+ /// Creates a new entity to represent a user role.
+ ///
+ ///
+ ///
+ ///
+ protected abstract TUserRole CreateUserRole(TUser user, TRole role);
+
+ ///
+ /// Create a new entity representing a user claim.
+ ///
+ ///
+ ///
+ ///
+ protected abstract TUserClaim CreateUserClaim(TUser user, Claim claim);
+
+ ///
+ /// Create a new entity representing a user login.
+ ///
+ ///
+ ///
+ ///
+ protected abstract TUserLogin CreateUserLogin(TUser user, UserLoginInfo login);
+
+ ///
+ /// Create a new entity representing a user token.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ protected abstract TUserToken CreateUserToken(TUser user, string loginProvider, string name, string value);
+
///
/// Gets or sets a flag indicating if changes should be persisted after CreateAsync, UpdateAsync and DeleteAsync are called.
///
@@ -109,7 +209,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
/// Saves the current store.
/// The used to propagate notifications that the operation should be canceled.
/// The that represents the asynchronous operation.
- private Task SaveChanges(CancellationToken cancellationToken)
+ protected Task SaveChanges(CancellationToken cancellationToken)
{
return AutoSaveChanges ? Context.SaveChangesAsync(cancellationToken) : Task.FromResult(0);
}
@@ -418,8 +518,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
{
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, Resources.RoleNotFound, normalizedRoleName));
}
- var ur = new IdentityUserRole { UserId = user.Id, RoleId = roleEntity.Id };
- UserRoles.Add(ur);
+ UserRoles.Add(CreateUserRole(user, roleEntity));
}
///
@@ -520,12 +619,6 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
_disposed = true;
}
- private DbSet Roles { get { return Context.Set(); } }
- private DbSet> UserClaims { get { return Context.Set>(); } }
- private DbSet> UserRoles { get { return Context.Set>(); } }
- private DbSet> UserLogins { get { return Context.Set>(); } }
- private DbSet> UserTokens { get { return Context.Set>(); } }
-
///
/// Get the claims associated with the specified as an asynchronous operation.
///
@@ -540,7 +633,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
throw new ArgumentNullException(nameof(user));
}
- return await UserClaims.Where(uc => uc.UserId.Equals(user.Id)).Select(c => new Claim(c.ClaimType, c.ClaimValue)).ToListAsync(cancellationToken);
+ return await UserClaims.Where(uc => uc.UserId.Equals(user.Id)).Select(c => c.ToClaim()).ToListAsync(cancellationToken);
}
///
@@ -563,7 +656,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
}
foreach (var claim in claims)
{
- UserClaims.Add(new IdentityUserClaim { UserId = user.Id, ClaimType = claim.Type, ClaimValue = claim.Value });
+ UserClaims.Add(CreateUserClaim(user, claim));
}
return Task.FromResult(false);
}
@@ -648,14 +741,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
{
throw new ArgumentNullException(nameof(login));
}
- var l = new IdentityUserLogin
- {
- UserId = user.Id,
- ProviderKey = login.ProviderKey,
- LoginProvider = login.LoginProvider,
- ProviderDisplayName = login.ProviderDisplayName
- };
- UserLogins.Add(l);
+ UserLogins.Add(CreateUserLogin(user, login));
return Task.FromResult(false);
}
@@ -676,8 +762,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
{
throw new ArgumentNullException(nameof(user));
}
- var userId = user.Id;
- var entry = await UserLogins.SingleOrDefaultAsync(l => l.UserId.Equals(userId) && l.LoginProvider == loginProvider && l.ProviderKey == providerKey, cancellationToken);
+ var entry = await UserLogins.SingleOrDefaultAsync(userLogin => userLogin.UserId.Equals(user.Id) && userLogin.LoginProvider == loginProvider && userLogin.ProviderKey == providerKey, cancellationToken);
if (entry != null)
{
UserLogins.Remove(entry);
@@ -1198,7 +1283,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
return new List();
}
- private Task> FindToken(TUser user, string loginProvider, string name, CancellationToken cancellationToken)
+ private Task FindToken(TUser user, string loginProvider, string name, CancellationToken cancellationToken)
{
var userId = user.Id;
return UserTokens.SingleOrDefaultAsync(l => l.UserId.Equals(userId) && l.LoginProvider == loginProvider && l.Name == name, cancellationToken);
@@ -1218,13 +1303,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
var token = await FindToken(user, loginProvider, name, cancellationToken);
if (token == null)
{
- UserTokens.Add(new IdentityUserToken
- {
- UserId = user.Id,
- LoginProvider = loginProvider,
- Name = name,
- Value = value
- });
+ UserTokens.Add(CreateUserToken(user, loginProvider, name, value));
}
else
{
diff --git a/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test/InMemoryContext.cs b/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test/InMemoryContext.cs
index 36b8e30152..bfbf9fdc6c 100644
--- a/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test/InMemoryContext.cs
+++ b/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test/InMemoryContext.cs
@@ -3,7 +3,6 @@
using System;
using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test
{
@@ -22,7 +21,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test
{ }
}
- public class InMemoryContext : IdentityDbContext
+ public class InMemoryContext : IdentityDbContext
where TUser : IdentityUser
where TRole : IdentityRole
where TKey : IEquatable
@@ -35,4 +34,23 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test
optionsBuilder.UseInMemoryDatabase();
}
}
+
+ public abstract class InMemoryContext : IdentityDbContext
+ where TUser : IdentityUser
+ where TRole : IdentityRole
+ where TKey : IEquatable
+ where TUserClaim : IdentityUserClaim
+ where TUserRole : IdentityUserRole
+ where TUserLogin : IdentityUserLogin
+ where TRoleClaim : IdentityRoleClaim
+ where TUserToken : IdentityUserToken
+ {
+ public InMemoryContext(DbContextOptions options) : base(options)
+ { }
+
+ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
+ {
+ optionsBuilder.UseInMemoryDatabase();
+ }
+ }
}
\ No newline at end of file
diff --git a/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test/InMemoryEFUserStoreTest.cs b/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test/InMemoryEFUserStoreTest.cs
index 3a373cadef..791f734e38 100644
--- a/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test/InMemoryEFUserStoreTest.cs
+++ b/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test/InMemoryEFUserStoreTest.cs
@@ -5,7 +5,6 @@ using System;
using System.Linq.Expressions;
using Microsoft.AspNetCore.Identity.Test;
using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test
diff --git a/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test/InMemoryStoreWithGenericsTest.cs b/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test/InMemoryStoreWithGenericsTest.cs
new file mode 100644
index 0000000000..3b771f3f56
--- /dev/null
+++ b/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test/InMemoryStoreWithGenericsTest.cs
@@ -0,0 +1,320 @@
+// 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.Linq.Expressions;
+using System.Security.Claims;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Identity.Test;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.DependencyInjection;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test
+{
+ public class InMemoryEFUserStoreTestWithGenerics : UserManagerTestBase, IDisposable
+ {
+ private readonly InMemoryContextWithGenerics _context;
+ private UserStoreWithGenerics _store;
+
+ public InMemoryEFUserStoreTestWithGenerics()
+ {
+ var services = new ServiceCollection();
+ services.AddSingleton();
+ services.AddDbContext(options => options.UseInMemoryDatabase());
+ _context = services.BuildServiceProvider().GetRequiredService();
+ }
+
+ protected override object CreateTestContext()
+ {
+ return _context;
+ }
+
+ protected override void AddUserStore(IServiceCollection services, object context = null)
+ {
+ _store = new UserStoreWithGenerics((InMemoryContextWithGenerics)context, "TestContext");
+ services.AddSingleton>(_store);
+ }
+
+ protected override void AddRoleStore(IServiceCollection services, object context = null)
+ {
+ services.AddSingleton>(new RoleStoreWithGenerics((InMemoryContextWithGenerics)context, "TestContext"));
+ }
+
+ protected override IdentityUserWithGenerics CreateTestUser(string namePrefix = "", string email = "", string phoneNumber = "",
+ bool lockoutEnabled = false, DateTimeOffset? lockoutEnd = default(DateTimeOffset?), bool useNamePrefixAsUserName = false)
+ {
+ return new IdentityUserWithGenerics
+ {
+ UserName = useNamePrefixAsUserName ? namePrefix : string.Format("{0}{1}", namePrefix, Guid.NewGuid()),
+ Email = email,
+ PhoneNumber = phoneNumber,
+ LockoutEnabled = lockoutEnabled,
+ LockoutEnd = lockoutEnd
+ };
+ }
+
+ protected override MyIdentityRole CreateTestRole(string roleNamePrefix = "", bool useRoleNamePrefixAsRoleName = false)
+ {
+ var roleName = useRoleNamePrefixAsRoleName ? roleNamePrefix : string.Format("{0}{1}", roleNamePrefix, Guid.NewGuid());
+ return new MyIdentityRole(roleName);
+ }
+
+ protected override void SetUserPasswordHash(IdentityUserWithGenerics user, string hashedPassword)
+ {
+ user.PasswordHash = hashedPassword;
+ }
+
+ protected override Expression> UserNameEqualsPredicate(string userName) => u => u.UserName == userName;
+
+ protected override Expression> RoleNameEqualsPredicate(string roleName) => r => r.Name == roleName;
+
+ protected override Expression> UserNameStartsWithPredicate(string userName) => u => u.UserName.StartsWith(userName);
+
+ protected override Expression> RoleNameStartsWithPredicate(string roleName) => r => r.Name.StartsWith(roleName);
+
+ [Fact]
+ public async Task CanAddRemoveUserClaimWithIssuer()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser();
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ Claim[] claims = { new Claim("c1", "v1", null, "i1"), new Claim("c2", "v2", null, "i2"), new Claim("c2", "v3", null, "i3") };
+ foreach (Claim c in claims)
+ {
+ IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user, c));
+ }
+
+ var userId = await manager.GetUserIdAsync(user);
+ var userClaims = await manager.GetClaimsAsync(user);
+ Assert.Equal(3, userClaims.Count);
+ Assert.Equal(3, userClaims.Intersect(claims, ClaimEqualityComparer.Default).Count());
+
+ IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[0]));
+ userClaims = await manager.GetClaimsAsync(user);
+ Assert.Equal(2, userClaims.Count);
+ IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[1]));
+ userClaims = await manager.GetClaimsAsync(user);
+ Assert.Equal(1, userClaims.Count);
+ IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[2]));
+ userClaims = await manager.GetClaimsAsync(user);
+ Assert.Equal(0, userClaims.Count);
+ }
+
+ [Fact]
+ public async Task RemoveClaimWithIssuerOnlyAffectsUser()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser();
+ var user2 = CreateTestUser();
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user2));
+ Claim[] claims = { new Claim("c", "v", null, "i1"), new Claim("c2", "v2", null, "i2"), new Claim("c2", "v3", null, "i3") };
+ foreach (Claim c in claims)
+ {
+ IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user, c));
+ IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user2, c));
+ }
+ var userClaims = await manager.GetClaimsAsync(user);
+ Assert.Equal(3, userClaims.Count);
+ IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[0]));
+ userClaims = await manager.GetClaimsAsync(user);
+ Assert.Equal(2, userClaims.Count);
+ IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[1]));
+ userClaims = await manager.GetClaimsAsync(user);
+ Assert.Equal(1, userClaims.Count);
+ IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[2]));
+ userClaims = await manager.GetClaimsAsync(user);
+ Assert.Equal(0, userClaims.Count);
+ var userClaims2 = await manager.GetClaimsAsync(user2);
+ Assert.Equal(3, userClaims2.Count);
+ }
+
+ [Fact]
+ public async Task CanReplaceUserClaimWithIssuer()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser();
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user, new Claim("c", "a", "i")));
+ var userClaims = await manager.GetClaimsAsync(user);
+ Assert.Equal(1, userClaims.Count);
+ Claim claim = new Claim("c", "b", "i");
+ Claim oldClaim = userClaims.FirstOrDefault();
+ IdentityResultAssert.IsSuccess(await manager.ReplaceClaimAsync(user, oldClaim, claim));
+ var newUserClaims = await manager.GetClaimsAsync(user);
+ Assert.Equal(1, newUserClaims.Count);
+ Claim newClaim = newUserClaims.FirstOrDefault();
+ Assert.Equal(claim.Type, newClaim.Type);
+ Assert.Equal(claim.Value, newClaim.Value);
+ Assert.Equal(claim.Issuer, newClaim.Issuer);
+ }
+
+ public void Dispose()
+ {
+ }
+ }
+
+ public class ClaimEqualityComparer : IEqualityComparer
+ {
+ public static IEqualityComparer Default = new ClaimEqualityComparer();
+
+ public bool Equals(Claim x, Claim y)
+ {
+ return x.Value == y.Value && x.Type == y.Type && x.Issuer == y.Issuer;
+ }
+
+ public int GetHashCode(Claim obj)
+ {
+ return 1;
+ }
+ }
+
+
+ #region Generic Type defintions
+
+ public class IdentityUserWithGenerics : IdentityUser
+ {
+ public IdentityUserWithGenerics()
+ {
+ Id = Guid.NewGuid().ToString();
+ }
+ }
+
+ public class UserStoreWithGenerics : UserStore
+ {
+ public string LoginContext { get; set; }
+
+ public UserStoreWithGenerics(InMemoryContextWithGenerics context, string loginContext) : base(context)
+ {
+ LoginContext = loginContext;
+ }
+
+ protected override IdentityUserRoleWithDate CreateUserRole(IdentityUserWithGenerics user, MyIdentityRole role)
+ {
+ return new IdentityUserRoleWithDate()
+ {
+ RoleId = role.Id,
+ UserId = user.Id,
+ Created = DateTime.UtcNow
+ };
+ }
+
+ protected 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)
+ {
+ return new IdentityUserLoginWithContext
+ {
+ UserId = user.Id,
+ ProviderKey = login.ProviderKey,
+ LoginProvider = login.LoginProvider,
+ ProviderDisplayName = login.ProviderDisplayName,
+ Context = LoginContext
+ };
+ }
+
+ protected override IdentityUserTokenWithStuff CreateUserToken(IdentityUserWithGenerics user, string loginProvider, string name, string value)
+ {
+ return new IdentityUserTokenWithStuff
+ {
+ UserId = user.Id,
+ LoginProvider = loginProvider,
+ Name = name,
+ Value = value,
+ Stuff = "stuff"
+ };
+ }
+ }
+
+ public class RoleStoreWithGenerics : RoleStore>
+ {
+ private string _loginContext;
+ public RoleStoreWithGenerics(InMemoryContextWithGenerics context, string loginContext) : base(context)
+ {
+ _loginContext = loginContext;
+ }
+
+ protected override IdentityRoleClaim CreateRoleClaim(MyIdentityRole role, Claim claim)
+ {
+ return new IdentityRoleClaim { RoleId = role.Id, ClaimType = claim.Type, ClaimValue = claim.Value };
+ }
+ }
+
+ public class IdentityUserClaimWithIssuer : IdentityUserClaim
+ {
+ public string Issuer { get; set; }
+
+ public override Claim ToClaim()
+ {
+ return new Claim(ClaimType, ClaimValue, null, Issuer);
+ }
+
+ public override void InitializeFromClaim(Claim other)
+ {
+ ClaimValue = other.Value;
+ ClaimType = other.Type;
+ Issuer = other.Issuer;
+ }
+ }
+
+ public class IdentityUserRoleWithDate : IdentityUserRole
+ {
+ public DateTime Created { get; set; }
+ }
+
+ public class MyIdentityRole : IdentityRole>
+ {
+ public MyIdentityRole() : base()
+ {
+ Id = Guid.NewGuid().ToString();
+ }
+
+ public MyIdentityRole(string roleName) : this()
+ {
+ Name = roleName;
+ }
+
+ }
+
+ public class IdentityUserTokenWithStuff : IdentityUserToken
+ {
+ public string Stuff { get; set; }
+ }
+
+ public class IdentityUserLoginWithContext : IdentityUserLogin
+ {
+ public string Context { get; set; }
+ }
+
+ public class InMemoryContextWithGenerics : InMemoryContext, IdentityUserTokenWithStuff>
+ {
+ public InMemoryContextWithGenerics(DbContextOptions options) : base(options)
+ { }
+
+ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
+ {
+ optionsBuilder.UseInMemoryDatabase();
+ }
+ }
+
+ #endregion
+}
\ No newline at end of file
diff --git a/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test/UserStoreTest.cs b/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test/UserStoreTest.cs
index 798e8c9d27..0d06b7b65a 100644
--- a/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test/UserStoreTest.cs
+++ b/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test/UserStoreTest.cs
@@ -9,7 +9,6 @@ using Microsoft.AspNetCore.Identity.Test;
using Microsoft.AspNetCore.Testing.xunit;
using Microsoft.AspNetCore.Testing;
using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
diff --git a/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test/UserStoreWithGenericsTest.cs b/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test/UserStoreWithGenericsTest.cs
new file mode 100644
index 0000000000..0e9b53a20b
--- /dev/null
+++ b/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test/UserStoreWithGenericsTest.cs
@@ -0,0 +1,316 @@
+// 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.Linq.Expressions;
+using System.Security.Claims;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Identity.Test;
+using Microsoft.AspNetCore.Testing;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.DependencyInjection;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test
+{
+ public class UserStoreWithGenericsTest : UserManagerTestBase, IClassFixture
+ {
+ private readonly ScratchDatabaseFixture _fixture;
+
+ public UserStoreWithGenericsTest(ScratchDatabaseFixture fixture)
+ {
+ _fixture = fixture;
+ }
+
+ public ContextWithGenerics CreateContext()
+ {
+ var db = DbUtil.Create(_fixture.ConnectionString);
+ db.Database.EnsureCreated();
+ return db;
+ }
+
+ protected override object CreateTestContext()
+ {
+ return CreateContext();
+ }
+
+ protected override bool ShouldSkipDbTests()
+ {
+ return TestPlatformHelper.IsMono || !TestPlatformHelper.IsWindows;
+ }
+
+ protected override void AddUserStore(IServiceCollection services, object context = null)
+ {
+ services.AddSingleton>(new UserStoreWithGenerics((ContextWithGenerics)context, "TestContext"));
+ }
+
+ protected override void AddRoleStore(IServiceCollection services, object context = null)
+ {
+ services.AddSingleton>(new RoleStoreWithGenerics((ContextWithGenerics)context, "TestContext"));
+ }
+
+ protected override IdentityUserWithGenerics CreateTestUser(string namePrefix = "", string email = "", string phoneNumber = "",
+ bool lockoutEnabled = false, DateTimeOffset? lockoutEnd = default(DateTimeOffset?), bool useNamePrefixAsUserName = false)
+ {
+ return new IdentityUserWithGenerics
+ {
+ UserName = useNamePrefixAsUserName ? namePrefix : string.Format("{0}{1}", namePrefix, Guid.NewGuid()),
+ Email = email,
+ PhoneNumber = phoneNumber,
+ LockoutEnabled = lockoutEnabled,
+ LockoutEnd = lockoutEnd
+ };
+ }
+
+ protected override MyIdentityRole CreateTestRole(string roleNamePrefix = "", bool useRoleNamePrefixAsRoleName = false)
+ {
+ var roleName = useRoleNamePrefixAsRoleName ? roleNamePrefix : string.Format("{0}{1}", roleNamePrefix, Guid.NewGuid());
+ return new MyIdentityRole(roleName);
+ }
+
+ protected override void SetUserPasswordHash(IdentityUserWithGenerics user, string hashedPassword)
+ {
+ user.PasswordHash = hashedPassword;
+ }
+
+ protected override Expression> UserNameEqualsPredicate(string userName) => u => u.UserName == userName;
+
+ protected override Expression> RoleNameEqualsPredicate(string roleName) => r => r.Name == roleName;
+
+ protected override Expression> UserNameStartsWithPredicate(string userName) => u => u.UserName.StartsWith(userName);
+
+ protected override Expression> RoleNameStartsWithPredicate(string roleName) => r => r.Name.StartsWith(roleName);
+
+ [Fact]
+ public async Task CanAddRemoveUserClaimWithIssuer()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser();
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ Claim[] claims = { new Claim("c1", "v1", null, "i1"), new Claim("c2", "v2", null, "i2"), new Claim("c2", "v3", null, "i3") };
+ foreach (Claim c in claims)
+ {
+ IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user, c));
+ }
+
+ var userId = await manager.GetUserIdAsync(user);
+ var userClaims = await manager.GetClaimsAsync(user);
+ Assert.Equal(3, userClaims.Count);
+ Assert.Equal(3, userClaims.Intersect(claims, ClaimEqualityComparer.Default).Count());
+
+ IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[0]));
+ userClaims = await manager.GetClaimsAsync(user);
+ Assert.Equal(2, userClaims.Count);
+ IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[1]));
+ userClaims = await manager.GetClaimsAsync(user);
+ Assert.Equal(1, userClaims.Count);
+ IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[2]));
+ userClaims = await manager.GetClaimsAsync(user);
+ Assert.Equal(0, userClaims.Count);
+ }
+
+ [Fact]
+ public async Task RemoveClaimWithIssuerOnlyAffectsUser()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser();
+ var user2 = CreateTestUser();
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user2));
+ Claim[] claims = { new Claim("c", "v", null, "i1"), new Claim("c2", "v2", null, "i2"), new Claim("c2", "v3", null, "i3") };
+ foreach (Claim c in claims)
+ {
+ IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user, c));
+ IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user2, c));
+ }
+ var userClaims = await manager.GetClaimsAsync(user);
+ Assert.Equal(3, userClaims.Count);
+ IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[0]));
+ userClaims = await manager.GetClaimsAsync(user);
+ Assert.Equal(2, userClaims.Count);
+ IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[1]));
+ userClaims = await manager.GetClaimsAsync(user);
+ Assert.Equal(1, userClaims.Count);
+ IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[2]));
+ userClaims = await manager.GetClaimsAsync(user);
+ Assert.Equal(0, userClaims.Count);
+ var userClaims2 = await manager.GetClaimsAsync(user2);
+ Assert.Equal(3, userClaims2.Count);
+ }
+
+ [Fact]
+ public async Task CanReplaceUserClaimWithIssuer()
+ {
+ if (ShouldSkipDbTests())
+ {
+ return;
+ }
+ var manager = CreateManager();
+ var user = CreateTestUser();
+ IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
+ IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user, new Claim("c", "a", "i")));
+ var userClaims = await manager.GetClaimsAsync(user);
+ Assert.Equal(1, userClaims.Count);
+ Claim claim = new Claim("c", "b", "i");
+ Claim oldClaim = userClaims.FirstOrDefault();
+ IdentityResultAssert.IsSuccess(await manager.ReplaceClaimAsync(user, oldClaim, claim));
+ var newUserClaims = await manager.GetClaimsAsync(user);
+ Assert.Equal(1, newUserClaims.Count);
+ Claim newClaim = newUserClaims.FirstOrDefault();
+ Assert.Equal(claim.Type, newClaim.Type);
+ Assert.Equal(claim.Value, newClaim.Value);
+ Assert.Equal(claim.Issuer, newClaim.Issuer);
+ }
+ }
+
+ public class ClaimEqualityComparer : IEqualityComparer
+ {
+ public static IEqualityComparer Default = new ClaimEqualityComparer();
+
+ public bool Equals(Claim x, Claim y)
+ {
+ return x.Value == y.Value && x.Type == y.Type && x.Issuer == y.Issuer;
+ }
+
+ public int GetHashCode(Claim obj)
+ {
+ return 1;
+ }
+ }
+
+
+ #region Generic Type defintions
+
+ public class IdentityUserWithGenerics : IdentityUser
+ {
+ public IdentityUserWithGenerics()
+ {
+ Id = Guid.NewGuid().ToString();
+ }
+ }
+
+ public class UserStoreWithGenerics : UserStore
+ {
+ public string LoginContext { get; set; }
+
+ public UserStoreWithGenerics(ContextWithGenerics context, string loginContext) : base(context)
+ {
+ LoginContext = loginContext;
+ }
+
+ protected override IdentityUserRoleWithDate CreateUserRole(IdentityUserWithGenerics user, MyIdentityRole role)
+ {
+ return new IdentityUserRoleWithDate()
+ {
+ RoleId = role.Id,
+ UserId = user.Id,
+ Created = DateTime.UtcNow
+ };
+ }
+
+ protected 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)
+ {
+ return new IdentityUserLoginWithContext
+ {
+ UserId = user.Id,
+ ProviderKey = login.ProviderKey,
+ LoginProvider = login.LoginProvider,
+ ProviderDisplayName = login.ProviderDisplayName,
+ Context = LoginContext
+ };
+ }
+
+ protected override IdentityUserTokenWithStuff CreateUserToken(IdentityUserWithGenerics user, string loginProvider, string name, string value)
+ {
+ return new IdentityUserTokenWithStuff
+ {
+ UserId = user.Id,
+ LoginProvider = loginProvider,
+ Name = name,
+ Value = value,
+ Stuff = "stuff"
+ };
+ }
+ }
+
+ public class RoleStoreWithGenerics : RoleStore>
+ {
+ private string _loginContext;
+ public RoleStoreWithGenerics(ContextWithGenerics context, string loginContext) : base(context)
+ {
+ _loginContext = loginContext;
+ }
+
+ protected override IdentityRoleClaim CreateRoleClaim(MyIdentityRole role, Claim claim)
+ {
+ return new IdentityRoleClaim { RoleId = role.Id, ClaimType = claim.Type, ClaimValue = claim.Value };
+ }
+ }
+
+ public class IdentityUserClaimWithIssuer : IdentityUserClaim
+ {
+ public string Issuer { get; set; }
+
+ public override Claim ToClaim()
+ {
+ return new Claim(ClaimType, ClaimValue, null, Issuer);
+ }
+
+ public override void InitializeFromClaim(Claim other)
+ {
+ ClaimValue = other.Value;
+ ClaimType = other.Type;
+ Issuer = other.Issuer;
+ }
+ }
+
+ public class IdentityUserRoleWithDate : IdentityUserRole
+ {
+ public DateTime Created { get; set; }
+ }
+
+ public class MyIdentityRole : IdentityRole>
+ {
+ public MyIdentityRole() : base()
+ {
+ Id = Guid.NewGuid().ToString();
+ }
+
+ public MyIdentityRole(string roleName) : this()
+ {
+ Name = roleName;
+ }
+ }
+
+ public class IdentityUserTokenWithStuff : IdentityUserToken
+ {
+ public string Stuff { get; set; }
+ }
+
+ public class IdentityUserLoginWithContext : IdentityUserLogin
+ {
+ public string Context { get; set; }
+ }
+
+ public class ContextWithGenerics : IdentityDbContext, IdentityUserTokenWithStuff>
+ {
+ public ContextWithGenerics(DbContextOptions options) : base(options) { }
+ }
+
+ #endregion
+}
\ No newline at end of file