diff --git a/src/Api/Controllers/OrganizationsController.cs b/src/Api/Controllers/OrganizationsController.cs index 026d0d4a66..b4bf2f73a5 100644 --- a/src/Api/Controllers/OrganizationsController.cs +++ b/src/Api/Controllers/OrganizationsController.cs @@ -8,6 +8,8 @@ using Bit.Core.Models.Api; using Bit.Core.Exceptions; using Bit.Core.Services; using Bit.Core; +using Microsoft.AspNetCore.Identity; +using Bit.Core.Models.Table; namespace Bit.Api.Controllers { @@ -20,19 +22,22 @@ namespace Bit.Api.Controllers private readonly IOrganizationService _organizationService; private readonly IUserService _userService; private readonly CurrentContext _currentContext; + private readonly UserManager _userManager; public OrganizationsController( IOrganizationRepository organizationRepository, IOrganizationUserRepository organizationUserRepository, IOrganizationService organizationService, IUserService userService, - CurrentContext currentContext) + CurrentContext currentContext, + UserManager userManager) { _organizationRepository = organizationRepository; _organizationUserRepository = organizationUserRepository; _organizationService = organizationService; _userService = userService; _currentContext = currentContext; + _userManager = userManager; } [HttpGet("{id}")] @@ -185,7 +190,7 @@ namespace Bit.Api.Controllers [HttpDelete("{id}")] [HttpPost("{id}/delete")] - public async Task Delete(string id) + public async Task Delete(string id, [FromBody]OrganizationDeleteRequestModel model) { var orgIdGuid = new Guid(id); if(!_currentContext.OrganizationOwner(orgIdGuid)) @@ -199,7 +204,16 @@ namespace Bit.Api.Controllers throw new NotFoundException(); } - await _organizationRepository.DeleteAsync(organization); + var user = await _userService.GetUserByPrincipalAsync(User); + if(!await _userManager.CheckPasswordAsync(user, model.MasterPasswordHash)) + { + ModelState.AddModelError("MasterPasswordHash", "Invalid password."); + await Task.Delay(2000); + } + else + { + await _organizationService.DeleteAsync(organization); + } } } } diff --git a/src/Core/Models/Api/Request/Organizations/OrganizationDeleteRequestModel.cs b/src/Core/Models/Api/Request/Organizations/OrganizationDeleteRequestModel.cs new file mode 100644 index 0000000000..b18388e8e5 --- /dev/null +++ b/src/Core/Models/Api/Request/Organizations/OrganizationDeleteRequestModel.cs @@ -0,0 +1,10 @@ +using System.ComponentModel.DataAnnotations; + +namespace Bit.Core.Models.Api +{ + public class OrganizationDeleteRequestModel + { + [Required] + public string MasterPasswordHash { get; set; } + } +} diff --git a/src/Core/Services/IOrganizationService.cs b/src/Core/Services/IOrganizationService.cs index 354815ba40..d0b4ad8dae 100644 --- a/src/Core/Services/IOrganizationService.cs +++ b/src/Core/Services/IOrganizationService.cs @@ -16,6 +16,7 @@ namespace Bit.Core.Services Task UpgradePlanAsync(Guid organizationId, PlanType plan, int additionalSeats); Task AdjustSeatsAsync(Guid organizationId, int seatAdjustment); Task> SignUpAsync(OrganizationSignup organizationSignup); + Task DeleteAsync(Organization organization); Task UpdateAsync(Organization organization, bool updateBilling = false); Task InviteUserAsync(Guid organizationId, Guid invitingUserId, string email, Enums.OrganizationUserType type, IEnumerable subvaults); diff --git a/src/Core/Services/Implementations/OrganizationService.cs b/src/Core/Services/Implementations/OrganizationService.cs index 2a74d524e8..cea64fd9dd 100644 --- a/src/Core/Services/Implementations/OrganizationService.cs +++ b/src/Core/Services/Implementations/OrganizationService.cs @@ -570,6 +570,21 @@ namespace Bit.Core.Services } } + public async Task DeleteAsync(Organization organization) + { + if(!string.IsNullOrWhiteSpace(organization.StripeSubscriptionId)) + { + var subscriptionService = new StripeSubscriptionService(); + var canceledSub = await subscriptionService.CancelAsync(organization.StripeSubscriptionId, false); + if(!canceledSub.CanceledAt.HasValue) + { + throw new BadRequestException("Unable to cancel subscription."); + } + } + + await _organizationRepository.DeleteAsync(organization); + } + public async Task UpdateAsync(Organization organization, bool updateBilling = false) { if(organization.Id == default(Guid))