| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361 |
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Threading.Tasks;
- 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 IwbZero.Authorization.Base;
- using IwbZero.Authorization.Base.Users;
- using IwbZero.Authorization.Roles;
- using IwbZero.IdentityFramework;
- using IwbZero.MultiTenancy;
- using IwbZero.Zero.Configuration;
- using Microsoft.AspNet.Identity;
- namespace IwbZero.Authorization.Users
- {
- public abstract class IwbLogInManager<TTenant, TRole, TUser> : ITransientDependency
- where TTenant : IwbTenant<TUser>
- where TRole : IwbSysRole<TUser>, new()
- where TUser : IwbSysUser<TUser>
- {
- public IClientInfoProvider ClientInfoProvider { get; set; }
- protected IMultiTenancyConfig MultiTenancyConfig { get; }
- protected IRepository<TTenant> TenantRepository { get; }
- protected IUnitOfWorkManager UnitOfWorkManager { get; }
- protected IwbUserManager<TRole, TUser> UserManager { get; }
- protected ISettingManager SettingManager { get; }
- protected IRepository<UserLoginAttempt, long> UserLoginAttemptRepository { get; }
- protected IUserManagementConfig UserManagementConfig { get; }
- protected IIocResolver IocResolver { get; }
- protected IwbRoleManager<TRole, TUser> RoleManager { get; }
- protected IwbLogInManager(
- IwbUserManager<TRole, TUser> userManager,
- IMultiTenancyConfig multiTenancyConfig,
- IRepository<TTenant> tenantRepository,
- IUnitOfWorkManager unitOfWorkManager,
- ISettingManager settingManager,
- IRepository<UserLoginAttempt, long> userLoginAttemptRepository,
- IUserManagementConfig userManagementConfig,
- IIocResolver iocResolver,
- IwbRoleManager<TRole, TUser> roleManager)
- {
- MultiTenancyConfig = multiTenancyConfig;
- TenantRepository = tenantRepository;
- UnitOfWorkManager = unitOfWorkManager;
- SettingManager = settingManager;
- UserLoginAttemptRepository = userLoginAttemptRepository;
- UserManagementConfig = userManagementConfig;
- IocResolver = iocResolver;
- RoleManager = roleManager;
- UserManager = userManager;
- ClientInfoProvider = NullClientInfoProvider.Instance;
- }
- [UnitOfWork]
- public virtual async Task<IwbLoginResult<TTenant, TUser>> LoginAsync(UserLoginInfo login, string tenancyName = null)
- {
- var result = await LoginAsyncInternal(login, tenancyName);
- await SaveLoginAttempt(result, tenancyName, login.ProviderKey + "@" + login.LoginProvider);
- return result;
- }
- protected virtual async Task<IwbLoginResult<TTenant, TUser>> LoginAsyncInternal(UserLoginInfo login, string tenancyName)
- {
- if (login == null || login.LoginProvider.IsNullOrEmpty() || login.ProviderKey.IsNullOrEmpty())
- {
- throw new ArgumentException("login");
- }
- //Get and check tenant
- TTenant 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 IwbLoginResult<TTenant, TUser>(IwbLoginResultType.InvalidTenancyName);
- }
- if (!tenant.IsActive)
- {
- return new IwbLoginResult<TTenant, TUser>(IwbLoginResultType.TenantIsNotActive, tenant);
- }
- }
- int? tenantId = tenant == null ? (int?)null : tenant.Id;
- using (UnitOfWorkManager.Current.SetTenantId(tenantId))
- {
- var user = await UserManager.IwbStore.FindAsync(tenantId, login);
- if (user == null)
- {
- return new IwbLoginResult<TTenant, TUser>(IwbLoginResultType.UnknownExternalLogin, tenant);
- }
- return await CreateLoginResultAsync(user, tenant);
- }
- }
- [UnitOfWork]
- public virtual async Task<IwbLoginResult<TTenant, TUser>> LoginAsync(string userNameOrEmailAddress, string plainPassword, string tenancyName = null, bool shouldLockout = true)
- {
- var result = await LoginAsyncInternal(userNameOrEmailAddress, plainPassword, tenancyName, shouldLockout);
- await SaveLoginAttempt(result, tenancyName, userNameOrEmailAddress);
- return result;
- }
- protected virtual async Task<IwbLoginResult<TTenant, TUser>> LoginAsyncInternal(string userNameOrEmailAddress, string plainPassword, string tenancyName, bool shouldLockout)
- {
- if (userNameOrEmailAddress.IsNullOrEmpty())
- {
- throw new ArgumentNullException(nameof(userNameOrEmailAddress));
- }
- if (plainPassword.IsNullOrEmpty())
- {
- throw new ArgumentNullException(nameof(plainPassword));
- }
- //Get and check tenant
- TTenant tenant = null;
- using (UnitOfWorkManager.Current.SetTenantId(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 IwbLoginResult<TTenant, TUser>(IwbLoginResultType.InvalidTenancyName);
- }
- if (!tenant.IsActive)
- {
- return new IwbLoginResult<TTenant, TUser>(IwbLoginResultType.TenantIsNotActive, tenant);
- }
- }
- }
- var tenantId = tenant == null ? (int?)null : tenant.Id;
- using (UnitOfWorkManager.Current.SetTenantId(tenantId))
- {
- //TryLoginFromExternalAuthenticationSources method may create the user, that's why we are calling it before IwbStore.FindByNameOrEmailOrMobileAsync
- var loggedInFromExternalSource = await TryLoginFromExternalAuthenticationSources(userNameOrEmailAddress, plainPassword, tenant);
- var user = await UserManager.IwbStore.FindByNameOrEmailOrMobileAsync(tenantId, userNameOrEmailAddress);
- if (user == null)
- {
- return new IwbLoginResult<TTenant, TUser>(IwbLoginResultType.InvalidUserNameOrEmailAddress, tenant);
- }
- if (await UserManager.IsLockedOutAsync(user.Id))
- {
- return new IwbLoginResult<TTenant, TUser>(IwbLoginResultType.LockedOut, tenant, user);
- }
- if (!loggedInFromExternalSource)
- {
- UserManager.InitializeLockoutSettings(tenantId);
- var verificationResult = UserManager.PasswordHasher.VerifyHashedPassword(user.Password, plainPassword);
- if (verificationResult == PasswordVerificationResult.Failed)
- {
- return await GetFailedPasswordValidationAsLoginResultAsync(user, tenant, shouldLockout);
- }
- if (verificationResult == PasswordVerificationResult.SuccessRehashNeeded)
- {
- return await GetSuccessRehashNeededAsLoginResultAsync(user, tenant);
- }
- await UserManager.ResetAccessFailedCountAsync(user.Id);
- }
- return await CreateLoginResultAsync(user, tenant);
- }
- }
- protected virtual async Task<IwbLoginResult<TTenant, TUser>> GetFailedPasswordValidationAsLoginResultAsync(TUser user, TTenant tenant = null, bool shouldLockout = false)
- {
- if (shouldLockout)
- {
- if (await TryLockOutAsync(user.TenantId, user.Id))
- {
- return new IwbLoginResult<TTenant, TUser>(IwbLoginResultType.LockedOut, tenant, user);
- }
- }
- return new IwbLoginResult<TTenant, TUser>(IwbLoginResultType.InvalidPassword, tenant, user);
- }
- protected virtual async Task<IwbLoginResult<TTenant, TUser>> GetSuccessRehashNeededAsLoginResultAsync(TUser user, TTenant tenant = null, bool shouldLockout = false)
- {
- return await GetFailedPasswordValidationAsLoginResultAsync(user, tenant, shouldLockout);
- }
- protected virtual async Task<IwbLoginResult<TTenant, TUser>> CreateLoginResultAsync(TUser user, TTenant tenant = null)
- {
- if (!user.IsActive)
- {
- return new IwbLoginResult<TTenant, TUser>(IwbLoginResultType.UserIsNotActive);
- }
- if (await IsEmailConfirmationRequiredForLoginAsync(user.TenantId) && !user.IsEmailConfirmed)
- {
- return new IwbLoginResult<TTenant, TUser>(IwbLoginResultType.UserEmailIsNotConfirmed);
- }
- return new IwbLoginResult<TTenant, TUser>(
- tenant,
- user,
- await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie)
- );
- }
- protected virtual async Task SaveLoginAttempt(IwbLoginResult<TTenant, TUser> loginResult, string tenancyName, string userNameOrEmailAddress)
- {
- using (var uow = UnitOfWorkManager.Begin(TransactionScopeOption.Suppress))
- {
- var tenantId = loginResult.Tenant != null ? loginResult.Tenant.Id : (int?)null;
- using (UnitOfWorkManager.Current.SetTenantId(tenantId))
- {
- var loginAttempt = new UserLoginAttempt
- {
- TenantId = tenantId,
- TenancyName = tenancyName,
- UserId = loginResult.User != null ? loginResult.User.Id : (long?)null,
- UserNameOrEmailAddress = userNameOrEmailAddress,
- 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 async Task<bool> TryLockOutAsync(int? tenantId, long userId)
- {
- using (var uow = UnitOfWorkManager.Begin(TransactionScopeOption.Suppress))
- {
- using (UnitOfWorkManager.Current.SetTenantId(tenantId))
- {
- (await UserManager.AccessFailedAsync(userId)).CheckErrors();
- var isLockOut = await UserManager.IsLockedOutAsync(userId);
- await UnitOfWorkManager.Current.SaveChangesAsync();
- await uow.CompleteAsync();
- return isLockOut;
- }
- }
- }
- protected virtual async Task<bool> TryLoginFromExternalAuthenticationSources(string userNameOrEmailAddress, string plainPassword, TTenant tenant)
- {
- if (!UserManagementConfig.ExternalAuthenticationSources.Any())
- {
- return false;
- }
- foreach (var sourceType in UserManagementConfig.ExternalAuthenticationSources)
- {
- using (var source = IocResolver.ResolveAsDisposable<IExternalAuthenticationSource<TTenant, TUser>>(sourceType))
- {
- if (await source.Object.TryAuthenticateAsync(userNameOrEmailAddress, plainPassword, tenant))
- {
- var tenantId = tenant == null ? (int?)null : tenant.Id;
- using (UnitOfWorkManager.Current.SetTenantId(tenantId))
- {
- var user = await UserManager.IwbStore.FindByNameOrEmailOrMobileAsync(tenantId, userNameOrEmailAddress);
- if (user == null)
- {
- user = await source.Object.CreateUserAsync(userNameOrEmailAddress, tenant);
- user.TenantId = tenantId;
- user.AuthenticationSource = source.Object.Name;
- user.Password = UserManager.PasswordHasher.HashPassword(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 defaultRole in RoleManager.Roles.Where(r => r.TenantId == tenantId && r.IsDefault).ToList())
- {
- user.Roles.Add(new UserRole(tenantId, user.Id, defaultRole.Id));
- }
- }
- await UserManager.IwbStore.CreateAsync(user);
- }
- else
- {
- await source.Object.UpdateUserAsync(user, tenant);
- user.AuthenticationSource = source.Object.Name;
- await UserManager.IwbStore.UpdateAsync(user);
- }
- await UnitOfWorkManager.Current.SaveChangesAsync();
- return true;
- }
- }
- }
- }
- return false;
- }
- protected virtual async Task<TTenant> GetDefaultTenantAsync()
- {
- var tenant = await TenantRepository.FirstOrDefaultAsync(t => t.TenancyName == TenantBase.DefaultTenantName);
- if (tenant == null)
- {
- throw new AbpException("There should be a 'Default' tenant if multi-tenancy is disabled!");
- }
- return tenant;
- }
- protected virtual async Task<bool> IsEmailConfirmationRequiredForLoginAsync(int? tenantId)
- {
- if (tenantId.HasValue)
- {
- return await SettingManager.GetSettingValueForTenantAsync<bool>(IwbZeroSettingNames.UserManagement.IsEmailConfirmationRequiredForLogin, tenantId.Value);
- }
- return await SettingManager.GetSettingValueForApplicationAsync<bool>(IwbZeroSettingNames.UserManagement.IsEmailConfirmationRequiredForLogin);
- }
- }
- }
|