diff --git a/src/VirtoCommerce.Platform.Core/Security/Events/UserPasswordChangedEvent.cs b/src/VirtoCommerce.Platform.Core/Security/Events/UserPasswordChangedEvent.cs index a8717f195b1..60895156cc4 100644 --- a/src/VirtoCommerce.Platform.Core/Security/Events/UserPasswordChangedEvent.cs +++ b/src/VirtoCommerce.Platform.Core/Security/Events/UserPasswordChangedEvent.cs @@ -4,10 +4,10 @@ namespace VirtoCommerce.Platform.Core.Security.Events { public class UserPasswordChangedEvent : DomainEvent { - public UserPasswordChangedEvent(string userId, string сustomPasswordHash) + public UserPasswordChangedEvent(string userId, string customPasswordHash) { UserId = userId; - CustomPasswordHash = сustomPasswordHash; + CustomPasswordHash = customPasswordHash; } public UserPasswordChangedEvent(ApplicationUser applicationUser) diff --git a/src/VirtoCommerce.Platform.Core/Security/Events/UserResetPasswordEvent.cs b/src/VirtoCommerce.Platform.Core/Security/Events/UserResetPasswordEvent.cs index 07039872ecb..1c42785d699 100644 --- a/src/VirtoCommerce.Platform.Core/Security/Events/UserResetPasswordEvent.cs +++ b/src/VirtoCommerce.Platform.Core/Security/Events/UserResetPasswordEvent.cs @@ -4,10 +4,10 @@ namespace VirtoCommerce.Platform.Core.Security.Events { public class UserResetPasswordEvent : DomainEvent { - public UserResetPasswordEvent(string userId, string сustomPasswordHash) + public UserResetPasswordEvent(string userId, string customPasswordHash) { UserId = userId; - CustomPasswordHash = сustomPasswordHash; + CustomPasswordHash = customPasswordHash; } public string UserId { get; set; } diff --git a/src/VirtoCommerce.Platform.Security/CustomUserManager.cs b/src/VirtoCommerce.Platform.Security/CustomUserManager.cs index 5b604d1fb87..1b751cc2477 100644 --- a/src/VirtoCommerce.Platform.Security/CustomUserManager.cs +++ b/src/VirtoCommerce.Platform.Security/CustomUserManager.cs @@ -3,7 +3,6 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -28,7 +27,7 @@ public class CustomUserManager : AspNetUserManager private readonly PasswordOptionsExtended _passwordOptionsExtended; private readonly IPasswordHasher _passwordHasher; - public CustomUserManager(IUserStore store, IOptions optionsAccessor, IPasswordHasher passwordHasher, + public CustomUserManager(IUserStore store, IOptions optionsAccessor, IPasswordHasher passwordHasher, IOptions userOptionsExtended, IEnumerable> userValidators, IEnumerable> passwordValidators, ILookupNormalizer keyNormalizer, IdentityErrorDescriber errors, IServiceProvider services, @@ -109,41 +108,45 @@ public override async Task FindByIdAsync(string userId) return result; } - public override async Task ResetPasswordAsync(ApplicationUser user, string token, string newPassword) + public override Task ResetPasswordAsync(ApplicationUser user, string token, string newPassword) { - //It is important to call base.FindByIdAsync method to avoid of update a cached user. - var existUser = await base.FindByIdAsync(user.Id); - existUser.LastPasswordChangedDate = DateTime.UtcNow; - - var result = await base.ResetPasswordAsync(existUser, token, newPassword); - if (result == IdentityResult.Success) - { - SecurityCacheRegion.ExpireUser(user); - - await SavePasswordHistory(user, newPassword); - - // Calculate password hash for external hash storage. This provided as workaround until password hash storage would implemented - var customPasswordHash = _passwordHasher.HashPassword(user, newPassword); - await _eventPublisher.Publish(new UserResetPasswordEvent(user.Id, customPasswordHash)); - } + return UpdatePasswordAsync(user, newPassword, + (user, newPassword) => base.ResetPasswordAsync(user, token, newPassword), + (userId, customPasswordHash) => new UserResetPasswordEvent(userId, customPasswordHash)); + } - return result; + public override Task ChangePasswordAsync(ApplicationUser user, string currentPassword, string newPassword) + { + return UpdatePasswordAsync(user, newPassword, + (user, newPassword) => base.ChangePasswordAsync(user, currentPassword, newPassword), + (userId, customPasswordHash) => new UserPasswordChangedEvent(userId, customPasswordHash)); } - public override async Task ChangePasswordAsync(ApplicationUser user, string currentPassword, string newPassword) + protected virtual async Task UpdatePasswordAsync( + ApplicationUser user, + string newPassword, + Func> updatePassword, + Func buildEvent) + where TEvent : class, IEvent { + var previousPasswordChangedDate = user.LastPasswordChangedDate; user.LastPasswordChangedDate = DateTime.UtcNow; - var result = await base.ChangePasswordAsync(user, currentPassword, newPassword); + var result = await updatePassword(user, newPassword); if (result == IdentityResult.Success) { SecurityCacheRegion.ExpireUser(user); await SavePasswordHistory(user, newPassword); - // Calculate password hash for external hash storage. This provided as workaround until password hash storage would implemented + // Calculate password hash for external hash storage. This provided as workaround until password hash storage is implemented. var customPasswordHash = _passwordHasher.HashPassword(user, newPassword); - await _eventPublisher.Publish(new UserPasswordChangedEvent(user.Id, customPasswordHash)); + var @event = buildEvent(user.Id, customPasswordHash); + await _eventPublisher.Publish(@event); + } + else + { + user.LastPasswordChangedDate = previousPasswordChangedDate; } return result; @@ -235,7 +238,7 @@ protected virtual async Task UpdateUserRolesAsync(ApplicationUser user) } var targetRoles = await GetRolesAsync(user); - var sourceRoles = user.Roles.Select(x => x.Name); + var sourceRoles = user.Roles.Select(x => x.Name).ToList(); //Add foreach (var newRole in sourceRoles.Except(targetRoles)) @@ -258,7 +261,7 @@ protected virtual async Task UpdateUserLoginsAsync(ApplicationUser user) } var targetLogins = await GetLoginsAsync(user); - var sourceLogins = user.Logins.Select(x => new UserLoginInfo(x.LoginProvider, x.ProviderKey, null)); + var sourceLogins = user.Logins.Select(x => new UserLoginInfo(x.LoginProvider, x.ProviderKey, null)).ToList(); foreach (var item in sourceLogins.Where(x => targetLogins.All(y => x.LoginProvider + x.ProviderKey != y.LoginProvider + y.ProviderKey))) { @@ -376,7 +379,7 @@ protected virtual async Task LoadUserDetailsAsync(ApplicationUser user) // Read associated logins var logins = await base.GetLoginsAsync(user); - user.Logins = logins.Select(x => new ApplicationUserLogin() { LoginProvider = x.LoginProvider, ProviderKey = x.ProviderKey }).ToArray(); + user.Logins = logins.Select(x => new ApplicationUserLogin { LoginProvider = x.LoginProvider, ProviderKey = x.ProviderKey }).ToArray(); } ///