diff --git a/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.html b/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.html
index 95febbd3c5..4d81d070fb 100644
--- a/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.html
+++ b/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.html
@@ -385,7 +385,12 @@
{{ "secretsManagerAccessDescription" | i18n }}
-
+
{{ "userAccessSecretsManagerGA" | i18n }}
diff --git a/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts b/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts
index 771b8cc505..f1af950650 100644
--- a/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts
+++ b/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts
@@ -63,6 +63,7 @@ export interface MemberDialogParams {
organizationUserId: string;
allOrganizationUserEmails: string[];
usesKeyConnector: boolean;
+ isOnSecretsManagerStandalone: boolean;
initialTab?: MemberDialogTab;
numConfirmedMembers: number;
}
@@ -88,6 +89,7 @@ export class MemberDialogComponent implements OnDestroy {
organizationUserType = OrganizationUserType;
PermissionMode = PermissionMode;
showNoMasterPasswordWarning = false;
+ isOnSecretsManagerStandalone: boolean;
protected organization$: Observable;
protected collectionAccessItems: AccessItemView[] = [];
@@ -160,6 +162,13 @@ export class MemberDialogComponent implements OnDestroy {
this.editMode = this.params.organizationUserId != null;
this.tabIndex = this.params.initialTab ?? MemberDialogTab.Role;
this.title = this.i18nService.t(this.editMode ? "editMember" : "inviteMember");
+ this.isOnSecretsManagerStandalone = this.params.isOnSecretsManagerStandalone;
+
+ if (this.isOnSecretsManagerStandalone) {
+ this.formGroup.patchValue({
+ accessSecretsManager: true,
+ });
+ }
const groups$ = this.organization$.pipe(
switchMap((organization) =>
diff --git a/apps/web/src/app/admin-console/organizations/members/people.component.ts b/apps/web/src/app/admin-console/organizations/members/people.component.ts
index 6b632dce38..0df247d7b0 100644
--- a/apps/web/src/app/admin-console/organizations/members/people.component.ts
+++ b/apps/web/src/app/admin-console/organizations/members/people.component.ts
@@ -37,6 +37,7 @@ import {
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
import { OrganizationKeysRequest } from "@bitwarden/common/admin-console/models/request/organization-keys.request";
+import { OrganizationBillingServiceAbstraction as OrganizationBillingService } from "@bitwarden/common/billing/abstractions/organization-billing.service";
import { ProductType } from "@bitwarden/common/enums";
import { ListResponse } from "@bitwarden/common/models/response/list.response";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
@@ -93,6 +94,7 @@ export class PeopleComponent extends BasePeopleComponent {
organization: Organization;
status: OrganizationUserStatusType = null;
orgResetPasswordPolicyEnabled = false;
+ orgIsOnSecretsManagerStandalone = false;
protected canUseSecretsManager$: Observable;
@@ -119,6 +121,7 @@ export class PeopleComponent extends BasePeopleComponent {
private groupService: GroupService,
private collectionService: CollectionService,
organizationManagementPreferencesService: OrganizationManagementPreferencesService,
+ private organizationBillingService: OrganizationBillingService,
) {
super(
apiService,
@@ -187,6 +190,11 @@ export class PeopleComponent extends BasePeopleComponent {
.find((p) => p.organizationId === this.organization.id);
this.orgResetPasswordPolicyEnabled = resetPasswordPolicy?.enabled;
+ this.orgIsOnSecretsManagerStandalone =
+ await this.organizationBillingService.isOnSecretsManagerStandalone(
+ this.organization.id,
+ );
+
await this.load();
this.searchText = qParams.search;
@@ -446,6 +454,7 @@ export class PeopleComponent extends BasePeopleComponent {
organizationUserId: user != null ? user.id : null,
allOrganizationUserEmails: this.allUsers?.map((user) => user.email) ?? [],
usesKeyConnector: user?.usesKeyConnector,
+ isOnSecretsManagerStandalone: this.orgIsOnSecretsManagerStandalone,
initialTab: initialTab,
numConfirmedMembers: this.confirmedCount,
},
diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts
index cc6d947b33..859103474d 100644
--- a/libs/angular/src/services/jslib-services.module.ts
+++ b/libs/angular/src/services/jslib-services.module.ts
@@ -1052,6 +1052,7 @@ const safeProviders: SafeProvider[] = [
useClass: OrganizationBillingService,
deps: [
ApiServiceAbstraction,
+ BillingApiServiceAbstraction,
CryptoServiceAbstraction,
EncryptService,
I18nServiceAbstraction,
diff --git a/libs/common/src/billing/abstractions/billilng-api.service.abstraction.ts b/libs/common/src/billing/abstractions/billilng-api.service.abstraction.ts
index 16b86c5844..15f0d4b551 100644
--- a/libs/common/src/billing/abstractions/billilng-api.service.abstraction.ts
+++ b/libs/common/src/billing/abstractions/billilng-api.service.abstraction.ts
@@ -1,5 +1,6 @@
import { SubscriptionCancellationRequest } from "../../billing/models/request/subscription-cancellation.request";
import { OrganizationBillingStatusResponse } from "../../billing/models/response/organization-billing-status.response";
+import { OrganizationSubscriptionResponse } from "../../billing/models/response/organization-subscription.response";
import { PlanResponse } from "../../billing/models/response/plan.response";
import { ListResponse } from "../../models/response/list.response";
import { CreateClientOrganizationRequest } from "../models/request/create-client-organization.request";
@@ -11,12 +12,16 @@ export abstract class BillingApiServiceAbstraction {
organizationId: string,
request: SubscriptionCancellationRequest,
) => Promise;
+
cancelPremiumUserSubscription: (request: SubscriptionCancellationRequest) => Promise;
createClientOrganization: (
providerId: string,
request: CreateClientOrganizationRequest,
) => Promise;
getBillingStatus: (id: string) => Promise;
+ getOrganizationSubscription: (
+ organizationId: string,
+ ) => Promise;
getPlans: () => Promise>;
getProviderSubscription: (providerId: string) => Promise;
updateClientOrganization: (
diff --git a/libs/common/src/billing/abstractions/organization-billing.service.ts b/libs/common/src/billing/abstractions/organization-billing.service.ts
index d19724b600..0917025eec 100644
--- a/libs/common/src/billing/abstractions/organization-billing.service.ts
+++ b/libs/common/src/billing/abstractions/organization-billing.service.ts
@@ -41,6 +41,8 @@ export type SubscriptionInformation = {
};
export abstract class OrganizationBillingServiceAbstraction {
+ isOnSecretsManagerStandalone: (organizationId: string) => Promise;
+
purchaseSubscription: (subscription: SubscriptionInformation) => Promise;
startFree: (subscription: SubscriptionInformation) => Promise;
diff --git a/libs/common/src/billing/services/billing-api.service.ts b/libs/common/src/billing/services/billing-api.service.ts
index 70d1d89252..1c119b971d 100644
--- a/libs/common/src/billing/services/billing-api.service.ts
+++ b/libs/common/src/billing/services/billing-api.service.ts
@@ -2,6 +2,7 @@ import { ApiService } from "../../abstractions/api.service";
import { BillingApiServiceAbstraction } from "../../billing/abstractions/billilng-api.service.abstraction";
import { SubscriptionCancellationRequest } from "../../billing/models/request/subscription-cancellation.request";
import { OrganizationBillingStatusResponse } from "../../billing/models/response/organization-billing-status.response";
+import { OrganizationSubscriptionResponse } from "../../billing/models/response/organization-subscription.response";
import { PlanResponse } from "../../billing/models/response/plan.response";
import { ListResponse } from "../../models/response/list.response";
import { CreateClientOrganizationRequest } from "../models/request/create-client-organization.request";
@@ -49,10 +50,22 @@ export class BillingApiService implements BillingApiServiceAbstraction {
true,
true,
);
-
return new OrganizationBillingStatusResponse(r);
}
+ async getOrganizationSubscription(
+ organizationId: string,
+ ): Promise {
+ const r = await this.apiService.send(
+ "GET",
+ "/organizations/" + organizationId + "/subscription",
+ null,
+ true,
+ true,
+ );
+ return new OrganizationSubscriptionResponse(r);
+ }
+
async getPlans(): Promise> {
const r = await this.apiService.send("GET", "/plans", null, false, true);
return new ListResponse(r, PlanResponse);
diff --git a/libs/common/src/billing/services/organization-billing.service.ts b/libs/common/src/billing/services/organization-billing.service.ts
index 6b326472c9..fb2084bb6a 100644
--- a/libs/common/src/billing/services/organization-billing.service.ts
+++ b/libs/common/src/billing/services/organization-billing.service.ts
@@ -9,6 +9,7 @@ import { I18nService } from "../../platform/abstractions/i18n.service";
import { EncString } from "../../platform/models/domain/enc-string";
import { OrgKey } from "../../types/key";
import { SyncService } from "../../vault/abstractions/sync/sync.service.abstraction";
+import { BillingApiServiceAbstraction as BillingApiService } from "../abstractions/billilng-api.service.abstraction";
import {
OrganizationBillingServiceAbstraction,
OrganizationInformation,
@@ -28,6 +29,7 @@ interface OrganizationKeys {
export class OrganizationBillingService implements OrganizationBillingServiceAbstraction {
constructor(
private apiService: ApiService,
+ private billingApiService: BillingApiService,
private cryptoService: CryptoService,
private encryptService: EncryptService,
private i18nService: I18nService,
@@ -35,6 +37,19 @@ export class OrganizationBillingService implements OrganizationBillingServiceAbs
private syncService: SyncService,
) {}
+ async isOnSecretsManagerStandalone(organizationId: string): Promise {
+ const response = await this.billingApiService.getOrganizationSubscription(organizationId);
+ if (response.customerDiscount?.id === "sm-standalone") {
+ const productIds = response.subscription.items.map((item) => item.productId);
+ return (
+ response.customerDiscount?.appliesTo.filter((appliesToProductId) =>
+ productIds.includes(appliesToProductId),
+ ).length > 0
+ );
+ }
+ return false;
+ }
+
async purchaseSubscription(subscription: SubscriptionInformation): Promise {
const request = new OrganizationCreateRequest();