Skip to content

Commit 95ab2fa

Browse files
authored
Split ILookupNormalizer.Normalize into Name/Email methods (#8412)
1 parent 22623b9 commit 95ab2fa

File tree

7 files changed

+146
-43
lines changed

7 files changed

+146
-43
lines changed

src/Identity/Extensions.Core/ref/Microsoft.Extensions.Identity.Core.netcoreapp3.0.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,8 @@ public IdentityResult() { }
109109
}
110110
public partial interface ILookupNormalizer
111111
{
112-
string Normalize(string key);
112+
string NormalizeEmail(string email);
113+
string NormalizeName(string name);
113114
}
114115
public partial interface ILookupProtector
115116
{
@@ -450,10 +451,11 @@ protected TotpSecurityStampBasedTokenProvider() { }
450451
[System.Diagnostics.DebuggerStepThroughAttribute]
451452
public virtual System.Threading.Tasks.Task<bool> ValidateAsync(string purpose, string token, Microsoft.AspNetCore.Identity.UserManager<TUser> manager, TUser user) { throw null; }
452453
}
453-
public partial class UpperInvariantLookupNormalizer : Microsoft.AspNetCore.Identity.ILookupNormalizer
454+
public sealed partial class UpperInvariantLookupNormalizer : Microsoft.AspNetCore.Identity.ILookupNormalizer
454455
{
455456
public UpperInvariantLookupNormalizer() { }
456-
public virtual string Normalize(string key) { throw null; }
457+
public string NormalizeEmail(string email) { throw null; }
458+
public string NormalizeName(string name) { throw null; }
457459
}
458460
public partial class UserClaimsPrincipalFactory<TUser> : Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory<TUser> where TUser : class
459461
{
@@ -600,7 +602,8 @@ protected virtual void Dispose(bool disposing) { }
600602
[System.Diagnostics.DebuggerStepThroughAttribute]
601603
public virtual System.Threading.Tasks.Task<bool> IsLockedOutAsync(TUser user) { throw null; }
602604
public virtual System.Threading.Tasks.Task<bool> IsPhoneNumberConfirmedAsync(TUser user) { throw null; }
603-
public virtual string NormalizeKey(string key) { throw null; }
605+
public virtual string NormalizeEmail(string email) { throw null; }
606+
public virtual string NormalizeName(string name) { throw null; }
604607
[System.Diagnostics.DebuggerStepThroughAttribute]
605608
public virtual System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult> RedeemTwoFactorRecoveryCodeAsync(TUser user, string code) { throw null; }
606609
public virtual void RegisterTokenProvider(string providerName, Microsoft.AspNetCore.Identity.IUserTwoFactorTokenProvider<TUser> provider) { }

src/Identity/Extensions.Core/src/ILookupNormalizer.cs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,23 @@
44
namespace Microsoft.AspNetCore.Identity
55
{
66
/// <summary>
7-
/// Provides an abstraction for normalizing keys for lookup purposes.
7+
/// Provides an abstraction for normalizing keys (emails/names) for lookup purposes.
88
/// </summary>
99
public interface ILookupNormalizer
1010
{
1111
/// <summary>
12-
/// Returns a normalized representation of the specified <paramref name="key"/>.
12+
/// Returns a normalized representation of the specified <paramref name="name"/>.
1313
/// </summary>
14-
/// <param name="key">The key to normalize.</param>
15-
/// <returns>A normalized representation of the specified <paramref name="key"/>.</returns>
16-
string Normalize(string key);
14+
/// <param name="name">The key to normalize.</param>
15+
/// <returns>A normalized representation of the specified <paramref name="name"/>.</returns>
16+
string NormalizeName(string name);
17+
18+
/// <summary>
19+
/// Returns a normalized representation of the specified <paramref name="email"/>.
20+
/// </summary>
21+
/// <param name="email">The email to normalize.</param>
22+
/// <returns>A normalized representation of the specified <paramref name="email"/>.</returns>
23+
string NormalizeEmail(string email);
24+
1725
}
18-
}
26+
}

src/Identity/Extensions.Core/src/RoleManager.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ public virtual async Task<bool> RoleExistsAsync(string roleName)
244244
/// <returns>A normalized representation of the specified <paramref name="key"/>.</returns>
245245
public virtual string NormalizeKey(string key)
246246
{
247-
return (KeyNormalizer == null) ? key : KeyNormalizer.Normalize(key);
247+
return (KeyNormalizer == null) ? key : KeyNormalizer.NormalizeName(key);
248248
}
249249

250250
/// <summary>

