1 // Copyright (c) .NET Foundation. All rights reserved. 2 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 4 using System; 5 using System.Collections.Generic; 6 using System.Linq; 7 using System.Linq.Expressions; 8 using System.Security.Claims; 9 using System.Threading.Tasks; 10 using Microsoft.AspNetCore.Http; 11 using Microsoft.AspNetCore.Identity.Test; 12 using Microsoft.EntityFrameworkCore; 13 using Microsoft.Extensions.DependencyInjection; 14 using Xunit; 15 16 namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.InMemory.Test 17 { 18 public class InMemoryEFUserStoreTestWithGenerics : IdentitySpecificationTestBase<IdentityUserWithGenerics, MyIdentityRole, string>, IDisposable 19 { 20 private readonly InMemoryContextWithGenerics _context; 21 private UserStoreWithGenerics _store; 22 InMemoryEFUserStoreTestWithGenerics()23 public InMemoryEFUserStoreTestWithGenerics() 24 { 25 var services = new ServiceCollection(); 26 services.AddHttpContextAccessor(); 27 services.AddDbContext<InMemoryContextWithGenerics>(options => options.UseInMemoryDatabase("Scratch")); 28 _context = services.BuildServiceProvider().GetRequiredService<InMemoryContextWithGenerics>(); 29 } 30 CreateTestContext()31 protected override object CreateTestContext() 32 { 33 return _context; 34 } 35 AddUserStore(IServiceCollection services, object context = null)36 protected override void AddUserStore(IServiceCollection services, object context = null) 37 { 38 _store = new UserStoreWithGenerics((InMemoryContextWithGenerics)context, "TestContext"); 39 services.AddSingleton<IUserStore<IdentityUserWithGenerics>>(_store); 40 } 41 AddRoleStore(IServiceCollection services, object context = null)42 protected override void AddRoleStore(IServiceCollection services, object context = null) 43 { 44 services.AddSingleton<IRoleStore<MyIdentityRole>>(new RoleStoreWithGenerics((InMemoryContextWithGenerics)context, "TestContext")); 45 } 46 CreateTestUser(string namePrefix = R, string email = R, string phoneNumber = R, bool lockoutEnabled = false, DateTimeOffset? lockoutEnd = default(DateTimeOffset?), bool useNamePrefixAsUserName = false)47 protected override IdentityUserWithGenerics CreateTestUser(string namePrefix = "", string email = "", string phoneNumber = "", 48 bool lockoutEnabled = false, DateTimeOffset? lockoutEnd = default(DateTimeOffset?), bool useNamePrefixAsUserName = false) 49 { 50 return new IdentityUserWithGenerics 51 { 52 UserName = useNamePrefixAsUserName ? namePrefix : string.Format("{0}{1}", namePrefix, Guid.NewGuid()), 53 Email = email, 54 PhoneNumber = phoneNumber, 55 LockoutEnabled = lockoutEnabled, 56 LockoutEnd = lockoutEnd 57 }; 58 } 59 CreateTestRole(string roleNamePrefix = R, bool useRoleNamePrefixAsRoleName = false)60 protected override MyIdentityRole CreateTestRole(string roleNamePrefix = "", bool useRoleNamePrefixAsRoleName = false) 61 { 62 var roleName = useRoleNamePrefixAsRoleName ? roleNamePrefix : string.Format("{0}{1}", roleNamePrefix, Guid.NewGuid()); 63 return new MyIdentityRole(roleName); 64 } 65 SetUserPasswordHash(IdentityUserWithGenerics user, string hashedPassword)66 protected override void SetUserPasswordHash(IdentityUserWithGenerics user, string hashedPassword) 67 { 68 user.PasswordHash = hashedPassword; 69 } 70 UserNameEqualsPredicate(string userName)71 protected override Expression<Func<IdentityUserWithGenerics, bool>> UserNameEqualsPredicate(string userName) => u => u.UserName == userName; 72 RoleNameEqualsPredicate(string roleName)73 protected override Expression<Func<MyIdentityRole, bool>> RoleNameEqualsPredicate(string roleName) => r => r.Name == roleName; 74 UserNameStartsWithPredicate(string userName)75 protected override Expression<Func<IdentityUserWithGenerics, bool>> UserNameStartsWithPredicate(string userName) => u => u.UserName.StartsWith(userName); 76 RoleNameStartsWithPredicate(string roleName)77 protected override Expression<Func<MyIdentityRole, bool>> RoleNameStartsWithPredicate(string roleName) => r => r.Name.StartsWith(roleName); 78 79 [Fact] CanAddRemoveUserClaimWithIssuer()80 public async Task CanAddRemoveUserClaimWithIssuer() 81 { 82 if (ShouldSkipDbTests()) 83 { 84 return; 85 } 86 var manager = CreateManager(); 87 var user = CreateTestUser(); 88 IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); 89 Claim[] claims = { new Claim("c1", "v1", null, "i1"), new Claim("c2", "v2", null, "i2"), new Claim("c2", "v3", null, "i3") }; 90 foreach (Claim c in claims) 91 { 92 IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user, c)); 93 } 94 95 var userId = await manager.GetUserIdAsync(user); 96 var userClaims = await manager.GetClaimsAsync(user); 97 Assert.Equal(3, userClaims.Count); 98 Assert.Equal(3, userClaims.Intersect(claims, ClaimEqualityComparer.Default).Count()); 99 100 IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[0])); 101 userClaims = await manager.GetClaimsAsync(user); 102 Assert.Equal(2, userClaims.Count); 103 IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[1])); 104 userClaims = await manager.GetClaimsAsync(user); 105 Assert.Equal(1, userClaims.Count); 106 IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[2])); 107 userClaims = await manager.GetClaimsAsync(user); 108 Assert.Equal(0, userClaims.Count); 109 } 110 111 [Fact] RemoveClaimWithIssuerOnlyAffectsUser()112 public async Task RemoveClaimWithIssuerOnlyAffectsUser() 113 { 114 if (ShouldSkipDbTests()) 115 { 116 return; 117 } 118 var manager = CreateManager(); 119 var user = CreateTestUser(); 120 var user2 = CreateTestUser(); 121 IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); 122 IdentityResultAssert.IsSuccess(await manager.CreateAsync(user2)); 123 Claim[] claims = { new Claim("c", "v", null, "i1"), new Claim("c2", "v2", null, "i2"), new Claim("c2", "v3", null, "i3") }; 124 foreach (Claim c in claims) 125 { 126 IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user, c)); 127 IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user2, c)); 128 } 129 var userClaims = await manager.GetClaimsAsync(user); 130 Assert.Equal(3, userClaims.Count); 131 IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[0])); 132 userClaims = await manager.GetClaimsAsync(user); 133 Assert.Equal(2, userClaims.Count); 134 IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[1])); 135 userClaims = await manager.GetClaimsAsync(user); 136 Assert.Equal(1, userClaims.Count); 137 IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[2])); 138 userClaims = await manager.GetClaimsAsync(user); 139 Assert.Equal(0, userClaims.Count); 140 var userClaims2 = await manager.GetClaimsAsync(user2); 141 Assert.Equal(3, userClaims2.Count); 142 } 143 144 [Fact] CanReplaceUserClaimWithIssuer()145 public async Task CanReplaceUserClaimWithIssuer() 146 { 147 if (ShouldSkipDbTests()) 148 { 149 return; 150 } 151 var manager = CreateManager(); 152 var user = CreateTestUser(); 153 IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); 154 IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user, new Claim("c", "a", "i"))); 155 var userClaims = await manager.GetClaimsAsync(user); 156 Assert.Equal(1, userClaims.Count); 157 Claim claim = new Claim("c", "b", "i"); 158 Claim oldClaim = userClaims.FirstOrDefault(); 159 IdentityResultAssert.IsSuccess(await manager.ReplaceClaimAsync(user, oldClaim, claim)); 160 var newUserClaims = await manager.GetClaimsAsync(user); 161 Assert.Equal(1, newUserClaims.Count); 162 Claim newClaim = newUserClaims.FirstOrDefault(); 163 Assert.Equal(claim.Type, newClaim.Type); 164 Assert.Equal(claim.Value, newClaim.Value); 165 Assert.Equal(claim.Issuer, newClaim.Issuer); 166 } 167 Dispose()168 public void Dispose() 169 { 170 } 171 } 172 173 public class ClaimEqualityComparer : IEqualityComparer<Claim> 174 { 175 public static IEqualityComparer<Claim> Default = new ClaimEqualityComparer(); 176 Equals(Claim x, Claim y)177 public bool Equals(Claim x, Claim y) 178 { 179 return x.Value == y.Value && x.Type == y.Type && x.Issuer == y.Issuer; 180 } 181 GetHashCode(Claim obj)182 public int GetHashCode(Claim obj) 183 { 184 return 1; 185 } 186 } 187 188 189 #region Generic Type defintions 190 191 public class IdentityUserWithGenerics : IdentityUser<string> 192 { IdentityUserWithGenerics()193 public IdentityUserWithGenerics() 194 { 195 Id = Guid.NewGuid().ToString(); 196 } 197 } 198 199 public class UserStoreWithGenerics : UserStore<IdentityUserWithGenerics, MyIdentityRole, InMemoryContextWithGenerics, string, IdentityUserClaimWithIssuer, IdentityUserRoleWithDate, IdentityUserLoginWithContext, IdentityUserTokenWithStuff, IdentityRoleClaimWithIssuer> 200 { 201 public string LoginContext { get; set; } 202 UserStoreWithGenerics(InMemoryContextWithGenerics context, string loginContext)203 public UserStoreWithGenerics(InMemoryContextWithGenerics context, string loginContext) : base(context) 204 { 205 LoginContext = loginContext; 206 } 207 CreateUserRole(IdentityUserWithGenerics user, MyIdentityRole role)208 protected override IdentityUserRoleWithDate CreateUserRole(IdentityUserWithGenerics user, MyIdentityRole role) 209 { 210 return new IdentityUserRoleWithDate() 211 { 212 RoleId = role.Id, 213 UserId = user.Id, 214 Created = DateTime.UtcNow 215 }; 216 } 217 CreateUserClaim(IdentityUserWithGenerics user, Claim claim)218 protected override IdentityUserClaimWithIssuer CreateUserClaim(IdentityUserWithGenerics user, Claim claim) 219 { 220 return new IdentityUserClaimWithIssuer { UserId = user.Id, ClaimType = claim.Type, ClaimValue = claim.Value, Issuer = claim.Issuer }; 221 } 222 CreateUserLogin(IdentityUserWithGenerics user, UserLoginInfo login)223 protected override IdentityUserLoginWithContext CreateUserLogin(IdentityUserWithGenerics user, UserLoginInfo login) 224 { 225 return new IdentityUserLoginWithContext 226 { 227 UserId = user.Id, 228 ProviderKey = login.ProviderKey, 229 LoginProvider = login.LoginProvider, 230 ProviderDisplayName = login.ProviderDisplayName, 231 Context = LoginContext 232 }; 233 } 234 CreateUserToken(IdentityUserWithGenerics user, string loginProvider, string name, string value)235 protected override IdentityUserTokenWithStuff CreateUserToken(IdentityUserWithGenerics user, string loginProvider, string name, string value) 236 { 237 return new IdentityUserTokenWithStuff 238 { 239 UserId = user.Id, 240 LoginProvider = loginProvider, 241 Name = name, 242 Value = value, 243 Stuff = "stuff" 244 }; 245 } 246 } 247 248 public class RoleStoreWithGenerics : RoleStore<MyIdentityRole, InMemoryContextWithGenerics, string, IdentityUserRoleWithDate, IdentityRoleClaimWithIssuer> 249 { 250 private string _loginContext; RoleStoreWithGenerics(InMemoryContextWithGenerics context, string loginContext)251 public RoleStoreWithGenerics(InMemoryContextWithGenerics context, string loginContext) : base(context) 252 { 253 _loginContext = loginContext; 254 } 255 256 } 257 258 public class IdentityUserClaimWithIssuer : IdentityUserClaim<string> 259 { 260 public string Issuer { get; set; } 261 ToClaim()262 public override Claim ToClaim() 263 { 264 return new Claim(ClaimType, ClaimValue, null, Issuer); 265 } 266 InitializeFromClaim(Claim other)267 public override void InitializeFromClaim(Claim other) 268 { 269 ClaimValue = other.Value; 270 ClaimType = other.Type; 271 Issuer = other.Issuer; 272 } 273 } 274 275 public class IdentityRoleClaimWithIssuer : IdentityRoleClaim<string> 276 { 277 public string Issuer { get; set; } 278 ToClaim()279 public override Claim ToClaim() 280 { 281 return new Claim(ClaimType, ClaimValue, null, Issuer); 282 } 283 InitializeFromClaim(Claim other)284 public override void InitializeFromClaim(Claim other) 285 { 286 ClaimValue = other.Value; 287 ClaimType = other.Type; 288 Issuer = other.Issuer; 289 } 290 } 291 292 public class IdentityUserRoleWithDate : IdentityUserRole<string> 293 { 294 public DateTime Created { get; set; } 295 } 296 297 public class MyIdentityRole : IdentityRole<string> 298 { MyIdentityRole()299 public MyIdentityRole() : base() 300 { 301 Id = Guid.NewGuid().ToString(); 302 } 303 MyIdentityRole(string roleName)304 public MyIdentityRole(string roleName) : this() 305 { 306 Name = roleName; 307 } 308 309 } 310 311 public class IdentityUserTokenWithStuff : IdentityUserToken<string> 312 { 313 public string Stuff { get; set; } 314 } 315 316 public class IdentityUserLoginWithContext : IdentityUserLogin<string> 317 { 318 public string Context { get; set; } 319 } 320 321 public class InMemoryContextWithGenerics : InMemoryContext<IdentityUserWithGenerics, MyIdentityRole, string, IdentityUserClaimWithIssuer, IdentityUserRoleWithDate, IdentityUserLoginWithContext, IdentityRoleClaimWithIssuer, IdentityUserTokenWithStuff> 322 { InMemoryContextWithGenerics(DbContextOptions options)323 public InMemoryContextWithGenerics(DbContextOptions options) : base(options) 324 { } 325 OnConfiguring(DbContextOptionsBuilder optionsBuilder)326 protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) 327 { 328 optionsBuilder.UseInMemoryDatabase("Scratch"); 329 } 330 } 331 332 #endregion 333 }