mirror of https://github.com/abpframework/abp.git
csharpabpc-sharpframeworkblazoraspnet-coredotnet-coreaspnetcorearchitecturesaasdomain-driven-designangularmulti-tenancy
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
492 lines
19 KiB
492 lines
19 KiB
using System;
|
|
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;
|
|
using NSubstitute;
|
|
using Shouldly;
|
|
using Volo.Abp.EventBus.Distributed;
|
|
using Volo.Abp.Identity.Settings;
|
|
using Volo.Abp.MultiTenancy;
|
|
using Volo.Abp.Uow;
|
|
using Xunit;
|
|
|
|
namespace Volo.Abp.Identity;
|
|
|
|
public class IdentityUserManager_Tests : AbpIdentityDomainTestBase
|
|
{
|
|
private readonly IdentityUserManager _identityUserManager;
|
|
private readonly IIdentityUserRepository _identityUserRepository;
|
|
private readonly IIdentityRoleRepository _identityRoleRepository;
|
|
private readonly IOrganizationUnitRepository _organizationUnitRepository;
|
|
private readonly ILookupNormalizer _lookupNormalizer;
|
|
private readonly IUnitOfWorkManager _unitOfWorkManager;
|
|
private readonly IdentityTestData _testData;
|
|
private readonly ICurrentTenant _currentTenant;
|
|
private readonly IOptions<IdentityOptions> _identityOptions;
|
|
private readonly IdentityLinkUserManager _identityLinkUserManager;
|
|
|
|
private IDistributedEventBus _distributedEventBus { get; set; }
|
|
|
|
public IdentityUserManager_Tests()
|
|
{
|
|
_identityUserManager = GetRequiredService<IdentityUserManager>();
|
|
_identityUserRepository = GetRequiredService<IIdentityUserRepository>();
|
|
_identityRoleRepository = GetRequiredService<IIdentityRoleRepository>();
|
|
_organizationUnitRepository = GetRequiredService<IOrganizationUnitRepository>();
|
|
_lookupNormalizer = GetRequiredService<ILookupNormalizer>();
|
|
_testData = GetRequiredService<IdentityTestData>();
|
|
_unitOfWorkManager = GetRequiredService<IUnitOfWorkManager>();
|
|
_currentTenant = GetRequiredService<ICurrentTenant>();
|
|
_identityOptions = GetRequiredService<IOptions<IdentityOptions>>();
|
|
_identityLinkUserManager = GetRequiredService<IdentityLinkUserManager>();
|
|
}
|
|
|
|
protected override void AfterAddApplication(IServiceCollection services)
|
|
{
|
|
_distributedEventBus = Substitute.For<IDistributedEventBus>();
|
|
services.Replace(ServiceDescriptor.Singleton(_distributedEventBus));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GetByIdAsync()
|
|
{
|
|
var user = await _identityUserManager.GetByIdAsync(_testData.UserJohnId);
|
|
|
|
user.ShouldNotBeNull();
|
|
user.UserName.ShouldBe("john.nash");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task SetRolesAsync()
|
|
{
|
|
using (var uow = _unitOfWorkManager.Begin())
|
|
{
|
|
var user = await _identityUserRepository.FindByNormalizedUserNameAsync(
|
|
_lookupNormalizer.NormalizeName("david")
|
|
);
|
|
|
|
user.ShouldNotBeNull();
|
|
|
|
var identityResult = await _identityUserManager.SetRolesAsync(user, new List<string>()
|
|
{
|
|
"moderator",
|
|
});
|
|
|
|
identityResult.Succeeded.ShouldBeTrue();
|
|
user.Roles.ShouldContain(x => x.RoleId == _testData.RoleModeratorId);
|
|
|
|
await uow.CompleteAsync();
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public async Task SetRoles_Should_Remove_Other_Roles()
|
|
{
|
|
using (var uow = _unitOfWorkManager.Begin())
|
|
{
|
|
var roleSupporter =
|
|
await _identityRoleRepository.FindByNormalizedNameAsync(_lookupNormalizer.NormalizeName("supporter"));
|
|
roleSupporter.ShouldNotBeNull();
|
|
|
|
var user = await _identityUserRepository.FindByNormalizedUserNameAsync(
|
|
_lookupNormalizer.NormalizeName("john.nash"));
|
|
user.ShouldNotBeNull();
|
|
|
|
var identityResult = await _identityUserManager.SetRolesAsync(user, new List<string>()
|
|
{
|
|
"admin",
|
|
});
|
|
|
|
identityResult.Succeeded.ShouldBeTrue();
|
|
user.Roles.ShouldNotContain(x => x.RoleId == _testData.RoleModeratorId);
|
|
user.Roles.ShouldNotContain(x => x.RoleId == roleSupporter.Id);
|
|
|
|
await uow.CompleteAsync();
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public async Task SetOrganizationUnitsAsync()
|
|
{
|
|
using (var uow = _unitOfWorkManager.Begin())
|
|
{
|
|
var user = await _identityUserRepository.FindByNormalizedUserNameAsync(
|
|
_lookupNormalizer.NormalizeName("david"));
|
|
user.ShouldNotBeNull();
|
|
|
|
var ou = await _organizationUnitRepository.GetAsync(
|
|
_lookupNormalizer.NormalizeName("OU11"));
|
|
ou.ShouldNotBeNull();
|
|
|
|
await _identityUserManager.SetOrganizationUnitsAsync(user, new Guid[]
|
|
{
|
|
ou.Id
|
|
});
|
|
|
|
user = await _identityUserRepository.FindByNormalizedUserNameAsync(
|
|
_lookupNormalizer.NormalizeName("david"));
|
|
user.OrganizationUnits.Count.ShouldBeGreaterThan(0);
|
|
user.OrganizationUnits.FirstOrDefault(uou => uou.OrganizationUnitId == ou.Id).ShouldNotBeNull();
|
|
|
|
await uow.CompleteAsync();
|
|
|
|
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public async Task AddDefaultRolesAsync_In_Same_Uow()
|
|
{
|
|
await _identityOptions.SetAsync();
|
|
|
|
await CreateRandomDefaultRoleAsync();
|
|
|
|
using (var uow = _unitOfWorkManager.Begin())
|
|
{
|
|
var user = CreateRandomUser();
|
|
|
|
(await _identityUserManager.CreateAsync(user)).CheckErrors();
|
|
|
|
user.Roles.Count.ShouldBe(0);
|
|
|
|
await _identityUserManager.AddDefaultRolesAsync(user);
|
|
|
|
user.Roles.Count.ShouldBeGreaterThan(0);
|
|
|
|
foreach (var roleId in user.Roles.Select(r => r.RoleId))
|
|
{
|
|
var role = await _identityRoleRepository.GetAsync(roleId);
|
|
role.IsDefault.ShouldBe(true);
|
|
}
|
|
|
|
await uow.CompleteAsync();
|
|
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public async Task SetOrganizationUnits_Should_Remove()
|
|
{
|
|
using (var uow = _unitOfWorkManager.Begin())
|
|
{
|
|
var ou = await _organizationUnitRepository.GetAsync(
|
|
_lookupNormalizer.NormalizeName("OU111"));
|
|
ou.ShouldNotBeNull();
|
|
|
|
var user = await _identityUserRepository.FindByNormalizedUserNameAsync(
|
|
_lookupNormalizer.NormalizeName("john.nash"));
|
|
user.ShouldNotBeNull();
|
|
|
|
var ouNew = await _organizationUnitRepository.GetAsync(
|
|
_lookupNormalizer.NormalizeName("OU2"));
|
|
ouNew.ShouldNotBeNull();
|
|
|
|
await _identityUserManager.SetOrganizationUnitsAsync(user, new Guid[]
|
|
{
|
|
ouNew.Id
|
|
});
|
|
|
|
user.OrganizationUnits.ShouldNotContain(x => x.OrganizationUnitId == ou.Id);
|
|
|
|
await uow.CompleteAsync();
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public async Task AddDefaultRolesAsync_In_Different_Uow()
|
|
{
|
|
await _identityOptions.SetAsync();
|
|
|
|
await CreateRandomDefaultRoleAsync();
|
|
|
|
Guid userId;
|
|
|
|
using (var uow = _unitOfWorkManager.Begin())
|
|
{
|
|
var user = CreateRandomUser();
|
|
userId = user.Id;
|
|
|
|
(await _identityUserManager.CreateAsync(user)).CheckErrors();
|
|
user.Roles.Count.ShouldBe(0);
|
|
await uow.CompleteAsync();
|
|
}
|
|
|
|
using (var uow = _unitOfWorkManager.Begin())
|
|
{
|
|
var user = await _identityUserManager.GetByIdAsync(userId);
|
|
|
|
await _identityUserManager.AddDefaultRolesAsync(user);
|
|
user.Roles.Count.ShouldBeGreaterThan(0);
|
|
|
|
foreach (var roleId in user.Roles.Select(r => r.RoleId))
|
|
{
|
|
var role = await _identityRoleRepository.GetAsync(roleId);
|
|
role.IsDefault.ShouldBe(true);
|
|
}
|
|
|
|
await uow.CompleteAsync();
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ShouldPeriodicallyChangePasswordAsync_Return_False()
|
|
{
|
|
var user = CreateRandomUser();
|
|
AddPeriodicallyChangePasswordSettings();
|
|
|
|
(await _identityUserManager.CreateAsync(user)).CheckErrors();
|
|
(await _identityUserManager.ShouldPeriodicallyChangePasswordAsync(user)).ShouldBeFalse();
|
|
|
|
await _identityUserManager.AddPasswordAsync(user, IdentityDataSeedContributor.AdminPasswordDefaultValue);
|
|
(await _identityUserManager.ShouldPeriodicallyChangePasswordAsync(user)).ShouldBeFalse();
|
|
|
|
user.CreationTime = DateTime.Now;
|
|
user.SetLastPasswordChangeTime(null);
|
|
(await _identityUserManager.ShouldPeriodicallyChangePasswordAsync(user)).ShouldBeFalse();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ShouldPeriodicallyChangePasswordAsync_Return_True()
|
|
{
|
|
var user = CreateRandomUser();
|
|
AddPeriodicallyChangePasswordSettings();
|
|
|
|
(await _identityUserManager.CreateAsync(user)).CheckErrors();
|
|
await _identityUserManager.AddPasswordAsync(user, IdentityDataSeedContributor.AdminPasswordDefaultValue);
|
|
|
|
user.SetLastPasswordChangeTime(DateTime.UtcNow.AddDays(-3));
|
|
(await _identityUserManager.ShouldPeriodicallyChangePasswordAsync(user)).ShouldBeTrue();
|
|
|
|
user.CreationTime = DateTime.Now.AddDays(-3);
|
|
user.SetLastPasswordChangeTime(null);
|
|
(await _identityUserManager.ShouldPeriodicallyChangePasswordAsync(user)).ShouldBeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task IdentityUserUserNameChangedEto_Test()
|
|
{
|
|
var user = CreateRandomUser();
|
|
|
|
(await _identityUserManager.CreateAsync(user)).CheckErrors();
|
|
|
|
await _distributedEventBus.DidNotReceive()
|
|
.PublishAsync(Arg.Any<IdentityUserUserNameChangedEto>(), Arg.Any<bool>(), Arg.Any<bool>());
|
|
|
|
var newUser = await _identityUserManager.FindByIdAsync(user.Id.ToString());
|
|
newUser.ShouldNotBeNull();
|
|
|
|
using (_currentTenant.Change(Guid.NewGuid()))
|
|
{
|
|
var oldUsername = newUser.UserName;
|
|
await _identityUserManager.SetUserNameAsync(newUser, "newUserName");
|
|
await _distributedEventBus.Received()
|
|
.PublishAsync(
|
|
Arg.Is<IdentityUserUserNameChangedEto>(x =>
|
|
x.Id == newUser.Id && x.TenantId == newUser.TenantId && x.OldUserName == oldUsername && x.UserName == "newUserName"),
|
|
Arg.Any<bool>(), Arg.Any<bool>());
|
|
}
|
|
|
|
_distributedEventBus.ClearReceivedCalls();
|
|
await _identityUserManager.SetUserNameAsync(newUser, newUser.UserName);
|
|
await _distributedEventBus.DidNotReceive()
|
|
.PublishAsync(Arg.Any<IdentityUserUserNameChangedEto>(), Arg.Any<bool>(), Arg.Any<bool>());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task IdentityUserEmailChangedEto_Test()
|
|
{
|
|
var user = CreateRandomUser();
|
|
|
|
(await _identityUserManager.CreateAsync(user)).CheckErrors();
|
|
|
|
await _distributedEventBus.DidNotReceive()
|
|
.PublishAsync(Arg.Any<IdentityUserEmailChangedEto>(), Arg.Any<bool>(), Arg.Any<bool>());
|
|
|
|
var newUser = await _identityUserManager.FindByIdAsync(user.Id.ToString());
|
|
newUser.ShouldNotBeNull();
|
|
|
|
using (_currentTenant.Change(Guid.NewGuid()))
|
|
{
|
|
var oldEmail = newUser.Email;
|
|
await _identityUserManager.SetEmailAsync(newUser, "newEmail@abp.io");
|
|
await _distributedEventBus.Received()
|
|
.PublishAsync(
|
|
Arg.Is<IdentityUserEmailChangedEto>(x =>
|
|
x.Id == newUser.Id && x.TenantId == newUser.TenantId && x.OldEmail == oldEmail && x.Email == "newEmail@abp.io"),
|
|
Arg.Any<bool>(), Arg.Any<bool>());
|
|
}
|
|
|
|
_distributedEventBus.ClearReceivedCalls();
|
|
await _identityUserManager.SetEmailAsync(newUser, newUser.Email);
|
|
await _distributedEventBus.DidNotReceive()
|
|
.PublishAsync(Arg.Any<IdentityUserEmailChangedEto>(), Arg.Any<bool>(), Arg.Any<bool>());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DeleteAsync()
|
|
{
|
|
await CreateRandomDefaultRoleAsync();
|
|
var user = CreateRandomUser();
|
|
(await _identityUserManager.CreateAsync(user)).CheckErrors();
|
|
|
|
var user2 = CreateRandomUser();
|
|
(await _identityUserManager.CreateAsync(user2)).CheckErrors();
|
|
|
|
using (var uow = _unitOfWorkManager.Begin())
|
|
{
|
|
user = await _identityUserManager.FindByIdAsync(user.Id.ToString());
|
|
user.ShouldNotBeNull();
|
|
|
|
await _identityUserManager.AddClaimAsync(user, new Claim("test", "test"));
|
|
await _identityUserManager.AddLoginAsync(user, new UserLoginInfo("test", "test", "test"));
|
|
await _identityUserManager.AddDefaultRolesAsync(user);
|
|
user.SetToken("test", "test", "test");
|
|
var ou = await _organizationUnitRepository.GetAsync(_lookupNormalizer.NormalizeName("OU11"));
|
|
await _identityUserManager.AddToOrganizationUnitAsync(user, ou);
|
|
await _identityLinkUserManager.LinkAsync(new IdentityLinkUserInfo(user.Id), new IdentityLinkUserInfo(user2.Id));
|
|
|
|
await uow.CompleteAsync();
|
|
}
|
|
|
|
using (var uow = _unitOfWorkManager.Begin())
|
|
{
|
|
user = await _identityUserManager.FindByIdAsync(user.Id.ToString());
|
|
user.ShouldNotBeNull();
|
|
|
|
user.Claims.Count.ShouldBeGreaterThan(0);
|
|
user.Logins.Count.ShouldBeGreaterThan(0);
|
|
user.Roles.Count.ShouldBeGreaterThan(0);
|
|
user.Tokens.Count.ShouldBeGreaterThan(0);
|
|
user.OrganizationUnits.Count.ShouldBeGreaterThan(0);
|
|
(await _identityLinkUserManager.IsLinkedAsync(new IdentityLinkUserInfo(user.Id), new IdentityLinkUserInfo(user2.Id))).ShouldBeTrue();
|
|
|
|
await _identityUserManager.DeleteAsync(user);
|
|
|
|
user.Claims.Count.ShouldBe(0);
|
|
user.Logins.Count.ShouldBe(0);
|
|
user.Roles.Count.ShouldBe(0);
|
|
user.Tokens.Count.ShouldBe(0);
|
|
user.OrganizationUnits.Count.ShouldBe(0);
|
|
(await _identityLinkUserManager.IsLinkedAsync(new IdentityLinkUserInfo(user.Id), new IdentityLinkUserInfo(user2.Id))).ShouldBeFalse();
|
|
|
|
await uow.CompleteAsync();
|
|
}
|
|
}
|
|
|
|
[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(
|
|
new IdentityRole(
|
|
Guid.NewGuid(),
|
|
Guid.NewGuid().ToString()
|
|
)
|
|
{
|
|
IsDefault = true
|
|
}
|
|
);
|
|
}
|
|
|
|
private static IdentityUser CreateRandomUser()
|
|
{
|
|
return new IdentityUser(
|
|
Guid.NewGuid(),
|
|
Guid.NewGuid().ToString(),
|
|
Guid.NewGuid().ToString() + "@abp.io"
|
|
);
|
|
}
|
|
|
|
private static void AddPeriodicallyChangePasswordSettings()
|
|
{
|
|
TestSettingValueProvider.AddSetting(IdentitySettingNames.Password.PasswordChangePeriodDays, 2.ToString());
|
|
TestSettingValueProvider.AddSetting(IdentitySettingNames.Password.ForceUsersToPeriodicallyChangePassword, true.ToString());
|
|
}
|
|
}
|
|
|