V15: User password resetting notification (#18679)
* Introduce UserPasswordResettingNotification * Removed changes to IUserService interface. --------- Co-authored-by: Andy Butland <abutland73@gmail.com>
This commit is contained in:
@@ -21,6 +21,10 @@ public abstract class SecurityControllerBase : ManagementApiControllerBase
|
||||
.WithTitle("The password reset token was invalid")
|
||||
.WithDetail("The specified password reset token was either used already or wrong.")
|
||||
.Build()),
|
||||
UserOperationStatus.CancelledByNotification => BadRequest(problemDetailsBuilder
|
||||
.WithTitle("Cancelled by notification")
|
||||
.WithDetail("A notification handler prevented the user operation.")
|
||||
.Build()),
|
||||
UserOperationStatus.UnknownFailure => BadRequest(problemDetailsBuilder
|
||||
.WithTitle("Unknown failure")
|
||||
.WithDetail(errorMessageResult?.Error?.ErrorMessage ?? "The error was unknown")
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
|
||||
namespace Umbraco.Cms.Core.Notifications;
|
||||
|
||||
public class UserPasswordResettingNotification : CancelableObjectNotification<IUser>
|
||||
{
|
||||
public UserPasswordResettingNotification(IUser target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public IUser User => Target;
|
||||
}
|
||||
@@ -1180,7 +1180,21 @@ internal partial class UserService : RepositoryService, IUserService
|
||||
return keys;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<Attempt<PasswordChangedModel, UserOperationStatus>> ChangePasswordAsync(Guid performingUserKey, ChangeUserPasswordModel model)
|
||||
{
|
||||
IServiceScope serviceScope = _serviceScopeFactory.CreateScope();
|
||||
IBackOfficeUserStore userStore = serviceScope.ServiceProvider.GetRequiredService<IBackOfficeUserStore>();
|
||||
IUser? performingUser = await userStore.GetAsync(performingUserKey);
|
||||
if (performingUser is null)
|
||||
{
|
||||
return Attempt.FailWithStatus(UserOperationStatus.MissingUser, new PasswordChangedModel());
|
||||
}
|
||||
|
||||
return await ChangePasswordAsync(performingUser, model);
|
||||
}
|
||||
|
||||
private async Task<Attempt<PasswordChangedModel, UserOperationStatus>> ChangePasswordAsync(IUser performingUser, ChangeUserPasswordModel model)
|
||||
{
|
||||
IServiceScope serviceScope = _serviceScopeFactory.CreateScope();
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope();
|
||||
@@ -1197,12 +1211,6 @@ internal partial class UserService : RepositoryService, IUserService
|
||||
return Attempt.FailWithStatus(UserOperationStatus.InvalidUserType, new PasswordChangedModel());
|
||||
}
|
||||
|
||||
IUser? performingUser = await userStore.GetAsync(performingUserKey);
|
||||
if (performingUser is null)
|
||||
{
|
||||
return Attempt.FailWithStatus(UserOperationStatus.MissingUser, new PasswordChangedModel());
|
||||
}
|
||||
|
||||
// require old password for self change when outside of invite or resetByToken flows
|
||||
if (performingUser.UserState != UserState.Invited && performingUser.Username == user.Username && string.IsNullOrEmpty(model.OldPassword) && string.IsNullOrEmpty(model.ResetPasswordToken))
|
||||
{
|
||||
@@ -1226,12 +1234,13 @@ internal partial class UserService : RepositoryService, IUserService
|
||||
IBackOfficePasswordChanger passwordChanger = serviceScope.ServiceProvider.GetRequiredService<IBackOfficePasswordChanger>();
|
||||
Attempt<PasswordChangedModel?> result = await passwordChanger.ChangeBackOfficePassword(
|
||||
new ChangeBackOfficeUserPasswordModel
|
||||
{
|
||||
NewPassword = model.NewPassword,
|
||||
OldPassword = model.OldPassword,
|
||||
User = user,
|
||||
ResetPasswordToken = model.ResetPasswordToken,
|
||||
}, performingUser);
|
||||
{
|
||||
NewPassword = model.NewPassword,
|
||||
OldPassword = model.OldPassword,
|
||||
User = user,
|
||||
ResetPasswordToken = model.ResetPasswordToken,
|
||||
},
|
||||
performingUser);
|
||||
|
||||
if (result.Success is false)
|
||||
{
|
||||
@@ -2184,9 +2193,26 @@ internal partial class UserService : RepositoryService, IUserService
|
||||
public async Task<Attempt<PasswordChangedModel, UserOperationStatus>> ResetPasswordAsync(Guid userKey, string token, string password)
|
||||
{
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope();
|
||||
IServiceScope serviceScope = _serviceScopeFactory.CreateScope();
|
||||
|
||||
EventMessages evtMsgs = EventMessagesFactory.Get();
|
||||
IBackOfficeUserStore userStore = serviceScope.ServiceProvider.GetRequiredService<IBackOfficeUserStore>();
|
||||
|
||||
IUser? user = await userStore.GetAsync(userKey);
|
||||
if (user is null)
|
||||
{
|
||||
return Attempt.FailWithStatus(UserOperationStatus.UserNotFound, new PasswordChangedModel());
|
||||
}
|
||||
|
||||
var savingNotification = new UserPasswordResettingNotification(user, evtMsgs);
|
||||
if (await scope.Notifications.PublishCancelableAsync(savingNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return Attempt.FailWithStatus(UserOperationStatus.CancelledByNotification, new PasswordChangedModel());
|
||||
}
|
||||
|
||||
Attempt<PasswordChangedModel, UserOperationStatus> changePasswordAttempt =
|
||||
await ChangePasswordAsync(userKey, new ChangeUserPasswordModel
|
||||
await ChangePasswordAsync(user, new ChangeUserPasswordModel
|
||||
{
|
||||
NewPassword = password,
|
||||
UserKey = userKey,
|
||||
|
||||
Reference in New Issue
Block a user