1
0
mirror of https://github.com/bitwarden/server.git synced 2025-07-17 23:50:58 -05:00

[AC-1895] AC Team code ownership moves: Bitwarden Portal (#3528)

---------

Co-authored-by: Addison Beck <hello@addisonbeck.com>
This commit is contained in:
Thomas Rittson
2024-02-21 09:18:09 +10:00
committed by GitHub
parent 3a6b2d85d3
commit 0abd52b5be
34 changed files with 52 additions and 38 deletions

View File

@ -1,325 +0,0 @@
@using Bit.SharedWeb.Utilities
@using Bit.Admin.Enums;
@using Bit.Core.AdminConsole.Enums.Provider
@inject Bit.Admin.Services.IAccessControlService AccessControlService;
@model OrganizationEditModel
@{
var canViewGeneralDetails = AccessControlService.UserHasPermission(Permission.Org_GeneralDetails_View);
var canViewBilling = AccessControlService.UserHasPermission(Permission.Org_Billing_View);
var canViewBusinessInformation = AccessControlService.UserHasPermission(Permission.Org_BusinessInformation_View);
var canViewPlan = AccessControlService.UserHasPermission(Permission.Org_Plan_View);
var canViewLicensing = AccessControlService.UserHasPermission(Permission.Org_Licensing_View);
var canCheckEnabled = AccessControlService.UserHasPermission(Permission.Org_CheckEnabledBox);
var canEditPlan = AccessControlService.UserHasPermission(Permission.Org_Plan_Edit);
var canEditLicensing = AccessControlService.UserHasPermission(Permission.Org_Licensing_Edit);
var canEditBilling = AccessControlService.UserHasPermission(Permission.Org_Billing_Edit);
var canLaunchGateway = AccessControlService.UserHasPermission(Permission.Org_Billing_LaunchGateway);
}
<form method="post" id="edit-form" asp-route-providerId="@Model.Provider?.Id">
<input asp-for="SalesAssistedTrialStarted" type="hidden">
@if (canViewGeneralDetails)
{
<h2>General</h2>
<div class="row">
<div class="col-sm">
<div class="form-group">
<label asp-for="Name"></label>
<input type="text" class="form-control" asp-for="Name" required>
</div>
</div>
</div>
@if (Model.Provider?.Type == ProviderType.Reseller)
{
<div class="row">
<div class="col-sm">
<div class="form-group">
<label>Client Owner Email</label>
@if (!string.IsNullOrWhiteSpace(Model.Owners))
{
<input type="text" class="form-control" asp-for="Owners" readonly="readonly">
}
else
{
<input type="text" class="form-control" asp-for="Owners" required>
}
<label class="form-check-label small text-muted align-top">This user should be independent of the Provider. If the Provider is disassociated with the organization, this user will maintain ownership of the organization.</label>
</div>
</div>
</div>
}
@if (Model.Organization != null)
{
<div class="form-check mb-3">
<input type="checkbox" class="form-check-input" asp-for="Enabled" disabled='@(canCheckEnabled ? null : "disabled")'>
<label class="form-check-label" asp-for="Enabled"></label>
</div>
}
}
@if (canViewBusinessInformation)
{
<h2>Business Information</h2>
<div class="row">
<div class="col-sm">
<div class="form-group">
<label asp-for="BusinessName"></label>
<input type="text" class="form-control" asp-for="BusinessName">
</div>
</div>
</div>
}
@if (canViewPlan)
{
<h2>Plan</h2>
<div class="row">
<div class="col-sm">
<div class="form-group">
<label asp-for="PlanType"></label>
@{
var planTypes = Enum.GetValues<PlanType>()
.Where(p =>
Model.Provider == null ||
(Model.Provider != null
&& p is >= PlanType.TeamsMonthly2019 and <= PlanType.EnterpriseAnnually2019 or >= PlanType.TeamsMonthly2020 and <= PlanType.EnterpriseAnnually)
)
.Select(e => new SelectListItem
{
Value = ((int)e).ToString(),
Text = e.GetDisplayAttribute()?.GetName() ?? e.ToString()
})
.ToList();
}
<select class="form-control" asp-for="PlanType" asp-items="planTypes" disabled='@(canEditPlan ? null : "disabled")'></select>
</div>
</div>
<div class="col-sm">
<div class="form-group">
<label asp-for="Plan"></label>
<input type="text" class="form-control" asp-for="Plan" required readonly='@(!canEditPlan)'>
</div>
</div>
</div>
<h2>Features</h2>
<div class="row mb-3">
<div class="col-4">
<h3>General</h3>
<div class="form-check mb-2">
<input type="checkbox" class="form-check-input" asp-for="SelfHost" disabled='@(canEditPlan ? null : "disabled")'>
<label class="form-check-label" asp-for="SelfHost"></label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" asp-for="Use2fa" disabled='@(canEditPlan ? null : "disabled")'>
<label class="form-check-label" asp-for="Use2fa"></label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" asp-for="UseApi" disabled='@(canEditPlan ? null : "disabled")'>
<label class="form-check-label" asp-for="UseApi"></label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" asp-for="UseGroups" disabled='@(canEditPlan ? null : "disabled")'>
<label class="form-check-label" asp-for="UseGroups"></label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" asp-for="UsePolicies" disabled='@(canEditPlan ? null : "disabled")'>
<label class="form-check-label" asp-for="UsePolicies"></label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" asp-for="UseSso" disabled='@(canEditPlan ? null : "disabled")'>
<label class="form-check-label" asp-for="UseSso"></label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" asp-for="UseKeyConnector" disabled='@(canEditPlan ? null : "disabled")'>
<label class="form-check-label" asp-for="UseKeyConnector"></label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" asp-for="UseScim" disabled='@(canEditPlan ? null : "disabled")'>
<label class="form-check-label" asp-for="UseScim"></label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" asp-for="UseDirectory" disabled='@(canEditPlan ? null : "disabled")'>
<label class="form-check-label" asp-for="UseDirectory"></label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" asp-for="UseEvents" disabled='@(canEditPlan ? null : "disabled")'>
<label class="form-check-label" asp-for="UseEvents"></label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" asp-for="UseResetPassword" disabled='@(canEditPlan ? null : "disabled")'>
<label class="form-check-label" asp-for="UseResetPassword"></label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" asp-for="UseCustomPermissions" disabled='@(canEditPlan ? null : "disabled")'>
<label class="form-check-label" asp-for="UseCustomPermissions"></label>
</div>
</div>
<div class="col-4">
<h3>Password Manager</h3>
<div class="form-check">
<input type="checkbox" class="form-check-input" asp-for="UseTotp" disabled='@(canEditPlan ? null : "disabled")'>
<label class="form-check-label" asp-for="UseTotp"></label>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" asp-for="UsersGetPremium" disabled='@(canEditPlan ? null : "disabled")'>
<label class="form-check-label" asp-for="UsersGetPremium"></label>
</div>
</div>
<div class="col-4">
<h3>Secrets Manager</h3>
<div class="form-check">
<input type="checkbox" class="form-check-input" asp-for="UseSecretsManager" disabled='@(canEditPlan ? null : "disabled")'>
<label class="form-check-label" asp-for="UseSecretsManager"></label>
</div>
</div>
</div>
}
@if (canViewPlan)
{
<h2>Password Manager Configuration</h2>
<div class="row">
<div class="col-sm">
<div class="form-group">
<label asp-for="Seats"></label>
<input type="number" class="form-control" asp-for="Seats" min="1" readonly='@(!canEditPlan)'>
</div>
</div>
<div class="col-sm">
<div class="form-group">
<label asp-for="MaxCollections"></label>
<input type="number" class="form-control" asp-for="MaxCollections" min="1" readonly='@(!canEditPlan)'>
</div>
</div>
<div class="col-sm">
<div class="form-group">
<label asp-for="MaxStorageGb"></label>
<input type="number" class="form-control" asp-for="MaxStorageGb" min="1" readonly='@(!canEditPlan)'>
</div>
</div>
</div>
<div class="row">
<div class="col-4">
<div class="form-group">
<label asp-for="MaxAutoscaleSeats"></label>
<input type="number" class="form-control" asp-for="MaxAutoscaleSeats" min="1" readonly='@(!canEditPlan)'>
</div>
</div>
</div>
}
@if (canViewPlan)
{
<div id="organization-secrets-configuration" hidden="@(!Model.UseSecretsManager)">
<h2>Secrets Manager Configuration</h2>
<div class="row">
<div class="col-sm">
<div class="form-group">
<label asp-for="SmSeats"></label>
<input type="number" class="form-control" asp-for="SmSeats" min="1" readonly='@(!canEditPlan)'>
</div>
</div>
<div class="col-sm">
<div class="form-group">
<label asp-for="MaxAutoscaleSmSeats"></label>
<input type="number" class="form-control" asp-for="MaxAutoscaleSmSeats" min="1" readonly='@(!canEditPlan)'>
</div>
</div>
<div class="col-sm">
<div class="form-group">
<label asp-for="SmServiceAccounts"></label>
<input type="number" class="form-control" asp-for="SmServiceAccounts" min="1" readonly='@(!canEditPlan)'>
</div>
</div>
</div>
<div class="row">
<div class="col-4">
<div class="form-group">
<label asp-for="MaxAutoscaleSmServiceAccounts"></label>
<input type="number" class="form-control" asp-for="MaxAutoscaleSmServiceAccounts" min="1" readonly='@(!canEditPlan)'>
</div>
</div>
</div>
</div>
}
@if(canViewLicensing)
{
<h2>Licensing</h2>
<div class="row">
<div class="col-sm">
<div class="form-group">
<label asp-for="LicenseKey"></label>
<input type="text" class="form-control" asp-for="LicenseKey" readonly='@(!canEditLicensing)'>
</div>
</div>
<div class="col-sm">
<div class="form-group">
<label asp-for="ExpirationDate"></label>
<input type="datetime-local" class="form-control" asp-for="ExpirationDate" readonly='@(!canEditLicensing)' step="1">
</div>
</div>
</div>
}
@if (canViewBilling)
{
<h2>Billing</h2>
<div class="row">
<div class="col-sm">
<div class="form-group">
<label asp-for="BillingEmail"></label>
<input type="email" class="form-control" asp-for="BillingEmail" readonly="readonly">
</div>
</div>
<div class="col-sm">
<div class="form-group">
<div class="form-group">
<label asp-for="Gateway"></label>
<select class="form-control" asp-for="Gateway" disabled='@(canEditBilling ? null : "disabled")'
asp-items="Html.GetEnumSelectList<Bit.Core.Enums.GatewayType>()">
<option value="">--</option>
</select>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-sm">
<div class="form-group">
<label asp-for="GatewayCustomerId"></label>
<div class="input-group">
<input type="text" class="form-control" asp-for="GatewayCustomerId" readonly='@(!canEditBilling)'>
@if(canLaunchGateway)
{
<div class="input-group-append">
<button class="btn btn-secondary" type="button" id="gateway-customer-link">
<i class="fa fa-external-link"></i>
</button>
</div>
}
</div>
</div>
</div>
<div class="col-sm">
<div class="form-group">
<label asp-for="GatewaySubscriptionId"></label>
<div class="input-group">
<input type="text" class="form-control" asp-for="GatewaySubscriptionId" readonly='@(!canEditBilling)'>
@if (canLaunchGateway)
{
<div class="input-group-append">
<button class="btn btn-secondary" type="button" id="gateway-subscription-link">
<i class="fa fa-external-link"></i>
</button>
</div>
}
</div>
</div>
</div>
</div>
}
</form>

View File

@ -1,153 +0,0 @@
@inject IWebHostEnvironment HostingEnvironment
@using Bit.Admin.Utilities
@model OrganizationEditModel
<script>
(() => {
document.getElementById('@(nameof(Model.PlanType))').addEventListener('change', () => {
const selectEl = document.getElementById('@(nameof(Model.PlanType))');
const selectText = selectEl.options[selectEl.selectedIndex].text;
document.getElementById('@(nameof(Model.Plan))').value = selectText;
togglePlanFeatures(selectEl.options[selectEl.selectedIndex].value);
});
document.getElementById('gateway-customer-link')?.addEventListener('click', () => {
const gateway = document.getElementById('@(nameof(Model.Gateway))');
const customerId = document.getElementById('@(nameof(Model.GatewayCustomerId))');
if (!gateway || gateway.value === '' || !customerId || customerId.value === '') {
return;
}
if (gateway.value === '@((byte)GatewayType.Stripe)') {
const url = `@(HostingEnvironment.GetStripeUrl())/customers/${customerId.value}/`;
window.open(url, '_blank');
} else if (gateway.value === '@((byte)GatewayType.Braintree)') {
const url = `@(HostingEnvironment.GetBraintreeMerchantUrl())/@Model.BraintreeMerchantId/${customerId.value}`;
window.open(url, '_blank');
}
});
document.getElementById('gateway-subscription-link')?.addEventListener('click', () => {
const gateway = document.getElementById('@(nameof(Model.Gateway))');
const subId = document.getElementById('@(nameof(Model.GatewaySubscriptionId))');
if (!gateway || gateway.value === '' || !subId || subId.value === '') {
return;
}
if (gateway.value === '@((byte)GatewayType.Stripe)') {
const url = `@(HostingEnvironment.GetStripeUrl())/subscriptions/${subId.value}/`;
window.open(url, '_blank');
} else if (gateway.value === '@((byte)GatewayType.Braintree)') {
const url = `@(HostingEnvironment.GetBraintreeMerchantUrl())/@Model.BraintreeMerchantId/subscriptions/${subId.value}`;
window.open(url, '_blank');
}
});
document.getElementById('@(nameof(Model.UseSecretsManager))').addEventListener('change', (event) => {
document.getElementById('organization-secrets-configuration').hidden = !event.target.checked;
if (event.target.checked) {
setInitialSecretsManagerConfiguration();
return;
}
clearSecretsManagerConfiguration();
});
})();
function togglePlanFeatures(planType) {
switch(planType) {
case '@((byte)PlanType.TeamsMonthly2019)':
case '@((byte)PlanType.TeamsAnnually2019)':
case '@((byte)PlanType.TeamsMonthly2020)':
case '@((byte)PlanType.TeamsAnnually2020)':
case '@((byte)PlanType.TeamsMonthly)':
case '@((byte)PlanType.TeamsAnnually)':
case '@((byte)PlanType.TeamsStarter)':
document.getElementById('@(nameof(Model.UsePolicies))').checked = false;
document.getElementById('@(nameof(Model.UseSso))').checked = false;
document.getElementById('@(nameof(Model.UseGroups))').checked = true;
document.getElementById('@(nameof(Model.UseDirectory))').checked = true;
document.getElementById('@(nameof(Model.UseEvents))').checked = true;
document.getElementById('@(nameof(Model.UsersGetPremium))').checked = true;
document.getElementById('@(nameof(Model.UseCustomPermissions))').checked = false;
document.getElementById('@(nameof(Model.UseTotp))').checked = true;
document.getElementById('@(nameof(Model.Use2fa))').checked = true;
document.getElementById('@(nameof(Model.UseApi))').checked = true;
document.getElementById('@(nameof(Model.SelfHost))').checked = false;
document.getElementById('@(nameof(Model.UseResetPassword))').checked = false;
document.getElementById('@(nameof(Model.UseScim))').checked = false;
break;
case '@((byte)PlanType.EnterpriseMonthly2019)':
case '@((byte)PlanType.EnterpriseAnnually2019)':
case '@((byte)PlanType.EnterpriseMonthly2020)':
case '@((byte)PlanType.EnterpriseAnnually2020)':
case '@((byte)PlanType.EnterpriseMonthly)':
case '@((byte)PlanType.EnterpriseAnnually)':
document.getElementById('@(nameof(Model.UsePolicies))').checked = true;
document.getElementById('@(nameof(Model.UseSso))').checked = true;
document.getElementById('@(nameof(Model.UseGroups))').checked = true;
document.getElementById('@(nameof(Model.UseDirectory))').checked = true;
document.getElementById('@(nameof(Model.UseEvents))').checked = true;
document.getElementById('@(nameof(Model.UsersGetPremium))').checked = true;
document.getElementById('@(nameof(Model.UseCustomPermissions))').checked = true;
document.getElementById('@(nameof(Model.UseTotp))').checked = true;
document.getElementById('@(nameof(Model.Use2fa))').checked = true;
document.getElementById('@(nameof(Model.UseApi))').checked = true;
document.getElementById('@(nameof(Model.SelfHost))').checked = true;
document.getElementById('@(nameof(Model.UseResetPassword))').checked = true;
document.getElementById('@(nameof(Model.UseScim))').checked = true;
break;
}
}
function unlinkProvider(id) {
if (confirm('Are you sure you want to unlink this organization from its provider?')) {
$.ajax({
type: "POST",
url: `@Url.Action("UnlinkOrganizationFromProvider", "Organizations")?id=${id}`,
dataType: 'json',
contentType: false,
processData: false,
success: function (response) {
alert("Successfully unlinked provider");
window.location.href = `@Url.Action("Edit", "Organizations")?id=${id}`;
},
error: function (response) {
alert("Error!");
}
});
}
return false;
}
/***
* Set Secrets Manager values based on current usage (for migrating from SM beta or reinstating an old subscription)
*/
function setInitialSecretsManagerConfiguration() {
const planType = document.getElementById('@(nameof(Model.PlanType))').value;
// Seats
document.getElementById('@(nameof(Model.SmSeats))').value = Math.max(@Model.OccupiedSmSeatsCount, 1);
// Service accounts
const baseServiceAccounts = getPlan(planType)?.baseServiceAccount ?? 0;
if (planType !== '@((byte)PlanType.Free)' && @Model.ServiceAccountsCount > baseServiceAccounts) {
document.getElementById('@(nameof(Model.SmServiceAccounts))').value = @Model.ServiceAccountsCount;
} else {
document.getElementById('@(nameof(Model.SmServiceAccounts))').value = baseServiceAccounts;
}
// Clear autoscale values (no defaults)
document.getElementById('@(nameof(Model.MaxAutoscaleSmSeats))').value = '';
document.getElementById('@(nameof(Model.MaxAutoscaleSmServiceAccounts))').value = '';
}
function clearSecretsManagerConfiguration() {
document.getElementById('@(nameof(Model.SmSeats))').value = '';
document.getElementById('@(nameof(Model.SmServiceAccounts))').value = '';
document.getElementById('@(nameof(Model.MaxAutoscaleSmSeats))').value = '';
document.getElementById('@(nameof(Model.MaxAutoscaleSmServiceAccounts))').value = '';
}
function getPlan(planType) {
const plans = @Html.Raw(Json.Serialize(Model.GetPlansHelper()));
return plans.find(p => p.type == planType);
}
</script>