src/Identity/Extensions.Core/src/UpperInvariantLookupNormalizer.cs

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,27 @@ namespace Microsoft.AspNetCore.Identity
66
/// <summary>
77
/// Implements <see cref="ILookupNormalizer"/> by converting keys to their upper cased invariant culture representation.
88
/// </summary>
9-
public class UpperInvariantLookupNormalizer : ILookupNormalizer
9+
public sealed class UpperInvariantLookupNormalizer : ILookupNormalizer
1010
{
1111
/// <summary>
12-
/// Returns a normalized representation of the specified <paramref name="key"/>
13-
/// by converting keys to their upper cased invariant culture representation.
12+
/// Returns a normalized representation of the specified <paramref name="name"/>.
1413
/// </summary>
15-
/// <param name="key">The key to normalize.</param>
16-
/// <returns>A normalized representation of the specified <paramref name="key"/>.</returns>
17-
public virtual string Normalize(string key)
14+
/// <param name="name">The key to normalize.</param>
15+
/// <returns>A normalized representation of the specified <paramref name="name"/>.</returns>
16+
public string NormalizeName(string name)
1817
{
19-
if (key == null)
18+
if (name == null)
2019
{
2120
return null;
2221
}
23-
return key.Normalize().ToUpperInvariant();
22+
return name.Normalize().ToUpperInvariant();
2423
}
24+
25+
/// <summary>
26+
/// Returns a normalized representation of the specified <paramref name="email"/>.
27+
/// </summary>
28+
/// <param name="email">The email to normalize.</param>
29+
/// <returns>A normalized representation of the specified <paramref name="email"/>.</returns>
30+
public string NormalizeEmail(string email) => NormalizeName(email);
2531
}
2632
}

src/Identity/Extensions.Core/src/UserManager.cs

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -105,15 +105,15 @@ public UserManager(IUserStore<TUser> store,
105105
foreach (var providerName in Options.Tokens.ProviderMap.Keys)
106106
{
107107
var description = Options.Tokens.ProviderMap[providerName];
108-
109-
var provider = (description.ProviderInstance ?? services.GetRequiredService(description.ProviderType))
108+
109+
var provider = (description.ProviderInstance ?? services.GetRequiredService(description.ProviderType))
110110
as IUserTwoFactorTokenProvider<TUser>;
111111
if (provider != null)
112112
{
113113
RegisterTokenProvider(providerName, provider);
114114
}
115115
}
116-
}
116+
}
117117

