mirror of
https://github.com/bitwarden/browser.git
synced 2025-02-09 00:11:30 +01:00
[AC-1418] Add secrets manager manage subscription component (#5661)
* [AC-1423] Add minWidth input to bit-progress component * [AC-1423] Add ProgressModule to shared.module.ts * [AC-1423] Update cloud subscription page styles - Remove bootstrap styles - Use CL components where applicable - Use CL typography directives - Update heading levels to prepare for new SM sections * [AC-1423] Add usePasswordManager boolean to organization domain * [AC-1423] Introduce BitwardenProductType enum * [AC-1423] Update Organization subscription line items - Add product type prefix - Indent addon services like additional storage and service accounts - Show line items for free plans * [AC-1423] Simply sort function * [AC-1423] Remove header border * [AC-1423] Remove redundant condition * [AC-1423] Remove ineffective div * [AC-1423] Make "Password Manager" the default fallback for product name * Revert "[AC-1423] Add minWidth input to bit-progress component" This reverts commit95b2223a30
. * [AC-1423] Remove minWidth attribute * [AC-1423] Switch to AddonProductType enum instead of boolean * Revert "[AC-1423] Switch to AddonProductType enum instead of boolean" This reverts commit204f64b4e7
. * [AC-1423] Tweak sorting comment * [AC-1418] Add initial SecretsManagerAdjustSubscription component * [AC-1418] Add initial SM adjustment form * [AC-1418] Adjust organization-subscription-update.request.ts to support both PM and SM * [AC-1418] Rename service account fields in the options interface * [AC-1418] Add api service call to update SM subscription * [AC-1418] Cleanup form html * [AC-1418] Add missing SM plan properties * [AC-1418] Add SM subscription adjust form and logic to hide it * [AC-1418] Add better docs to options interface * [AC-1418] Fix conflicting required/optional labels for auto-scaling limits * [AC-1418] Adjust labels and appearance to better match design * [AC-1418] Use the SM plan for billing interval * [AC-1418] Hide SM billing adjustment component behind feature flag * [AC-1418] Update request model to match server * [AC-1418] Cleanup BitwardenProductType after merge Add to barrel file and update applicable imports. * [AC-1418] Revert change to update PM subscription request model * [AC-1418] Add new update SM subscription request model * [AC-1418] Add new service method to update SM subscription * [AC-1418] Use new model and service method * [AC-1418] Cleanup SM subscription UI flags * [AC-1418] Move SM adjust subscription component into SM billing module * [AC-1418] Update SM seat count minimum to 1 * [AC-1418] Add missing currency codes * [AC-1418] Simplify monthly price calculation * [AC-1418] Increase PM adjust subscription form input width * [AC-1418] Add check for null subscription --------- Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com>
This commit is contained in:
parent
03079735f3
commit
69d601fa78
@ -1,7 +1,7 @@
|
||||
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise" ngNativeValidate>
|
||||
<div>
|
||||
<div class="row">
|
||||
<div class="form-group col-6">
|
||||
<div class="form-group col-8">
|
||||
<label for="newSeatCount">{{ "subscriptionSeats" | i18n }}</label>
|
||||
<input
|
||||
id="newSeatCount"
|
||||
@ -41,7 +41,7 @@
|
||||
<label for="maxAutoscaleSeats">{{ "maxSeatLimit" | i18n }}</label>
|
||||
<input
|
||||
id="maxAutoscaleSeats"
|
||||
class="form-control col-6"
|
||||
class="form-control col-8"
|
||||
type="number"
|
||||
name="MaxAutoscaleSeats"
|
||||
[(ngModel)]="newMaxSeats"
|
||||
|
@ -177,6 +177,14 @@
|
||||
></app-adjust-storage>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="showAdjustSecretsManager">
|
||||
<h3 bitTypography="h3" class="tw-mt-9">{{ "secretsManager" | i18n }}</h3>
|
||||
<app-sm-adjust-subscription
|
||||
[organizationId]="organizationId"
|
||||
[options]="smOptions"
|
||||
(onAdjusted)="subscriptionAdjusted()"
|
||||
></app-sm-adjust-subscription>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
<h2 bitTypography="h2" class="tw-mt-7">{{ "selfHostingTitle" | i18n }}</h2>
|
||||
|
@ -9,8 +9,7 @@ import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-conso
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { OrganizationApiKeyType } from "@bitwarden/common/admin-console/enums";
|
||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||
import { PlanType } from "@bitwarden/common/billing/enums";
|
||||
import { BitwardenProductType } from "@bitwarden/common/billing/enums/bitwarden-product-type.enum";
|
||||
import { BitwardenProductType, PlanType } from "@bitwarden/common/billing/enums";
|
||||
import { OrganizationSubscriptionResponse } from "@bitwarden/common/billing/models/response/organization-subscription.response";
|
||||
import { BillingSubscriptionItemResponse } from "@bitwarden/common/billing/models/response/subscription.response";
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
@ -23,6 +22,7 @@ import {
|
||||
BillingSyncApiKeyComponent,
|
||||
BillingSyncApiModalData,
|
||||
} from "./billing-sync-api-key.component";
|
||||
import { SecretsManagerSubscriptionOptions } from "./secrets-manager/sm-adjust-subscription.component";
|
||||
|
||||
@Component({
|
||||
selector: "app-org-subscription-cloud",
|
||||
@ -38,6 +38,7 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
|
||||
adjustStorageAdd = true;
|
||||
showAdjustStorage = false;
|
||||
hasBillingSyncToken: boolean;
|
||||
showAdjustSecretsManager = false;
|
||||
|
||||
showSecretsManagerSubscribe = false;
|
||||
|
||||
@ -113,15 +114,26 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
|
||||
this.showSecretsManagerSubscribe =
|
||||
this.userOrg.canEditSubscription &&
|
||||
!this.userOrg.useSecretsManager &&
|
||||
this.subscription != null &&
|
||||
!this.subscription.cancelled &&
|
||||
!this.subscriptionMarkedForCancel;
|
||||
|
||||
// Remove next line when the sm-ga-billing flag is deleted
|
||||
this.showSecretsManagerSubscribe =
|
||||
this.showSecretsManagerSubscribe &&
|
||||
(await this.configService.getFeatureFlagBool(FeatureFlag.SecretsManagerBilling));
|
||||
this.showAdjustSecretsManager =
|
||||
this.userOrg.canEditSubscription &&
|
||||
this.userOrg.useSecretsManager &&
|
||||
this.subscription != null &&
|
||||
this.sub.secretsManagerPlan?.hasAdditionalSeatsOption &&
|
||||
!this.subscription.cancelled &&
|
||||
!this.subscriptionMarkedForCancel;
|
||||
|
||||
this.loading = false;
|
||||
|
||||
// Remove the remaining lines when the sm-ga-billing flag is deleted
|
||||
const smBillingEnabled = await this.configService.getFeatureFlagBool(
|
||||
FeatureFlag.SecretsManagerBilling
|
||||
);
|
||||
this.showSecretsManagerSubscribe = this.showSecretsManagerSubscribe && smBillingEnabled;
|
||||
this.showAdjustSecretsManager = this.showAdjustSecretsManager && smBillingEnabled;
|
||||
}
|
||||
|
||||
get subscription() {
|
||||
@ -169,6 +181,19 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
|
||||
return this.sub.seats;
|
||||
}
|
||||
|
||||
get smOptions(): SecretsManagerSubscriptionOptions {
|
||||
return {
|
||||
seatCount: this.sub.smSeats,
|
||||
seatLimit: this.sub.maxAutoscaleSmSeats,
|
||||
seatPrice: this.sub.secretsManagerPlan.seatPrice,
|
||||
serviceAccountLimit: this.sub.maxAutoscaleSmServiceAccounts,
|
||||
serviceAccountCount: this.sub.smServiceAccounts,
|
||||
interval: this.sub.secretsManagerPlan.isAnnual ? "year" : "month",
|
||||
additionalServiceAccountPrice: this.sub.secretsManagerPlan.additionalPricePerServiceAccount,
|
||||
baseServiceAccountCount: this.sub.secretsManagerPlan.baseServiceAccount,
|
||||
};
|
||||
}
|
||||
|
||||
get maxAutoscaleSeats() {
|
||||
return this.sub.maxAutoscaleSeats;
|
||||
}
|
||||
|
@ -0,0 +1,92 @@
|
||||
<form [formGroup]="formGroup" [bitSubmit]="submit">
|
||||
<bit-form-field class="tw-w-2/3">
|
||||
<bit-label>{{ "subscriptionSeats" | i18n }}</bit-label>
|
||||
<input bitInput id="smSeatCount" formControlName="seatCount" type="number" step="1" min="1" />
|
||||
<bit-hint>
|
||||
<strong>{{ "total" | i18n }}:</strong>
|
||||
{{ formGroup.value.seatCount || 0 }} × {{ options.seatPrice | currency : "$" }} =
|
||||
{{ seatTotal | currency : "$" }} / {{ options.interval | i18n }}
|
||||
</bit-hint>
|
||||
</bit-form-field>
|
||||
<bit-form-control>
|
||||
<bit-label>{{ "limitSubscription" | i18n }}</bit-label>
|
||||
<input type="checkbox" bitCheckbox id="limitSmSeats" formControlName="limitSeats" />
|
||||
<bit-hint>
|
||||
{{ "limitSmSubscriptionDesc" | i18n }}
|
||||
</bit-hint>
|
||||
</bit-form-control>
|
||||
<bit-form-field class="tw-w-2/3" *ngIf="formGroup.value.limitSeats">
|
||||
<bit-label>{{ "maxSeatLimit" | i18n }}</bit-label>
|
||||
<input
|
||||
bitInput
|
||||
id="smSeatLimit"
|
||||
formControlName="seatLimit"
|
||||
type="number"
|
||||
step="1"
|
||||
[min]="formGroup.value.seatCount"
|
||||
/>
|
||||
<bit-hint>
|
||||
<strong>{{ "maxSeatCost" | i18n }}:</strong>
|
||||
{{ formGroup.value.seatLimit || 0 }} × {{ options.seatPrice | currency : "$" }} =
|
||||
{{ maxSeatTotal | currency : "$" }} / {{ options.interval | i18n }}
|
||||
</bit-hint>
|
||||
</bit-form-field>
|
||||
<bit-form-field class="tw-w-2/3">
|
||||
<bit-label>{{ "additionalServiceAccounts" | i18n }}</bit-label>
|
||||
<input
|
||||
bitInput
|
||||
id="additionalServiceAccountCount"
|
||||
formControlName="serviceAccountCount"
|
||||
type="number"
|
||||
step="1"
|
||||
min="0"
|
||||
/>
|
||||
<bit-hint>
|
||||
<div>
|
||||
{{
|
||||
"additionalServiceAccountsDesc"
|
||||
| i18n : options.baseServiceAccountCount : (monthlyServiceAccountPrice | currency : "$")
|
||||
}}
|
||||
</div>
|
||||
<div>
|
||||
<strong>{{ "total" | i18n }}:</strong>
|
||||
{{ formGroup.value.serviceAccountCount || 0 }} ×
|
||||
{{ options.additionalServiceAccountPrice | currency : "$" }} =
|
||||
{{ serviceAccountTotal | currency : "$" }} / {{ options.interval | i18n }}
|
||||
</div>
|
||||
</bit-hint>
|
||||
</bit-form-field>
|
||||
<bit-form-control>
|
||||
<bit-label>{{ "limitServiceAccounts" | i18n }}</bit-label>
|
||||
<input
|
||||
type="checkbox"
|
||||
bitCheckbox
|
||||
id="limitServiceAccounts"
|
||||
formControlName="limitServiceAccounts"
|
||||
/>
|
||||
<bit-hint>
|
||||
{{ "limitServiceAccountsDesc" | i18n }}
|
||||
</bit-hint>
|
||||
</bit-form-control>
|
||||
<bit-form-field class="tw-w-2/3" *ngIf="formGroup.value.limitServiceAccounts">
|
||||
<bit-label>{{ "serviceAccountLimit" | i18n }}</bit-label>
|
||||
<input
|
||||
bitInput
|
||||
id="additionalServiceAccountLimit"
|
||||
formControlName="serviceAccountLimit"
|
||||
type="number"
|
||||
step="1"
|
||||
[min]="formGroup.value.serviceAccountCount"
|
||||
/>
|
||||
<bit-hint>
|
||||
<strong>{{ "maxServiceAccountCost" | i18n }}:</strong>
|
||||
{{ formGroup.value.serviceAccountLimit || 0 }} ×
|
||||
{{ options.additionalServiceAccountPrice | currency : "$" }} =
|
||||
{{ maxServiceAccountTotal | currency : "$" }} / {{ options.interval | i18n }}
|
||||
</bit-hint>
|
||||
</bit-form-field>
|
||||
<button type="submit" bitButton buttonType="primary" bitFormButton>
|
||||
{{ "save" | i18n }}
|
||||
</button>
|
||||
<bit-error-summary [formGroup]="formGroup" class="tw-mt-2"></bit-error-summary>
|
||||
</form>
|
@ -0,0 +1,168 @@
|
||||
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from "@angular/core";
|
||||
import { FormBuilder, Validators } from "@angular/forms";
|
||||
import { Subject, takeUntil } from "rxjs";
|
||||
|
||||
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
||||
import { OrganizationSmSubscriptionUpdateRequest } from "@bitwarden/common/billing/models/request/organization-sm-subscription-update.request";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
|
||||
export interface SecretsManagerSubscriptionOptions {
|
||||
interval: "year" | "month";
|
||||
|
||||
/**
|
||||
* The current number of seats the organization subscribes to.
|
||||
*/
|
||||
seatCount: number;
|
||||
|
||||
/**
|
||||
* Optional auto-scaling limit for the number of seats the organization can subscribe to.
|
||||
*/
|
||||
seatLimit: number;
|
||||
|
||||
/**
|
||||
* The price per seat for the subscription.
|
||||
*/
|
||||
seatPrice: number;
|
||||
|
||||
/**
|
||||
* The number of service accounts that are included in the base subscription.
|
||||
*/
|
||||
baseServiceAccountCount: number;
|
||||
|
||||
/**
|
||||
* The current number of additional service accounts the organization subscribes to.
|
||||
*/
|
||||
serviceAccountCount: number;
|
||||
|
||||
/**
|
||||
* Optional auto-scaling limit for the number of additional service accounts the organization can subscribe to.
|
||||
*/
|
||||
serviceAccountLimit: number;
|
||||
|
||||
/**
|
||||
* The price per additional service account for the subscription.
|
||||
*/
|
||||
additionalServiceAccountPrice: number;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: "app-sm-adjust-subscription",
|
||||
templateUrl: "sm-adjust-subscription.component.html",
|
||||
})
|
||||
export class SecretsManagerAdjustSubscriptionComponent implements OnInit, OnDestroy {
|
||||
@Input() organizationId: string;
|
||||
@Input() options: SecretsManagerSubscriptionOptions;
|
||||
@Output() onAdjusted = new EventEmitter();
|
||||
|
||||
private destroy$ = new Subject<void>();
|
||||
|
||||
formGroup = this.formBuilder.group({
|
||||
seatCount: [0, [Validators.required, Validators.min(1)]],
|
||||
limitSeats: [false],
|
||||
seatLimit: [null as number | null],
|
||||
serviceAccountCount: [0, [Validators.required, Validators.min(0)]],
|
||||
limitServiceAccounts: [false],
|
||||
serviceAccountLimit: [null as number | null],
|
||||
});
|
||||
|
||||
get monthlyServiceAccountPrice(): number {
|
||||
return this.options.interval == "month"
|
||||
? this.options.additionalServiceAccountPrice
|
||||
: this.options.additionalServiceAccountPrice / 12;
|
||||
}
|
||||
|
||||
get serviceAccountTotal(): number {
|
||||
return Math.abs(
|
||||
this.formGroup.value.serviceAccountCount * this.options.additionalServiceAccountPrice
|
||||
);
|
||||
}
|
||||
|
||||
get seatTotal(): number {
|
||||
return Math.abs(this.formGroup.value.seatCount * this.options.seatPrice);
|
||||
}
|
||||
|
||||
get maxServiceAccountTotal(): number {
|
||||
return Math.abs(
|
||||
(this.formGroup.value.serviceAccountLimit ?? 0) * this.options.additionalServiceAccountPrice
|
||||
);
|
||||
}
|
||||
|
||||
get maxSeatTotal(): number {
|
||||
return Math.abs((this.formGroup.value.seatLimit ?? 0) * this.options.seatPrice);
|
||||
}
|
||||
|
||||
constructor(
|
||||
private formBuilder: FormBuilder,
|
||||
private organizationApiService: OrganizationApiServiceAbstraction,
|
||||
private i18nService: I18nService,
|
||||
private platformUtilsService: PlatformUtilsService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.formGroup.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((value) => {
|
||||
const seatLimitControl = this.formGroup.controls.seatLimit;
|
||||
const serviceAccountLimitControl = this.formGroup.controls.serviceAccountLimit;
|
||||
|
||||
if (value.limitSeats) {
|
||||
seatLimitControl.setValidators([Validators.min(value.seatCount)]);
|
||||
seatLimitControl.enable({ emitEvent: false });
|
||||
} else {
|
||||
seatLimitControl.disable({ emitEvent: false });
|
||||
}
|
||||
|
||||
if (value.limitServiceAccounts) {
|
||||
serviceAccountLimitControl.setValidators([Validators.min(value.serviceAccountCount)]);
|
||||
serviceAccountLimitControl.enable({ emitEvent: false });
|
||||
} else {
|
||||
serviceAccountLimitControl.disable({ emitEvent: false });
|
||||
}
|
||||
});
|
||||
|
||||
this.formGroup.patchValue({
|
||||
seatCount: this.options.seatCount,
|
||||
seatLimit: this.options.seatLimit,
|
||||
serviceAccountCount: this.options.serviceAccountCount,
|
||||
serviceAccountLimit: this.options.serviceAccountLimit,
|
||||
limitSeats: this.options.seatLimit != null,
|
||||
limitServiceAccounts: this.options.serviceAccountLimit != null,
|
||||
});
|
||||
}
|
||||
|
||||
submit = async () => {
|
||||
this.formGroup.markAllAsTouched();
|
||||
|
||||
if (this.formGroup.invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
const seatAdjustment = this.formGroup.value.seatCount - this.options.seatCount;
|
||||
const serviceAccountAdjustment =
|
||||
this.formGroup.value.serviceAccountCount - this.options.serviceAccountCount;
|
||||
|
||||
const request = new OrganizationSmSubscriptionUpdateRequest(
|
||||
seatAdjustment,
|
||||
serviceAccountAdjustment,
|
||||
this.formGroup.value.seatLimit,
|
||||
this.formGroup.value.serviceAccountLimit
|
||||
);
|
||||
|
||||
await this.organizationApiService.updateSecretsManagerSubscription(
|
||||
this.organizationId,
|
||||
request
|
||||
);
|
||||
|
||||
await this.platformUtilsService.showToast(
|
||||
"success",
|
||||
null,
|
||||
this.i18nService.t("subscriptionUpdated")
|
||||
);
|
||||
|
||||
this.onAdjusted.emit();
|
||||
};
|
||||
|
||||
ngOnDestroy() {
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
}
|
||||
}
|
@ -2,12 +2,21 @@ import { NgModule } from "@angular/core";
|
||||
|
||||
import { SharedModule } from "../../../shared";
|
||||
|
||||
import { SecretsManagerAdjustSubscriptionComponent } from "./sm-adjust-subscription.component";
|
||||
import { SecretsManagerSubscribeStandaloneComponent } from "./sm-subscribe-standalone.component";
|
||||
import { SecretsManagerSubscribeComponent } from "./sm-subscribe.component";
|
||||
|
||||
@NgModule({
|
||||
imports: [SharedModule],
|
||||
declarations: [SecretsManagerSubscribeComponent, SecretsManagerSubscribeStandaloneComponent],
|
||||
exports: [SecretsManagerSubscribeComponent, SecretsManagerSubscribeStandaloneComponent],
|
||||
declarations: [
|
||||
SecretsManagerSubscribeComponent,
|
||||
SecretsManagerSubscribeStandaloneComponent,
|
||||
SecretsManagerAdjustSubscriptionComponent,
|
||||
],
|
||||
exports: [
|
||||
SecretsManagerSubscribeComponent,
|
||||
SecretsManagerSubscribeStandaloneComponent,
|
||||
SecretsManagerAdjustSubscriptionComponent,
|
||||
],
|
||||
})
|
||||
export class SecretsManagerBillingModule {}
|
||||
|
@ -20,8 +20,7 @@ import { OrganizationCreateRequest } from "@bitwarden/common/admin-console/model
|
||||
import { OrganizationKeysRequest } from "@bitwarden/common/admin-console/models/request/organization-keys.request";
|
||||
import { OrganizationUpgradeRequest } from "@bitwarden/common/admin-console/models/request/organization-upgrade.request";
|
||||
import { ProviderOrganizationCreateRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-organization-create.request";
|
||||
import { PaymentMethodType, PlanType } from "@bitwarden/common/billing/enums";
|
||||
import { BitwardenProductType } from "@bitwarden/common/billing/enums/bitwarden-product-type";
|
||||
import { BitwardenProductType, PaymentMethodType, PlanType } from "@bitwarden/common/billing/enums";
|
||||
import { PlanResponse } from "@bitwarden/common/billing/models/response/plan.response";
|
||||
import { ProductType } from "@bitwarden/common/enums";
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
@ -56,24 +55,29 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
|
||||
@Input() showFree = true;
|
||||
@Input() showCancel = false;
|
||||
@Input() acceptingSponsorship = false;
|
||||
|
||||
@Input()
|
||||
get product(): ProductType {
|
||||
return this._product;
|
||||
}
|
||||
|
||||
set product(product: ProductType) {
|
||||
this._product = product;
|
||||
this.formGroup?.controls?.product?.setValue(product);
|
||||
}
|
||||
|
||||
private _product = ProductType.Free;
|
||||
|
||||
@Input()
|
||||
get plan(): PlanType {
|
||||
return this._plan;
|
||||
}
|
||||
|
||||
set plan(plan: PlanType) {
|
||||
this._plan = plan;
|
||||
this.formGroup?.controls?.plan?.setValue(plan);
|
||||
}
|
||||
|
||||
private _plan = PlanType.Free;
|
||||
@Input() providerId?: string;
|
||||
@Output() onSuccess = new EventEmitter<OnSuccessArgs>();
|
||||
|
@ -404,8 +404,7 @@
|
||||
"viewItem": {
|
||||
"message": "View item"
|
||||
},
|
||||
"new":
|
||||
{
|
||||
"new": {
|
||||
"message": "New",
|
||||
"description": "for adding new items"
|
||||
},
|
||||
@ -3324,6 +3323,9 @@
|
||||
"limitSubscriptionDesc": {
|
||||
"message": "Set a seat limit for your subscription. Once this limit is reached, you will not be able to invite new members."
|
||||
},
|
||||
"limitSmSubscriptionDesc": {
|
||||
"message": "Set a seat limit for your Secrets Manger subscription. Once this limit is reached, you will not be able to invite new members."
|
||||
},
|
||||
"maxSeatLimit": {
|
||||
"message": "Seat Limit (optional)",
|
||||
"description": "Upper limit of seats to allow through autoscaling"
|
||||
@ -5855,10 +5857,10 @@
|
||||
"message": "Delete secrets",
|
||||
"description": "The action to delete multiple secrets from the system."
|
||||
},
|
||||
"hardDeleteSecret":{
|
||||
"hardDeleteSecret": {
|
||||
"message": "Permanently delete secret"
|
||||
},
|
||||
"hardDeleteSecrets":{
|
||||
"hardDeleteSecrets": {
|
||||
"message": "Permanently delete secrets"
|
||||
},
|
||||
"secretProjectAssociationDescription": {
|
||||
@ -5937,14 +5939,14 @@
|
||||
"message": "To get started, add a new secret or import secrets.",
|
||||
"description": "Message to encourage the user to start adding secrets."
|
||||
},
|
||||
"secretsTrashNoItemsMessage":{
|
||||
"secretsTrashNoItemsMessage": {
|
||||
"message": "There are no secrets in the trash."
|
||||
},
|
||||
"serviceAccountsNoItemsMessage": {
|
||||
"message": "Create a new service account to get started automating secret access.",
|
||||
"description": "Message to encourage the user to start creating service accounts."
|
||||
},
|
||||
"serviceAccountsNoItemsTitle": {
|
||||
"serviceAccountsNoItemsTitle": {
|
||||
"message": "Nothing to show yet",
|
||||
"description": "Title to indicate that there are no service accounts to display."
|
||||
},
|
||||
@ -5965,7 +5967,7 @@
|
||||
"description": "Action to view the details of a service account."
|
||||
},
|
||||
"deleteServiceAccountDialogMessage": {
|
||||
"message": "Deleting service account $SERVICE_ACCOUNT$ is permanent and irreversible.",
|
||||
"message": "Deleting service account $SERVICE_ACCOUNT$ is permanent and irreversible.",
|
||||
"placeholders": {
|
||||
"service_account": {
|
||||
"content": "$1",
|
||||
@ -5973,11 +5975,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"deleteServiceAccountsDialogMessage":{
|
||||
"deleteServiceAccountsDialogMessage": {
|
||||
"message": "Deleting service accounts is permanent and irreversible."
|
||||
},
|
||||
"deleteServiceAccountsConfirmMessage":{
|
||||
"message": "Delete $COUNT$ service accounts",
|
||||
"deleteServiceAccountsConfirmMessage": {
|
||||
"message": "Delete $COUNT$ service accounts",
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"content": "$1",
|
||||
@ -5985,19 +5987,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"deleteServiceAccountToast":{
|
||||
"deleteServiceAccountToast": {
|
||||
"message": "Service account deleted"
|
||||
},
|
||||
"deleteServiceAccountsToast":{
|
||||
"deleteServiceAccountsToast": {
|
||||
"message": "Service accounts deleted"
|
||||
},
|
||||
"searchServiceAccounts": {
|
||||
"message": "Search service accounts",
|
||||
"description": "Placeholder text for searching service accounts."
|
||||
},
|
||||
"editServiceAccount":{
|
||||
"message":"Edit service account",
|
||||
"description" : "Title for editing a service account."
|
||||
"editServiceAccount": {
|
||||
"message": "Edit service account",
|
||||
"description": "Title for editing a service account."
|
||||
},
|
||||
"addProject": {
|
||||
"message": "Add project",
|
||||
@ -6037,8 +6039,8 @@
|
||||
"hardDeleteSecretsConfirmation": {
|
||||
"message": "Are you sure you want to permanently delete these secrets?"
|
||||
},
|
||||
"hardDeletesSuccessToast":{
|
||||
"message":"Secrets permanently deleted"
|
||||
"hardDeletesSuccessToast": {
|
||||
"message": "Secrets permanently deleted"
|
||||
},
|
||||
"smAccess": {
|
||||
"message": "Access",
|
||||
@ -6052,7 +6054,7 @@
|
||||
"message": "Service account name",
|
||||
"description": "Label for the name of a service account"
|
||||
},
|
||||
"serviceAccountCreated": {
|
||||
"serviceAccountCreated": {
|
||||
"message": "Service account created",
|
||||
"description": "Notifies that a new service account has been created"
|
||||
},
|
||||
@ -6140,8 +6142,8 @@
|
||||
"message": "Secret sent to trash",
|
||||
"description": "Notification to be displayed when a secret is successfully sent to the trash."
|
||||
},
|
||||
"hardDeleteSuccessToast":{
|
||||
"message":"Secret permanently deleted"
|
||||
"hardDeleteSuccessToast": {
|
||||
"message": "Secret permanently deleted"
|
||||
},
|
||||
"accessTokens": {
|
||||
"message": "Access tokens",
|
||||
@ -6844,8 +6846,8 @@
|
||||
"message": "with automatic enrollment will turn on when this option is used.",
|
||||
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The master password reset policy with automatic enrollment will turn on when this option is used.'"
|
||||
},
|
||||
"notFound":{
|
||||
"message": "$RESOURCE$ not found",
|
||||
"notFound": {
|
||||
"message": "$RESOURCE$ not found",
|
||||
"placeholders": {
|
||||
"resource": {
|
||||
"content": "$1",
|
||||
@ -6998,6 +7000,17 @@
|
||||
},
|
||||
"freeOrganization": {
|
||||
"message": "Free Organization"
|
||||
},
|
||||
"limitServiceAccounts": {
|
||||
"message": "Limit service accounts (optional)"
|
||||
},
|
||||
"limitServiceAccountsDesc": {
|
||||
"message": "Set a limit for your service accounts. Once this limit is reached, you will not be able to create new service accounts."
|
||||
},
|
||||
"serviceAccountLimit": {
|
||||
"message": "Service account limit (optional)"
|
||||
},
|
||||
"maxServiceAccountCost": {
|
||||
"message": "Max potential service account cost"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@ import { OrganizationSsoRequest } from "../../../auth/models/request/organizatio
|
||||
import { SecretVerificationRequest } from "../../../auth/models/request/secret-verification.request";
|
||||
import { ApiKeyResponse } from "../../../auth/models/response/api-key.response";
|
||||
import { OrganizationSsoResponse } from "../../../auth/models/response/organization-sso.response";
|
||||
import { OrganizationSmSubscriptionUpdateRequest } from "../../../billing/models/request/organization-sm-subscription-update.request";
|
||||
import { OrganizationSubscriptionUpdateRequest } from "../../../billing/models/request/organization-subscription-update.request";
|
||||
import { OrganizationTaxInfoUpdateRequest } from "../../../billing/models/request/organization-tax-info-update.request";
|
||||
import { PaymentRequest } from "../../../billing/models/request/payment.request";
|
||||
@ -41,6 +42,10 @@ export class OrganizationApiServiceAbstraction {
|
||||
id: string,
|
||||
request: OrganizationSubscriptionUpdateRequest
|
||||
) => Promise<void>;
|
||||
updateSecretsManagerSubscription: (
|
||||
id: string,
|
||||
request: OrganizationSmSubscriptionUpdateRequest
|
||||
) => Promise<void>;
|
||||
updateSeats: (id: string, request: SeatRequest) => Promise<PaymentResponse>;
|
||||
updateStorage: (id: string, request: StorageRequest) => Promise<PaymentResponse>;
|
||||
verifyBank: (id: string, request: VerifyBankRequest) => Promise<void>;
|
||||
|
@ -28,6 +28,11 @@ export class OrganizationResponse extends BaseResponse {
|
||||
useResetPassword: boolean;
|
||||
useSecretsManager: boolean;
|
||||
hasPublicAndPrivateKeys: boolean;
|
||||
usePasswordManager: boolean;
|
||||
smSeats?: number;
|
||||
smServiceAccounts?: number;
|
||||
maxAutoscaleSmSeats?: number;
|
||||
maxAutoscaleSmServiceAccounts?: number;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
@ -62,5 +67,10 @@ export class OrganizationResponse extends BaseResponse {
|
||||
this.useResetPassword = this.getResponseProperty("UseResetPassword");
|
||||
this.useSecretsManager = this.getResponseProperty("UseSecretsManager");
|
||||
this.hasPublicAndPrivateKeys = this.getResponseProperty("HasPublicAndPrivateKeys");
|
||||
this.usePasswordManager = this.getResponseProperty("UsePasswordManager");
|
||||
this.smSeats = this.getResponseProperty("SmSeats");
|
||||
this.smServiceAccounts = this.getResponseProperty("SmServiceAccounts");
|
||||
this.maxAutoscaleSmSeats = this.getResponseProperty("MaxAutoscaleSmSeats");
|
||||
this.maxAutoscaleSmServiceAccounts = this.getResponseProperty("MaxAutoscaleSmServiceAccounts");
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import { OrganizationSsoRequest } from "../../../auth/models/request/organizatio
|
||||
import { SecretVerificationRequest } from "../../../auth/models/request/secret-verification.request";
|
||||
import { ApiKeyResponse } from "../../../auth/models/response/api-key.response";
|
||||
import { OrganizationSsoResponse } from "../../../auth/models/response/organization-sso.response";
|
||||
import { OrganizationSmSubscriptionUpdateRequest } from "../../../billing/models/request/organization-sm-subscription-update.request";
|
||||
import { OrganizationSubscriptionUpdateRequest } from "../../../billing/models/request/organization-subscription-update.request";
|
||||
import { OrganizationTaxInfoUpdateRequest } from "../../../billing/models/request/organization-tax-info-update.request";
|
||||
import { PaymentRequest } from "../../../billing/models/request/payment.request";
|
||||
@ -133,6 +134,19 @@ export class OrganizationApiService implements OrganizationApiServiceAbstraction
|
||||
);
|
||||
}
|
||||
|
||||
async updateSecretsManagerSubscription(
|
||||
id: string,
|
||||
request: OrganizationSmSubscriptionUpdateRequest
|
||||
): Promise<void> {
|
||||
return this.apiService.send(
|
||||
"POST",
|
||||
"/organizations/" + id + "/sm-subscription",
|
||||
request,
|
||||
true,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
async updateSeats(id: string, request: SeatRequest): Promise<PaymentResponse> {
|
||||
const r = await this.apiService.send(
|
||||
"POST",
|
||||
|
@ -1,4 +0,0 @@
|
||||
export enum BitwardenProductType {
|
||||
PasswordManager = 0,
|
||||
SecretsManager = 1,
|
||||
}
|
@ -2,3 +2,4 @@ export * from "./payment-method-type.enum";
|
||||
export * from "./plan-sponsorship-type.enum";
|
||||
export * from "./plan-type.enum";
|
||||
export * from "./transaction-type.enum";
|
||||
export * from "./bitwarden-product-type.enum";
|
||||
|
@ -0,0 +1,40 @@
|
||||
export class OrganizationSmSubscriptionUpdateRequest {
|
||||
/**
|
||||
* The number of seats to add or remove from the subscription.
|
||||
*/
|
||||
seatAdjustment: number;
|
||||
|
||||
/**
|
||||
* The maximum number of seats that can be auto-scaled for the subscription.
|
||||
*/
|
||||
maxAutoscaleSeats?: number;
|
||||
|
||||
/**
|
||||
* The number of additional service accounts to add or remove from the subscription.
|
||||
*/
|
||||
serviceAccountAdjustment: number;
|
||||
|
||||
/**
|
||||
* The maximum number of additional service accounts that can be auto-scaled for the subscription.
|
||||
*/
|
||||
maxAutoscaleServiceAccounts?: number;
|
||||
|
||||
/**
|
||||
* Build a subscription update request for the Secrets Manager product type.
|
||||
* @param seatAdjustment - The number of seats to add or remove from the subscription.
|
||||
* @param serviceAccountAdjustment - The number of additional service accounts to add or remove from the subscription.
|
||||
* @param maxAutoscaleSeats - The maximum number of seats that can be auto-scaled for the subscription.
|
||||
* @param maxAutoscaleServiceAccounts - The maximum number of additional service accounts that can be auto-scaled for the subscription.
|
||||
*/
|
||||
constructor(
|
||||
seatAdjustment: number,
|
||||
serviceAccountAdjustment: number,
|
||||
maxAutoscaleSeats?: number,
|
||||
maxAutoscaleServiceAccounts?: number
|
||||
) {
|
||||
this.seatAdjustment = seatAdjustment;
|
||||
this.serviceAccountAdjustment = serviceAccountAdjustment;
|
||||
this.maxAutoscaleSeats = maxAutoscaleSeats;
|
||||
this.maxAutoscaleServiceAccounts = maxAutoscaleServiceAccounts;
|
||||
}
|
||||
}
|
@ -1,3 +1,23 @@
|
||||
export class OrganizationSubscriptionUpdateRequest {
|
||||
constructor(public seatAdjustment: number, public maxAutoscaleSeats?: number) {}
|
||||
/**
|
||||
* The number of seats to add or remove from the subscription.
|
||||
* Applies to both PM and SM request types.
|
||||
*/
|
||||
seatAdjustment: number;
|
||||
|
||||
/**
|
||||
* The maximum number of seats that can be auto-scaled for the subscription.
|
||||
* Applies to both PM and SM request types.
|
||||
*/
|
||||
maxAutoscaleSeats?: number;
|
||||
|
||||
/**
|
||||
* Build a subscription update request for the Password Manager product type.
|
||||
* @param seatAdjustment - The number of seats to add or remove from the subscription.
|
||||
* @param maxAutoscaleSeats - The maximum number of seats that can be auto-scaled for the subscription.
|
||||
*/
|
||||
constructor(seatAdjustment: number, maxAutoscaleSeats?: number) {
|
||||
this.seatAdjustment = seatAdjustment;
|
||||
this.maxAutoscaleSeats = maxAutoscaleSeats;
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { ProductType } from "../../../enums";
|
||||
import { BaseResponse } from "../../../models/response/base.response";
|
||||
import { PlanType } from "../../enums";
|
||||
import { BitwardenProductType } from "../../enums/bitwarden-product-type";
|
||||
import { BitwardenProductType, PlanType } from "../../enums";
|
||||
|
||||
export class PlanResponse extends BaseResponse {
|
||||
type: PlanType;
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { BaseResponse } from "../../../models/response/base.response";
|
||||
import { BitwardenProductType } from "../../enums/bitwarden-product-type.enum";
|
||||
import { BitwardenProductType } from "../../enums";
|
||||
|
||||
export class SubscriptionResponse extends BaseResponse {
|
||||
storageName: string;
|
||||
|
Loading…
Reference in New Issue
Block a user