From 296e3d881db09c6a7e4e0f14a8a78287306c4130 Mon Sep 17 00:00:00 2001 From: Vincent Salucci <26154748+vincentsalucci@users.noreply.github.com> Date: Tue, 30 Mar 2021 09:48:52 -0500 Subject: [PATCH] [Reset Password] Enrollment API, Service, and Model updates (#1245) * [Reset Password] Enrollment API, Service and Model updates * Added conditional check for calling User's ID --- .../Controllers/OrganizationUsersController.cs | 7 +++++++ src/Core/Enums/EventType.cs | 2 ++ .../OrganizationUserRequestModels.cs | 5 +++++ .../ProfileOrganizationResponseModel.cs | 4 ++++ .../OrganizationUserOrganizationDetails.cs | 1 + .../Models/Data/OrganizationUserUserDetails.cs | 1 + src/Core/Models/Table/OrganizationUser.cs | 1 + src/Core/Services/IOrganizationService.cs | 1 + .../Implementations/OrganizationService.cs | 18 ++++++++++++++++++ 9 files changed, 40 insertions(+) diff --git a/src/Api/Controllers/OrganizationUsersController.cs b/src/Api/Controllers/OrganizationUsersController.cs index bbeb1c921f..219a757e7b 100644 --- a/src/Api/Controllers/OrganizationUsersController.cs +++ b/src/Api/Controllers/OrganizationUsersController.cs @@ -180,6 +180,13 @@ namespace Bit.Api.Controllers var loggedInUserId = _userService.GetProperUserId(User); await _organizationService.UpdateUserGroupsAsync(organizationUser, model.GroupIds.Select(g => new Guid(g)), loggedInUserId); } + + [HttpPut("{userId}/reset-password-enrollment")] + public async Task PutResetPasswordEnrollment(string orgId, string userId, [FromBody]OrganizationUserResetPasswordEnrollmentRequestModel model) + { + var callingUserId = _userService.GetProperUserId(User); + await _organizationService.UpdateUserResetPasswordEnrollmentAsync(new Guid(orgId), new Guid(userId), model.ResetPasswordKey, callingUserId); + } [HttpDelete("{id}")] [HttpPost("{id}/delete")] diff --git a/src/Core/Enums/EventType.cs b/src/Core/Enums/EventType.cs index 5181808ec4..4c6a4217c4 100644 --- a/src/Core/Enums/EventType.cs +++ b/src/Core/Enums/EventType.cs @@ -43,6 +43,8 @@ OrganizationUser_Removed = 1503, OrganizationUser_UpdatedGroups = 1504, OrganizationUser_UnlinkedSso = 1505, + OrganizationUser_ResetPassword_Enroll = 1506, + OrganizationUser_ResetPassword_Withdraw = 1507, Organization_Updated = 1600, Organization_PurgedVault = 1601, diff --git a/src/Core/Models/Api/Request/Organizations/OrganizationUserRequestModels.cs b/src/Core/Models/Api/Request/Organizations/OrganizationUserRequestModels.cs index fa73209adc..8be3867eef 100644 --- a/src/Core/Models/Api/Request/Organizations/OrganizationUserRequestModels.cs +++ b/src/Core/Models/Api/Request/Organizations/OrganizationUserRequestModels.cs @@ -84,4 +84,9 @@ namespace Bit.Core.Models.Api [Required] public IEnumerable GroupIds { get; set; } } + + public class OrganizationUserResetPasswordEnrollmentRequestModel + { + public string ResetPasswordKey { get; set; } + } } diff --git a/src/Core/Models/Api/Response/ProfileOrganizationResponseModel.cs b/src/Core/Models/Api/Response/ProfileOrganizationResponseModel.cs index 70ad880d05..c28a6b753f 100644 --- a/src/Core/Models/Api/Response/ProfileOrganizationResponseModel.cs +++ b/src/Core/Models/Api/Response/ProfileOrganizationResponseModel.cs @@ -30,6 +30,8 @@ namespace Bit.Core.Models.Api SsoBound = !string.IsNullOrWhiteSpace(organization.SsoExternalId); Identifier = organization.Identifier; Permissions = CoreHelpers.LoadClassFromJsonData(organization.Permissions); + ResetPasswordKey = organization.ResetPasswordKey; + UserId = organization.UserId?.ToString(); } public string Id { get; set; } @@ -55,5 +57,7 @@ namespace Bit.Core.Models.Api public bool SsoBound { get; set; } public string Identifier { get; set; } public Permissions Permissions { get; set; } + public string ResetPasswordKey { get; set; } + public string UserId { get; set; } } } diff --git a/src/Core/Models/Data/OrganizationUserOrganizationDetails.cs b/src/Core/Models/Data/OrganizationUserOrganizationDetails.cs index e08c4a9ae7..8b504f3390 100644 --- a/src/Core/Models/Data/OrganizationUserOrganizationDetails.cs +++ b/src/Core/Models/Data/OrganizationUserOrganizationDetails.cs @@ -28,5 +28,6 @@ namespace Bit.Core.Models.Data public string SsoExternalId { get; set; } public string Identifier { get; set; } public string Permissions { get; set; } + public string ResetPasswordKey { get; set; } } } diff --git a/src/Core/Models/Data/OrganizationUserUserDetails.cs b/src/Core/Models/Data/OrganizationUserUserDetails.cs index d438950ab0..d14469e677 100644 --- a/src/Core/Models/Data/OrganizationUserUserDetails.cs +++ b/src/Core/Models/Data/OrganizationUserUserDetails.cs @@ -22,6 +22,7 @@ namespace Bit.Core.Models.Data public string ExternalId { get; set; } public string SsoExternalId { get; set; } public string Permissions { get; set; } + public string ResetPasswordKey { get; set; } public Dictionary GetTwoFactorProviders() { diff --git a/src/Core/Models/Table/OrganizationUser.cs b/src/Core/Models/Table/OrganizationUser.cs index d7215c28f9..13ab71ab3f 100644 --- a/src/Core/Models/Table/OrganizationUser.cs +++ b/src/Core/Models/Table/OrganizationUser.cs @@ -11,6 +11,7 @@ namespace Bit.Core.Models.Table public Guid? UserId { get; set; } public string Email { get; set; } public string Key { get; set; } + public string ResetPasswordKey { get; set; } public OrganizationUserStatusType Status { get; set; } public OrganizationUserType Type { get; set; } public bool AccessAll { get; set; } diff --git a/src/Core/Services/IOrganizationService.cs b/src/Core/Services/IOrganizationService.cs index 3a158ec743..f51b3a31bf 100644 --- a/src/Core/Services/IOrganizationService.cs +++ b/src/Core/Services/IOrganizationService.cs @@ -43,6 +43,7 @@ namespace Bit.Core.Services Task DeleteUserAsync(Guid organizationId, Guid organizationUserId, Guid? deletingUserId); Task DeleteUserAsync(Guid organizationId, Guid userId); Task UpdateUserGroupsAsync(OrganizationUser organizationUser, IEnumerable groupIds, Guid? loggedInUserId); + Task UpdateUserResetPasswordEnrollmentAsync(Guid organizationId, Guid organizationUserId, string resetPasswordKey, Guid? callingUserId); Task GenerateLicenseAsync(Guid organizationId, Guid installationId); Task GenerateLicenseAsync(Organization organization, Guid installationId, int? version = null); diff --git a/src/Core/Services/Implementations/OrganizationService.cs b/src/Core/Services/Implementations/OrganizationService.cs index 2850996e53..ea74bd4107 100644 --- a/src/Core/Services/Implementations/OrganizationService.cs +++ b/src/Core/Services/Implementations/OrganizationService.cs @@ -1378,6 +1378,24 @@ namespace Bit.Core.Services await _eventService.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_UpdatedGroups); } + + public async Task UpdateUserResetPasswordEnrollmentAsync(Guid organizationId, Guid organizationUserId, string resetPasswordKey, Guid? callingUserId) + { + var orgUser = await _organizationUserRepository.GetByOrganizationAsync(organizationId, organizationUserId); + if (!callingUserId.HasValue || orgUser == null || orgUser.UserId != callingUserId.Value || + orgUser.Status != OrganizationUserStatusType.Confirmed || + orgUser.OrganizationId != organizationId) + { + throw new BadRequestException("User not valid."); + } + + // TODO - Block certain org types from using this feature? + + orgUser.ResetPasswordKey = resetPasswordKey; + await _organizationUserRepository.ReplaceAsync(orgUser); + await _eventService.LogOrganizationUserEventAsync(orgUser, resetPasswordKey != null ? + EventType.OrganizationUser_ResetPassword_Enroll : EventType.OrganizationUser_ResetPassword_Withdraw); + } public async Task GenerateLicenseAsync(Guid organizationId, Guid installationId) {