mirror of
https://github.com/bitwarden/server
synced 2025-12-31 23:53:17 +00:00
[AC-1218] Add ability to delete Provider Portals (#3973)
* add new classes * initial commit * revert the changes on this files Signed-off-by: Cy Okeke <cokeke@bitwarden.com> * revert unnecessary changes * Add a model * add the delete token endpoint * add a unit test for delete provider Signed-off-by: Cy Okeke <cokeke@bitwarden.com> * add the delete provider method Signed-off-by: Cy Okeke <cokeke@bitwarden.com> * resolve the failing test Signed-off-by: Cy Okeke <cokeke@bitwarden.com> * resolve the delete request redirect issue Signed-off-by: Cy Okeke <cokeke@bitwarden.com> * changes to correct the json issue Signed-off-by: Cy Okeke <cokeke@bitwarden.com> * resolve errors Signed-off-by: Cy Okeke <cokeke@bitwarden.com> * resolve pr comment Signed-off-by: Cy Okeke <cokeke@bitwarden.com> * move ProviderDeleteTokenable to the adminConsole Signed-off-by: Cy Okeke <cokeke@bitwarden.com> * Add feature flag * resolve pr comments Signed-off-by: Cy Okeke <cokeke@bitwarden.com> * add some unit test Signed-off-by: Cy Okeke <cokeke@bitwarden.com> * resolve the failing test Signed-off-by: Cy Okeke <cokeke@bitwarden.com> * resolve test Signed-off-by: Cy Okeke <cokeke@bitwarden.com> * add the remove feature flag Signed-off-by: Cy Okeke <cokeke@bitwarden.com> * [AC-2378] Added `ProviderId` to PayPal transaction model (#3995) * Added ProviderId to PayPal transaction model * Fixed issue with parsing provider id * [AC-1923] Add endpoint to create client organization (#3977) * Add new endpoint for creating client organizations in consolidated billing * Create empty org and then assign seats for code re-use * Fixes made from debugging client side * few more small fixes * Vincent's feedback * Bumped version to 2024.4.1 (#3997) * [AC-1923] Add endpoint to create client organization (#3977) * Add new endpoint for creating client organizations in consolidated billing * Create empty org and then assign seats for code re-use * Fixes made from debugging client side * few more small fixes * Vincent's feedback * [AC-1923] Add endpoint to create client organization (#3977) * Add new endpoint for creating client organizations in consolidated billing * Create empty org and then assign seats for code re-use * Fixes made from debugging client side * few more small fixes * Vincent's feedback * add changes after merge conflict Signed-off-by: Cy Okeke <cokeke@bitwarden.com> --------- Signed-off-by: Cy Okeke <cokeke@bitwarden.com> Co-authored-by: Conner Turnbull <133619638+cturnbull-bitwarden@users.noreply.github.com> Co-authored-by: Alex Morask <144709477+amorask-bitwarden@users.noreply.github.com> Co-authored-by: Bitwarden DevOps <106330231+bitwarden-devops-bot@users.noreply.github.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using System.Net;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Net;
|
||||
using Bit.Admin.AdminConsole.Models;
|
||||
using Bit.Admin.Enums;
|
||||
using Bit.Admin.Utilities;
|
||||
@@ -10,6 +11,7 @@ using Bit.Core.AdminConsole.Repositories;
|
||||
using Bit.Core.AdminConsole.Services;
|
||||
using Bit.Core.Billing.Entities;
|
||||
using Bit.Core.Billing.Repositories;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Settings;
|
||||
@@ -275,4 +277,64 @@ public class ProvidersController : Controller
|
||||
|
||||
return RedirectToAction("Edit", "Providers", new { id = providerId });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
[RequirePermission(Permission.Provider_Edit)]
|
||||
public async Task<IActionResult> Delete(Guid id, string providerName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(providerName))
|
||||
{
|
||||
return BadRequest("Invalid provider name");
|
||||
}
|
||||
|
||||
var providerOrganizations = await _providerOrganizationRepository.GetManyDetailsByProviderAsync(id);
|
||||
|
||||
if (providerOrganizations.Count > 0)
|
||||
{
|
||||
return BadRequest("You must unlink all clients before you can delete a provider");
|
||||
}
|
||||
|
||||
var provider = await _providerRepository.GetByIdAsync(id);
|
||||
|
||||
if (provider is null)
|
||||
{
|
||||
return BadRequest("Provider does not exist");
|
||||
}
|
||||
|
||||
if (!string.Equals(providerName.Trim(), provider.Name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return BadRequest("Invalid provider name");
|
||||
}
|
||||
|
||||
await _providerService.DeleteAsync(provider);
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
[RequirePermission(Permission.Provider_Edit)]
|
||||
public async Task<IActionResult> DeleteInitiation(Guid id, string providerEmail)
|
||||
{
|
||||
var emailAttribute = new EmailAddressAttribute();
|
||||
if (!emailAttribute.IsValid(providerEmail))
|
||||
{
|
||||
return BadRequest("Invalid provider admin email");
|
||||
}
|
||||
|
||||
var provider = await _providerRepository.GetByIdAsync(id);
|
||||
if (provider != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _providerService.InitiateDeleteAsync(provider, providerEmail);
|
||||
}
|
||||
catch (BadRequestException ex)
|
||||
{
|
||||
return BadRequest(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
@model ProviderEditModel
|
||||
@{
|
||||
ViewData["Title"] = "Provider: " + Model.Provider.DisplayName();
|
||||
|
||||
var canEdit = AccessControlService.UserHasPermission(Permission.Provider_Edit);
|
||||
}
|
||||
|
||||
@@ -62,10 +61,89 @@
|
||||
}
|
||||
</form>
|
||||
@await Html.PartialAsync("Organizations", Model)
|
||||
@if (canEdit)
|
||||
{
|
||||
<div class="d-flex mt-4">
|
||||
<button type="submit" class="btn btn-primary" form="edit-form">Save</button>
|
||||
</div>
|
||||
}
|
||||
@if (canEdit)
|
||||
{
|
||||
<!-- Modals -->
|
||||
<div class="modal fade rounded" id="requestDeletionModal" tabindex="-1" aria-labelledby="requestDeletionModal" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content rounded">
|
||||
<div class="p-3">
|
||||
<h3 class="font-weight-bolder" id="exampleModalLabel">Request provider deletion</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<span class="font-weight-light">
|
||||
Enter the email of the provider admin that will receive the request to delete the provider portal.
|
||||
</span>
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<label for="provider-email" class="col-form-label">Provider email</label>
|
||||
<input type="email" class="form-control" id="provider-email">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-outline-primary btn-pill" data-dismiss="modal">Cancel</button>
|
||||
<button type="button" class="btn btn-danger btn-pill" onclick="initiateDeleteProvider('@Model.Provider.Id')">Send email request</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade" id="DeleteModal" tabindex="-1" aria-labelledby="DeleteModal" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content rounded">
|
||||
<div class="p-3">
|
||||
<h3 class="font-weight-bolder" id="exampleModalLabel">Delete provider</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<span class="font-weight-light">
|
||||
This action is permanent and irreversible. Enter the provide name to complete deletion of the provider and associated data.
|
||||
</span>
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<label for="provider-name" class="col-form-label">Provider name</label>
|
||||
<input type="text" class="form-control" id="provider-name">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-outline-primary btn-pill" data-dismiss="modal">Cancel</button>
|
||||
<button type="button" class="btn btn-danger btn-pill" onclick="deleteProvider('@Model.Provider.Id');">Delete provider</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade" id="linkedWarningModal" tabindex="-1" role="dialog" aria-labelledby="linkedWarningModal" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content rounded">
|
||||
<div class="modal-body">
|
||||
<h4 class="font-weight-bolder">Cannot Delete @Model.Name</h4>
|
||||
<p class="font-weight-lighter">you must unlink all clients before deleting @Model.Name</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-outline-primary btn-pill" data-dismiss="modal">Cancel</button>
|
||||
<button type="button" class="btn btn-primary btn-pill" data-dismiss="modal">Ok</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- End of Modal Section -->
|
||||
|
||||
<div class="d-flex mt-4">
|
||||
<button type="submit" class="btn btn-primary" form="edit-form">Save</button>
|
||||
@if (FeatureService.IsEnabled(FeatureFlagKeys.EnableDeleteProvider))
|
||||
{
|
||||
<div class="ml-auto d-flex">
|
||||
<button class="btn btn-danger" onclick="openRequestDeleteModal(@Model.ProviderOrganizations.Count())">Request Delete</button>
|
||||
<button id="requestDeletionBtn" hidden="hidden" data-toggle="modal" data-target="#requestDeletionModal"></button>
|
||||
|
||||
<button class="btn btn-outline-danger ml-2" onclick="openDeleteModal(@Model.ProviderOrganizations.Count())">Delete</button>
|
||||
<button id="deleteBtn" hidden="hidden" data-toggle="modal" data-target="#DeleteModal"></button>
|
||||
|
||||
<button id="linkAccWarningBtn" hidden="hidden" data-toggle="modal" data-target="#linkedWarningModal"></button>
|
||||
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
}
|
||||
|
||||
@@ -17,4 +17,60 @@
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function deleteProvider(id) {
|
||||
const providerName = $('#DeleteModal input#provider-name').val();
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: `@Url.Action("Delete", "Providers")?id=${id}&providerName=${providerName}`,
|
||||
dataType: 'json',
|
||||
contentType: false,
|
||||
processData: false,
|
||||
success: function () {
|
||||
$('#DeleteModal').modal('hide');
|
||||
window.location.href = `@Url.Action("Index", "Providers")`;
|
||||
},
|
||||
error: function (response) {
|
||||
alert("Error!: " + response.responseText);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function initiateDeleteProvider(id) {
|
||||
const email = $('#requestDeletionModal input#provider-email').val();
|
||||
const providerEmail = encodeURIComponent(email);
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: `@Url.Action("DeleteInitiation", "Providers")?id=${id}&providerEmail=${providerEmail}`,
|
||||
dataType: 'json',
|
||||
contentType: false,
|
||||
processData: false,
|
||||
success: function () {
|
||||
$('#requestDeletionModal').modal('hide');
|
||||
window.location.href = `@Url.Action("Index", "Providers")`;
|
||||
},
|
||||
error: function (response) {
|
||||
alert("Error!: " + response.responseText);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function openDeleteModal(providerOrganizations) {
|
||||
|
||||
if (providerOrganizations > 0){
|
||||
$('#linkAccWarningBtn').click()
|
||||
} else {
|
||||
$('#deleteBtn').click()
|
||||
}
|
||||
}
|
||||
|
||||
function openRequestDeleteModal(providerOrganizations) {
|
||||
|
||||
if (providerOrganizations > 0){
|
||||
$('#linkAccWarningBtn').click()
|
||||
} else {
|
||||
$('#requestDeletionBtn').click()
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user