using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Security.Claims; using System.Threading.Tasks; using Abp; using Abp.Application.Features; using Abp.Authorization; using Abp.Collections.Extensions; using Abp.Configuration; using Abp.Configuration.Startup; using Abp.Domain.Services; using Abp.Domain.Uow; using Abp.Localization; using Abp.MultiTenancy; using Abp.Runtime.Caching; using Abp.Runtime.Security; using IwbZero.Authorization.Base.Permissions; using IwbZero.Authorization.Base.Users; using IwbZero.Authorization.Roles; using IwbZero.IdentityFramework; using IwbZero.Runtime.Caching; using IwbZero.Runtime.Session; using IwbZero.Zero.Configuration; using Microsoft.AspNet.Identity; namespace IwbZero.Authorization.Users { public abstract class IwbUserManager : UserManager, IDomainService where TRole : IwbSysRole, new() where TUser : IwbSysUser { protected IUserPermissionStore UserPermissionStore { get { if (!(Store is IUserPermissionStore)) { throw new AbpException("Store is not IUserPermissionStore"); } return Store as IUserPermissionStore; } } public ILocalizationManager LocalizationManager { get; } protected string LocalizationSourceName { get; set; } public IIwbSession AbpSession { get; set; } public FeatureDependencyContext FeatureDependencyContext { get; set; } protected IwbRoleManager RoleManager { get; } public IwbUserStore IwbStore { get; } public IMultiTenancyConfig MultiTenancy { get; set; } private readonly IPermissionManager _permissionManager; private readonly IUnitOfWorkManager _unitOfWorkManager; private readonly ICacheManager _cacheManager; private readonly ISettingManager _settingManager; protected IwbUserManager( IwbUserStore userStore, IwbRoleManager roleManager, IPermissionManager permissionManager, IUnitOfWorkManager unitOfWorkManager, ICacheManager cacheManager, ILocalizationManager localizationManager, IdentityEmailMessageService emailService, ISettingManager settingManager, IUserTokenProviderAccessor userTokenProviderAccessor) : base(userStore) { IwbStore = userStore; RoleManager = roleManager; LocalizationManager = localizationManager; LocalizationSourceName = IwbZeroConsts.LocalizationSourceName; _settingManager = settingManager; _permissionManager = permissionManager; _unitOfWorkManager = unitOfWorkManager; _cacheManager = cacheManager; AbpSession = NullIwbSession.Instance; UserLockoutEnabledByDefault = true; DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5); MaxFailedAccessAttemptsBeforeLockout = 5; EmailService = emailService; UserTokenProvider = userTokenProviderAccessor.GetUserTokenProviderOrNull(); } public override async Task CreateAsync(TUser user) { user.SetNormalizedNames(); var result = await CheckDuplicateUsernameOrEmailAddressAsync(user.Id, user.UserName, user.EmailAddress); if (!result.Succeeded) { return result; } var tenantId = GetCurrentTenantId(); if (tenantId.HasValue && !user.TenantId.HasValue) { user.TenantId = tenantId.Value; } InitializeLockoutSettings(user.TenantId); return await base.CreateAsync(user); } /// /// Check whether a user is granted for a permission. /// /// User id /// Permission name /// public virtual async Task IsGrantedAsync(long userId, string permissionName, bool isOnlyUser = false) { return await IsGrantedAsync( userId, _permissionManager.GetPermission(permissionName), isOnlyUser ); } /// /// Check whether a user is granted for a permission. /// /// User /// Permission /// public virtual Task IsGrantedAsync(TUser user, Permission permission, bool isOnlyUser = false) { return IsGrantedAsync(user.Id, permission, isOnlyUser); } /// /// Check whether a user is granted for a permission. /// /// User id /// Permission /// public virtual async Task IsGrantedAsync(long userId, Permission permission, bool isOnlyUser = false) { //Check for multi-tenancy side if (!permission.MultiTenancySides.HasFlag(GetCurrentMultiTenancySide())) { return false; } //Check for depended features if (permission.FeatureDependency != null && GetCurrentMultiTenancySide() == MultiTenancySides.Tenant) { FeatureDependencyContext.TenantId = GetCurrentTenantId(); if (!await permission.FeatureDependency.IsSatisfiedAsync(FeatureDependencyContext)) { return false; } } //Get cached user permissions var cacheItem = await GetUserPermissionCacheItemAsync(userId); if (cacheItem == null) { return false; } //Check for user-specific value if (cacheItem.GrantedPermissions.Contains(permission.Name)) { return true; } if (cacheItem.ProhibitedPermissions.Contains(permission.Name)) { return false; } if (isOnlyUser) { return false; } //Check for roles foreach (var roleId in cacheItem.RoleIds) { if (await RoleManager.IsGrantedAsync(roleId, permission)) { return true; } } return false; } /// /// Check whether a user is granted for a permission. /// /// User id /// /// Permission /// 只查用户权限 /// public virtual async Task IsGrantedAsync(TUser user, string dataKey, int operType, Permission permission, bool isOnlyUser = false) { return await IsGrantedAsync(user.Id, dataKey, operType, permission.Name, isOnlyUser); } /// /// Check whether a user is granted for a permission. /// /// User id /// /// Permission /// 只查用户权限 /// public virtual async Task IsGrantedAsync(TUser user, string dataKey, int operType, string permissionName, bool isOnlyUser = false) { return await IsGrantedAsync(user.Id, dataKey, operType, permissionName, isOnlyUser); } /// /// Check whether a user is granted for a permission. /// /// User id /// /// Permission /// 只查用户权限 /// public virtual async Task IsGrantedAsync(long userId, string dataKey, int operType, Permission permission, bool isOnlyUser = false) { return await IsGrantedAsync(userId, dataKey, operType, permission.Name, isOnlyUser); } /// /// Check whether a user is granted for a permission. /// /// User id /// /// Permission /// 只查用户权限 /// public virtual async Task IsGrantedAsync(long userId, string dataKey, int operType, string permissionName, bool isOnlyUser = false) { //Get cached user permissions var cacheItem = await GetUserDataPermissionCacheItemAsync(userId); if (cacheItem == null) { return false; } //Check for user-specific value // ReSharper disable once PossibleUnintendedLinearSearchInSet if (cacheItem.GrantedPermissions.Contains($"{permissionName}@{dataKey}@{operType}", DataPermissionNameComparer.Instance)) { return true; } // ReSharper disable once PossibleUnintendedLinearSearchInSet if (cacheItem.ProhibitedPermissions.Contains($"{permissionName}@{dataKey}@{operType}", DataPermissionNameComparer.Instance)) { return false; } if (isOnlyUser) { return false; } //Check for roles foreach (var roleId in cacheItem.RoleIds) { if (await RoleManager.IsGrantedAsync(roleId, dataKey, operType, permissionName)) { return true; } } return false; } /// /// Gets granted permissions for a user. /// /// Role /// List of granted permissions public virtual async Task> GetGrantedPermissionsAsync(TUser user) { var permissionList = new List(); foreach (var permission in _permissionManager.GetAllPermissions()) { if (await IsGrantedAsync(user.Id, permission)) { permissionList.Add(permission); } } return permissionList; } /// /// Sets all granted permissions of a user at once. /// Prohibits all other permissions. /// /// The user /// Permissions public virtual async Task SetGrantedPermissionsAsync(TUser user, IEnumerable permissions) { var oldPermissions = await GetGrantedPermissionsAsync(user); var newPermissions = permissions.ToArray(); foreach (var permission in oldPermissions.Where(p => !newPermissions.Contains(p))) { //await ProhibitPermissionAsync(user, permission); await RemovePermissionAsync(user, permission); } foreach (var permission in newPermissions.Where(p => !oldPermissions.Contains(p))) { await GrantPermissionAsync(user, permission); } } /// /// Prohibits all permissions for a user. /// /// User public async Task ProhibitAllPermissionsAsync(TUser user) { foreach (var permission in _permissionManager.GetAllPermissions()) { await ProhibitPermissionAsync(user, permission); } } /// /// Resets all permission settings for a user. /// It removes all permission settings for the user. /// User will have permissions according to his roles. /// This method does not prohibit all permissions. /// For that, use . /// /// User public async Task ResetAllPermissionsAsync(TUser user) { await UserPermissionStore.RemoveAllPermissionSettingsAsync(user); } /// /// Grants a permission for a user if not already granted. /// /// User /// Permission public virtual async Task GrantPermissionAsync(TUser user, Permission permission) { await UserPermissionStore.RemovePermissionAsync(user, new PermissionGrantInfo(permission.Name, false)); if (await IsGrantedAsync(user.Id, permission)) { return; } await UserPermissionStore.AddPermissionAsync(user, new PermissionGrantInfo(permission.Name, true)); await SetGrantedPermissionCacheItem(user.Id, permission.Name); } public virtual async Task RemovePermissionAsync(TUser user, Permission permission) { await UserPermissionStore.RemovePermissionAsync(user, new PermissionGrantInfo(permission.Name, true)); await SetGrantedPermissionCacheItem(user.Id, permission.Name, false); } /// /// Prohibits a permission for a user if it's granted. /// /// User /// Permission public virtual async Task ProhibitPermissionAsync(TUser user, Permission permission) { await UserPermissionStore.RemovePermissionAsync(user, new PermissionGrantInfo(permission.Name, true)); if (!await IsGrantedAsync(user.Id, permission)) { return; } await UserPermissionStore.AddPermissionAsync(user, new PermissionGrantInfo(permission.Name, false)); await SetGrantedPermissionCacheItem(user.Id, permission.Name, false); } private async Task SetGrantedPermissionCacheItem(long userId, string permissionName, bool isAdded = true) { var permissionCacheItem = await GetUserPermissionCacheItemAsync(userId); if (isAdded) permissionCacheItem.GrantedPermissions.AddIfNotContains(permissionName); else permissionCacheItem.GrantedPermissions.Remove(permissionName); } public virtual async Task FindByNameOrEmailAsync(string userNameOrEmailAddress) { return await IwbStore.FindByNameOrEmailOrMobileAsync(userNameOrEmailAddress); } public virtual Task> FindAllAsync(UserLoginInfo login) { return IwbStore.FindAllAsync(login); } /// /// Gets a user by given id. /// Throws exception if no user found with given id. /// /// User id /// User /// Throws exception if no user found with given id public virtual async Task GetUserByIdAsync(long userId) { var user = await FindByIdAsync(userId); if (user == null) { throw new AbpException("There is no user with id: " + userId); } return user; } public override async Task CreateIdentityAsync(TUser user, string authenticationType) { var identity = await base.CreateIdentityAsync(user, authenticationType); identity.AddClaim(new Claim(IwbClaimTypes.UserName, user.UserName)); identity.AddClaim(new Claim(IwbClaimTypes.PhoneNumber, user.PhoneNumber ?? "")); identity.AddClaim(new Claim(IwbClaimTypes.RealName, user.Name)); identity.AddClaim(new Claim(IwbClaimTypes.UserType, user.UserType.ToString())); identity.AddClaim(new Claim(IwbClaimTypes.EmailAddress, user.EmailAddress)); identity.AddClaim(new Claim(IwbClaimTypes.AccountType, user.AccountType.ToString())); identity.AddClaim(new Claim(IwbClaimTypes.AccountNo, user.AccountNo ?? "")); identity.AddClaim(new Claim(IwbClaimTypes.AvatarImagePath, user.ImagePath ?? "")); IList roleList = await GetRolesAsync(user.Id); string userRoles = roleList.Any() ? string.Join(",", roleList.ToArray()) : ""; identity.AddClaim(new Claim(IwbClaimTypes.UserRoles, userRoles)); var roleIdList = user.Roles?.Select(a => a.RoleId.ToString()) ?? new List(); string userRoleIds = roleList.Any() ? string.Join("|", roleIdList.ToArray()) : ""; identity.AddClaim(new Claim(IwbClaimTypes.UserRoleIds, userRoleIds)); if (user.TenantId.HasValue) { identity.AddClaim(new Claim(AbpClaimTypes.TenantId, user.TenantId.Value.ToString(CultureInfo.InvariantCulture))); } return identity; } public override async Task UpdateAsync(TUser user) { user.SetNormalizedNames(); var result = await CheckDuplicateUsernameOrEmailAddressAsync(user.Id, user.UserName, user.EmailAddress); if (!result.Succeeded) { return result; } var oldName = await GetOldUserNameAsync(user.Id); //Admin user's username can not be changed! if (user.UserName != UserBase.AdminUserName) { if (oldName == UserBase.AdminUserName) { return AbpIdentityResult.Failed(string.Format(L("CanNotRenameAdminUser"), UserBase.AdminUserName)); } } if (user.UserName != UserBase.SystemUserName) { if (oldName == UserBase.SystemUserName) { return AbpIdentityResult.Failed(string.Format(L("CanNotRenameAdminUser"), UserBase.SystemUserName)); } } return await base.UpdateAsync(user); } public override async Task DeleteAsync(TUser user) { if (user.UserName == UserBase.AdminUserName) { return AbpIdentityResult.Failed(string.Format(L("CanNotDeleteAdminUser"), UserBase.AdminUserName)); } if (user.UserName == UserBase.SystemUserName) { return AbpIdentityResult.Failed(string.Format(L("CanNotDeleteAdminUser"), UserBase.SystemUserName)); } return await base.DeleteAsync(user); } public virtual async Task ChangePasswordAsync(TUser user, string newPassword) { var result = await PasswordValidator.ValidateAsync(newPassword); if (!result.Succeeded) { return result; } await IwbStore.SetPasswordHashAsync(user, PasswordHasher.HashPassword(newPassword)); return IdentityResult.Success; } public virtual async Task UnLockUserLogin(TUser user) { if (await IwbStore.GetLockoutEnabledAsync(user)) { await IwbStore.SetLockoutEnabledAsync(user, false); } } public virtual async Task CheckDuplicateUsernameOrEmailAddressAsync(long? expectedUserId, string userName, string emailAddress) { var user = (await FindByNameAsync(userName)); if (user != null && user.Id != expectedUserId) { return AbpIdentityResult.Failed(string.Format(L("Identity.DuplicateUserName"), userName)); } user = (await FindByEmailAsync(emailAddress)); if (user != null && user.Id != expectedUserId) { return AbpIdentityResult.Failed(string.Format(L("Identity.DuplicateEmail"), emailAddress)); } return IdentityResult.Success; } public virtual async Task SetRoles(TUser user, string[] roleNames) { //Remove from removed roles if (user.Roles != null) { foreach (var userRole in user.Roles.ToList()) { var role = await RoleManager.FindByIdAsync(userRole.RoleId); if (roleNames.All(roleName => role.Name != roleName)) { var result = await RemoveFromRoleAsync(user.Id, role.Name); if (!result.Succeeded) { return result; } } } } //Add to added roles foreach (var roleName in roleNames) { var role = await RoleManager.GetRoleByNameAsync(roleName); if (user.Roles != null && user.Roles.All(ur => ur.RoleId != role.Id)) { var result = await AddToRoleAsync(user.Id, roleName); if (!result.Succeeded) { return result; } } } return IdentityResult.Success; } public virtual void RegisterTwoFactorProviders(int? tenantId) { TwoFactorProviders.Clear(); if (!IsTrue(IwbZeroSettingNames.UserManagement.TwoFactorLogin.IsEnabled, tenantId)) { return; } if (EmailService != null && IsTrue(IwbZeroSettingNames.UserManagement.TwoFactorLogin.IsEmailProviderEnabled, tenantId)) { RegisterTwoFactorProvider( L("Email"), new EmailTokenProvider { Subject = L("EmailSecurityCodeSubject"), BodyFormat = L("EmailSecurityCodeBody") } ); } if (SmsService != null && IsTrue(IwbZeroSettingNames.UserManagement.TwoFactorLogin.IsSmsProviderEnabled, tenantId)) { RegisterTwoFactorProvider( L("Sms"), new PhoneNumberTokenProvider { MessageFormat = L("SmsSecurityCodeMessage") } ); } } public virtual void InitializeLockoutSettings(int? tenantId) { UserLockoutEnabledByDefault = IsTrue(IwbZeroSettingNames.UserManagement.UserLockOut.IsEnabled, tenantId); DefaultAccountLockoutTimeSpan = TimeSpan.FromSeconds(GetSettingValue(IwbZeroSettingNames.UserManagement.UserLockOut.DefaultAccountLockoutSeconds, tenantId)); MaxFailedAccessAttemptsBeforeLockout = GetSettingValue(IwbZeroSettingNames.UserManagement.UserLockOut.MaxFailedAccessAttemptsBeforeLockout, tenantId); } public override async Task> GetValidTwoFactorProvidersAsync(long userId) { var user = await GetUserByIdAsync(userId); RegisterTwoFactorProviders(user.TenantId); return await base.GetValidTwoFactorProvidersAsync(userId); } public override async Task NotifyTwoFactorTokenAsync(long userId, string twoFactorProvider, string token) { var user = await GetUserByIdAsync(userId); RegisterTwoFactorProviders(user.TenantId); return await base.NotifyTwoFactorTokenAsync(userId, twoFactorProvider, token); } public override async Task GenerateTwoFactorTokenAsync(long userId, string twoFactorProvider) { var user = await GetUserByIdAsync(userId); RegisterTwoFactorProviders(user.TenantId); return await base.GenerateTwoFactorTokenAsync(userId, twoFactorProvider); } public override async Task VerifyTwoFactorTokenAsync(long userId, string twoFactorProvider, string token) { var user = await GetUserByIdAsync(userId); RegisterTwoFactorProviders(user.TenantId); return await base.VerifyTwoFactorTokenAsync(userId, twoFactorProvider, token); } protected virtual Task GetOldUserNameAsync(long userId) { return IwbStore.GetUserNameFromDatabaseAsync(userId); } private async Task GetUserPermissionCacheItemAsync(long userId) { var cacheKey = userId + "@" + (GetCurrentTenantId() ?? 0); return await _cacheManager.GetUserPermissionCache().GetAsync(cacheKey, async () => { var user = await FindByIdAsync(userId); if (user == null) { return null; } var newCacheItem = new UserPermissionCacheItem(userId); foreach (var roleName in await GetRolesAsync(userId)) { newCacheItem.RoleIds.Add((await RoleManager.GetRoleByNameAsync(roleName)).Id); } foreach (var permissionInfo in await UserPermissionStore.GetPermissionsAsync(userId)) { if (permissionInfo.IsGranted) { newCacheItem.GrantedPermissions.Add(permissionInfo.Name); } else { newCacheItem.ProhibitedPermissions.Add(permissionInfo.Name); } } return newCacheItem; }); } private async Task GetUserDataPermissionCacheItemAsync(long userId) { var cacheKey = $"Data-{userId}@{GetCurrentTenantId() ?? 0}"; return await _cacheManager.GetUserPermissionCache().GetAsync(cacheKey, async () => { var user = await FindByIdAsync(userId); if (user == null) { return null; } var newCacheItem = new UserPermissionCacheItem(userId); foreach (var roleName in await GetRolesAsync(userId)) { newCacheItem.RoleIds.Add((await RoleManager.GetRoleByNameAsync(roleName)).Id); } foreach (var permissionInfo in await UserPermissionStore.GetPermissionsAsync(userId)) { if (permissionInfo.OperType == 0 || string.IsNullOrEmpty(permissionInfo.DataKey)) { continue; } if (permissionInfo.IsGranted) { if (permissionInfo.OperType == 1) { newCacheItem.GrantedPermissions.Add($"{permissionInfo.Name}@{permissionInfo.DataKey}@{(int)OperType.Create}"); newCacheItem.GrantedPermissions.Add($"{permissionInfo.Name}@{permissionInfo.DataKey}@{(int)OperType.Update}"); newCacheItem.GrantedPermissions.Add($"{permissionInfo.Name}@{permissionInfo.DataKey}@{(int)OperType.Delete}"); newCacheItem.GrantedPermissions.Add($"{permissionInfo.Name}@{permissionInfo.DataKey}@{(int)OperType.Query}"); } else { newCacheItem.GrantedPermissions.Add($"{permissionInfo.Name}@{permissionInfo.DataKey}@{permissionInfo.OperType}"); } } else { newCacheItem.ProhibitedPermissions.Add($"{permissionInfo.Name}@{permissionInfo.DataKey}@{permissionInfo.OperType}"); } } return newCacheItem; }); } private bool IsTrue(string settingName, int? tenantId) { return GetSettingValue(settingName, tenantId); } private T GetSettingValue(string settingName, int? tenantId) where T : struct { return tenantId == null ? _settingManager.GetSettingValueForApplication(settingName) : _settingManager.GetSettingValueForTenant(settingName, tenantId.Value); } protected virtual string L(string name) { return LocalizationManager.GetString(LocalizationSourceName, name); } protected virtual string L(string name, CultureInfo cultureInfo) { return LocalizationManager.GetString(LocalizationSourceName, name, cultureInfo); } private int? GetCurrentTenantId() { if (_unitOfWorkManager.Current != null) { return _unitOfWorkManager.Current.GetTenantId(); } return AbpSession.TenantId; } private MultiTenancySides GetCurrentMultiTenancySide() { if (_unitOfWorkManager.Current != null) { return MultiTenancy.IsEnabled && !_unitOfWorkManager.Current.GetTenantId().HasValue ? MultiTenancySides.Host : MultiTenancySides.Tenant; } return AbpSession.MultiTenancySide; } } }