Browse Source

Merge pull request #18612 from abpframework/AllowedUserNameCharacters

Add `GetUserNameFromEmailAsync` to `IdentityUserManager`.
pull/18655/head
SALİH ÖZKARA 3 years ago
committed by GitHub
parent
commit
e54bb8ae05
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 19
      modules/account/src/Volo.Abp.Account.Web/Pages/Account/AccountPageModel.cs
  2. 4
      modules/account/src/Volo.Abp.Account.Web/Pages/Account/Register.cshtml.cs
  3. 125
      modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserManager.cs
  4. 84
      modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/IdentityUserManager_Tests.cs

19
modules/account/src/Volo.Abp.Account.Web/Pages/Account/AccountPageModel.cs

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using Volo.Abp.Account.Localization;
@ -55,22 +54,4 @@ public abstract class AccountPageModel : AbpPageModel
return exception.Message;
}
protected virtual async Task<string> GetUserNameFromEmail(string email)
{
var userName = email.Split('@')[0];
var existUser = await UserManager.FindByNameAsync(userName);
while (existUser != null)
{
var randomUserName = userName + RandomHelper.GetRandom(1000, 9999);
existUser = await UserManager.FindByNameAsync(randomUserName);
if (existUser == null)
{
userName = randomUserName;
break;
}
}
return userName;
}
}

4
modules/account/src/Volo.Abp.Account.Web/Pages/Account/Register.cshtml.cs

@ -102,7 +102,7 @@ public class RegisterModel : AccountPageModel
return;
}
var userName = await GetUserNameFromEmail(emailClaim.Value);
var userName = await UserManager.GetUserNameFromEmailAsync(emailClaim.Value);
Input = new PostInput { UserName = userName, EmailAddress = emailClaim.Value };
}
}
@ -128,7 +128,7 @@ public class RegisterModel : AccountPageModel
}
if (Input.UserName.IsNullOrWhiteSpace())
{
Input.UserName = await GetUserNameFromEmail(Input.EmailAddress);
Input.UserName = await UserManager.GetUserNameFromEmailAsync(Input.EmailAddress);
}
await RegisterExternalUserAsync(externalLoginInfo, Input.UserName, Input.EmailAddress);
}

125
modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserManager.cs

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
@ -82,7 +83,7 @@ public class IdentityUserManager : UserManager<IdentityUser>, IDomainService
return await CreateAsync(user);
}
public async override Task<IdentityResult> DeleteAsync(IdentityUser user)
{
user.Claims.Clear();
@ -397,4 +398,126 @@ public class IdentityUserManager : UserManager<IdentityUser>, IDomainService
await UserRepository.UpdateOrganizationAsync(sourceOrganizationId, targetOrganizationId, CancellationToken);
}
public virtual async Task<bool> ValidateUserNameAsync(string userName, Guid? userId = null)
{
if (string.IsNullOrWhiteSpace(userName))
{
return false;
}
if (!string.IsNullOrEmpty(Options.User.AllowedUserNameCharacters) && userName.Any(c => !Options.User.AllowedUserNameCharacters.Contains(c)))
{
return false;
}
var owner = await FindByNameAsync(userName);
if (owner != null && owner.Id != userId)
{
return false;
}
return true;
}
public virtual Task<string> GetRandomUserNameAsync(int length)
{
var allowedUserNameCharacters = Options.User.AllowedUserNameCharacters;
if (allowedUserNameCharacters.IsNullOrWhiteSpace())
{
allowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
}
var randomUserName = string.Empty;
var random = new Random();
while (randomUserName.Length < length)
{
randomUserName += allowedUserNameCharacters[random.Next(0, allowedUserNameCharacters.Length)];
}
return Task.FromResult(randomUserName);
}
public virtual async Task<string> GetUserNameFromEmailAsync(string email)
{
const int maxTryCount = 20;
var tryCount = 0;
var userName = email.Split('@')[0];
if (await ValidateUserNameAsync(userName))
{
// The username is valid.
return userName;
}
if (Options.User.AllowedUserNameCharacters.IsNullOrWhiteSpace())
{
// The AllowedUserNameCharacters is not set. So, we are generating a random username.
tryCount = 0;
do
{
var randomUserName = userName + RandomHelper.GetRandom(1000, 9999);
if ( await ValidateUserNameAsync(randomUserName))
{
return randomUserName;
}
tryCount++;
} while (tryCount < maxTryCount);
}
else if (!userName.All(Options.User.AllowedUserNameCharacters.Contains))
{
// The username contains not allowed characters. So, we are generating a random username.
do
{
var randomUserName = await GetRandomUserNameAsync(userName.Length);
if ( await ValidateUserNameAsync(randomUserName))
{
return randomUserName;
}
tryCount++;
} while (tryCount < maxTryCount);
}
else if (Options.User.AllowedUserNameCharacters.Where(char.IsDigit).Distinct().Count() >= 4)
{
// The AllowedUserNameCharacters includes 4 numbers. So, we are generating 4 random numbers and appending to the username.
var numbers = Options.User.AllowedUserNameCharacters.Where(char.IsDigit).OrderBy(x => Guid.NewGuid()).Take(4).ToArray();
var minArray = numbers.OrderBy(x => x).ToArray();
if (minArray[0] == '0')
{
var secondItem = minArray[1];
minArray[0] = secondItem;
minArray[1] = '0';
}
var min = int.Parse(new string(minArray));
var max = int.Parse(new string(numbers.OrderByDescending(x => x).ToArray()));
tryCount = 0;
do
{
var randomUserName = userName + RandomHelper.GetRandom(min, max);
if ( await ValidateUserNameAsync(randomUserName))
{
return randomUserName;
}
tryCount++;
} while (tryCount < maxTryCount);
}
else
{
tryCount = 0;
do
{
// The AllowedUserNameCharacters does not include numbers. So, we are generating 4 random characters and appending to the username.
var randomUserName = userName + await GetRandomUserNameAsync(4);
if (await ValidateUserNameAsync(randomUserName))
{
return randomUserName;
}
tryCount++;
} while (tryCount < maxTryCount);
}
Logger.LogError($"Could not get a valid user name for the given email address: {email}, allowed characters: {Options.User.AllowedUserNameCharacters}, tried {maxTryCount} times.");
throw new AbpIdentityResultException(IdentityResult.Failed(new IdentityErrorDescriber().InvalidUserName(userName)));
}
}

