1
0
mirror of https://github.com/bitwarden/server.git synced 2025-07-04 01:22:50 -05:00

[PM-7004] Org Admin Initiate Delete (#3905)

* org delete

* move org id to URL path

* tweaks

* lint fixes

* Update src/Core/Services/Implementations/HandlebarsMailService.cs

Co-authored-by: Rui Tomé <108268980+r-tome@users.noreply.github.com>

* Update src/Core/Services/Implementations/HandlebarsMailService.cs

Co-authored-by: Rui Tomé <108268980+r-tome@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: Rui Tomé <108268980+r-tome@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: Rui Tomé <108268980+r-tome@users.noreply.github.com>

* PR feedback

* fix id

* [PM-7004] Move OrgDeleteTokenable to AdminConsole ownership

* [PM-7004] Add consolidated billing logic into organization delete request acceptance endpoint

* [PM-7004] Delete unused IOrganizationService.DeleteAsync(Organization organization, string token) method

* [PM-7004] Fix unit tests

* [PM-7004] Update delete organization request email templates

* Add success message when initiating organization deletion

* Refactor OrganizationsController request delete initiation action to handle exceptions

---------

Co-authored-by: Rui Tomé <108268980+r-tome@users.noreply.github.com>
Co-authored-by: Rui Tome <rtome@bitwarden.com>
This commit is contained in:
Kyle Spearrin
2024-05-22 12:59:19 -04:00
committed by GitHub
parent 56c523f76f
commit 4264fc0729
18 changed files with 334 additions and 28 deletions

View File

@ -11,6 +11,7 @@ using Bit.Api.Models.Request.Organizations;
using Bit.Api.Models.Response;
using Bit.Core;
using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.Models.Business.Tokenables;
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationApiKeys.Interfaces;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationCollectionEnhancements.Interfaces;
@ -26,6 +27,7 @@ using Bit.Core.Exceptions;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Settings;
using Bit.Core.Tokens;
using Bit.Core.Utilities;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
@ -54,6 +56,7 @@ public class OrganizationsController : Controller
private readonly IOrganizationEnableCollectionEnhancementsCommand _organizationEnableCollectionEnhancementsCommand;
private readonly IProviderRepository _providerRepository;
private readonly IScaleSeatsCommand _scaleSeatsCommand;
private readonly IDataProtectorTokenFactory<OrgDeleteTokenable> _orgDeleteTokenDataFactory;
public OrganizationsController(
IOrganizationRepository organizationRepository,
@ -73,7 +76,8 @@ public class OrganizationsController : Controller
IPushNotificationService pushNotificationService,
IOrganizationEnableCollectionEnhancementsCommand organizationEnableCollectionEnhancementsCommand,
IProviderRepository providerRepository,
IScaleSeatsCommand scaleSeatsCommand)
IScaleSeatsCommand scaleSeatsCommand,
IDataProtectorTokenFactory<OrgDeleteTokenable> orgDeleteTokenDataFactory)
{
_organizationRepository = organizationRepository;
_organizationUserRepository = organizationUserRepository;
@ -93,6 +97,7 @@ public class OrganizationsController : Controller
_organizationEnableCollectionEnhancementsCommand = organizationEnableCollectionEnhancementsCommand;
_providerRepository = providerRepository;
_scaleSeatsCommand = scaleSeatsCommand;
_orgDeleteTokenDataFactory = orgDeleteTokenDataFactory;
}
[HttpGet("{id}")]
@ -279,6 +284,37 @@ public class OrganizationsController : Controller
await _organizationService.DeleteAsync(organization);
}
[HttpPost("{id}/delete-recover-token")]
[AllowAnonymous]
public async Task PostDeleteRecoverToken(Guid id, [FromBody] OrganizationVerifyDeleteRecoverRequestModel model)
{
var organization = await _organizationRepository.GetByIdAsync(id);
if (organization == null)
{
throw new NotFoundException();
}
if (!_orgDeleteTokenDataFactory.TryUnprotect(model.Token, out var data) || !data.IsValid(organization))
{
throw new BadRequestException("Invalid token.");
}
var consolidatedBillingEnabled = _featureService.IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling);
if (consolidatedBillingEnabled && organization.IsValidClient())
{
var provider = await _providerRepository.GetByOrganizationIdAsync(organization.Id);
if (provider.IsBillable())
{
await _scaleSeatsCommand.ScalePasswordManagerSeats(
provider,
organization.PlanType,
-organization.Seats ?? 0);
}
}
await _organizationService.DeleteAsync(organization);
}
[HttpPost("{id}/import")]
public async Task Import(string id, [FromBody] ImportOrganizationUsersRequestModel model)
{

View File

@ -0,0 +1,9 @@
using System.ComponentModel.DataAnnotations;
namespace Bit.Api.AdminConsole.Models.Request.Organizations;
public class OrganizationVerifyDeleteRecoverRequestModel
{
[Required]
public string Token { get; set; }
}