118118
if (Options.Stores.ProtectPersonalData)
119119
{
@@ -161,7 +161,7 @@ public UserManager(IUserStore<TUser> store,
161161
/// The <see cref="ILookupNormalizer"/> used to normalize things like user and role names.
162162
/// </summary>
163163
public ILookupNormalizer KeyNormalizer { get; set; }
164-
164+
165165
/// <summary>
166166
/// The <see cref="IdentityErrorDescriber"/> used to generate error messages.
167167
/// </summary>
@@ -547,7 +547,7 @@ public virtual async Task<TUser> FindByNameAsync(string userName)
547547
{
548548
throw new ArgumentNullException(nameof(userName));
549549
}
550-
userName = NormalizeKey(userName);
550+
userName = NormalizeName(userName);
551551

552552
var user = await Store.FindByNameAsync(userName, CancellationToken);
553553

@@ -603,15 +603,21 @@ public virtual async Task<IdentityResult> CreateAsync(TUser user, string passwor
603603
}
604604

605605
/// <summary>
606-
/// Normalize a key (user name, email) for consistent comparisons.
606+
/// Normalize user or role name for consistent comparisons.
607607
/// </summary>
608-
/// <param name="key">The key to normalize.</param>
609-
/// <returns>A normalized value representing the specified <paramref name="key"/>.</returns>
610-
public virtual string NormalizeKey(string key)
611-
{
612-
return (KeyNormalizer == null) ? key : KeyNormalizer.Normalize(key);
613-
}
608+
/// <param name="name">The name to normalize.</param>
609+
/// <returns>A normalized value representing the specified <paramref name="name"/>.</returns>
610+
public virtual string NormalizeName(string name)
611+
=> (KeyNormalizer == null) ? name : KeyNormalizer.NormalizeName(name);
614612

613+
/// <summary>
614+
/// Normalize email for consistent comparisons.
615+
/// </summary>
616+
/// <param name="email">The email to normalize.</param>
617+
/// <returns>A normalized value representing the specified <paramref name="email"/>.</returns>
618+
public virtual string NormalizeEmail(string email)
619+
=> (KeyNormalizer == null) ? email : KeyNormalizer.NormalizeEmail(email);
620+
615621
private string ProtectPersonalData(string data)
616622
{
617623
if (Options.Stores.ProtectPersonalData)
@@ -630,7 +636,7 @@ private string ProtectPersonalData(string data)
630636
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
631637
public virtual async Task UpdateNormalizedUserNameAsync(TUser user)
632638
{
633-
var normalizedName = NormalizeKey(await GetUserNameAsync(user));
639+
var normalizedName = NormalizeName(await GetUserNameAsync(user));
634640
normalizedName = ProtectPersonalData(normalizedName);
635641
await Store.SetNormalizedUserNameAsync(user, normalizedName, CancellationToken);
636642
}
@@ -1199,7 +1205,7 @@ public virtual async Task<IdentityResult> AddToRoleAsync(TUser user, string role
11991205
throw new ArgumentNullException(nameof(user));
12001206
}
12011207

1202-
var normalizedRole = NormalizeKey(role);
1208+
var normalizedRole = NormalizeName(role);
12031209
if (await userRoleStore.IsInRoleAsync(user, normalizedRole, CancellationToken))
12041210
{
12051211
return await UserAlreadyInRoleError(user, role);
@@ -1232,7 +1238,7 @@ public virtual async Task<IdentityResult> AddToRolesAsync(TUser user, IEnumerabl
12321238

12331239
foreach (var role in roles.Distinct())
12341240
{
1235-
var normalizedRole = NormalizeKey(role);
1241+
var normalizedRole = NormalizeName(role);
12361242
if (await userRoleStore.IsInRoleAsync(user, normalizedRole, CancellationToken))
12371243
{
12381244
return await UserAlreadyInRoleError(user, role);
@@ -1260,7 +1266,7 @@ public virtual async Task<IdentityResult> RemoveFromRoleAsync(TUser user, string
12601266
throw new ArgumentNullException(nameof(user));
12611267
}
12621268

1263-
var normalizedRole = NormalizeKey(role);
1269+
var normalizedRole = NormalizeName(role);
12641270
if (!await userRoleStore.IsInRoleAsync(user, normalizedRole, CancellationToken))
12651271
{
12661272
return await UserNotInRoleError(user, role);
@@ -1305,7 +1311,7 @@ public virtual async Task<IdentityResult> RemoveFromRolesAsync(TUser user, IEnum
13051311

13061312
foreach (var role in roles)
13071313
{
1308-
var normalizedRole = NormalizeKey(role);
1314+
var normalizedRole = NormalizeName(role);
13091315
if (!await userRoleStore.IsInRoleAsync(user, normalizedRole, CancellationToken))
13101316
{
13111317
return await UserNotInRoleError(user, role);
@@ -1348,7 +1354,7 @@ public virtual async Task<bool> IsInRoleAsync(TUser user, string role)
13481354
{
13491355
throw new ArgumentNullException(nameof(user));
13501356
}
1351-
return await userRoleStore.IsInRoleAsync(user, NormalizeKey(role), CancellationToken);
1357+
return await userRoleStore.IsInRoleAsync(user, NormalizeName(role), CancellationToken);
13521358
}
13531359

13541360
/// <summary>
@@ -1409,7 +1415,7 @@ public virtual async Task<TUser> FindByEmailAsync(string email)
14091415
throw new ArgumentNullException(nameof(email));
14101416
}
14111417

1412-
email = NormalizeKey(email);
1418+
email = NormalizeEmail(email);
14131419
var user = await store.FindByEmailAsync(email, CancellationToken);
14141420

14151421
// Need to potentially check all keys
@@ -1444,7 +1450,7 @@ public virtual async Task UpdateNormalizedEmailAsync(TUser user)
14441450
if (store != null)
14451451
{
14461452
var email = await GetEmailAsync(user);
1447-
await store.SetNormalizedEmailAsync(user, ProtectPersonalData(NormalizeKey(email)), CancellationToken);
1453+
await store.SetNormalizedEmailAsync(user, ProtectPersonalData(NormalizeEmail(email)), CancellationToken);
14481454
}
14491455
}
14501456

@@ -2097,7 +2103,7 @@ public virtual Task<IList<TUser>> GetUsersInRoleAsync(string roleName)
20972103
throw new ArgumentNullException(nameof(roleName));
20982104
}
20992105

2100-
return store.GetUsersInRoleAsync(NormalizeKey(roleName), CancellationToken);
2106+
return store.GetUsersInRoleAsync(NormalizeName(roleName), CancellationToken);
21012107
}
21022108

21032109
/// <summary>

src/Identity/test/Identity.Test/RoleManagerTest.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,6 @@ public async Task RoleExistsCallsStoreWithNormalizedName()
111111
store.VerifyAll();
112112
}
113113

114-
115114
[Fact]
116115
public void DisposeAfterDisposeDoesNotThrow()
117116
{
@@ -209,4 +208,4 @@ public void Dispose()
209208
}
210209
}
211210
}
212-
}
211+
}

src/Identity/test/Identity.Test/UserManagerTest.cs

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,48 @@ public async Task CanFindByEmailCallsStoreWithoutNormalizedEmail()
269269
store.VerifyAll();
270270
}
271271

