using System; using System.Collections.Generic; using System.Linq; using System.Security.Claims; using System.Threading.Tasks; using System.Transactions; using Abp; using Abp.Dependency; using Abp.Domain.Repositories; using Abp.Domain.Uow; using Abp.Linq; using IwbZero.Authorization.Base.Permissions; using IwbZero.Authorization.Base.Users; using IwbZero.Authorization.Roles; using Microsoft.AspNet.Identity; namespace IwbZero.Authorization.Users { public abstract class IwbUserStore : IUserPasswordStore, IUserEmailStore, IUserLoginStore, IUserRoleStore, IQueryableUserStore, IUserLockoutStore, IUserPermissionStore, IUserPhoneNumberStore, IUserClaimStore, IUserSecurityStampStore, IUserTwoFactorStore, ITransientDependency where TRole : IwbSysRole where TUser : IwbSysUser { public IAsyncQueryableExecuter AsyncQueryableExecuter { get; set; } private readonly IRepository _userRepository; private readonly IRepository _userLoginRepository; private readonly IRepository _userRoleRepository; private readonly IRepository _userClaimRepository; private readonly IRepository _roleRepository; private readonly IRepository _permissionSettingRepository; private readonly IUnitOfWorkManager _unitOfWorkManager; /// /// Constructor. /// protected IwbUserStore( IRepository userRepository, IRepository userLoginRepository, IRepository userRoleRepository, IRepository roleRepository, IRepository permissionSettingRepository, IUnitOfWorkManager unitOfWorkManager, IRepository userClaimRepository) { _userRepository = userRepository; _userLoginRepository = userLoginRepository; _userRoleRepository = userRoleRepository; _roleRepository = roleRepository; _unitOfWorkManager = unitOfWorkManager; _userClaimRepository = userClaimRepository; _permissionSettingRepository = permissionSettingRepository; AsyncQueryableExecuter = NullAsyncQueryableExecuter.Instance; } #region IQueryableUserStore public virtual IQueryable Users => _userRepository.GetAll(); #endregion IQueryableUserStore #region IUserStore public virtual async Task CreateAsync(TUser user) { await _userRepository.InsertAsync(user); } public virtual async Task UpdateAsync(TUser user) { await _userRepository.UpdateAsync(user); } public virtual async Task DeleteAsync(TUser user) { await _userRepository.DeleteAsync(user.Id); } public virtual async Task FindByIdAsync(long userId) { return await _userRepository.FirstOrDefaultAsync(a => a.Id == userId); } public virtual async Task FindByNameAsync(string userName) { var normalizedUsername = NormalizeKey(userName); return await _userRepository.FirstOrDefaultAsync( user => user.NormalizedUserName == normalizedUsername ); } public virtual async Task FindByEmailAsync(string email) { var normalizedEmail = NormalizeKey(email); return await _userRepository.FirstOrDefaultAsync( user => user.NormalizedEmailAddress == normalizedEmail ); } /// /// Tries to find a user with user name or email address in current tenant. /// /// User name or email address /// User or null public virtual async Task FindByNameOrEmailOrMobileAsync(string userNameOrEmailOrMobile) { var normalizedUserNameOrEmailAddress = NormalizeKey(userNameOrEmailOrMobile); return await _userRepository.FirstOrDefaultAsync( user => (user.NormalizedUserName == normalizedUserNameOrEmailAddress || user.NormalizedEmailAddress == normalizedUserNameOrEmailAddress || user.PhoneNumber == userNameOrEmailOrMobile) ); } /// /// Tries to find a user with user name or email address in given tenant. /// /// Tenant Id /// User name or email address /// User or null [UnitOfWork] public virtual async Task FindByNameOrEmailOrMobileAsync(int? tenantId, string userNameOrEmailOrMobile) { using (_unitOfWorkManager.Current.SetTenantId(tenantId)) { return await FindByNameOrEmailOrMobileAsync(userNameOrEmailOrMobile); } } #endregion IUserStore #region IUserPasswordStore public virtual Task SetPasswordHashAsync(TUser user, string passwordHash) { user.Password = passwordHash; return Task.FromResult(0); } public virtual Task GetPasswordHashAsync(TUser user) { return Task.FromResult(user.Password); } public virtual Task HasPasswordAsync(TUser user) { return Task.FromResult(!string.IsNullOrEmpty(user.Password)); } #endregion IUserPasswordStore #region IUserEmailStore public virtual Task SetEmailAsync(TUser user, string email) { user.EmailAddress = email; return Task.FromResult(0); } public virtual Task GetEmailAsync(TUser user) { return Task.FromResult(user.EmailAddress); } public virtual Task GetEmailConfirmedAsync(TUser user) { return Task.FromResult(user.IsEmailConfirmed); } public virtual Task SetEmailConfirmedAsync(TUser user, bool confirmed) { user.IsEmailConfirmed = confirmed; return Task.FromResult(0); } #endregion IUserEmailStore #region IUserLoginStore public virtual async Task AddLoginAsync(TUser user, UserLoginInfo login) { await _userLoginRepository.InsertAsync( new UserLogin { TenantId = user.TenantId, LoginProvider = login.LoginProvider, ProviderKey = login.ProviderKey, UserId = user.Id }); } public virtual async Task RemoveLoginAsync(TUser user, UserLoginInfo login) { await _userLoginRepository.DeleteAsync( ul => ul.UserId == user.Id && ul.LoginProvider == login.LoginProvider && ul.ProviderKey == login.ProviderKey ); } public virtual async Task> GetLoginsAsync(TUser user) { return (await _userLoginRepository.GetAllListAsync(ul => ul.UserId == user.Id)) .Select(ul => new UserLoginInfo(ul.LoginProvider, ul.ProviderKey)) .ToList(); } public virtual async Task FindAsync(UserLoginInfo login) { var userLogin = await _userLoginRepository.FirstOrDefaultAsync( ul => ul.LoginProvider == login.LoginProvider && ul.ProviderKey == login.ProviderKey ); if (userLogin == null) { return null; } return await _userRepository.FirstOrDefaultAsync(u => u.Id == userLogin.UserId); } [UnitOfWork] public virtual Task> FindAllAsync(UserLoginInfo login) { var query = from userLogin in _userLoginRepository.GetAll() join user in _userRepository.GetAll() on userLogin.UserId equals user.Id where userLogin.LoginProvider == login.LoginProvider && userLogin.ProviderKey == login.ProviderKey select user; return Task.FromResult(query.ToList()); } public virtual Task FindAsync(int? tenantId, UserLoginInfo login) { using (_unitOfWorkManager.Current.SetTenantId(tenantId)) { var query = from userLogin in _userLoginRepository.GetAll() join user in _userRepository.GetAll() on userLogin.UserId equals user.Id where userLogin.LoginProvider == login.LoginProvider && userLogin.ProviderKey == login.ProviderKey select user; return Task.FromResult(query.FirstOrDefault()); } } #endregion IUserLoginStore #region IUserRoleStore public virtual async Task AddToRoleAsync(TUser user, string roleName) { var role = await GetRoleByNameAsync(roleName); await _userRoleRepository.InsertAsync(new UserRole(user.TenantId, user.Id, role.Id)); } public virtual async Task RemoveFromRoleAsync(TUser user, string roleName) { var role = await GetRoleByNameAsync(roleName); var userRole = await _userRoleRepository.FirstOrDefaultAsync(ur => ur.UserId == user.Id && ur.RoleId == role.Id); if (userRole == null) { return; } await _userRoleRepository.DeleteAsync(userRole); } [UnitOfWork] public virtual async Task> GetRolesAsync(TUser user) { var userRoles = await AsyncQueryableExecuter.ToListAsync(from userRole in _userRoleRepository.GetAll() join role in _roleRepository.GetAll() on userRole.RoleId equals role.Id where userRole.UserId == user.Id select role.Name); return userRoles.ToList(); } public virtual async Task IsInRoleAsync(TUser user, string roleName) { return (await GetRolesAsync(user)).Any(r => r == roleName); } #endregion IUserRoleStore #region IUserPermissionStore public virtual async Task AddPermissionAsync(TUser user, PermissionGrantInfo permissionGrant) { if (await HasPermissionAsync(user.Id, permissionGrant)) { return; } await _permissionSettingRepository.InsertAsync( new PermissionSetting { TenantId = user.TenantId, Master = 2, MasterValue = user.Id + "", Name = permissionGrant.Name, IsGranted = permissionGrant.IsGranted }); } public virtual async Task RemovePermissionAsync(TUser user, PermissionGrantInfo permissionGrant) { await _permissionSettingRepository.DeleteAsync( a => a.Master == 2 && a.MasterValue == user.Id + "" && a.Name == permissionGrant.Name && a.IsGranted == permissionGrant.IsGranted ); } public virtual async Task> GetPermissionsAsync(long userId) { var list = (await _permissionSettingRepository.GetAllListAsync(p => p.Master == 2 && p.MasterValue == userId + "")) .Select(p => new PermissionGrantInfo(p.Name, p.IsGranted)) .ToList(); return list; } public virtual async Task HasPermissionAsync(long userId, PermissionGrantInfo permissionGrant) { return await _permissionSettingRepository.FirstOrDefaultAsync( p => p.Master == 2 && p.MasterValue == userId + "" && p.Name == permissionGrant.Name && p.IsGranted == permissionGrant.IsGranted ) != null; } public virtual async Task RemoveAllPermissionSettingsAsync(TUser user) { await _permissionSettingRepository.DeleteAsync(s => s.Master == 2 && s.MasterValue == user.Id + ""); } #endregion IUserPermissionStore #region IUserLockoutStore public Task GetLockoutEndDateAsync(TUser user) { return Task.FromResult( user.LockoutEndDateUtc.HasValue ? new DateTimeOffset(DateTime.SpecifyKind(user.LockoutEndDateUtc.Value, DateTimeKind.Utc)) : new DateTimeOffset() ); } public Task SetLockoutEndDateAsync(TUser user, DateTimeOffset lockoutEnd) { user.LockoutEndDateUtc = lockoutEnd == DateTimeOffset.MinValue ? new DateTime?() : lockoutEnd.UtcDateTime; return Task.FromResult(0); } public Task IncrementAccessFailedCountAsync(TUser user) { return Task.FromResult(++user.AccessFailedCount); } public Task ResetAccessFailedCountAsync(TUser user) { user.AccessFailedCount = 0; return Task.FromResult(0); } public Task GetAccessFailedCountAsync(TUser user) { return Task.FromResult(user.AccessFailedCount); } public Task GetLockoutEnabledAsync(TUser user) { return Task.FromResult(user.IsLockoutEnabled); } public Task SetLockoutEnabledAsync(TUser user, bool enabled) { user.IsLockoutEnabled = enabled; return Task.FromResult(0); } #endregion IUserLockoutStore #region IUserPhoneNumberStore public Task SetPhoneNumberAsync(TUser user, string phoneNumber) { user.PhoneNumber = phoneNumber; return Task.FromResult(0); } public Task GetPhoneNumberAsync(TUser user) { return Task.FromResult(user.PhoneNumber); } public Task GetPhoneNumberConfirmedAsync(TUser user) { return Task.FromResult(user.IsPhoneNumberConfirmed); } public Task SetPhoneNumberConfirmedAsync(TUser user, bool confirmed) { user.IsPhoneNumberConfirmed = confirmed; return Task.FromResult(0); } #endregion IUserPhoneNumberStore #region IUserClaimStore public async Task> GetClaimsAsync(TUser user) { var userClaims = await _userClaimRepository.GetAllListAsync(uc => uc.UserId == user.Id); return userClaims.Select(uc => new Claim(uc.ClaimType, uc.ClaimValue)).ToList(); } public Task AddClaimAsync(TUser user, Claim claim) { return _userClaimRepository.InsertAsync(new UserClaim(user, claim)); } public Task RemoveClaimAsync(TUser user, Claim claim) { return _userClaimRepository.DeleteAsync( uc => uc.UserId == user.Id && uc.ClaimType == claim.Type && uc.ClaimValue == claim.Value ); } #endregion IUserClaimStore #region IDisposable public virtual void Dispose() { //No need to dispose since using IOC. } #endregion IDisposable #region Helpers protected virtual string NormalizeKey(string key) { return key.ToUpperInvariant(); } private async Task GetRoleByNameAsync(string roleName) { var normalizedName = NormalizeKey(roleName); var role = await _roleRepository.FirstOrDefaultAsync(r => r.NormalizedName == normalizedName); if (role == null) { throw new AbpException("Could not find a role with name: " + normalizedName); } return role; } #endregion Helpers #region IUserSecurityStampStore public Task SetSecurityStampAsync(TUser user, string stamp) { user.SecurityStamp = stamp; return Task.FromResult(0); } public Task GetSecurityStampAsync(TUser user) { return Task.FromResult(user.SecurityStamp); } #endregion IUserSecurityStampStore public Task SetTwoFactorEnabledAsync(TUser user, bool enabled) { user.IsTwoFactorEnabled = enabled; return Task.FromResult(0); } public Task GetTwoFactorEnabledAsync(TUser user) { return Task.FromResult(user.IsTwoFactorEnabled); } public async Task GetUserNameFromDatabaseAsync(long userId) { //note: This workaround will not be needed after fixing https://github.com/aspnetboilerplate/aspnetboilerplate/issues/1828 var outerUow = _unitOfWorkManager.Current; using (var uow = _unitOfWorkManager.Begin(new UnitOfWorkOptions { Scope = TransactionScopeOption.RequiresNew, IsTransactional = false, IsolationLevel = IsolationLevel.ReadUncommitted })) { if (outerUow != null) { _unitOfWorkManager.Current.SetTenantId(outerUow.GetTenantId()); } var user = await _userRepository.FirstOrDefaultAsync(a => a.Id == userId); await uow.CompleteAsync(); return user.UserName; } } } }