1
0
mirror of https://github.com/bitwarden/server.git synced 2025-05-20 19:14:32 -05:00

[AC-1577] [AC-1575] Prefill Bitwarden portal values etc (#3155)

* [AC-1575] Don't overwrite existing org information when changing plans

* [AC-1577] Prefill SM configuration section when starting trial
This commit is contained in:
Thomas Rittson 2023-08-16 09:03:17 +10:00 committed by GitHub
parent 4ec01b0ef0
commit 60eab8a28e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 135 additions and 71 deletions

View File

@ -5,6 +5,7 @@ using Bit.Admin.Utilities;
using Bit.Core.Context; using Bit.Core.Context;
using Bit.Core.Entities; using Bit.Core.Entities;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Models.OrganizationConnectionConfigs; using Bit.Core.Models.OrganizationConnectionConfigs;
using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Interfaces; using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Interfaces;
using Bit.Core.Repositories; using Bit.Core.Repositories;
@ -199,6 +200,14 @@ public class OrganizationsController : Controller
{ {
var organization = await GetOrganization(id, model); var organization = await GetOrganization(id, model);
if (organization.UseSecretsManager &&
!organization.SecretsManagerBeta
&& StaticStore.GetSecretsManagerPlan(organization.PlanType) == null
)
{
throw new BadRequestException("Plan does not support Secrets Manager");
}
await _organizationRepository.ReplaceAsync(organization); await _organizationRepository.ReplaceAsync(organization);
await _applicationCacheService.UpsertOrganizationAbilityAsync(organization); await _applicationCacheService.UpsertOrganizationAbilityAsync(organization);
await _referenceEventService.RaiseEventAsync(new ReferenceEvent(ReferenceEventType.OrganizationEditedByAdmin, organization, _currentContext) await _referenceEventService.RaiseEventAsync(new ReferenceEvent(ReferenceEventType.OrganizationEditedByAdmin, organization, _currentContext)

View File

@ -28,9 +28,9 @@ public class OrganizationEditModel : OrganizationViewModel
public OrganizationEditModel(Organization org, Provider provider, IEnumerable<OrganizationUserUserDetails> orgUsers, public OrganizationEditModel(Organization org, Provider provider, IEnumerable<OrganizationUserUserDetails> orgUsers,
IEnumerable<Cipher> ciphers, IEnumerable<Collection> collections, IEnumerable<Group> groups, IEnumerable<Cipher> ciphers, IEnumerable<Collection> collections, IEnumerable<Group> groups,
IEnumerable<Policy> policies, BillingInfo billingInfo, IEnumerable<OrganizationConnection> connections, IEnumerable<Policy> policies, BillingInfo billingInfo, IEnumerable<OrganizationConnection> connections,
GlobalSettings globalSettings, int secrets, int projects, int serviceAccounts, int smSeats) GlobalSettings globalSettings, int secrets, int projects, int serviceAccounts, int occupiedSmSeats)
: base(org, provider, connections, orgUsers, ciphers, collections, groups, policies, secrets, projects, : base(org, provider, connections, orgUsers, ciphers, collections, groups, policies, secrets, projects,
serviceAccounts, smSeats) serviceAccounts, occupiedSmSeats)
{ {
BillingInfo = billingInfo; BillingInfo = billingInfo;
BraintreeMerchantId = globalSettings.Braintree.MerchantId; BraintreeMerchantId = globalSettings.Braintree.MerchantId;
@ -145,13 +145,26 @@ public class OrganizationEditModel : OrganizationViewModel
public int? SmSeats { get; set; } public int? SmSeats { get; set; }
[Display(Name = "Max Autoscale Seats")] [Display(Name = "Max Autoscale Seats")]
public int? MaxAutoscaleSmSeats { get; set; } public int? MaxAutoscaleSmSeats { get; set; }
[Display(Name = "Max Service Accounts")] [Display(Name = "Service Accounts")]
public int? SmServiceAccounts { get; set; } public int? SmServiceAccounts { get; set; }
[Display(Name = "Max Autoscale Service Accounts")] [Display(Name = "Max Autoscale Service Accounts")]
public int? MaxAutoscaleSmServiceAccounts { get; set; } public int? MaxAutoscaleSmServiceAccounts { get; set; }
[Display(Name = "Secrets Manager Beta")] [Display(Name = "Secrets Manager Beta")]
public bool SecretsManagerBeta { get; set; } public bool SecretsManagerBeta { get; set; }
/**
* Creates a Plan[] object for use in Javascript
* This is mapped manually below to provide some type safety in case the plan objects change
* Add mappings for individual properties as you need them
*/
public IEnumerable<Dictionary<string, object>> GetPlansHelper() =>
StaticStore.SecretManagerPlans.Select(p =>
new Dictionary<string, object>
{
{ "type", p.Type },
{ "baseServiceAccount", p.BaseServiceAccount }
});
public Organization CreateOrganization(Provider provider) public Organization CreateOrganization(Provider provider)
{ {
BillingEmail = provider.BillingEmail; BillingEmail = provider.BillingEmail;

View File

@ -13,7 +13,7 @@ public class OrganizationViewModel
public OrganizationViewModel(Organization org, Provider provider, IEnumerable<OrganizationConnection> connections, public OrganizationViewModel(Organization org, Provider provider, IEnumerable<OrganizationConnection> connections,
IEnumerable<OrganizationUserUserDetails> orgUsers, IEnumerable<Cipher> ciphers, IEnumerable<Collection> collections, IEnumerable<OrganizationUserUserDetails> orgUsers, IEnumerable<Cipher> ciphers, IEnumerable<Collection> collections,
IEnumerable<Group> groups, IEnumerable<Policy> policies, int secretsCount, int projectCount, int serviceAccountsCount, IEnumerable<Group> groups, IEnumerable<Policy> policies, int secretsCount, int projectCount, int serviceAccountsCount,
int smSeatsCount) int occupiedSmSeatsCount)
{ {
Organization = org; Organization = org;
@ -39,10 +39,10 @@ public class OrganizationViewModel
orgUsers orgUsers
.Where(u => u.Type == OrganizationUserType.Admin && u.Status == organizationUserStatus) .Where(u => u.Type == OrganizationUserType.Admin && u.Status == organizationUserStatus)
.Select(u => u.Email)); .Select(u => u.Email));
Secrets = secretsCount; SecretsCount = secretsCount;
Projects = projectCount; ProjectsCount = projectCount;
ServiceAccounts = serviceAccountsCount; ServiceAccountsCount = serviceAccountsCount;
SmSeats = smSeatsCount; OccupiedSmSeatsCount = occupiedSmSeatsCount;
} }
public Organization Organization { get; set; } public Organization Organization { get; set; }
@ -59,9 +59,9 @@ public class OrganizationViewModel
public int GroupCount { get; set; } public int GroupCount { get; set; }
public int PolicyCount { get; set; } public int PolicyCount { get; set; }
public bool HasPublicPrivateKeys { get; set; } public bool HasPublicPrivateKeys { get; set; }
public int Secrets { get; set; } public int SecretsCount { get; set; }
public int Projects { get; set; } public int ProjectsCount { get; set; }
public int ServiceAccounts { get; set; } public int ServiceAccountsCount { get; set; }
public int SmSeats { get; set; } public int OccupiedSmSeatsCount { get; set; }
public bool UseSecretsManager => Organization.UseSecretsManager; public bool UseSecretsManager => Organization.UseSecretsManager;
} }

View File

@ -12,25 +12,45 @@
@section Scripts { @section Scripts {
@await Html.PartialAsync("_OrganizationFormScripts") @await Html.PartialAsync("_OrganizationFormScripts")
<script> <script>
(() => { (() => {
document.getElementById('teams-trial').addEventListener('click', () => { document.getElementById('teams-trial').addEventListener('click', () => {
if (document.getElementById('@(nameof(Model.PlanType))').value !== '@((byte)Bit.Core.Enums.PlanType.Free)') { if (document.getElementById('@(nameof(Model.PlanType))').value !== '@((byte)PlanType.Free)') {
alert('Organization is not on a free plan.'); alert('Organization is not on a free plan.');
return; return;
} }
togglePlanSettings('@((byte)Bit.Core.Enums.PlanType.TeamsAnnually)'); setTrialDefaults('@((byte)PlanType.TeamsAnnually)');
togglePlanFeatures('@((byte)PlanType.TeamsAnnually)');
document.getElementById('@(nameof(Model.Plan))').value = 'Teams (Trial)'; document.getElementById('@(nameof(Model.Plan))').value = 'Teams (Trial)';
}); });
document.getElementById('enterprise-trial').addEventListener('click', () => { document.getElementById('enterprise-trial').addEventListener('click', () => {
if (document.getElementById('@(nameof(Model.PlanType))').value !== '@((byte)Bit.Core.Enums.PlanType.Free)') { if (document.getElementById('@(nameof(Model.PlanType))').value !== '@((byte)PlanType.Free)') {
alert('Organization is not on a free plan.'); alert('Organization is not on a free plan.');
return; return;
} }
togglePlanSettings('@((byte)Bit.Core.Enums.PlanType.EnterpriseAnnually)'); setTrialDefaults('@((byte)PlanType.EnterpriseAnnually)');
togglePlanFeatures('@((byte)PlanType.EnterpriseAnnually)');
document.getElementById('@(nameof(Model.Plan))').value = 'Enterprise (Trial)'; document.getElementById('@(nameof(Model.Plan))').value = 'Enterprise (Trial)';
}); });
function setTrialDefaults(planType) {
// Plan
document.getElementById('@(nameof(Model.PlanType))').value = planType;
// Password Manager
document.getElementById('@(nameof(Model.Seats))').value = '10';
document.getElementById('@(nameof(Model.MaxCollections))').value = '';
document.getElementById('@(nameof(Model.MaxStorageGb))').value = '1';
// Secret Manager
if (document.getElementById('@(nameof(Model.UseSecretsManager))').checked) {
document.getElementById('@(nameof(Model.SmSeats))').value = '10';
document.getElementById('@(nameof(Model.SmServiceAccounts))').value = getPlan(planType)?.baseServiceAccount;
}
// Licensing
document.getElementById('@(nameof(Model.LicenseKey))').value = '@Model.RandomLicenseKey';
document.getElementById('@(nameof(Model.ExpirationDate))').value = '@Model.FourteenDayExpirationDate';
document.getElementById('@(nameof(Model.SalesAssistedTrialStarted))').value = true;
}
})(); })();
</script> </script>
} }

View File

@ -33,16 +33,16 @@
<dd class="col-sm-8 col-lg-9">@Model.CollectionCount</dd> <dd class="col-sm-8 col-lg-9">@Model.CollectionCount</dd>
<dt class="col-sm-4 col-lg-3">Secrets</dt> <dt class="col-sm-4 col-lg-3">Secrets</dt>
<dd class="col-sm-8 col-lg-9">@(Model.UseSecretsManager ? Model.Secrets: "N/A")</dd> <dd class="col-sm-8 col-lg-9">@(Model.UseSecretsManager ? Model.SecretsCount: "N/A")</dd>
<dt class="col-sm-4 col-lg-3">Projects</dt> <dt class="col-sm-4 col-lg-3">Projects</dt>
<dd class="col-sm-8 col-lg-9">@(Model.UseSecretsManager ? Model.Projects: "N/A")</dd> <dd class="col-sm-8 col-lg-9">@(Model.UseSecretsManager ? Model.ProjectsCount: "N/A")</dd>
<dt class="col-sm-4 col-lg-3">Service Accounts</dt> <dt class="col-sm-4 col-lg-3">Service Accounts</dt>
<dd class="col-sm-8 col-lg-9">@(Model.UseSecretsManager ? Model.ServiceAccounts: "N/A")</dd> <dd class="col-sm-8 col-lg-9">@(Model.UseSecretsManager ? Model.ServiceAccountsCount: "N/A")</dd>
<dt class="col-sm-4 col-lg-3">Secrets Manager Seats</dt> <dt class="col-sm-4 col-lg-3">Secrets Manager Seats</dt>
<dd class="col-sm-8 col-lg-9">@(Model.UseSecretsManager ? Model.SmSeats: "N/A" )</dd> <dd class="col-sm-8 col-lg-9">@(Model.UseSecretsManager ? Model.OccupiedSmSeatsCount: "N/A" )</dd>
<dt class="col-sm-4 col-lg-3">Groups</dt> <dt class="col-sm-4 col-lg-3">Groups</dt>
<dd class="col-sm-8 col-lg-9">@Model.GroupCount</dd> <dd class="col-sm-8 col-lg-9">@Model.GroupCount</dd>

View File

@ -5,10 +5,10 @@
@section Scripts { @section Scripts {
@await Html.PartialAsync("_OrganizationFormScripts") @await Html.PartialAsync("_OrganizationFormScripts")
<script> <script>
(() => { (() => {
togglePlanSettings('@((byte)Model.PlanType)'); togglePlanFeatures('@((byte)Model.PlanType)');
})(); })();
</script> </script>
} }

View File

@ -83,7 +83,7 @@
var planTypes = Enum.GetValues<PlanType>() var planTypes = Enum.GetValues<PlanType>()
.Where(p => Model.Provider == null || p is >= PlanType.TeamsMonthly and <= PlanType.EnterpriseAnnually) .Where(p => Model.Provider == null || p is >= PlanType.TeamsMonthly and <= PlanType.EnterpriseAnnually)
.Select(e => new SelectListItem .Select(e => new SelectListItem
{ {
Value = ((int)e).ToString(), Value = ((int)e).ToString(),
Text = e.GetDisplayAttribute()?.GetName() ?? e.ToString() Text = e.GetDisplayAttribute()?.GetName() ?? e.ToString()
}) })
@ -176,7 +176,7 @@
</div> </div>
</div> </div>
} }
@if (canViewPlan) @if (canViewPlan)
{ {
<h2>Password Manager Configuration</h2> <h2>Password Manager Configuration</h2>
@ -212,7 +212,7 @@
@if (canViewPlan) @if (canViewPlan)
{ {
<div id="organization-secrets-configuration" hidden="@(!Model.UseSecretsManager)"> <div id="organization-secrets-configuration" hidden="@(!Model.UseSecretsManager || Model.SecretsManagerBeta)">
<h2>Secrets Manager Configuration</h2> <h2>Secrets Manager Configuration</h2>
<div class="row"> <div class="row">
<div class="col-sm"> <div class="col-sm">
@ -265,7 +265,7 @@
} }
@if (canViewBilling) @if (canViewBilling)
{ {
<h2>Billing</h2> <h2>Billing</h2>
<div class="row"> <div class="row">
<div class="col-sm"> <div class="col-sm">
@ -315,7 +315,7 @@
<label asp-for="GatewaySubscriptionId"></label> <label asp-for="GatewaySubscriptionId"></label>
<div class="input-group"> <div class="input-group">
<input type="text" class="form-control" asp-for="GatewaySubscriptionId" readonly='@(!canEditBilling)'> <input type="text" class="form-control" asp-for="GatewaySubscriptionId" readonly='@(!canEditBilling)'>
@if (canLaunchGateway) @if (canLaunchGateway)
{ {
<div class="input-group-append"> <div class="input-group-append">
<button class="btn btn-secondary" type="button" id="gateway-subscription-link"> <button class="btn btn-secondary" type="button" id="gateway-subscription-link">
@ -328,4 +328,4 @@
</div> </div>
</div> </div>
} }
</form> </form>

View File

@ -1,10 +1,12 @@
@model OrganizationEditModel
<script> <script>
(() => { (() => {
document.getElementById('@(nameof(Model.PlanType))').addEventListener('change', () => { document.getElementById('@(nameof(Model.PlanType))').addEventListener('change', () => {
const selectEl = document.getElementById('@(nameof(Model.PlanType))'); const selectEl = document.getElementById('@(nameof(Model.PlanType))');
const selectText = selectEl.options[selectEl.selectedIndex].text; const selectText = selectEl.options[selectEl.selectedIndex].text;
document.getElementById('@(nameof(Model.Plan))').value = selectText; document.getElementById('@(nameof(Model.Plan))').value = selectText;
togglePlanSettings(selectEl.options[selectEl.selectedIndex].value); togglePlanFeatures(selectEl.options[selectEl.selectedIndex].value);
}); });
document.getElementById('gateway-customer-link')?.addEventListener('click', () => { document.getElementById('gateway-customer-link')?.addEventListener('click', () => {
const gateway = document.getElementById('@(nameof(Model.Gateway))'); const gateway = document.getElementById('@(nameof(Model.Gateway))');
@ -12,9 +14,9 @@
if (!gateway || gateway.value === '' || !customerId || customerId.value === '') { if (!gateway || gateway.value === '' || !customerId || customerId.value === '') {
return; return;
} }
if (gateway.value === '@((byte)Bit.Core.Enums.GatewayType.Stripe)') { if (gateway.value === '@((byte)GatewayType.Stripe)') {
window.open('https://dashboard.stripe.com/customers/' + customerId.value, '_blank'); window.open('https://dashboard.stripe.com/customers/' + customerId.value, '_blank');
} else if (gateway.value === '@((byte)Bit.Core.Enums.GatewayType.Braintree)') { } else if (gateway.value === '@((byte)GatewayType.Braintree)') {
window.open('https://www.braintreegateway.com/merchants/@(Model.BraintreeMerchantId)/' window.open('https://www.braintreegateway.com/merchants/@(Model.BraintreeMerchantId)/'
+ customerId.value, '_blank'); + customerId.value, '_blank');
} }
@ -25,41 +27,45 @@
if (!gateway || gateway.value === '' || !subId || subId.value === '') { if (!gateway || gateway.value === '' || !subId || subId.value === '') {
return; return;
} }
if (gateway.value === '@((byte)Bit.Core.Enums.GatewayType.Stripe)') { if (gateway.value === '@((byte)GatewayType.Stripe)') {
window.open('https://dashboard.stripe.com/subscriptions/' + subId.value, '_blank'); window.open('https://dashboard.stripe.com/subscriptions/' + subId.value, '_blank');
} else if (gateway.value === '@((byte)Bit.Core.Enums.GatewayType.Braintree)') { } else if (gateway.value === '@((byte)GatewayType.Braintree)') {
window.open('https://www.braintreegateway.com/merchants/@(Model.BraintreeMerchantId)/' + window.open('https://www.braintreegateway.com/merchants/@(Model.BraintreeMerchantId)/' +
'subscriptions/' + subId.value, '_blank'); 'subscriptions/' + subId.value, '_blank');
} }
}); });
document.getElementById('@(nameof(Model.UseSecretsManager))').addEventListener('change', (event) => { document.getElementById('@(nameof(Model.UseSecretsManager))').addEventListener('change', (event) => {
document.getElementById('organization-secrets-configuration').hidden = !event.target.checked; document.getElementById('organization-secrets-configuration').hidden = !event.target.checked;
if (event.target.checked) { if (event.target.checked) {
setInitialSecretsManagerConfiguration();
return; return;
} }
document.getElementById('@(nameof(Model.SmSeats))').value = ''; // SM beta requires SM access
document.getElementById('@(nameof(Model.MaxAutoscaleSmSeats))').value = ''; document.getElementById('@(nameof(Model.SecretsManagerBeta))').checked = false;
document.getElementById('@(nameof(Model.SmServiceAccounts))').value = ''; clearSecretsManagerConfiguration();
document.getElementById('@(nameof(Model.MaxAutoscaleSmServiceAccounts))').value = ''; });
document.getElementById('@(nameof(Model.SecretsManagerBeta))').addEventListener('change', (event) => {
document.getElementById('organization-secrets-configuration').hidden = event.target.checked;
if (event.target.checked) {
// SM beta requires SM access
document.getElementById('@(nameof(Model.UseSecretsManager))').checked = true;
// SM Beta orgs do not have subscription limits
clearSecretsManagerConfiguration();
return;
}
setInitialSecretsManagerConfiguration();
}); });
})(); })();
function togglePlanSettings(planType) { function togglePlanFeatures(planType) {
document.getElementById('@(nameof(Model.PlanType))').value = planType;
switch(planType) { switch(planType) {
case '@((byte)Bit.Core.Enums.PlanType.TeamsMonthly)': case '@((byte)PlanType.TeamsMonthly)':
case '@((byte)Bit.Core.Enums.PlanType.TeamsAnnually)': case '@((byte)PlanType.TeamsAnnually)':
// Plan
document.getElementById('@(nameof(Model.Seats))').value = '10';
document.getElementById('@(nameof(Model.MaxCollections))').value = '';
document.getElementById('@(nameof(Model.MaxStorageGb))').value = '1';
// Secrets
if (document.getElementById('@(nameof(Model.UseSecretsManager))').checked) {
document.getElementById('@(nameof(Model.SmServiceAccounts))').value = '50';
}
// Features
document.getElementById('@(nameof(Model.UsePolicies))').checked = false; document.getElementById('@(nameof(Model.UsePolicies))').checked = false;
document.getElementById('@(nameof(Model.UseSso))').checked = false; document.getElementById('@(nameof(Model.UseSso))').checked = false;
document.getElementById('@(nameof(Model.UseGroups))').checked = true; document.getElementById('@(nameof(Model.UseGroups))').checked = true;
@ -73,23 +79,10 @@
document.getElementById('@(nameof(Model.SelfHost))').checked = false; document.getElementById('@(nameof(Model.SelfHost))').checked = false;
document.getElementById('@(nameof(Model.UseResetPassword))').checked = false; document.getElementById('@(nameof(Model.UseResetPassword))').checked = false;
document.getElementById('@(nameof(Model.UseScim))').checked = false; document.getElementById('@(nameof(Model.UseScim))').checked = false;
// Licensing
document.getElementById('@(nameof(Model.LicenseKey))').value = '@Model.RandomLicenseKey';
document.getElementById('@(nameof(Model.ExpirationDate))').value = '@Model.FourteenDayExpirationDate';
document.getElementById('@(nameof(Model.SalesAssistedTrialStarted))').value = true;
break; break;
case '@((byte)Bit.Core.Enums.PlanType.EnterpriseMonthly)': case '@((byte)PlanType.EnterpriseMonthly)':
case '@((byte)Bit.Core.Enums.PlanType.EnterpriseAnnually)': case '@((byte)PlanType.EnterpriseAnnually)':
// Plan
document.getElementById('@(nameof(Model.Seats))').value = '10';
document.getElementById('@(nameof(Model.MaxCollections))').value = '';
document.getElementById('@(nameof(Model.MaxStorageGb))').value = '1';
// Secrets
if (document.getElementById('@(nameof(Model.UseSecretsManager))').checked) {
document.getElementById('@(nameof(Model.SmServiceAccounts))').value = '200';
}
// Features
document.getElementById('@(nameof(Model.UsePolicies))').checked = true; document.getElementById('@(nameof(Model.UsePolicies))').checked = true;
document.getElementById('@(nameof(Model.UseSso))').checked = true; document.getElementById('@(nameof(Model.UseSso))').checked = true;
document.getElementById('@(nameof(Model.UseGroups))').checked = true; document.getElementById('@(nameof(Model.UseGroups))').checked = true;
@ -103,12 +96,41 @@
document.getElementById('@(nameof(Model.SelfHost))').checked = true; document.getElementById('@(nameof(Model.SelfHost))').checked = true;
document.getElementById('@(nameof(Model.UseResetPassword))').checked = true; document.getElementById('@(nameof(Model.UseResetPassword))').checked = true;
document.getElementById('@(nameof(Model.UseScim))').checked = true; document.getElementById('@(nameof(Model.UseScim))').checked = true;
// Licensing
document.getElementById('@(nameof(Model.LicenseKey))').value = '@Model.RandomLicenseKey';
document.getElementById('@(nameof(Model.ExpirationDate))').value = '@Model.FourteenDayExpirationDate';
document.getElementById('@(nameof(Model.SalesAssistedTrialStarted))').value = true;
break; break;
} }
}
/***
* 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> </script>