84
modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/IdentityUserManager_Tests.cs

@ -2,8 +2,10 @@
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
@ -378,6 +380,88 @@ public class IdentityUserManager_Tests : AbpIdentityDomainTestBase
}
}
[Fact]
public async Task ValidateUserNameAsync()
{
var result = await _identityUserManager.ValidateUserNameAsync("M_y+User-001@abp.io");
result.ShouldBeTrue();
var user = CreateRandomUser();
(await _identityUserManager.CreateAsync(user)).CheckErrors();
result = await _identityUserManager.ValidateUserNameAsync(user.UserName, user.Id);
result.ShouldBeTrue();
result = await _identityUserManager.ValidateUserNameAsync(user.UserName);
result.ShouldBeFalse();
result = await _identityUserManager.ValidateUserNameAsync("无效的字符");
result.ShouldBeFalse();
}
[Fact]
public async Task GetRandomUserNameAsync()
{
_identityUserManager.Options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
var username = await _identityUserManager.GetRandomUserNameAsync(15);
username.Length.ShouldBe(15);
username.All(c => _identityUserManager.Options.User.AllowedUserNameCharacters.Contains(c)).ShouldBeTrue();
_identityUserManager.Options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyz0123456789";
username = await _identityUserManager.GetRandomUserNameAsync(15);
username.Length.ShouldBe(15);
username.All(c => _identityUserManager.Options.User.AllowedUserNameCharacters.Contains(c)).ShouldBeTrue();
_identityUserManager.Options.User.AllowedUserNameCharacters = "0123456789";
username = await _identityUserManager.GetRandomUserNameAsync(15);
username.Length.ShouldBe(15);
username.All(c => _identityUserManager.Options.User.AllowedUserNameCharacters.Contains(c)).ShouldBeTrue();
_identityUserManager.Options.User.AllowedUserNameCharacters = null!;
username = await _identityUserManager.GetRandomUserNameAsync(15);
username.Length.ShouldBe(15);
username.All(c => "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+".Contains(c)).ShouldBeTrue();
}
[Fact]
public async Task GetUserNameFromEmailAsync()
{
_identityUserManager.Options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyz0123456789";
var username = await _identityUserManager.GetUserNameFromEmailAsync("Yönetici@abp.io");
username.Length.ShouldBe("Yönetici".Length); //random username
username.All(c => "abcdefghijklmnopqrstuvwxyz0123456789".Contains(c)).ShouldBeTrue();
_identityUserManager.Options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyz0123456789";
username = await _identityUserManager.GetUserNameFromEmailAsync("admin@abp.io");
username.Length.ShouldBe(9); //admin and random 4 numbers
username.ShouldContain("admin");
Regex.IsMatch(username, @"\d{4}$").ShouldBeTrue();
_identityUserManager.Options.User.AllowedUserNameCharacters = "admin01234";
username = await _identityUserManager.GetUserNameFromEmailAsync("admin@abp.io");
username.Length.ShouldBe(9); //admin and random 4 numbers
username.ShouldContain("admin");
Regex.IsMatch(username, @"[0-4]{3}$").ShouldBeTrue();
_identityUserManager.Options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyz";
username = await _identityUserManager.GetUserNameFromEmailAsync("admin@abp.io");
username.Length.ShouldBe(9); //admin and random 4 characters
username.ShouldContain("admin");
Regex.IsMatch(username, @"[a-z]{4}$").ShouldBeTrue();
_identityUserManager.Options.User.AllowedUserNameCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
username = await _identityUserManager.GetUserNameFromEmailAsync("ADMIN@abp.io");
username.Length.ShouldBe(9); //admin and random 4 characters
username.ShouldContain("ADMIN");
Regex.IsMatch(username, @"[A-Z]{4}$").ShouldBeTrue();
_identityUserManager.Options.User.AllowedUserNameCharacters = null!;
username = await _identityUserManager.GetUserNameFromEmailAsync("admin@abp.io");
username.Length.ShouldBe(9); //admin and random 4 numbers
username.ShouldContain("admin");
Regex.IsMatch(username, @"[0-9]{4}$").ShouldBeTrue();
}
private async Task CreateRandomDefaultRoleAsync()
{
await _identityRoleRepository.InsertAsync(

Loading…
Cancel
Save