diff --git a/src/Api/AdminConsole/Controllers/OrganizationUsersController.cs b/src/Api/AdminConsole/Controllers/OrganizationUsersController.cs index 0b93839d2d..cc5b76a241 100644 --- a/src/Api/AdminConsole/Controllers/OrganizationUsersController.cs +++ b/src/Api/AdminConsole/Controllers/OrganizationUsersController.cs @@ -456,6 +456,11 @@ public class OrganizationUsersController : Controller throw new UnauthorizedAccessException(); } + if (!string.IsNullOrWhiteSpace(model.ResetPasswordKey) && !await _userService.VerifySecretAsync(user, model.Secret)) + { + throw new BadRequestException("Incorrect password"); + } + var callingUserId = user.Id; await _organizationService.UpdateUserResetPasswordEnrollmentAsync( orgId, userId, model.ResetPasswordKey, callingUserId); diff --git a/src/Api/AdminConsole/Models/Request/Organizations/OrganizationUserRequestModels.cs b/src/Api/AdminConsole/Models/Request/Organizations/OrganizationUserRequestModels.cs index 0313d85798..44e9853bb2 100644 --- a/src/Api/AdminConsole/Models/Request/Organizations/OrganizationUserRequestModels.cs +++ b/src/Api/AdminConsole/Models/Request/Organizations/OrganizationUserRequestModels.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using Bit.Api.Auth.Models.Request.Accounts; using Bit.Api.Models.Request; using Bit.Core.Entities; using Bit.Core.Enums; @@ -98,7 +99,7 @@ public class OrganizationUserUpdateRequestModel } } -public class OrganizationUserResetPasswordEnrollmentRequestModel +public class OrganizationUserResetPasswordEnrollmentRequestModel : SecretVerificationRequestModel { public string ResetPasswordKey { get; set; } } diff --git a/test/Api.Test/AdminConsole/Controllers/OrganizationUsersControllerTests.cs b/test/Api.Test/AdminConsole/Controllers/OrganizationUsersControllerTests.cs index 5fef2885eb..80c458d697 100644 --- a/test/Api.Test/AdminConsole/Controllers/OrganizationUsersControllerTests.cs +++ b/test/Api.Test/AdminConsole/Controllers/OrganizationUsersControllerTests.cs @@ -40,6 +40,7 @@ public class OrganizationUsersControllerTests { orgUser.Status = Core.Enums.OrganizationUserStatusType.Invited; sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(user); + sutProvider.GetDependency<IUserService>().VerifySecretAsync(default, default).ReturnsForAnyArgs(true); sutProvider.GetDependency<IOrganizationUserRepository>().GetByOrganizationAsync(default, default).ReturnsForAnyArgs(orgUser); await sutProvider.Sut.PutResetPasswordEnrollment(orgId, userId, model); @@ -54,6 +55,7 @@ public class OrganizationUsersControllerTests { orgUser.Status = Core.Enums.OrganizationUserStatusType.Confirmed; sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(user); + sutProvider.GetDependency<IUserService>().VerifySecretAsync(default, default).ReturnsForAnyArgs(true); sutProvider.GetDependency<IOrganizationUserRepository>().GetByOrganizationAsync(default, default).ReturnsForAnyArgs(orgUser); await sutProvider.Sut.PutResetPasswordEnrollment(orgId, userId, model); @@ -61,6 +63,33 @@ public class OrganizationUsersControllerTests await sutProvider.GetDependency<IAcceptOrgUserCommand>().Received(0).AcceptOrgUserByOrgIdAsync(orgId, user, sutProvider.GetDependency<IUserService>()); } + [Theory] + [BitAutoData] + public async Task PutResetPasswordEnrollment_PasswordValidationFails_Throws(Guid orgId, Guid userId, OrganizationUserResetPasswordEnrollmentRequestModel model, + User user, SutProvider<OrganizationUsersController> sutProvider) + { + model.MasterPasswordHash = "NotThePassword"; + sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(user); + await Assert.ThrowsAsync<BadRequestException>(async () => await sutProvider.Sut.PutResetPasswordEnrollment(orgId, userId, model)); + } + + [Theory] + [BitAutoData] + public async Task PutResetPasswordEnrollment_PasswordValidationPasses_Continues(Guid orgId, Guid userId, OrganizationUserResetPasswordEnrollmentRequestModel model, + User user, OrganizationUser orgUser, SutProvider<OrganizationUsersController> sutProvider) + { + sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(user); + sutProvider.GetDependency<IUserService>().VerifySecretAsync(user, model.Secret).Returns(true); + sutProvider.GetDependency<IOrganizationUserRepository>().GetByOrganizationAsync(default, default).ReturnsForAnyArgs(orgUser); + await sutProvider.Sut.PutResetPasswordEnrollment(orgId, userId, model); + await sutProvider.GetDependency<IOrganizationService>().Received(1).UpdateUserResetPasswordEnrollmentAsync( + orgId, + userId, + model.ResetPasswordKey, + user.Id + ); + } + [Theory] [BitAutoData] public async Task Accept_RequiresKnownUser(Guid orgId, Guid orgUserId, OrganizationUserAcceptRequestModel model,