diff --git a/apps/web/src/app/layouts/product-switcher/product-switcher-content.component.ts b/apps/web/src/app/layouts/product-switcher/product-switcher-content.component.ts
index e0705dd070..7a637c642b 100644
--- a/apps/web/src/app/layouts/product-switcher/product-switcher-content.component.ts
+++ b/apps/web/src/app/layouts/product-switcher/product-switcher-content.component.ts
@@ -47,9 +47,10 @@ export class ProductSwitcherContentComponent {
map(([orgs, paramMap]) => {
const routeOrg = orgs.find((o) => o.id === paramMap.get("organizationId"));
// If the active route org doesn't have access to SM, find the first org that does.
- const smOrg = routeOrg?.canAccessSecretsManager
- ? routeOrg
- : orgs.find((o) => o.canAccessSecretsManager);
+ const smOrg =
+ routeOrg?.canAccessSecretsManager && routeOrg?.enabled == true
+ ? routeOrg
+ : orgs.find((o) => o.canAccessSecretsManager && o.enabled == true);
/**
* We can update this to the "satisfies" type upon upgrading to TypeScript 4.9
diff --git a/apps/web/src/app/layouts/product-switcher/product-switcher.stories.ts b/apps/web/src/app/layouts/product-switcher/product-switcher.stories.ts
index 87e4202747..46a2df458b 100644
--- a/apps/web/src/app/layouts/product-switcher/product-switcher.stories.ts
+++ b/apps/web/src/app/layouts/product-switcher/product-switcher.stories.ts
@@ -131,5 +131,5 @@ OrgWithoutSecretsManager.args = {
export const OrgWithSecretsManager = Template.bind({});
OrgWithSecretsManager.args = {
- mockOrgs: [{ id: "b", canAccessSecretsManager: true }],
+ mockOrgs: [{ id: "b", canAccessSecretsManager: true, enabled: true }],
};
diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json
index b9d613877f..60fed7a538 100644
--- a/apps/web/src/locales/en/messages.json
+++ b/apps/web/src/locales/en/messages.json
@@ -3638,6 +3638,18 @@
"organizationIsDisabled": {
"message": "Organization suspended"
},
+ "secretsAccessSuspended": {
+ "message": "Suspended organizations cannot be accessed. Please contact your organization owner for assistance."
+ },
+ "secretsCannotCreate": {
+ "message": "Secrets cannot be created in suspended organizations. Please contact your organization owner for assistance."
+ },
+ "projectsCannotCreate": {
+ "message": "Projects cannot be created in suspended organizations. Please contact your organization owner for assistance."
+ },
+ "serviceAccountsCannotCreate": {
+ "message": "Service accounts cannot be created in suspended organizations. Please contact your organization owner for assistance."
+ },
"disabledOrganizationFilterError": {
"message": "Items in suspended organizations cannot be accessed. Contact your organization owner for assistance."
},
diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/guards/sm-org-enabled.guard.ts b/bitwarden_license/bit-web/src/app/secrets-manager/guards/sm-org-enabled.guard.ts
new file mode 100644
index 0000000000..3ff4d998a3
--- /dev/null
+++ b/bitwarden_license/bit-web/src/app/secrets-manager/guards/sm-org-enabled.guard.ts
@@ -0,0 +1,29 @@
+import { inject } from "@angular/core";
+import { ActivatedRouteSnapshot, CanActivateFn, createUrlTreeFromSnapshot } from "@angular/router";
+
+import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
+import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
+
+/**
+ * Redirects from root `/sm` to first organization with access to SM
+ */
+export const organizationEnabledGuard: CanActivateFn = async (route: ActivatedRouteSnapshot) => {
+ const syncService = inject(SyncService);
+ const orgService = inject(OrganizationService);
+
+ /** Workaround to avoid service initialization race condition. */
+ if ((await syncService.getLastSync()) == null) {
+ await syncService.fullSync(false);
+ }
+
+ const org = orgService.get(route.params.organizationId);
+ if (org == null || !org.canAccessSecretsManager) {
+ return createUrlTreeFromSnapshot(route, ["/"]);
+ }
+
+ if (!org.enabled) {
+ return createUrlTreeFromSnapshot(route, ["/sm", org.id, "organization-suspended"]);
+ }
+
+ return true;
+};
diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/sm.guard.ts b/bitwarden_license/bit-web/src/app/secrets-manager/guards/sm.guard.ts
similarity index 100%
rename from bitwarden_license/bit-web/src/app/secrets-manager/sm.guard.ts
rename to bitwarden_license/bit-web/src/app/secrets-manager/guards/sm.guard.ts
diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/layout/org-switcher.component.html b/bitwarden_license/bit-web/src/app/secrets-manager/layout/org-switcher.component.html
index e639c5f126..d7a404bf1d 100644
--- a/bitwarden_license/bit-web/src/app/secrets-manager/layout/org-switcher.component.html
+++ b/bitwarden_license/bit-web/src/app/secrets-manager/layout/org-switcher.component.html
@@ -7,6 +7,11 @@
[(open)]="open"
[exactMatch]="true"
>
+
+
=
this.organizationService.organizations$.pipe(
- map((orgs) => orgs.filter(this.filter).sort((a, b) => a.name.localeCompare(b.name)))
+ map((orgs) =>
+ orgs
+ .filter((org) => this.filter(org))
+ .sort((a, b) => a.name.localeCompare(b.name))
+ .sort((a, b) => (a.enabled ? -1 : 1))
+ )
);
+
protected activeOrganization$: Observable = combineLatest([
this.route.paramMap,
this.organizations$,
diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/overview/overview.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/overview/overview.component.ts
index 86fab25608..868026a843 100644
--- a/bitwarden_license/bit-web/src/app/secrets-manager/overview/overview.component.ts
+++ b/bitwarden_license/bit-web/src/app/secrets-manager/overview/overview.component.ts
@@ -70,6 +70,7 @@ export class OverviewComponent implements OnInit, OnDestroy {
protected userIsAdmin: boolean;
protected showOnboarding = false;
protected loading = true;
+ protected organizationEnabled = false;
protected view$: Observable<{
allProjects: ProjectListView[];
@@ -107,6 +108,7 @@ export class OverviewComponent implements OnInit, OnDestroy {
this.organizationName = org.name;
this.userIsAdmin = org.isAdmin;
this.loading = true;
+ this.organizationEnabled = org.enabled;
});
const projects$ = combineLatest([
@@ -208,6 +210,7 @@ export class OverviewComponent implements OnInit, OnDestroy {
data: {
organizationId: this.organizationId,
operation: OperationType.Edit,
+ organizationEnabled: this.organizationEnabled,
projectId: projectId,
},
});
@@ -218,6 +221,7 @@ export class OverviewComponent implements OnInit, OnDestroy {
data: {
organizationId: this.organizationId,
operation: OperationType.Add,
+ organizationEnabled: this.organizationEnabled,
},
});
}
@@ -227,6 +231,7 @@ export class OverviewComponent implements OnInit, OnDestroy {
data: {
organizationId: this.organizationId,
operation: OperationType.Add,
+ organizationEnabled: this.organizationEnabled,
},
});
}
@@ -246,6 +251,7 @@ export class OverviewComponent implements OnInit, OnDestroy {
data: {
organizationId: this.organizationId,
operation: OperationType.Add,
+ organizationEnabled: this.organizationEnabled,
},
});
}
@@ -256,6 +262,7 @@ export class OverviewComponent implements OnInit, OnDestroy {
organizationId: this.organizationId,
operation: OperationType.Edit,
secretId: secretId,
+ organizationEnabled: this.organizationEnabled,
},
});
}
@@ -273,6 +280,7 @@ export class OverviewComponent implements OnInit, OnDestroy {
data: {
organizationId: this.organizationId,
operation: OperationType.Add,
+ organizationEnabled: this.organizationEnabled,
},
});
}
diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/projects/dialog/project-dialog.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/projects/dialog/project-dialog.component.ts
index a6a3c958d0..3fd723c758 100644
--- a/bitwarden_license/bit-web/src/app/secrets-manager/projects/dialog/project-dialog.component.ts
+++ b/bitwarden_license/bit-web/src/app/secrets-manager/projects/dialog/project-dialog.component.ts
@@ -18,6 +18,7 @@ export enum OperationType {
export interface ProjectOperation {
organizationId: string;
operation: OperationType;
+ organizationEnabled: boolean;
projectId?: string;
}
@@ -63,6 +64,15 @@ export class ProjectDialogComponent implements OnInit {
}
submit = async () => {
+ if (!this.data.organizationEnabled) {
+ this.platformUtilsService.showToast(
+ "error",
+ null,
+ this.i18nService.t("projectsCannotCreate")
+ );
+ return;
+ }
+
this.formGroup.markAllAsTouched();
if (this.formGroup.invalid) {
diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/projects/project/project-secrets.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/projects/project/project-secrets.component.ts
index 2d1690ef0e..a952a35153 100644
--- a/bitwarden_license/bit-web/src/app/secrets-manager/projects/project/project-secrets.component.ts
+++ b/bitwarden_license/bit-web/src/app/secrets-manager/projects/project/project-secrets.component.ts
@@ -2,6 +2,7 @@ import { Component } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
import { combineLatest, combineLatestWith, filter, Observable, startWith, switchMap } from "rxjs";
+import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { DialogService } from "@bitwarden/components";
@@ -31,6 +32,7 @@ export class ProjectSecretsComponent {
private organizationId: string;
private projectId: string;
protected project$: Observable;
+ private organizationEnabled: boolean;
constructor(
private route: ActivatedRoute,
@@ -38,7 +40,8 @@ export class ProjectSecretsComponent {
private secretService: SecretService,
private dialogService: DialogService,
private platformUtilsService: PlatformUtilsService,
- private i18nService: I18nService
+ private i18nService: I18nService,
+ private organizationService: OrganizationService
) {}
ngOnInit() {
@@ -60,6 +63,7 @@ export class ProjectSecretsComponent {
switchMap(async ([_, params]) => {
this.organizationId = params.organizationId;
this.projectId = params.projectId;
+ this.organizationEnabled = this.organizationService.get(params.organizationId)?.enabled;
return await this.getSecretsByProject();
})
);
@@ -75,6 +79,7 @@ export class ProjectSecretsComponent {
organizationId: this.organizationId,
operation: OperationType.Edit,
secretId: secretId,
+ organizationEnabled: this.organizationEnabled,
},
});
}
@@ -93,6 +98,7 @@ export class ProjectSecretsComponent {
organizationId: this.organizationId,
operation: OperationType.Add,
projectId: this.projectId,
+ organizationEnabled: this.organizationEnabled,
},
});
}
diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/projects/project/project.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/projects/project/project.component.ts
index c87d238d6a..148ccc79d2 100644
--- a/bitwarden_license/bit-web/src/app/secrets-manager/projects/project/project.component.ts
+++ b/bitwarden_license/bit-web/src/app/secrets-manager/projects/project/project.component.ts
@@ -12,6 +12,7 @@ import {
takeUntil,
} from "rxjs";
+import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { DialogService } from "@bitwarden/components";
@@ -33,7 +34,7 @@ export class ProjectComponent implements OnInit, OnDestroy {
private organizationId: string;
private projectId: string;
-
+ private organizationEnabled: boolean;
private destroy$ = new Subject();
constructor(
@@ -42,7 +43,8 @@ export class ProjectComponent implements OnInit, OnDestroy {
private router: Router,
private dialogService: DialogService,
private platformUtilsService: PlatformUtilsService,
- private i18nService: I18nService
+ private i18nService: I18nService,
+ private organizationService: OrganizationService
) {}
ngOnInit(): void {
@@ -69,6 +71,7 @@ export class ProjectComponent implements OnInit, OnDestroy {
this.route.params.pipe(takeUntil(this.destroy$)).subscribe((params) => {
this.organizationId = params.organizationId;
this.projectId = params.projectId;
+ this.organizationEnabled = this.organizationService.get(params.organizationId)?.enabled;
});
}
@@ -82,6 +85,7 @@ export class ProjectComponent implements OnInit, OnDestroy {
data: {
organizationId: this.organizationId,
operation: OperationType.Edit,
+ organizationEnabled: this.organizationEnabled,
projectId: this.projectId,
},
});
diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/projects/projects/projects.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/projects/projects/projects.component.ts
index 7128e26a3d..1066828f21 100644
--- a/bitwarden_license/bit-web/src/app/secrets-manager/projects/projects/projects.component.ts
+++ b/bitwarden_license/bit-web/src/app/secrets-manager/projects/projects/projects.component.ts
@@ -2,6 +2,7 @@ import { Component, OnInit } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
import { combineLatest, lastValueFrom, Observable, startWith, switchMap } from "rxjs";
+import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { DialogService } from "@bitwarden/components";
import { ProjectListView } from "../../models/view/project-list.view";
@@ -32,12 +33,14 @@ export class ProjectsComponent implements OnInit {
protected search: string;
private organizationId: string;
+ private organizationEnabled: boolean;
constructor(
private route: ActivatedRoute,
private projectService: ProjectService,
private accessPolicyService: AccessPolicyService,
- private dialogService: DialogService
+ private dialogService: DialogService,
+ private organizationService: OrganizationService
) {}
ngOnInit() {
@@ -48,6 +51,8 @@ export class ProjectsComponent implements OnInit {
]).pipe(
switchMap(async ([params]) => {
this.organizationId = params.organizationId;
+ this.organizationEnabled = this.organizationService.get(params.organizationId)?.enabled;
+
return await this.getProjects();
})
);
@@ -62,6 +67,7 @@ export class ProjectsComponent implements OnInit {
data: {
organizationId: this.organizationId,
operation: OperationType.Edit,
+ organizationEnabled: this.organizationEnabled,
projectId: projectId,
},
});
@@ -72,6 +78,7 @@ export class ProjectsComponent implements OnInit {
data: {
organizationId: this.organizationId,
operation: OperationType.Add,
+ organizationEnabled: this.organizationEnabled,
},
});
}
diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.ts
index 426542823f..70eca54e3c 100644
--- a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.ts
+++ b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.ts
@@ -29,6 +29,7 @@ export interface SecretOperation {
operation: OperationType;
projectId?: string;
secretId?: string;
+ organizationEnabled: boolean;
}
@Component({
@@ -163,6 +164,11 @@ export class SecretDialogComponent implements OnInit {
}
submit = async () => {
+ if (!this.data.organizationEnabled) {
+ this.platformUtilsService.showToast("error", null, this.i18nService.t("secretsCannotCreate"));
+ return;
+ }
+
this.formGroup.markAllAsTouched();
if (this.formGroup.invalid) {
diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/secrets.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/secrets.component.ts
index 7c05f169a3..b23393de60 100644
--- a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/secrets.component.ts
+++ b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/secrets.component.ts
@@ -2,6 +2,7 @@ import { Component, OnInit } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
import { combineLatestWith, Observable, startWith, switchMap } from "rxjs";
+import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { DialogService } from "@bitwarden/components";
@@ -29,13 +30,15 @@ export class SecretsComponent implements OnInit {
protected search: string;
private organizationId: string;
+ private organizationEnabled: boolean;
constructor(
private route: ActivatedRoute,
private secretService: SecretService,
private dialogService: DialogService,
private platformUtilsService: PlatformUtilsService,
- private i18nService: I18nService
+ private i18nService: I18nService,
+ private organizationService: OrganizationService
) {}
ngOnInit() {
@@ -44,6 +47,8 @@ export class SecretsComponent implements OnInit {
combineLatestWith(this.route.params),
switchMap(async ([_, params]) => {
this.organizationId = params.organizationId;
+ this.organizationEnabled = this.organizationService.get(params.organizationId)?.enabled;
+
return await this.getSecrets();
})
);
@@ -63,6 +68,7 @@ export class SecretsComponent implements OnInit {
organizationId: this.organizationId,
operation: OperationType.Edit,
secretId: secretId,
+ organizationEnabled: this.organizationEnabled,
},
});
}
@@ -80,6 +86,7 @@ export class SecretsComponent implements OnInit {
data: {
organizationId: this.organizationId,
operation: OperationType.Add,
+ organizationEnabled: this.organizationEnabled,
},
});
}
diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/dialog/service-account-dialog.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/dialog/service-account-dialog.component.ts
index 1f42537f95..decd042cc1 100644
--- a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/dialog/service-account-dialog.component.ts
+++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/dialog/service-account-dialog.component.ts
@@ -18,6 +18,7 @@ export interface ServiceAccountOperation {
organizationId: string;
serviceAccountId?: string;
operation: OperationType;
+ organizationEnabled: boolean;
}
@Component({
@@ -62,6 +63,15 @@ export class ServiceAccountDialogComponent {
}
submit = async () => {
+ if (!this.data.organizationEnabled) {
+ this.platformUtilsService.showToast(
+ "error",
+ null,
+ this.i18nService.t("serviceAccountsCannotCreate")
+ );
+ return;
+ }
+
this.formGroup.markAllAsTouched();
if (this.formGroup.invalid) {
diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-accounts.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-accounts.component.ts
index 808073ba81..bebd9ddca6 100644
--- a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-accounts.component.ts
+++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-accounts.component.ts
@@ -2,6 +2,7 @@ import { Component, OnInit } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
import { combineLatest, Observable, startWith, switchMap } from "rxjs";
+import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { DialogService } from "@bitwarden/components";
import {
@@ -30,12 +31,14 @@ export class ServiceAccountsComponent implements OnInit {
protected search: string;
private organizationId: string;
+ private organizationEnabled: boolean;
constructor(
private route: ActivatedRoute,
private dialogService: DialogService,
private accessPolicyService: AccessPolicyService,
- private serviceAccountService: ServiceAccountService
+ private serviceAccountService: ServiceAccountService,
+ private organizationService: OrganizationService
) {}
ngOnInit() {
@@ -46,6 +49,8 @@ export class ServiceAccountsComponent implements OnInit {
]).pipe(
switchMap(async ([params]) => {
this.organizationId = params.organizationId;
+ this.organizationEnabled = this.organizationService.get(params.organizationId)?.enabled;
+
return await this.getServiceAccounts();
})
);
@@ -56,6 +61,7 @@ export class ServiceAccountsComponent implements OnInit {
data: {
organizationId: this.organizationId,
operation: OperationType.Add,
+ organizationEnabled: this.organizationEnabled,
},
});
}
@@ -66,6 +72,7 @@ export class ServiceAccountsComponent implements OnInit {
organizationId: this.organizationId,
serviceAccountId: serviceAccountId,
operation: OperationType.Edit,
+ organizationEnabled: this.organizationEnabled,
},
});
}
diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/shared/new-menu.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/shared/new-menu.component.ts
index 7ecc2f917a..67a93e8ad8 100644
--- a/bitwarden_license/bit-web/src/app/secrets-manager/shared/new-menu.component.ts
+++ b/bitwarden_license/bit-web/src/app/secrets-manager/shared/new-menu.component.ts
@@ -2,6 +2,7 @@ import { Component, OnDestroy, OnInit } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
import { Subject, takeUntil } from "rxjs";
+import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { DialogService } from "@bitwarden/components";
import {
@@ -24,13 +25,18 @@ import {
})
export class NewMenuComponent implements OnInit, OnDestroy {
private organizationId: string;
+ private organizationEnabled: boolean;
private destroy$: Subject = new Subject();
-
- constructor(private route: ActivatedRoute, private dialogService: DialogService) {}
+ constructor(
+ private route: ActivatedRoute,
+ private dialogService: DialogService,
+ private organizationService: OrganizationService
+ ) {}
ngOnInit() {
this.route.params.pipe(takeUntil(this.destroy$)).subscribe((params: any) => {
this.organizationId = params.organizationId;
+ this.organizationEnabled = this.organizationService.get(params.organizationId)?.enabled;
});
}
@@ -44,6 +50,7 @@ export class NewMenuComponent implements OnInit, OnDestroy {
data: {
organizationId: this.organizationId,
operation: OperationType.Add,
+ organizationEnabled: this.organizationEnabled,
},
});
}
@@ -53,6 +60,7 @@ export class NewMenuComponent implements OnInit, OnDestroy {
data: {
organizationId: this.organizationId,
operation: OperationType.Add,
+ organizationEnabled: this.organizationEnabled,
},
});
}
@@ -62,6 +70,7 @@ export class NewMenuComponent implements OnInit, OnDestroy {
data: {
organizationId: this.organizationId,
operation: OperationType.Add,
+ organizationEnabled: this.organizationEnabled,
},
});
}
diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/shared/org-suspended.component.html b/bitwarden_license/bit-web/src/app/secrets-manager/shared/org-suspended.component.html
new file mode 100644
index 0000000000..8de68f6598
--- /dev/null
+++ b/bitwarden_license/bit-web/src/app/secrets-manager/shared/org-suspended.component.html
@@ -0,0 +1,7 @@
+
+
+
+
+ {{ "organizationIsDisabled" | i18n }}
+ {{ "secretsAccessSuspended" | i18n }}
+
diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/shared/org-suspended.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/shared/org-suspended.component.ts
new file mode 100644
index 0000000000..73f89c0826
--- /dev/null
+++ b/bitwarden_license/bit-web/src/app/secrets-manager/shared/org-suspended.component.ts
@@ -0,0 +1,18 @@
+import { Component } from "@angular/core";
+import { ActivatedRoute } from "@angular/router";
+import { map } from "rxjs";
+
+import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
+import { Icon, Icons } from "@bitwarden/components";
+
+@Component({
+ templateUrl: "./org-suspended.component.html",
+})
+export class OrgSuspendedComponent {
+ constructor(private organizationService: OrganizationService, private route: ActivatedRoute) {}
+
+ protected NoAccess: Icon = Icons.NoAccess;
+ protected organizationName$ = this.route.params.pipe(
+ map((params) => this.organizationService.get(params.organizationId)?.name)
+ );
+}
diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/shared/sm-shared.module.ts b/bitwarden_license/bit-web/src/app/secrets-manager/shared/sm-shared.module.ts
index 6d59503b50..d2990f4c67 100644
--- a/bitwarden_license/bit-web/src/app/secrets-manager/shared/sm-shared.module.ts
+++ b/bitwarden_license/bit-web/src/app/secrets-manager/shared/sm-shared.module.ts
@@ -17,6 +17,7 @@ import { BulkConfirmationDialogComponent } from "./dialogs/bulk-confirmation-dia
import { BulkStatusDialogComponent } from "./dialogs/bulk-status-dialog.component";
import { HeaderComponent } from "./header.component";
import { NewMenuComponent } from "./new-menu.component";
+import { OrgSuspendedComponent } from "./org-suspended.component";
import { ProjectsListComponent } from "./projects-list.component";
import { SecretsListComponent } from "./secrets-list.component";
@@ -55,6 +56,7 @@ import { SecretsListComponent } from "./secrets-list.component";
ProjectsListComponent,
SecretsListComponent,
AccessSelectorComponent,
+ OrgSuspendedComponent,
],
providers: [],
bootstrap: [],
diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/sm-routing.module.ts b/bitwarden_license/bit-web/src/app/secrets-manager/sm-routing.module.ts
index 5c18bab4e4..0cad3129a4 100644
--- a/bitwarden_license/bit-web/src/app/secrets-manager/sm-routing.module.ts
+++ b/bitwarden_license/bit-web/src/app/secrets-manager/sm-routing.module.ts
@@ -2,10 +2,10 @@ import { NgModule } from "@angular/core";
import { RouterModule, Routes } from "@angular/router";
import { AuthGuard } from "@bitwarden/angular/auth/guards";
-import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
-import { OrganizationPermissionsGuard } from "@bitwarden/web-vault/app/admin-console/organizations/guards/org-permissions.guard";
import { buildFlaggedRoute } from "@bitwarden/web-vault/app/oss-routing.module";
+import { organizationEnabledGuard } from "./guards/sm-org-enabled.guard";
+import { canActivateSM } from "./guards/sm.guard";
import { LayoutComponent } from "./layout/layout.component";
import { NavigationComponent } from "./layout/navigation.component";
import { OverviewModule } from "./overview/overview.module";
@@ -13,7 +13,7 @@ import { ProjectsModule } from "./projects/projects.module";
import { SecretsModule } from "./secrets/secrets.module";
import { ServiceAccountsModule } from "./service-accounts/service-accounts.module";
import { SettingsModule } from "./settings/settings.module";
-import { canActivateSM } from "./sm.guard";
+import { OrgSuspendedComponent } from "./shared/org-suspended.component";
import { TrashModule } from "./trash/trash.module";
const routes: Routes = [
@@ -29,52 +29,59 @@ const routes: Routes = [
{
path: ":organizationId",
component: LayoutComponent,
- canActivate: [AuthGuard, OrganizationPermissionsGuard],
- data: {
- organizationPermissions: (org: Organization) => org.canAccessSecretsManager,
- },
+ canActivate: [AuthGuard],
children: [
{
path: "",
component: NavigationComponent,
outlet: "sidebar",
},
- {
- path: "secrets",
- loadChildren: () => SecretsModule,
- data: {
- titleId: "secrets",
- },
- },
- {
- path: "projects",
- loadChildren: () => ProjectsModule,
- data: {
- titleId: "projects",
- },
- },
- {
- path: "service-accounts",
- loadChildren: () => ServiceAccountsModule,
- data: {
- titleId: "serviceAccounts",
- },
- },
- {
- path: "trash",
- loadChildren: () => TrashModule,
- data: {
- titleId: "trash",
- },
- },
- {
- path: "settings",
- loadChildren: () => SettingsModule,
- },
{
path: "",
- loadChildren: () => OverviewModule,
- pathMatch: "full",
+ canActivate: [organizationEnabledGuard],
+ children: [
+ {
+ path: "secrets",
+ loadChildren: () => SecretsModule,
+ data: {
+ titleId: "secrets",
+ },
+ },
+ {
+ path: "projects",
+ loadChildren: () => ProjectsModule,
+ data: {
+ titleId: "projects",
+ },
+ },
+ {
+ path: "service-accounts",
+ loadChildren: () => ServiceAccountsModule,
+ data: {
+ titleId: "serviceAccounts",
+ },
+ },
+ {
+ path: "trash",
+ loadChildren: () => TrashModule,
+ data: {
+ titleId: "trash",
+ },
+ },
+ {
+ path: "settings",
+ loadChildren: () => SettingsModule,
+ },
+ {
+ path: "",
+ loadChildren: () => OverviewModule,
+ pathMatch: "full",
+ },
+ ],
+ },
+ {
+ path: "organization-suspended",
+ component: OrgSuspendedComponent,
},
],
},
diff --git a/libs/components/src/icon/icons/index.ts b/libs/components/src/icon/icons/index.ts
index 03fdb729bc..02cb975e09 100644
--- a/libs/components/src/icon/icons/index.ts
+++ b/libs/components/src/icon/icons/index.ts
@@ -1 +1,2 @@
export * from "./search";
+export * from "./no-access";
diff --git a/libs/components/src/icon/icons/no-access.ts b/libs/components/src/icon/icons/no-access.ts
new file mode 100644
index 0000000000..f9ad048752
--- /dev/null
+++ b/libs/components/src/icon/icons/no-access.ts
@@ -0,0 +1,12 @@
+import { svgIcon } from "../icon";
+
+export const NoAccess = svgIcon`
+
+`;
diff --git a/libs/components/src/navigation/nav-group.component.html b/libs/components/src/navigation/nav-group.component.html
index ca9a7c3aec..118f78a186 100644
--- a/libs/components/src/navigation/nav-group.component.html
+++ b/libs/components/src/navigation/nav-group.component.html
@@ -17,7 +17,7 @@
[bitIconButton]="
open ? 'bwi-angle-up' : variant === 'tree' ? 'bwi-angle-right' : 'bwi-angle-down'
"
- [buttonType]="'main'"
+ [buttonType]="'light'"
(click)="toggle($event)"
size="small"
[title]="'toggleCollapse' | i18n"
@@ -32,8 +32,11 @@
-
-
+
+
+
+
+
diff --git a/libs/components/src/navigation/nav-item.component.html b/libs/components/src/navigation/nav-item.component.html
index 32c8dfbf98..02705e821e 100644
--- a/libs/components/src/navigation/nav-item.component.html
+++ b/libs/components/src/navigation/nav-item.component.html
@@ -73,7 +73,7 @@
diff --git a/libs/components/src/navigation/nav-item.stories.ts b/libs/components/src/navigation/nav-item.stories.ts
index 7fdbadce31..c8f90eabcf 100644
--- a/libs/components/src/navigation/nav-item.stories.ts
+++ b/libs/components/src/navigation/nav-item.stories.ts
@@ -64,7 +64,7 @@ export const WithChildButtons: Story = {
slot="start"
class="tw-ml-auto"
[bitIconButton]="'bwi-clone'"
- [buttonType]="'contrast'"
+ [buttonType]="'light'"
size="small"
aria-label="option 1"
>
@@ -72,7 +72,7 @@ export const WithChildButtons: Story = {
slot="end"
class="tw-ml-auto"
[bitIconButton]="'bwi-pencil-square'"
- [buttonType]="'contrast'"
+ [buttonType]="'light'"
size="small"
aria-label="option 2"
>
@@ -80,7 +80,7 @@ export const WithChildButtons: Story = {
slot="end"
class="tw-ml-auto"
[bitIconButton]="'bwi-check'"
- [buttonType]="'contrast'"
+ [buttonType]="'light'"
size="small"
aria-label="option 3"
>