// 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 : IdentitySpecificationTestBase, IDisposable { private readonly InMemoryContextWithGenerics _context; private UserStoreWithGenerics _store; public InMemoryEFUserStoreTestWithGenerics() { var services = new ServiceCollection(); services.AddHttpContextAccessor(); services.AddDbContext(options => options.UseInMemoryDatabase("Scratch")); _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; } } 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 IdentityRoleClaimWithIssuer : IdentityRoleClaim { 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 { public InMemoryContextWithGenerics(DbContextOptions options) : base(options) { } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseInMemoryDatabase("Scratch"); } } #endregion }