| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445 |
- #nullable enable
- using System.Security.Claims;
- using System.Transactions;
- using Abp;
- using Abp.Auditing;
- using Abp.Configuration;
- using Abp.Configuration.Startup;
- using Abp.Dependency;
- using Abp.Domain.Repositories;
- using Abp.Domain.Uow;
- using Abp.Extensions;
- using Microsoft.AspNetCore.Identity;
- using VberZero.Authorization.Roles;
- using VberZero.Authorization.Users;
- using VberZero.BaseSystem.MultiTenancy;
- using VberZero.BaseSystem.Roles;
- using VberZero.BaseSystem.Users;
- using VberZero.Configuration;
- using VberZero.IdentityFramework;
- using VberZero.Settings;
- namespace VberZero.Authorization;
- public class VzLogInManager : ITransientDependency
- {
- public IClientInfoProvider ClientInfoProvider { get; set; }
- protected IMultiTenancyConfig MultiTenancyConfig { get; }
- protected IRepository<Tenant?> TenantRepository { get; }
- protected IUnitOfWorkManager UnitOfWorkManager { get; }
- protected VzUserManager UserManager { get; }
- protected ISettingManager SettingManager { get; }
- protected IRepository<UserLoginAttempt, long> UserLoginAttemptRepository { get; }
- protected IUserManagementConfig UserManagementConfig { get; }
- protected IIocResolver IocResolver { get; }
- protected VzRoleManager RoleManager { get; }
- private readonly IPasswordHasher<User> _passwordHasher;
- private readonly UserClaimsPrincipalFactory<User, Role> _claimsPrincipalFactory;
- public VzLogInManager(
- VzUserManager userManager,
- IMultiTenancyConfig multiTenancyConfig,
- IRepository<Tenant?> tenantRepository,
- IUnitOfWorkManager unitOfWorkManager,
- ISettingManager settingManager,
- IRepository<UserLoginAttempt, long> userLoginAttemptRepository,
- IUserManagementConfig userManagementConfig,
- IIocResolver iocResolver,
- IPasswordHasher<User> passwordHasher,
- VzRoleManager roleManager,
- UserClaimsPrincipalFactory<User, Role> claimsPrincipalFactory)
- {
- _passwordHasher = passwordHasher;
- _claimsPrincipalFactory = claimsPrincipalFactory;
- MultiTenancyConfig = multiTenancyConfig;
- TenantRepository = tenantRepository;
- UnitOfWorkManager = unitOfWorkManager;
- SettingManager = settingManager;
- UserLoginAttemptRepository = userLoginAttemptRepository;
- UserManagementConfig = userManagementConfig;
- IocResolver = iocResolver;
- RoleManager = roleManager;
- UserManager = userManager;
- ClientInfoProvider = NullClientInfoProvider.Instance;
- }
- public virtual async Task<AbpLoginResult> LoginAsync(UserLoginInfo login, string? tenancyName = null)
- {
- return await UnitOfWorkManager.WithUnitOfWorkAsync(async () =>
- {
- var result = await LoginAsyncInternal(login, tenancyName);
- await SaveLoginAttemptAsync(result, tenancyName, login.ProviderKey + "@" + login.LoginProvider);
- return result;
- });
- }
- protected virtual async Task<AbpLoginResult> LoginAsyncInternal(UserLoginInfo login, string? tenancyName)
- {
- if (login == null || login.LoginProvider.IsNullOrEmpty() || login.ProviderKey.IsNullOrEmpty())
- {
- throw new ArgumentException("login");
- }
- //Get and check tenant
- Tenant? tenant = null;
- if (!MultiTenancyConfig.IsEnabled)
- {
- tenant = await GetDefaultTenantAsync();
- }
- else if (!string.IsNullOrWhiteSpace(tenancyName))
- {
- tenant = await TenantRepository.FirstOrDefaultAsync(t => t!.TenancyName == tenancyName);
- if (tenant == null)
- {
- return new AbpLoginResult(VzLoginResultType.InvalidTenancyName);
- }
- if (!tenant.IsActive)
- {
- return new AbpLoginResult(VzLoginResultType.TenantIsNotActive, tenant);
- }
- }
- int? tenantId = tenant?.Id;
- using (UnitOfWorkManager.Current.SetTenantId(tenantId))
- {
- var user = await UserManager.FindAsync(tenantId, login);
- if (user == null)
- {
- return new AbpLoginResult(VzLoginResultType.UnknownExternalLogin, tenant);
- }
- return await CreateLoginResultAsync(user, tenant);
- }
- }
- public virtual async Task<AbpLoginResult> LoginAsync(string userNameOrEmailOrPhone, string plainPassword, string? tenancyName = null, bool shouldLockout = true)
- {
- return await UnitOfWorkManager.WithUnitOfWorkAsync(async () =>
- {
- var result = await LoginAsyncInternal(userNameOrEmailOrPhone, plainPassword, tenancyName, shouldLockout);
- await SaveLoginAttemptAsync(result, tenancyName, userNameOrEmailOrPhone);
- return result;
- });
- }
- protected virtual async Task<AbpLoginResult> LoginAsyncInternal(string userNameOrEmailOrPhone, string plainPassword, string? tenancyName, bool shouldLockout)
- {
- if (userNameOrEmailOrPhone.IsNullOrEmpty())
- {
- throw new ArgumentNullException(nameof(userNameOrEmailOrPhone));
- }
- if (plainPassword.IsNullOrEmpty())
- {
- throw new ArgumentNullException(nameof(plainPassword));
- }
- //Get and check tenant
- Tenant? tenant = null;
- using (UnitOfWorkManager.Current.SetTenantId(null))
- {
- if (!MultiTenancyConfig.IsEnabled)
- {
- tenant = await GetDefaultTenantAsync();
- }
- else if (!string.IsNullOrWhiteSpace(tenancyName))
- {
- tenant = await TenantRepository.FirstOrDefaultAsync(t => t != null && t.TenancyName == tenancyName);
- if (tenant == null)
- {
- return new AbpLoginResult(VzLoginResultType.InvalidTenancyName);
- }
- if (!tenant.IsActive)
- {
- return new AbpLoginResult(VzLoginResultType.TenantIsNotActive, tenant);
- }
- }
- }
- var tenantId = tenant?.Id;
- using (UnitOfWorkManager.Current.SetTenantId(tenantId))
- {
- await UserManager.InitializeOptionsAsync(tenantId);
- //TryLoginFromExternalAuthenticationSources method may create the user, that's why we are calling it before UserStore.FindByNameOrEmailAsync
- var loggedInFromExternalSource =
- await TryLoginFromExternalAuthenticationSourcesAsync(userNameOrEmailOrPhone, plainPassword, tenant);
- var user = await UserManager.FindByNameOrEmailOrPhoneAsync(tenantId, userNameOrEmailOrPhone);
- if (user == null)
- {
- return new AbpLoginResult(VzLoginResultType.InvaliduserNameOrEmailOrPhone, tenant);
- }
- if (await UserManager.IsLockedOutAsync(user))
- {
- return new AbpLoginResult(VzLoginResultType.LockedOut, tenant, user);
- }
- if (!loggedInFromExternalSource)
- {
- if (!await UserManager.CheckPasswordAsync(user, plainPassword))
- {
- if (shouldLockout)
- {
- if (await TryLockOutAsync(tenantId, user.Id))
- {
- return new AbpLoginResult(VzLoginResultType.LockedOut, tenant, user);
- }
- }
- return new AbpLoginResult(VzLoginResultType.InvalidPassword, tenant, user);
- }
- await UserManager.ResetAccessFailedCountAsync(user);
- }
- return await CreateLoginResultAsync(user, tenant);
- }
- }
- protected virtual async Task<AbpLoginResult> CreateLoginResultAsync(User user,
- Tenant? tenant = null)
- {
- if (!user.IsActive)
- {
- return new AbpLoginResult(VzLoginResultType.UserIsNotActive);
- }
- if (await IsEmailConfirmationRequiredForLoginAsync(user.TenantId) && !user.IsEmailConfirmed)
- {
- return new AbpLoginResult(VzLoginResultType.UserEmailIsNotConfirmed);
- }
- if (await IsPhoneConfirmationRequiredForLoginAsync(user.TenantId) && !user.IsPhoneNumberConfirmed)
- {
- return new AbpLoginResult(VzLoginResultType.UserPhoneNumberIsNotConfirmed);
- }
- var principal = await _claimsPrincipalFactory.CreateAsync(user);
- return new AbpLoginResult(
- tenant,
- user,
- principal.Identity as ClaimsIdentity
- );
- }
- protected virtual async Task SaveLoginAttemptAsync(AbpLoginResult loginResult,
- string? tenancyName, string userNameOrEmailOrPhone)
- {
- using var uow = UnitOfWorkManager.Begin(TransactionScopeOption.Suppress);
- var tenantId = loginResult.Tenant?.Id;
- using (UnitOfWorkManager.Current.SetTenantId(tenantId))
- {
- var loginAttempt = new UserLoginAttempt
- {
- TenantId = tenantId,
- TenancyName = tenancyName,
- UserId = loginResult.User.Id,
- UserNameOrEmailOrPhone = userNameOrEmailOrPhone,
- Result = loginResult.Result,
- BrowserInfo = ClientInfoProvider.BrowserInfo,
- ClientIpAddress = ClientInfoProvider.ClientIpAddress,
- ClientName = ClientInfoProvider.ComputerName,
- };
- await UserLoginAttemptRepository.InsertAsync(loginAttempt);
- await UnitOfWorkManager.Current.SaveChangesAsync();
- await uow.CompleteAsync();
- }
- }
- protected virtual void SaveLoginAttempt(AbpLoginResult loginResult, string tenancyName,
- string userNameOrEmailOrPhone)
- {
- using var uow = UnitOfWorkManager.Begin(TransactionScopeOption.Suppress);
- var tenantId = loginResult.Tenant.Id;
- using (UnitOfWorkManager.Current.SetTenantId(tenantId))
- {
- var loginAttempt = new UserLoginAttempt
- {
- TenantId = tenantId,
- TenancyName = tenancyName,
- UserId = loginResult.User.Id,
- UserNameOrEmailOrPhone = userNameOrEmailOrPhone,
- Result = loginResult.Result,
- BrowserInfo = ClientInfoProvider.BrowserInfo,
- ClientIpAddress = ClientInfoProvider.ClientIpAddress,
- ClientName = ClientInfoProvider.ComputerName,
- };
- UserLoginAttemptRepository.Insert(loginAttempt);
- UnitOfWorkManager.Current.SaveChanges();
- uow.Complete();
- }
- }
- protected virtual async Task<bool> TryLockOutAsync(int? tenantId, long userId)
- {
- using (var uow = UnitOfWorkManager.Begin(TransactionScopeOption.Suppress))
- {
- using (UnitOfWorkManager.Current.SetTenantId(tenantId))
- {
- var user = await UserManager.FindByIdAsync(userId.ToString());
- (await UserManager.AccessFailedAsync(user)).CheckErrors();
- var isLockOut = await UserManager.IsLockedOutAsync(user);
- await UnitOfWorkManager.Current.SaveChangesAsync();
- await uow.CompleteAsync();
- return isLockOut;
- }
- }
- }
- protected virtual async Task<bool> TryLoginFromExternalAuthenticationSourcesAsync(string userNameOrEmailOrPhone,
- string plainPassword, Tenant? tenant)
- {
- if (!UserManagementConfig.ExternalAuthenticationSources.Any())
- {
- return false;
- }
- foreach (var sourceType in UserManagementConfig.ExternalAuthenticationSources)
- {
- using (var source =
- IocResolver.ResolveAsDisposable<IExternalAuthenticationSource>(sourceType))
- {
- if (await source.Object.TryAuthenticateAsync(userNameOrEmailOrPhone, plainPassword, tenant))
- {
- var tenantId = tenant == null ? (int?)null : tenant.Id;
- using (UnitOfWorkManager.Current.SetTenantId(tenantId))
- {
- var user = await UserManager.FindByNameOrEmailOrPhoneAsync(tenantId, userNameOrEmailOrPhone);
- if (user == null)
- {
- user = await source.Object.CreateUserAsync(userNameOrEmailOrPhone, tenant);
- user.TenantId = tenantId;
- user.AuthenticationSource = source.Object.Name;
- user.Password =
- _passwordHasher.HashPassword(user,
- Guid.NewGuid().ToString("N")
- .Left(16)); //Setting a random password since it will not be used
- user.SetNormalizedNames();
- if (user.Roles == null)
- {
- user.Roles = new List<UserRole>();
- foreach (var defaulRole in RoleManager.Roles
- .Where(r => r.TenantId == tenantId && r.IsDefault).ToList())
- {
- user.Roles.Add(new UserRole(tenantId, user.Id, defaulRole.Id));
- }
- }
- await UserManager.CreateAsync(user);
- }
- else
- {
- await source.Object.UpdateUserAsync(user, tenant);
- user.AuthenticationSource = source.Object.Name;
- await UserManager.UpdateAsync(user);
- }
- await UnitOfWorkManager.Current.SaveChangesAsync();
- return true;
- }
- }
- }
- }
- return false;
- }
- protected virtual async Task<Tenant?> GetDefaultTenantAsync()
- {
- var tenant = await TenantRepository.FirstOrDefaultAsync(
- t => t != null && t.TenancyName == Tenant.DefaultTenantName
- );
- if (tenant == null)
- {
- throw new AbpException("如果禁用多租户,则应该有一个“默认”租户!");
- }
- return tenant;
- }
- protected virtual Tenant GetDefaultTenant()
- {
- var tenant = TenantRepository.FirstOrDefault(t => t != null && t.TenancyName == Tenant.DefaultTenantName);
- if (tenant == null)
- {
- throw new AbpException("如果禁用多租户,则应该有一个“默认”租户!");
- }
- return tenant;
- }
- protected virtual async Task<bool> IsEmailConfirmationRequiredForLoginAsync(int? tenantId)
- {
- if (tenantId.HasValue)
- {
- return await SettingManager.GetSettingValueForTenantAsync<bool>(
- VzSettingNames.UserManagement.IsEmailConfirmationRequiredForLogin,
- tenantId.Value
- );
- }
- return await SettingManager.GetSettingValueForApplicationAsync<bool>(
- VzSettingNames.UserManagement.IsEmailConfirmationRequiredForLogin
- );
- }
- protected virtual bool IsEmailConfirmationRequiredForLogin(int? tenantId)
- {
- if (tenantId.HasValue)
- {
- return SettingManager.GetSettingValueForTenant<bool>(
- VzSettingNames.UserManagement.IsEmailConfirmationRequiredForLogin,
- tenantId.Value
- );
- }
- return SettingManager.GetSettingValueForApplication<bool>(
- VzSettingNames.UserManagement.IsEmailConfirmationRequiredForLogin
- );
- }
- protected virtual Task<bool> IsPhoneConfirmationRequiredForLoginAsync(int? tenantId)
- {
- return Task.FromResult(false);
- }
- protected virtual bool IsPhoneConfirmationRequiredForLogin(int? tenantId)
- {
- return false;
- }
- }
|