272+
private class CustomNormalizer : ILookupNormalizer
273+
{
274+
public string NormalizeName(string key) => "#"+key;
275+
public string NormalizeEmail(string key) => "@" + key;
276+
}
277+
278+
[Fact]
279+
public async Task FindByEmailCallsStoreWithCustomNormalizedEmail()
280+
{
281+
// Setup
282+
var store = new Mock<IUserEmailStore<PocoUser>>();
283+
var user = new PocoUser { Email = "Foo" };
284+
store.Setup(s => s.FindByEmailAsync("@Foo", CancellationToken.None)).Returns(Task.FromResult(user)).Verifiable();
285+
var userManager = MockHelpers.TestUserManager(store.Object);
286+
userManager.KeyNormalizer = new CustomNormalizer();
287+
288+
// Act
289+
var result = await userManager.FindByEmailAsync(user.Email);
290+
291+
// Assert
292+
Assert.Equal(user, result);
293+
store.VerifyAll();
294+
}
295+
296+
[Fact]
297+
public async Task FindByNameCallsStoreWithCustomNormalizedName()
298+
{
299+
// Setup
300+
var store = new Mock<IUserEmailStore<PocoUser>>();
301+
var user = new PocoUser { UserName = "Foo", Email = "Bar" };
302+
store.Setup(s => s.FindByNameAsync("#Foo", CancellationToken.None)).Returns(Task.FromResult(user)).Verifiable();
303+
var userManager = MockHelpers.TestUserManager(store.Object);
304+
userManager.KeyNormalizer = new CustomNormalizer();
305+
306+
// Act
307+
var result = await userManager.FindByNameAsync(user.UserName);
308+
309+
// Assert
310+
Assert.Equal(user, result);
311+
store.VerifyAll();
312+
}
313+
272314
[Fact]
273315
public async Task AddToRolesCallsStore()
274316
{
@@ -307,6 +349,45 @@ public async Task AddToRolesCallsStore()
307349
store.Verify(s => s.AddToRoleAsync(user, "C", CancellationToken.None), Times.Once());
308350
}
309351

352+
[Fact]
353+
public async Task AddToRolesCallsStoreWithCustomNameNormalizer()
354+
{
355+
// Setup
356+
var store = new Mock<IUserRoleStore<PocoUser>>();
357+
var user = new PocoUser { UserName = "Foo" };
358+
var roles = new string[] { "A", "B", "C", "C" };
359+
store.Setup(s => s.AddToRoleAsync(user, "#A", CancellationToken.None))
360+
.Returns(Task.FromResult(0))
361+
.Verifiable();
362+
store.Setup(s => s.AddToRoleAsync(user, "#B", CancellationToken.None))
363+
.Returns(Task.FromResult(0))
364+
.Verifiable();
365+
store.Setup(s => s.AddToRoleAsync(user, "#C", CancellationToken.None))
366+
.Returns(Task.FromResult(0))
367+
.Verifiable();
368+
369+
store.Setup(s => s.UpdateAsync(user, CancellationToken.None)).ReturnsAsync(IdentityResult.Success).Verifiable();
370+
store.Setup(s => s.IsInRoleAsync(user, "#A", CancellationToken.None))
371+
.Returns(Task.FromResult(false))
372+
.Verifiable();
373+
store.Setup(s => s.IsInRoleAsync(user, "#B", CancellationToken.None))
374+
.Returns(Task.FromResult(false))
375+
.Verifiable();
376+
store.Setup(s => s.IsInRoleAsync(user, "#C", CancellationToken.None))
377+
.Returns(Task.FromResult(false))
378+
.Verifiable();
379+
var userManager = MockHelpers.TestUserManager<PocoUser>(store.Object);
380+
userManager.KeyNormalizer = new CustomNormalizer();
381+
382+
// Act
383+
var result = await userManager.AddToRolesAsync(user, roles);
384+
385+
// Assert
386+
Assert.True(result.Succeeded);
387+
store.VerifyAll();
388+
store.Verify(s => s.AddToRoleAsync(user, "#C", CancellationToken.None), Times.Once());
389+
}
390+
310391
[Fact]
311392
public async Task AddToRolesFailsIfUserInRole()
312393
{
@@ -1664,4 +1745,4 @@ public override IdentityError DuplicateEmail(string email)
16641745
}
16651746

16661747
}
1667-
}
1748+
}

0 commit comments

Comments
 (0)