Skip to content

Commit 76f2772

Browse files
ZeegaanAndyButland
andauthored
V15: User password resetting notification (#18679)
* Introduce UserPasswordResettingNotification * Removed changes to IUserService interface. --------- Co-authored-by: Andy Butland <[email protected]>
1 parent d9d6646 commit 76f2772

File tree

3 files changed

+56
-13
lines changed

3 files changed

+56
-13
lines changed

src/Umbraco.Cms.Api.Management/Controllers/Security/SecurityControllerBase.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ protected IActionResult UserOperationStatusResult(UserOperationStatus status, Er
2121
.WithTitle("The password reset token was invalid")
2222
.WithDetail("The specified password reset token was either used already or wrong.")
2323
.Build()),
24+
UserOperationStatus.CancelledByNotification => BadRequest(problemDetailsBuilder
25+
.WithTitle("Cancelled by notification")
26+
.WithDetail("A notification handler prevented the user operation.")
27+
.Build()),
2428
UserOperationStatus.UnknownFailure => BadRequest(problemDetailsBuilder
2529
.WithTitle("Unknown failure")
2630
.WithDetail(errorMessageResult?.Error?.ErrorMessage ?? "The error was unknown")
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using Umbraco.Cms.Core.Events;
2+
using Umbraco.Cms.Core.Models.Membership;
3+
4+
namespace Umbraco.Cms.Core.Notifications;
5+
6+
public class UserPasswordResettingNotification : CancelableObjectNotification<IUser>
7+
{
8+
public UserPasswordResettingNotification(IUser target, EventMessages messages) : base(target, messages)
9+
{
10+
}
11+
12+
public IUser User => Target;
13+
}

src/Umbraco.Core/Services/UserService.cs

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1180,7 +1180,21 @@ private UserOperationStatus ValidateUserUpdateModel(IUser existingUser, UserUpda
11801180
return keys;
11811181
}
11821182

1183+
/// <inheritdoc/>
11831184
public async Task<Attempt<PasswordChangedModel, UserOperationStatus>> ChangePasswordAsync(Guid performingUserKey, ChangeUserPasswordModel model)
1185+
{
1186+
IServiceScope serviceScope = _serviceScopeFactory.CreateScope();
1187+
IBackOfficeUserStore userStore = serviceScope.ServiceProvider.GetRequiredService<IBackOfficeUserStore>();
1188+
IUser? performingUser = await userStore.GetAsync(performingUserKey);
1189+
if (performingUser is null)
1190+
{
1191+
return Attempt.FailWithStatus(UserOperationStatus.MissingUser, new PasswordChangedModel());
1192+
}
1193+
1194+
return await ChangePasswordAsync(performingUser, model);
1195+
}
1196+
1197+
private async Task<Attempt<PasswordChangedModel, UserOperationStatus>> ChangePasswordAsync(IUser performingUser, ChangeUserPasswordModel model)
11841198
{
11851199
IServiceScope serviceScope = _serviceScopeFactory.CreateScope();
11861200
using ICoreScope scope = ScopeProvider.CreateCoreScope();
@@ -1197,12 +1211,6 @@ public async Task<Attempt<PasswordChangedModel, UserOperationStatus>> ChangePass
11971211
return Attempt.FailWithStatus(UserOperationStatus.InvalidUserType, new PasswordChangedModel());
11981212
}
11991213

1200-
IUser? performingUser = await userStore.GetAsync(performingUserKey);
1201-
if (performingUser is null)
1202-
{
1203-
return Attempt.FailWithStatus(UserOperationStatus.MissingUser, new PasswordChangedModel());
1204-
}
1205-
12061214
// require old password for self change when outside of invite or resetByToken flows
12071215
if (performingUser.UserState != UserState.Invited && performingUser.Username == user.Username && string.IsNullOrEmpty(model.OldPassword) && string.IsNullOrEmpty(model.ResetPasswordToken))
12081216
{
@@ -1226,12 +1234,13 @@ public async Task<Attempt<PasswordChangedModel, UserOperationStatus>> ChangePass
12261234
IBackOfficePasswordChanger passwordChanger = serviceScope.ServiceProvider.GetRequiredService<IBackOfficePasswordChanger>();
12271235
Attempt<PasswordChangedModel?> result = await passwordChanger.ChangeBackOfficePassword(
12281236
new ChangeBackOfficeUserPasswordModel
1229-
{
1230-
NewPassword = model.NewPassword,
1231-
OldPassword = model.OldPassword,
1232-
User = user,
1233-
ResetPasswordToken = model.ResetPasswordToken,
1234-
}, performingUser);
1237+
{
1238+
NewPassword = model.NewPassword,
1239+
OldPassword = model.OldPassword,
1240+
User = user,
1241+
ResetPasswordToken = model.ResetPasswordToken,
1242+
},
1243+
performingUser);
12351244

12361245
if (result.Success is false)
12371246
{
@@ -2184,9 +2193,26 @@ public async Task<Attempt<PasswordChangedModel, UserOperationStatus>> CreateInit
21842193
public async Task<Attempt<PasswordChangedModel, UserOperationStatus>> ResetPasswordAsync(Guid userKey, string token, string password)
21852194
{
21862195
using ICoreScope scope = ScopeProvider.CreateCoreScope();
2196+
IServiceScope serviceScope = _serviceScopeFactory.CreateScope();
2197+
2198+
EventMessages evtMsgs = EventMessagesFactory.Get();
2199+
IBackOfficeUserStore userStore = serviceScope.ServiceProvider.GetRequiredService<IBackOfficeUserStore>();
2200+
2201+
IUser? user = await userStore.GetAsync(userKey);
2202+
if (user is null)
2203+
{
2204+
return Attempt.FailWithStatus(UserOperationStatus.UserNotFound, new PasswordChangedModel());
2205+
}
2206+
2207+
var savingNotification = new UserPasswordResettingNotification(user, evtMsgs);
2208+
if (await scope.Notifications.PublishCancelableAsync(savingNotification))
2209+
{
2210+
scope.Complete();
2211+
return Attempt.FailWithStatus(UserOperationStatus.CancelledByNotification, new PasswordChangedModel());
2212+
}
21872213

21882214
Attempt<PasswordChangedModel, UserOperationStatus> changePasswordAttempt =
2189-
await ChangePasswordAsync(userKey, new ChangeUserPasswordModel
2215+
await ChangePasswordAsync(user, new ChangeUserPasswordModel
21902216
{
21912217
NewPassword = password,
21922218
UserKey = userKey,

0 commit comments

Comments
 (0)