diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserManager.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserManager.cs index 46f7a635f0..73f997d982 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserManager.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserManager.cs @@ -555,22 +555,18 @@ public class IdentityUserManager : UserManager, IDomainService } 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())); + // The AllowedUserNameCharacters includes at least 4 distinct digits. So, we are picking 4 random digits from them and appending to the username. + var allowedDigits = Options.User.AllowedUserNameCharacters.Where(char.IsDigit).Distinct().ToArray(); tryCount = 0; do { - var randomUserName = userName + RandomHelper.GetRandom(min, max); - if ( await ValidateUserNameAsync(randomUserName)) + var randomDigits = new char[4]; + for (var i = 0; i < randomDigits.Length; i++) + { + randomDigits[i] = allowedDigits[RandomHelper.GetRandom(0, allowedDigits.Length)]; + } + var randomUserName = userName + new string(randomDigits); + if (await ValidateUserNameAsync(randomUserName)) { return randomUserName; } diff --git a/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/IdentityUserManager_Tests.cs b/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/IdentityUserManager_Tests.cs index 82c133e698..f027444e55 100644 --- a/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/IdentityUserManager_Tests.cs +++ b/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/IdentityUserManager_Tests.cs @@ -441,7 +441,7 @@ public class IdentityUserManager_Tests : AbpIdentityDomainTestBase 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(); + Regex.IsMatch(username, @"[0-4]{4}$").ShouldBeTrue(); _identityUserManager.Options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyz"; username = await _identityUserManager.GetUserNameFromEmailAsync("admin@abp.io"); @@ -462,6 +462,51 @@ public class IdentityUserManager_Tests : AbpIdentityDomainTestBase Regex.IsMatch(username, @"[0-9]{4}$").ShouldBeTrue(); } + [Fact] + public async Task GetUserNameFromEmailAsync_Should_Only_Use_Allowed_Digits_When_AllowedUserNameCharacters_Has_Partial_Digits() + { + _identityUserManager.Options.User.AllowedUserNameCharacters = "admin01234"; + for (var i = 0; i < 200; i++) + { + var username = await _identityUserManager.GetUserNameFromEmailAsync("admin@abp.io"); + username.Length.ShouldBe(9); + username.ShouldStartWith("admin"); + username.All(c => "admin01234".Contains(c)).ShouldBeTrue($"username '{username}' contains chars outside AllowedUserNameCharacters"); + } + } + + [Fact] + public async Task GetUserNameFromEmailAsync_Should_Only_Use_Allowed_Digits_With_Exact_Four_Digits() + { + _identityUserManager.Options.User.AllowedUserNameCharacters = "admin1234"; + for (var i = 0; i < 200; i++) + { + var username = await _identityUserManager.GetUserNameFromEmailAsync("admin@abp.io"); + username.Length.ShouldBe(9); + username.ShouldStartWith("admin"); + username.Substring(5).All(c => "1234".Contains(c)).ShouldBeTrue($"suffix of '{username}' contains chars outside the allowed digits"); + } + } + + [Fact] + public async Task GetUserNameFromEmailAsync_Should_Allow_Leading_Zero_In_Random_Digits() + { + _identityUserManager.Options.User.AllowedUserNameCharacters = "admin0123"; + var sawLeadingZero = false; + for (var i = 0; i < 100; i++) + { + var username = await _identityUserManager.GetUserNameFromEmailAsync("admin@abp.io"); + username.Length.ShouldBe(9); + username.ShouldStartWith("admin"); + username.Substring(5).All(c => "0123".Contains(c)).ShouldBeTrue($"suffix of '{username}' contains chars outside the allowed digits"); + if (username[5] == '0') + { + sawLeadingZero = true; + } + } + sawLeadingZero.ShouldBeTrue("expected at least one username with leading-zero random suffix across 100 runs"); + } + private async Task CreateRandomDefaultRoleAsync() { await _identityRoleRepository.InsertAsync(