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 }