Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[AC-1486] Feature: SM Billing #3073

Merged
merged 31 commits into from
Jul 24, 2023
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
fd51797
[AC-1423] Add AddonProduct and BitwardenProduct properties to Billing…
shane-melton Jun 29, 2023
039e216
Merge remote-tracking branch 'origin/master' into feature/sm-billing
eliykat Jun 29, 2023
12e6696
Add SecretsManagerBilling feature flag to Constants
eliykat Jun 29, 2023
46b2260
[AC 1409] Secrets Manager Subscription Stripe Integration (#3019)
cyprain-okeke Jul 1, 2023
6c0c9c6
[AC-1418] Add missing SecretsManagerPlan property to OrganizationResp…
shane-melton Jul 3, 2023
4a88894
Merge remote-tracking branch 'origin/master' into feature/sm-billing
eliykat Jul 4, 2023
692c7ff
[AC 1460] Update Stripe Configuration (#3070)
cyprain-okeke Jul 4, 2023
a1f8ca8
[AC 1410] Secrets Manager subscription adjustment back-end changes (#…
cyprain-okeke Jul 10, 2023
ce03d38
Merge branch 'master' into feature/sm-billing
eliykat Jul 10, 2023
a5efec3
[AC-1495] Extract UpgradePlanAsync into a command (#3081)
eliykat Jul 11, 2023
cab23cb
[AC-1503] Fix Stripe integration on organization upgrade (#3084)
eliykat Jul 11, 2023
3a6b17b
[AC-1504] Allow SM max autoscale limits to be disabled (#3085)
eliykat Jul 11, 2023
3ea14a2
[AC-1488] Changed SM Signup and Upgrade paths to set SmServiceAccount…
r-tome Jul 11, 2023
0ec3020
[AC-1510] Enable access to Secrets Manager to Organization owner for …
cyprain-okeke Jul 11, 2023
38ff569
Merge branch 'master' into feature/sm-billing
eliykat Jul 12, 2023
e1bede1
Revert changes to ReferenceEvent code (#3091)
eliykat Jul 12, 2023
b14c383
Revert ReferenceEventType change
eliykat Jul 13, 2023
aa5c200
Move NoopServiceAccountRepository to SM and update namespace
eliykat Jul 13, 2023
a9dcc32
Merge branch 'master' into feature/sm-billing
eliykat Jul 13, 2023
99b598a
Merge branch 'master' into feature/sm-billing
eliykat Jul 14, 2023
a79a523
Merge branch 'master' into feature/sm-billing
cturnbull-bitwarden Jul 14, 2023
e3387c2
Add UsePasswordManager to sync data (#3114)
eliykat Jul 18, 2023
1b6898e
[AC-1522] Fix service account check on upgrading (#3111)
eliykat Jul 18, 2023
10f671a
Merge branch 'master' into feature/sm-billing
eliykat Jul 18, 2023
1087f66
Resolved the checkmarx issues
cyprain-okeke Jul 19, 2023
c72d29b
[AC-1521] Address checkmarx security feedback (#3124)
eliykat Jul 19, 2023
2e7a480
Merge branch 'master' into feature/sm-billing
r-tome Jul 21, 2023
2c0ff09
Update date on migration script
eliykat Jul 24, 2023
4fcb9da
Remove unused constant
cyprain-okeke Jul 24, 2023
5b215cd
Merge branch 'feature/sm-billing' of https://github.com/bitwarden/ser…
cyprain-okeke Jul 24, 2023
fd4e306
Revert "Remove unused constant"
eliykat Jul 24, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,16 @@ public async Task DeleteManyByIdAsync(IEnumerable<Guid> ids)
return policy == null ? (false, false) : (policy.Read, policy.Write);
}

public async Task<int> GetServiceAccountCountByOrganizationIdAsync(Guid organizationId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
return await dbContext.ServiceAccount
.CountAsync(ou => ou.OrganizationId == organizationId);
}
}

private static Expression<Func<ServiceAccount, bool>> UserHasReadAccessToServiceAccount(Guid userId) => sa =>
sa.UserAccessPolicies.Any(ap => ap.OrganizationUser.User.Id == userId && ap.Read) ||
sa.GroupAccessPolicies.Any(ap => ap.Group.GroupUsers.Any(gu => gu.OrganizationUser.User.Id == userId && ap.Read));
Expand Down
37 changes: 34 additions & 3 deletions src/Api/Controllers/OrganizationsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
using Bit.Core.Models.Data.Organizations.Policies;
using Bit.Core.OrganizationFeatures.OrganizationApiKeys.Interfaces;
using Bit.Core.OrganizationFeatures.OrganizationLicenses.Interfaces;
using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Settings;
Expand Down Expand Up @@ -50,6 +51,8 @@ public class OrganizationsController : Controller
private readonly IFeatureService _featureService;
private readonly GlobalSettings _globalSettings;
private readonly ILicensingService _licensingService;
private readonly IUpdateSecretsManagerSubscriptionCommand _updateSecretsManagerSubscriptionCommand;
private readonly IUpgradeOrganizationPlanCommand _upgradeOrganizationPlanCommand;

public OrganizationsController(
IOrganizationRepository organizationRepository,
Expand All @@ -70,7 +73,9 @@ public OrganizationsController(
ICloudGetOrganizationLicenseQuery cloudGetOrganizationLicenseQuery,
IFeatureService featureService,
GlobalSettings globalSettings,
ILicensingService licensingService)
ILicensingService licensingService,
IUpdateSecretsManagerSubscriptionCommand updateSecretsManagerSubscriptionCommand,
IUpgradeOrganizationPlanCommand upgradeOrganizationPlanCommand)
{
_organizationRepository = organizationRepository;
_organizationUserRepository = organizationUserRepository;
Expand All @@ -91,6 +96,8 @@ public OrganizationsController(
_featureService = featureService;
_globalSettings = globalSettings;
_licensingService = licensingService;
_updateSecretsManagerSubscriptionCommand = updateSecretsManagerSubscriptionCommand;
_upgradeOrganizationPlanCommand = upgradeOrganizationPlanCommand;
}

[HttpGet("{id}")]
Expand Down Expand Up @@ -306,7 +313,7 @@ public async Task<PaymentResponseModel> PostUpgrade(string id, [FromBody] Organi
throw new NotFoundException();
}

var result = await _organizationService.UpgradePlanAsync(orgIdGuid, model.ToOrganizationUpgrade());
var result = await _upgradeOrganizationPlanCommand.UpgradePlanAsync(orgIdGuid, model.ToOrganizationUpgrade());
return new PaymentResponseModel { Success = result.Item1, PaymentIntentClientSecret = result.Item2 };
}

Expand All @@ -319,10 +326,34 @@ public async Task PostSubscription(string id, [FromBody] OrganizationSubscriptio
{
throw new NotFoundException();
}

await _organizationService.UpdateSubscription(orgIdGuid, model.SeatAdjustment, model.MaxAutoscaleSeats);
}

[HttpPost("{id}/sm-subscription")]
[SelfHosted(NotSelfHostedOnly = true)]
public async Task PostSmSubscription(Guid id, [FromBody] SecretsManagerSubscriptionUpdateRequestModel model)
{
var organization = await _organizationRepository.GetByIdAsync(id);
if (organization == null)
{
throw new NotFoundException();
}

if (!await _currentContext.EditSubscription(id))
{
throw new NotFoundException();
}

var secretsManagerPlan = StaticStore.GetSecretsManagerPlan(organization.PlanType);
if (secretsManagerPlan == null)
{
throw new NotFoundException("Invalid Secrets Manager plan.");
}

var organizationUpdate = model.ToSecretsManagerSubscriptionUpdate(organization, secretsManagerPlan);
await _updateSecretsManagerSubscriptionCommand.UpdateSecretsManagerSubscription(organizationUpdate);
}

[HttpPost("{id}/seat")]
[SelfHosted(NotSelfHostedOnly = true)]
public async Task<PaymentResponseModel> PostSeat(string id, [FromBody] OrganizationSeatRequestModel model)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ public class OrganizationCreateRequestModel : IValidatableObject
[StringLength(2)]
public string BillingAddressCountry { get; set; }
public int? MaxAutoscaleSeats { get; set; }
[Range(0, int.MaxValue)]
public int? AdditionalSmSeats { get; set; }
[Range(0, int.MaxValue)]
public int? AdditionalServiceAccounts { get; set; }
[Required]
public bool UseSecretsManager { get; set; }

public virtual OrganizationSignup ToOrganizationSignup(User user)
{
Expand All @@ -58,6 +64,9 @@ public virtual OrganizationSignup ToOrganizationSignup(User user)
BillingEmail = BillingEmail,
BusinessName = BusinessName,
CollectionName = CollectionName,
AdditionalSmSeats = AdditionalSmSeats.GetValueOrDefault(),
AdditionalServiceAccounts = AdditionalServiceAccounts.GetValueOrDefault(),
UseSecretsManager = UseSecretsManager,
TaxInfo = new TaxInfo
{
TaxIdNumber = TaxIdNumber,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ public class OrganizationUpgradeRequestModel
public int AdditionalSeats { get; set; }
[Range(0, 99)]
public short? AdditionalStorageGb { get; set; }
[Range(0, int.MaxValue)]
public int? AdditionalSmSeats { get; set; }
[Range(0, int.MaxValue)]
public int? AdditionalServiceAccounts { get; set; }
[Required]
public bool UseSecretsManager { get; set; }
public bool PremiumAccessAddon { get; set; }
public string BillingAddressCountry { get; set; }
public string BillingAddressPostalCode { get; set; }
Expand All @@ -24,6 +30,9 @@ public OrganizationUpgrade ToOrganizationUpgrade()
{
AdditionalSeats = AdditionalSeats,
AdditionalStorageGb = AdditionalStorageGb.GetValueOrDefault(),
AdditionalServiceAccounts = AdditionalServiceAccounts.GetValueOrDefault(0),
AdditionalSmSeats = AdditionalSmSeats.GetValueOrDefault(0),
UseSecretsManager = UseSecretsManager,
BusinessName = BusinessName,
Plan = PlanType,
PremiumAccessAddon = PremiumAccessAddon,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using System.ComponentModel.DataAnnotations;
using Bit.Core.Entities;
using Bit.Core.Models.Business;
using Bit.Core.Models.StaticStore;

namespace Bit.Api.Models.Request.Organizations;

public class SecretsManagerSubscriptionUpdateRequestModel
{
[Required]
public int SeatAdjustment { get; set; }
public int? MaxAutoscaleSeats { get; set; }
public int ServiceAccountAdjustment { get; set; }
public int? MaxAutoscaleServiceAccounts { get; set; }

public virtual SecretsManagerSubscriptionUpdate ToSecretsManagerSubscriptionUpdate(Organization organization, Plan plan)
{
var newTotalSeats = organization.SmSeats.GetValueOrDefault() + SeatAdjustment;
var newTotalServiceAccounts = organization.SmServiceAccounts.GetValueOrDefault() + ServiceAccountAdjustment;

var orgUpdate = new SecretsManagerSubscriptionUpdate
{
OrganizationId = organization.Id,

SmSeatsAdjustment = SeatAdjustment,
SmSeats = newTotalSeats,
SmSeatsExcludingBase = newTotalSeats - plan.BaseSeats,

MaxAutoscaleSmSeats = MaxAutoscaleSeats,

SmServiceAccountsAdjustment = ServiceAccountAdjustment,
SmServiceAccounts = newTotalServiceAccounts,
SmServiceAccountsExcludingBase = newTotalServiceAccounts - plan.BaseServiceAccount.GetValueOrDefault(),

MaxAutoscaleSmServiceAccounts = MaxAutoscaleServiceAccounts,

MaxAutoscaleSmSeatsChanged =
MaxAutoscaleSeats.GetValueOrDefault() != organization.MaxAutoscaleSmSeats.GetValueOrDefault(),
MaxAutoscaleSmServiceAccountsChanged =
MaxAutoscaleServiceAccounts.GetValueOrDefault() != organization.MaxAutoscaleSmServiceAccounts.GetValueOrDefault()
};

return orgUpdate;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public OrganizationResponseModel(Organization organization, string obj = "organi
BusinessTaxNumber = organization.BusinessTaxNumber;
BillingEmail = organization.BillingEmail;
Plan = new PlanResponseModel(StaticStore.PasswordManagerPlans.FirstOrDefault(plan => plan.Type == organization.PlanType));
SecretsManagerPlan = new PlanResponseModel(StaticStore.SecretManagerPlans.FirstOrDefault(plan => plan.Type == organization.PlanType));
PlanType = organization.PlanType;
Seats = organization.Seats;
MaxAutoscaleSeats = organization.MaxAutoscaleSeats;
Expand Down Expand Up @@ -65,6 +66,7 @@ public OrganizationResponseModel(Organization organization, string obj = "organi
public string BusinessTaxNumber { get; set; }
public string BillingEmail { get; set; }
public PlanResponseModel Plan { get; set; }
public PlanResponseModel SecretsManagerPlan { get; set; }
public PlanType PlanType { get; set; }
public int? Seats { get; set; }
public int? MaxAutoscaleSeats { get; set; } = null;
Expand Down
9 changes: 6 additions & 3 deletions src/Api/Models/Response/PlanResponseModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,12 @@ public PlanResponseModel(Plan plan, string obj = "plan")

AdditionalPricePerServiceAccount = plan.AdditionalPricePerServiceAccount;
BaseServiceAccount = plan.BaseServiceAccount;
MaxServiceAccount = plan.MaxServiceAccount;
MaxServiceAccounts = plan.MaxServiceAccounts;
MaxAdditionalServiceAccounts = plan.MaxAdditionalServiceAccount;
HasAdditionalServiceAccountOption = plan.HasAdditionalServiceAccountOption;
MaxProjects = plan.MaxProjects;
BitwardenProduct = plan.BitwardenProduct;
StripeServiceAccountPlanId = plan.StripeServiceAccountPlanId;
}

public PlanType Type { get; set; }
Expand Down Expand Up @@ -105,10 +107,11 @@ public PlanResponseModel(Plan plan, string obj = "plan")
public decimal SeatPrice { get; set; }
public decimal AdditionalStoragePricePerGb { get; set; }
public decimal PremiumAccessOptionPrice { get; set; }

public string StripeServiceAccountPlanId { get; set; }
public decimal? AdditionalPricePerServiceAccount { get; set; }
public short? BaseServiceAccount { get; set; }
public short? MaxServiceAccount { get; set; }
public short? MaxServiceAccounts { get; set; }
public short? MaxAdditionalServiceAccounts { get; set; }
public bool HasAdditionalServiceAccountOption { get; set; }
public short? MaxProjects { get; set; }
public BitwardenProductType BitwardenProduct { get; set; }
Expand Down
2 changes: 2 additions & 0 deletions src/Api/Models/Response/ProfileOrganizationResponseModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public ProfileOrganizationResponseModel(OrganizationUserOrganizationDetails orga
UseApi = organization.UseApi;
UseResetPassword = organization.UseResetPassword;
UseSecretsManager = organization.UseSecretsManager;
UsePasswordManager = organization.UsePasswordManager;
UsersGetPremium = organization.UsersGetPremium;
UseCustomPermissions = organization.UseCustomPermissions;
UseActivateAutofillPolicy = organization.PlanType == PlanType.EnterpriseAnnually ||
Expand Down Expand Up @@ -82,6 +83,7 @@ public ProfileOrganizationResponseModel(OrganizationUserOrganizationDetails orga
public bool UseApi { get; set; }
public bool UseResetPassword { get; set; }
public bool UseSecretsManager { get; set; }
public bool UsePasswordManager { get; set; }
public bool UsersGetPremium { get; set; }
public bool UseCustomPermissions { get; set; }
public bool UseActivateAutofillPolicy { get; set; }
Expand Down
5 changes: 5 additions & 0 deletions src/Api/Models/Response/SubscriptionResponseModel.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Models.Api;
using Bit.Core.Models.Business;
using Bit.Core.Utilities;
Expand Down Expand Up @@ -82,13 +83,17 @@ public BillingSubscriptionItem(SubscriptionInfo.BillingSubscription.BillingSubsc
Interval = item.Interval;
Quantity = item.Quantity;
SponsoredSubscriptionItem = item.SponsoredSubscriptionItem;
AddonSubscriptionItem = item.AddonSubscriptionItem;
BitwardenProduct = item.BitwardenProduct;
}

public string Name { get; set; }
public decimal Amount { get; set; }
public int Quantity { get; set; }
public string Interval { get; set; }
public bool SponsoredSubscriptionItem { get; set; }
public bool AddonSubscriptionItem { get; set; }
public BitwardenProductType BitwardenProduct { get; set; }
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/Api/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Bit.Core.Auth.Identity;
using Bit.Core.OrganizationFeatures.OrganizationSubscriptions;

#if !OSS
using Bit.Commercial.Core.SecretsManager;
Expand Down Expand Up @@ -133,6 +134,7 @@ public void ConfigureServices(IServiceCollection services)
// Services
services.AddBaseServices(globalSettings);
services.AddDefaultServices(globalSettings);
services.AddOrganizationSubscriptionServices();
services.AddCoreLocalizationServices();

//health check
Expand Down
1 change: 1 addition & 0 deletions src/Core/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public static class FeatureFlagKeys
public const string DisplayEuEnvironment = "display-eu-environment";
public const string DisplayLowKdfIterationWarning = "display-kdf-iteration-warning";
public const string TrustedDeviceEncryption = "trusted-device-encryption";
public const string SecretsManagerBilling = "sm-ga-billing";
eliykat marked this conversation as resolved.
Show resolved Hide resolved

public static List<string> GetAllKeys()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{{#>FullHtmlLayout}}
<table width="100%" cellpadding="0" cellspacing="0"
style="margin: 0; box-sizing: border-box; ">
<tr
style="margin: 0; box-sizing: border-box; ">
<td class="content-block"
style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0 0 10px; -webkit-text-size-adjust: none;"
valign="top">
Your organization has reached the Secrets Manager seat limit of {{MaxSeatCount}} and new members cannot be invited.
</td>
</tr>
<tr
style="margin: 0; box-sizing: border-box; ">
<td class="content-block last"
style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0; -webkit-text-size-adjust: none;"
valign="top">
For more information, please refer to the following help article:
<a href="https://bitwarden.com/help/managing-users" class="inline-link">
Member management
</a>
<br class="line-break" />
<br class="line-break" />
</td>
</tr>
<tr style="margin: 0; box-sizing: border-box; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
<td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0 0 10px; -webkit-text-size-adjust: none; text-align: center;" valign="top" align="center">
<a href="https://vault.bitwarden.com/#/organizations/{{{OrganizationId}}}/billing/subscription" clicktracking=off target="_blank" rel="noopener noreferrer" style="color: #ffffff; text-decoration: none; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; background-color: #175DDC; border-color: #175DDC; border-style: solid; border-width: 10px 20px; margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
Manage subscription
</a>
<br class="line-break" />
</td>
</tr>
</table>
{{/FullHtmlLayout}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{{#>BasicTextLayout}}
Your organization has reached the Secrets Manager seat limit of {{MaxSeatCount}} and new members cannot be invited.

For more information, please refer to the following help article: https://bitwarden.com/help/managing-users
{{/BasicTextLayout}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{{#>FullHtmlLayout}}
<table width="100%" cellpadding="0" cellspacing="0"
style="margin: 0; box-sizing: border-box; ">
<tr
style="margin: 0; box-sizing: border-box; ">
<td class="content-block"
style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0 0 10px; -webkit-text-size-adjust: none;"
valign="top">
Your organization has reached the Secrets Manager service accounts limit of {{MaxServiceAccountsCount}}. New service accounts cannot be created
</td>
</tr>
<tr
style="margin: 0; box-sizing: border-box; ">
<td class="content-block last"
style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0; -webkit-text-size-adjust: none;"
valign="top">
For more information, please refer to the following help article:
<a href="https://bitwarden.com/help/managing-users" class="inline-link">
Member management
</a>
<br class="line-break" />
<br class="line-break" />
</td>
</tr>
<tr style="margin: 0; box-sizing: border-box; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
<td class="content-block" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0 0 10px; -webkit-text-size-adjust: none; text-align: center;" valign="top" align="center">
<a href="https://vault.bitwarden.com/#/organizations/{{{OrganizationId}}}/billing/subscription" clicktracking=off target="_blank" rel="noopener noreferrer" style="color: #ffffff; text-decoration: none; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; background-color: #175DDC; border-color: #175DDC; border-style: solid; border-width: 10px 20px; margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
Manage subscription
</a>
<br class="line-break" />
</td>
</tr>
</table>
{{/FullHtmlLayout}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{{#>BasicTextLayout}}
Your organization has reached the Secrets Manager service accounts limit of {{MaxServiceAccountsCount}}. New service accounts cannot be created

For more information, please refer to the following help article: https://bitwarden.com/help/managing-users
{{/BasicTextLayout}}
3 changes: 3 additions & 0 deletions src/Core/Models/Business/OrganizationUpgrade.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,7 @@ public class OrganizationUpgrade
public TaxInfo TaxInfo { get; set; }
public string PublicKey { get; set; }
public string PrivateKey { get; set; }
public int? AdditionalSmSeats { get; set; }
public int? AdditionalServiceAccounts { get; set; }
public bool UseSecretsManager { get; set; }
}
Loading
Loading