LogInManager.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. using System;
  2. using System.Threading.Tasks;
  3. using System.Transactions;
  4. using Abp.Auditing;
  5. using Abp.Dependency;
  6. using Abp.Domain.Repositories;
  7. using Abp.Domain.Uow;
  8. using Abp.Extensions;
  9. using Abp.Timing;
  10. using IwbZero.Authorization.Roles;
  11. using IwbZero.Authorization.Users;
  12. using IwbZero.Configuration;
  13. using IwbZero.IdentityFramework;
  14. using IwbZero.Setting;
  15. using Microsoft.AspNet.Identity;
  16. namespace IwbZero.Authorization
  17. {
  18. public abstract class IwbLogInManager<TRole, TUser> : ITransientDependency
  19. where TRole : IwbSysRole<TUser>, new()
  20. where TUser : IwbSysUser<TUser>, new()
  21. {
  22. public IClientInfoProvider ClientInfoProvider { get; set; }
  23. //protected IMultiTenancyConfig MultiTenancyConfig { get; }
  24. protected IRepository<UserLoginAttempt, long> UserLoginAttemptRepository { get; }
  25. protected IUnitOfWorkManager UnitOfWorkManager { get; }
  26. protected IwbUserManager<TRole, TUser> UserManager { get; }
  27. protected IIwbSettingManager SettingManager { get; }
  28. protected IIwbUserManagementConfig IwbUserManagementConfig { get; }
  29. protected IIocResolver IocResolver { get; }
  30. protected IwbRoleManager<TRole, TUser> RoleManager { get; }
  31. public IwbLogInManager(
  32. IwbUserManager<TRole, TUser> userManager,
  33. //IMultiTenancyConfig multiTenancyConfig,
  34. IRepository<UserLoginAttempt, long> userLoginAttemptRepository,
  35. IUnitOfWorkManager unitOfWorkManager,
  36. IIwbSettingManager settingManager,
  37. IIwbUserManagementConfig userManagementConfig,
  38. IIocResolver iocResolver,
  39. IwbRoleManager<TRole, TUser> roleManager)
  40. {
  41. //MultiTenancyConfig = multiTenancyConfig;
  42. UserLoginAttemptRepository = userLoginAttemptRepository;
  43. UnitOfWorkManager = unitOfWorkManager;
  44. SettingManager = settingManager;
  45. IwbUserManagementConfig = userManagementConfig;
  46. IocResolver = iocResolver;
  47. RoleManager = roleManager;
  48. UserManager = userManager;
  49. ClientInfoProvider = NullClientInfoProvider.Instance;
  50. }
  51. [UnitOfWork]
  52. public virtual async Task<IwbLoginResult<TUser>> LoginAsync(UserLoginInfo login)
  53. {
  54. var result = await LoginAsyncInternal(login);
  55. await SaveLoginAttempt(result, login.ProviderKey + "@" + login.LoginProvider);
  56. return result;
  57. }
  58. protected virtual async Task<IwbLoginResult<TUser>> LoginAsyncInternal(UserLoginInfo login)
  59. {
  60. if (login == null || login.LoginProvider.IsNullOrEmpty() || login.ProviderKey.IsNullOrEmpty())
  61. {
  62. throw new ArgumentException("login");
  63. }
  64. using (UnitOfWorkManager.Current.SetTenantId(null))
  65. {
  66. var user = await UserManager.UserStore.FindAsync(null, login);
  67. if (user == null)
  68. {
  69. return new IwbLoginResult<TUser>(AbpLoginResultType.UnknownExternalLogin);
  70. }
  71. return await CreateLoginResultAsync(user);
  72. }
  73. }
  74. [UnitOfWork]
  75. public virtual async Task<IwbLoginResult<TUser>> LoginAsync(string userNameOrEmailAddress, string plainPassword, bool shouldLockout = true, bool isUpdatePwd = false)
  76. {
  77. var result = await LoginAsyncInternal(userNameOrEmailAddress, plainPassword, shouldLockout);
  78. if (!isUpdatePwd)
  79. {
  80. await SaveLoginAttempt(result, userNameOrEmailAddress);
  81. }
  82. return result;
  83. }
  84. protected virtual async Task<IwbLoginResult<TUser>> LoginAsyncInternal(string userNameOrEmailAddress, string plainPassword, bool shouldLockout)
  85. {
  86. if (userNameOrEmailAddress.IsNullOrEmpty())
  87. {
  88. throw new ArgumentNullException(nameof(userNameOrEmailAddress));
  89. }
  90. if (plainPassword.IsNullOrEmpty())
  91. {
  92. throw new ArgumentNullException(nameof(plainPassword));
  93. }
  94. //Get and check tenant
  95. //TTenant tenant = null;
  96. //using (UnitOfWorkManager.Current.SetTenantId(null))
  97. //{
  98. // if (!MultiTenancyConfig.IsEnabled)
  99. // {
  100. // tenant = await GetDefaultTenantAsync();
  101. // }
  102. // else if (!string.IsNullOrWhiteSpace(tenancyName))
  103. // {
  104. // tenant = await TenantRepository.FirstOrDefaultAsync(t => t.TenancyName == tenancyName);
  105. // if (tenant == null)
  106. // {
  107. // return new AbpLoginResult(AbpLoginResultType.InvalidTenancyName);
  108. // }
  109. // if (!tenant.IsActive)
  110. // {
  111. // return new AbpLoginResult(AbpLoginResultType.TenantIsNotActive, tenant);
  112. // }
  113. // }
  114. //}
  115. //var tenantId = tenant == null ? (int?)null : tenant.Id;
  116. using (UnitOfWorkManager.Current.SetTenantId(null))
  117. {
  118. //TryLoginFromExternalAuthenticationSources method may create the user, that's why we are calling it before AbpStore.FindByNameOrEmailAsync
  119. // var loggedInFromExternalSource = await TryLoginFromExternalAuthenticationSources(userNameOrEmailAddress, plainPassword, tenant);
  120. var user = await UserManager.UserStore.FindByNameOrEmailAsync(null, userNameOrEmailAddress);
  121. if (user == null)
  122. {
  123. return new IwbLoginResult<TUser>(AbpLoginResultType.InvalidUserNameOrEmailAddress);
  124. }
  125. if (await UserManager.IsLockedOutAsync(user.Id))
  126. {
  127. return new IwbLoginResult<TUser>(AbpLoginResultType.LockedOut, user);
  128. }
  129. UserManager.InitializeLockoutSettings();
  130. var verificationResult = UserManager.PasswordHasher.VerifyHashedPassword(user.Password, plainPassword);
  131. if (verificationResult == PasswordVerificationResult.Failed)
  132. {
  133. return await GetFailedPasswordValidationAsLoginResultAsync(user, shouldLockout);
  134. }
  135. if (verificationResult == PasswordVerificationResult.SuccessRehashNeeded)
  136. {
  137. return await GetSuccessRehashNeededAsLoginResultAsync(user);
  138. }
  139. await UserManager.ResetAccessFailedCountAsync(user.Id);
  140. return await CreateLoginResultAsync(user);
  141. }
  142. }
  143. protected virtual async Task<IwbLoginResult<TUser>> GetFailedPasswordValidationAsLoginResultAsync(TUser user, bool shouldLockout = false)
  144. {
  145. if (shouldLockout)
  146. {
  147. if (await TryLockOutAsync(null, user.Id))
  148. {
  149. return new IwbLoginResult<TUser>(AbpLoginResultType.LockedOut, user);
  150. }
  151. }
  152. return new IwbLoginResult<TUser>(AbpLoginResultType.InvalidPassword, user);
  153. }
  154. protected virtual async Task<IwbLoginResult<TUser>> GetSuccessRehashNeededAsLoginResultAsync(TUser user, bool shouldLockout = false)
  155. {
  156. return await GetFailedPasswordValidationAsLoginResultAsync(user, shouldLockout);
  157. }
  158. protected virtual async Task<IwbLoginResult<TUser>> CreateLoginResultAsync(TUser user)
  159. {
  160. if (!user.IsActive)
  161. {
  162. return new IwbLoginResult<TUser>(AbpLoginResultType.UserIsNotActive);
  163. }
  164. if (await IsEmailConfirmationRequiredForLoginAsync() && !user.IsEmailConfirmed)
  165. {
  166. return new IwbLoginResult<TUser>(AbpLoginResultType.UserEmailIsNotConfirmed);
  167. }
  168. user.LastLoginTime = Clock.Now;
  169. await UserManager.UserStore.UpdateAsync(user);
  170. await UnitOfWorkManager.Current.SaveChangesAsync();
  171. return new IwbLoginResult<TUser>(user,
  172. await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie)
  173. );
  174. }
  175. protected virtual async Task SaveLoginAttempt(IwbLoginResult<TUser> loginResult, string userNameOrEmailAddress)
  176. {
  177. using (var uow = UnitOfWorkManager.Begin(TransactionScopeOption.Suppress))
  178. {
  179. using (UnitOfWorkManager.Current.SetTenantId(null))
  180. {
  181. var loginAttempt = new UserLoginAttempt
  182. {
  183. UserId = loginResult.User != null ? loginResult.User.Id : (long?)null,
  184. UserNameOrEmailAddress = userNameOrEmailAddress,
  185. Result = loginResult.Result,
  186. BrowserInfo = ClientInfoProvider.BrowserInfo,
  187. ClientIpAddress = ClientInfoProvider.ClientIpAddress,
  188. ClientName = ClientInfoProvider.ComputerName,
  189. };
  190. await UserLoginAttemptRepository.InsertAsync(loginAttempt);
  191. await UnitOfWorkManager.Current.SaveChangesAsync();
  192. await uow.CompleteAsync();
  193. }
  194. }
  195. }
  196. protected virtual async Task<bool> TryLockOutAsync(int? tenantId, long userId)
  197. {
  198. using (var uow = UnitOfWorkManager.Begin(TransactionScopeOption.Suppress))
  199. {
  200. using (UnitOfWorkManager.Current.SetTenantId(tenantId))
  201. {
  202. (await UserManager.AccessFailedAsync(userId)).CheckErrors();
  203. var isLockOut = await UserManager.IsLockedOutAsync(userId);
  204. await UnitOfWorkManager.Current.SaveChangesAsync();
  205. await uow.CompleteAsync();
  206. return isLockOut;
  207. }
  208. }
  209. }
  210. //protected virtual async Task<bool> TryLoginFromExternalAuthenticationSources(string userNameOrEmailAddress, string plainPassword, TTenant tenant)
  211. //{
  212. // if (!UserManagementConfig.ExternalAuthenticationSources.Any())
  213. // {
  214. // return false;
  215. // }
  216. // foreach (var sourceType in UserManagementConfig.ExternalAuthenticationSources)
  217. // {
  218. // using (var source = IocResolver.ResolveAsDisposable<IExternalAuthenticationSource>(sourceType))
  219. // {
  220. // if (await source.Object.TryAuthenticateAsync(userNameOrEmailAddress, plainPassword, tenant))
  221. // {
  222. // var tenantId = tenant == null ? (int?)null : tenant.Id;
  223. // using (UnitOfWorkManager.Current.SetTenantId(tenantId))
  224. // {
  225. // var user = await UserManager.AbpStore.FindByNameOrEmailAsync(tenantId, userNameOrEmailAddress);
  226. // if (user == null)
  227. // {
  228. // user = await source.Object.CreateUserAsync(userNameOrEmailAddress, tenant);
  229. // user.TenantId = tenantId;
  230. // user.AuthenticationSource = source.Object.Name;
  231. // user.Password = UserManager.PasswordHasher.HashPassword(Guid.NewGuid().ToString("N").Left(16)); //Setting a random password since it will not be used
  232. // if (user.Roles == null)
  233. // {
  234. // user.Roles = new List<UserRole>();
  235. // foreach (var defaultRole in RoleManager.Roles.Where(r => r.TenantId == tenantId && r.IsDefault).ToList())
  236. // {
  237. // user.Roles.Add(new UserRole(tenantId, user.Id, defaultRole.Id));
  238. // }
  239. // }
  240. // await UserManager.AbpStore.CreateAsync(user);
  241. // }
  242. // else
  243. // {
  244. // await source.Object.UpdateUserAsync(user, tenant);
  245. // user.AuthenticationSource = source.Object.Name;
  246. // await UserManager.AbpStore.UpdateAsync(user);
  247. // }
  248. // await UnitOfWorkManager.Current.SaveChangesAsync();
  249. // return true;
  250. // }
  251. // }
  252. // }
  253. // }
  254. // return false;
  255. //}
  256. //protected virtual async Task<TTenant> GetDefaultTenantAsync()
  257. //{
  258. // var tenant = await TenantRepository.FirstOrDefaultAsync(t => t.TenancyName == AbpTenant<SysUser>.DefaultTenantName);
  259. // if (tenant == null)
  260. // {
  261. // throw new AbpException("There should be a 'Default' tenant if multi-tenancy is disabled!");
  262. // }
  263. // return tenant;
  264. //}
  265. protected virtual async Task<bool> IsEmailConfirmationRequiredForLoginAsync()
  266. {
  267. return await SettingManager.GetSettingValueForApplicationAsync<bool>(IwbAdminSettingNames.UserManagement.IsEmailConfirmationRequiredForLogin);
  268. }
  269. }
  270. }