diff --git a/bitwarden_license/src/app/app.module.ts b/bitwarden_license/src/app/app.module.ts
index 9e0de2af..781e3f45 100644
--- a/bitwarden_license/src/app/app.module.ts
+++ b/bitwarden_license/src/app/app.module.ts
@@ -19,17 +19,17 @@ import { MaximumVaultTimeoutPolicyComponent } from "./policies/maximum-vault-tim
@NgModule({
imports: [
- AppRoutingModule,
- BrowserAnimationsModule,
- DragDropModule,
- FormsModule,
- InfiniteScrollModule,
JslibModule,
- OrganizationsModule,
- OssRoutingModule,
+ BrowserAnimationsModule,
+ FormsModule,
ReactiveFormsModule,
- RouterModule,
ServicesModule,
+ InfiniteScrollModule,
+ DragDropModule,
+ AppRoutingModule,
+ OssRoutingModule,
+ OrganizationsModule, // Must be after OssRoutingModule for competing routes to resolve properly
+ RouterModule,
WildcardRoutingModule, // Needs to be last to catch all non-existing routes
],
declarations: [
diff --git a/bitwarden_license/src/app/organizations/organizations-routing.module.ts b/bitwarden_license/src/app/organizations/organizations-routing.module.ts
index 0bb5063b..7ce7f56c 100644
--- a/bitwarden_license/src/app/organizations/organizations-routing.module.ts
+++ b/bitwarden_license/src/app/organizations/organizations-routing.module.ts
@@ -4,10 +4,10 @@ import { RouterModule, Routes } from "@angular/router";
import { AuthGuard } from "jslib-angular/guards/auth.guard";
import { Permissions } from "jslib-common/enums/permissions";
-import { OrganizationLayoutComponent } from "src/app/layouts/organization-layout.component";
+import { PermissionsGuard } from "src/app/organizations/guards/permissions.guard";
+import { OrganizationLayoutComponent } from "src/app/organizations/layouts/organization-layout.component";
import { ManageComponent } from "src/app/organizations/manage/manage.component";
-import { OrganizationGuardService } from "src/app/services/organization-guard.service";
-import { OrganizationTypeGuardService } from "src/app/services/organization-type-guard.service";
+import { NavigationPermissionsService } from "src/app/organizations/services/navigation-permissions.service";
import { SsoComponent } from "./manage/sso.component";
@@ -15,24 +15,15 @@ const routes: Routes = [
{
path: "organizations/:organizationId",
component: OrganizationLayoutComponent,
- canActivate: [AuthGuard, OrganizationGuardService],
+ canActivate: [AuthGuard, PermissionsGuard],
children: [
{
path: "manage",
component: ManageComponent,
- canActivate: [OrganizationTypeGuardService],
+ canActivate: [PermissionsGuard],
data: {
permissions: [
- Permissions.CreateNewCollections,
- Permissions.EditAnyCollection,
- Permissions.DeleteAnyCollection,
- Permissions.EditAssignedCollections,
- Permissions.DeleteAssignedCollections,
- Permissions.AccessEventLogs,
- Permissions.ManageGroups,
- Permissions.ManageUsers,
- Permissions.ManagePolicies,
- Permissions.ManageSso,
+ NavigationPermissionsService.getPermissions("manage").concat(Permissions.ManageSso),
],
},
children: [
diff --git a/bitwarden_license/src/app/providers/services/provider-type-guard.service.ts b/bitwarden_license/src/app/providers/guards/provider-type.guard.ts
similarity index 94%
rename from bitwarden_license/src/app/providers/services/provider-type-guard.service.ts
rename to bitwarden_license/src/app/providers/guards/provider-type.guard.ts
index 84997686..107fd044 100644
--- a/bitwarden_license/src/app/providers/services/provider-type-guard.service.ts
+++ b/bitwarden_license/src/app/providers/guards/provider-type.guard.ts
@@ -5,7 +5,7 @@ import { ProviderService } from "jslib-common/abstractions/provider.service";
import { Permissions } from "jslib-common/enums/permissions";
@Injectable()
-export class ProviderTypeGuardService implements CanActivate {
+export class PermissionsGuard implements CanActivate {
constructor(private providerService: ProviderService, private router: Router) {}
async canActivate(route: ActivatedRouteSnapshot) {
diff --git a/bitwarden_license/src/app/providers/services/provider-guard.service.ts b/bitwarden_license/src/app/providers/guards/provider.guard.ts
similarity index 94%
rename from bitwarden_license/src/app/providers/services/provider-guard.service.ts
rename to bitwarden_license/src/app/providers/guards/provider.guard.ts
index 51f62272..444d65fd 100644
--- a/bitwarden_license/src/app/providers/services/provider-guard.service.ts
+++ b/bitwarden_license/src/app/providers/guards/provider.guard.ts
@@ -6,7 +6,7 @@ import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.se
import { ProviderService } from "jslib-common/abstractions/provider.service";
@Injectable()
-export class ProviderGuardService implements CanActivate {
+export class ProviderGuard implements CanActivate {
constructor(
private router: Router,
private platformUtilsService: PlatformUtilsService,
diff --git a/bitwarden_license/src/app/providers/providers-routing.module.ts b/bitwarden_license/src/app/providers/providers-routing.module.ts
index 21b67319..14cd7c47 100644
--- a/bitwarden_license/src/app/providers/providers-routing.module.ts
+++ b/bitwarden_license/src/app/providers/providers-routing.module.ts
@@ -9,13 +9,13 @@ import { ProvidersComponent } from "src/app/providers/providers.component";
import { ClientsComponent } from "./clients/clients.component";
import { CreateOrganizationComponent } from "./clients/create-organization.component";
+import { PermissionsGuard } from "./guards/provider-type.guard";
+import { ProviderGuard } from "./guards/provider.guard";
import { AcceptProviderComponent } from "./manage/accept-provider.component";
import { EventsComponent } from "./manage/events.component";
import { ManageComponent } from "./manage/manage.component";
import { PeopleComponent } from "./manage/people.component";
import { ProvidersLayoutComponent } from "./providers-layout.component";
-import { ProviderGuardService } from "./services/provider-guard.service";
-import { ProviderTypeGuardService } from "./services/provider-type-guard.service";
import { AccountComponent } from "./settings/account.component";
import { SettingsComponent } from "./settings/settings.component";
import { SetupProviderComponent } from "./setup/setup-provider.component";
@@ -54,7 +54,7 @@ const routes: Routes = [
{
path: ":providerId",
component: ProvidersLayoutComponent,
- canActivate: [ProviderGuardService],
+ canActivate: [ProviderGuard],
children: [
{ path: "", pathMatch: "full", redirectTo: "clients" },
{ path: "clients/create", component: CreateOrganizationComponent },
@@ -71,7 +71,7 @@ const routes: Routes = [
{
path: "people",
component: PeopleComponent,
- canActivate: [ProviderTypeGuardService],
+ canActivate: [PermissionsGuard],
data: {
titleId: "people",
permissions: [Permissions.ManageUsers],
@@ -80,7 +80,7 @@ const routes: Routes = [
{
path: "events",
component: EventsComponent,
- canActivate: [ProviderTypeGuardService],
+ canActivate: [PermissionsGuard],
data: {
titleId: "eventLogs",
permissions: [Permissions.AccessEventLogs],
@@ -100,7 +100,7 @@ const routes: Routes = [
{
path: "account",
component: AccountComponent,
- canActivate: [ProviderTypeGuardService],
+ canActivate: [PermissionsGuard],
data: {
titleId: "myProvider",
permissions: [Permissions.ManageProvider],
diff --git a/bitwarden_license/src/app/providers/providers.module.ts b/bitwarden_license/src/app/providers/providers.module.ts
index 9f762cf4..7880d78c 100644
--- a/bitwarden_license/src/app/providers/providers.module.ts
+++ b/bitwarden_license/src/app/providers/providers.module.ts
@@ -10,6 +10,8 @@ import { OssModule } from "src/app/oss.module";
import { AddOrganizationComponent } from "./clients/add-organization.component";
import { ClientsComponent } from "./clients/clients.component";
import { CreateOrganizationComponent } from "./clients/create-organization.component";
+import { PermissionsGuard } from "./guards/provider-type.guard";
+import { ProviderGuard } from "./guards/provider.guard";
import { AcceptProviderComponent } from "./manage/accept-provider.component";
import { BulkConfirmComponent } from "./manage/bulk/bulk-confirm.component";
import { BulkRemoveComponent } from "./manage/bulk/bulk-remove.component";
@@ -19,8 +21,6 @@ import { PeopleComponent } from "./manage/people.component";
import { UserAddEditComponent } from "./manage/user-add-edit.component";
import { ProvidersLayoutComponent } from "./providers-layout.component";
import { ProvidersRoutingModule } from "./providers-routing.module";
-import { ProviderGuardService } from "./services/provider-guard.service";
-import { ProviderTypeGuardService } from "./services/provider-type-guard.service";
import { WebProviderService } from "./services/webProvider.service";
import { AccountComponent } from "./settings/account.component";
import { SettingsComponent } from "./settings/settings.component";
@@ -46,7 +46,7 @@ import { SetupComponent } from "./setup/setup.component";
SetupProviderComponent,
UserAddEditComponent,
],
- providers: [WebProviderService, ProviderGuardService, ProviderTypeGuardService],
+ providers: [WebProviderService, ProviderGuard, PermissionsGuard],
})
export class ProvidersModule {
constructor(modalService: ModalService, componentFactoryResolver: ComponentFactoryResolver) {
diff --git a/src/app/layouts/navbar.component.html b/src/app/layouts/navbar.component.html
index e3ae3d56..3fb1de31 100644
--- a/src/app/layouts/navbar.component.html
+++ b/src/app/layouts/navbar.component.html
@@ -27,6 +27,11 @@
{{ "reports" | i18n }}
+ = 1" class="nav-item" routerLinkActive="active">
+ {{
+ "organizations" | i18n
+ }}
+
diff --git a/src/app/layouts/navbar.component.ts b/src/app/layouts/navbar.component.ts
index 8007176c..41585ef4 100644
--- a/src/app/layouts/navbar.component.ts
+++ b/src/app/layouts/navbar.component.ts
@@ -1,12 +1,18 @@
import { Component, OnInit } from "@angular/core";
+import { I18nService } from "jslib-common/abstractions/i18n.service";
import { MessagingService } from "jslib-common/abstractions/messaging.service";
+import { OrganizationService } from "jslib-common/abstractions/organization.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { ProviderService } from "jslib-common/abstractions/provider.service";
import { SyncService } from "jslib-common/abstractions/sync.service";
import { TokenService } from "jslib-common/abstractions/token.service";
+import { Utils } from "jslib-common/misc/utils";
+import { Organization } from "jslib-common/models/domain/organization";
import { Provider } from "jslib-common/models/domain/provider";
+import { NavigationPermissionsService as OrgNavigationPermissionsService } from "../organizations/services/navigation-permissions.service";
+
@Component({
selector: "app-navbar",
templateUrl: "navbar.component.html",
@@ -16,13 +22,16 @@ export class NavbarComponent implements OnInit {
name: string;
email: string;
providers: Provider[] = [];
+ organizations: Organization[] = [];
constructor(
private messagingService: MessagingService,
private platformUtilsService: PlatformUtilsService,
private tokenService: TokenService,
private providerService: ProviderService,
- private syncService: SyncService
+ private syncService: SyncService,
+ private organizationService: OrganizationService,
+ private i18nService: I18nService
) {
this.selfHosted = this.platformUtilsService.isSelfHost();
}
@@ -34,11 +43,16 @@ export class NavbarComponent implements OnInit {
this.name = this.email;
}
- // Ensure provides are loaded
+ // Ensure providers and organizations are loaded
if ((await this.syncService.getLastSync()) == null) {
await this.syncService.fullSync(false);
}
this.providers = await this.providerService.getAll();
+
+ const allOrgs = await this.organizationService.getAll();
+ this.organizations = allOrgs
+ .filter((org) => OrgNavigationPermissionsService.canAccessAdmin(org))
+ .sort(Utils.getSortFunction(this.i18nService, "name"));
}
lock() {
diff --git a/src/app/services/organization-guard.service.ts b/src/app/organizations/guards/permissions.guard.ts
similarity index 54%
rename from src/app/services/organization-guard.service.ts
rename to src/app/organizations/guards/permissions.guard.ts
index bd999eea..a2f4c73c 100644
--- a/src/app/services/organization-guard.service.ts
+++ b/src/app/organizations/guards/permissions.guard.ts
@@ -1,33 +1,42 @@
import { Injectable } from "@angular/core";
import { ActivatedRouteSnapshot, CanActivate, Router } from "@angular/router";
+import { BaseGuard } from "jslib-angular/guards/base.guard";
import { I18nService } from "jslib-common/abstractions/i18n.service";
import { OrganizationService } from "jslib-common/abstractions/organization.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
+import { Permissions } from "jslib-common/enums/permissions";
@Injectable()
-export class OrganizationGuardService implements CanActivate {
+export class PermissionsGuard extends BaseGuard implements CanActivate {
constructor(
- private router: Router,
+ router: Router,
+ private organizationService: OrganizationService,
private platformUtilsService: PlatformUtilsService,
- private i18nService: I18nService,
- private organizationService: OrganizationService
- ) {}
+ private i18nService: I18nService
+ ) {
+ super(router);
+ }
async canActivate(route: ActivatedRouteSnapshot) {
const org = await this.organizationService.get(route.params.organizationId);
if (org == null) {
- this.router.navigate(["/"]);
- return false;
+ return this.redirect();
}
+
if (!org.isOwner && !org.enabled) {
this.platformUtilsService.showToast(
"error",
null,
this.i18nService.t("organizationIsDisabled")
);
- this.router.navigate(["/"]);
- return false;
+ return this.redirect();
+ }
+
+ const permissions = route.data == null ? [] : (route.data.permissions as Permissions[]);
+ if (permissions != null && !org.hasAnyPermission(permissions)) {
+ this.platformUtilsService.showToast("error", null, this.i18nService.t("accessDenied"));
+ return this.redirect();
}
return true;
diff --git a/src/app/layouts/organization-layout.component.html b/src/app/organizations/layouts/organization-layout.component.html
similarity index 92%
rename from src/app/layouts/organization-layout.component.html
rename to src/app/organizations/layouts/organization-layout.component.html
index 51fec6ab..84b265cd 100644
--- a/src/app/layouts/organization-layout.component.html
+++ b/src/app/organizations/layouts/organization-layout.component.html
@@ -1,4 +1,3 @@
-
-
+
-
@@ -46,7 +45,7 @@
{{ "tools" | i18n }}
- -
+
-
{{ "settings" | i18n }}
@@ -57,4 +56,3 @@
-
diff --git a/src/app/layouts/organization-layout.component.ts b/src/app/organizations/layouts/organization-layout.component.ts
similarity index 81%
rename from src/app/layouts/organization-layout.component.ts
rename to src/app/organizations/layouts/organization-layout.component.ts
index e4047a82..050e12f4 100644
--- a/src/app/layouts/organization-layout.component.ts
+++ b/src/app/organizations/layouts/organization-layout.component.ts
@@ -5,6 +5,8 @@ import { BroadcasterService } from "jslib-common/abstractions/broadcaster.servic
import { OrganizationService } from "jslib-common/abstractions/organization.service";
import { Organization } from "jslib-common/models/domain/organization";
+import { NavigationPermissionsService } from "../services/navigation-permissions.service";
+
const BroadcasterSubscriptionId = "OrganizationLayoutComponent";
@Component({
@@ -25,7 +27,7 @@ export class OrganizationLayoutComponent implements OnInit, OnDestroy {
ngOnInit() {
document.body.classList.remove("layout_frontend");
- this.route.params.subscribe(async (params) => {
+ this.route.params.subscribe(async (params: any) => {
this.organizationId = params.organizationId;
await this.load();
});
@@ -48,23 +50,16 @@ export class OrganizationLayoutComponent implements OnInit, OnDestroy {
this.organization = await this.organizationService.get(this.organizationId);
}
- get showMenuBar() {
- return this.showManageTab || this.showToolsTab || this.organization.isOwner;
- }
-
get showManageTab(): boolean {
- return (
- this.organization.canManageUsers ||
- this.organization.canViewAllCollections ||
- this.organization.canViewAssignedCollections ||
- this.organization.canManageGroups ||
- this.organization.canManagePolicies ||
- this.organization.canAccessEventLogs
- );
+ return NavigationPermissionsService.canAccessManage(this.organization);
}
get showToolsTab(): boolean {
- return this.organization.canAccessImportExport || this.organization.canAccessReports;
+ return NavigationPermissionsService.canAccessTools(this.organization);
+ }
+
+ get showSettingsTab(): boolean {
+ return NavigationPermissionsService.canAccessSettings(this.organization);
}
get toolsRoute(): string {
diff --git a/src/app/organizations/organization-routing.module.ts b/src/app/organizations/organization-routing.module.ts
new file mode 100644
index 00000000..39306467
--- /dev/null
+++ b/src/app/organizations/organization-routing.module.ts
@@ -0,0 +1,217 @@
+import { NgModule } from "@angular/core";
+import { RouterModule, Routes } from "@angular/router";
+
+import { AuthGuard } from "jslib-angular/guards/auth.guard";
+import { Permissions } from "jslib-common/enums/permissions";
+
+import { PermissionsGuard } from "./guards/permissions.guard";
+import { OrganizationLayoutComponent } from "./layouts/organization-layout.component";
+import { CollectionsComponent } from "./manage/collections.component";
+import { EventsComponent } from "./manage/events.component";
+import { GroupsComponent } from "./manage/groups.component";
+import { ManageComponent } from "./manage/manage.component";
+import { PeopleComponent } from "./manage/people.component";
+import { PoliciesComponent } from "./manage/policies.component";
+import { NavigationPermissionsService } from "./services/navigation-permissions.service";
+import { AccountComponent } from "./settings/account.component";
+import { OrganizationBillingComponent } from "./settings/organization-billing.component";
+import { OrganizationSubscriptionComponent } from "./settings/organization-subscription.component";
+import { SettingsComponent } from "./settings/settings.component";
+import { TwoFactorSetupComponent } from "./settings/two-factor-setup.component";
+import { ExportComponent } from "./tools/export.component";
+import { ExposedPasswordsReportComponent } from "./tools/exposed-passwords-report.component";
+import { ImportComponent } from "./tools/import.component";
+import { InactiveTwoFactorReportComponent } from "./tools/inactive-two-factor-report.component";
+import { ReusedPasswordsReportComponent } from "./tools/reused-passwords-report.component";
+import { ToolsComponent } from "./tools/tools.component";
+import { UnsecuredWebsitesReportComponent } from "./tools/unsecured-websites-report.component";
+import { WeakPasswordsReportComponent } from "./tools/weak-passwords-report.component";
+import { VaultComponent } from "./vault/vault.component";
+
+const routes: Routes = [
+ {
+ path: ":organizationId",
+ component: OrganizationLayoutComponent,
+ canActivate: [AuthGuard, PermissionsGuard],
+ data: {
+ permissions: NavigationPermissionsService.getPermissions("admin"),
+ },
+ children: [
+ { path: "", pathMatch: "full", redirectTo: "vault" },
+ { path: "vault", component: VaultComponent, data: { titleId: "vault" } },
+ {
+ path: "tools",
+ component: ToolsComponent,
+ canActivate: [PermissionsGuard],
+ data: { permissions: NavigationPermissionsService.getPermissions("tools") },
+ children: [
+ {
+ path: "",
+ pathMatch: "full",
+ redirectTo: "import",
+ },
+ {
+ path: "import",
+ component: ImportComponent,
+ canActivate: [PermissionsGuard],
+ data: {
+ titleId: "importData",
+ permissions: [Permissions.AccessImportExport],
+ },
+ },
+ {
+ path: "export",
+ component: ExportComponent,
+ canActivate: [PermissionsGuard],
+ data: {
+ titleId: "exportVault",
+ permissions: [Permissions.AccessImportExport],
+ },
+ },
+ {
+ path: "exposed-passwords-report",
+ component: ExposedPasswordsReportComponent,
+ canActivate: [PermissionsGuard],
+ data: {
+ titleId: "exposedPasswordsReport",
+ permissions: [Permissions.AccessReports],
+ },
+ },
+ {
+ path: "inactive-two-factor-report",
+ component: InactiveTwoFactorReportComponent,
+ canActivate: [PermissionsGuard],
+ data: {
+ titleId: "inactive2faReport",
+ permissions: [Permissions.AccessReports],
+ },
+ },
+ {
+ path: "reused-passwords-report",
+ component: ReusedPasswordsReportComponent,
+ canActivate: [PermissionsGuard],
+ data: {
+ titleId: "reusedPasswordsReport",
+ permissions: [Permissions.AccessReports],
+ },
+ },
+ {
+ path: "unsecured-websites-report",
+ component: UnsecuredWebsitesReportComponent,
+ canActivate: [PermissionsGuard],
+ data: {
+ titleId: "unsecuredWebsitesReport",
+ permissions: [Permissions.AccessReports],
+ },
+ },
+ {
+ path: "weak-passwords-report",
+ component: WeakPasswordsReportComponent,
+ canActivate: [PermissionsGuard],
+ data: {
+ titleId: "weakPasswordsReport",
+ permissions: [Permissions.AccessReports],
+ },
+ },
+ ],
+ },
+ {
+ path: "manage",
+ component: ManageComponent,
+ canActivate: [PermissionsGuard],
+ data: {
+ permissions: NavigationPermissionsService.getPermissions("manage"),
+ },
+ children: [
+ {
+ path: "",
+ pathMatch: "full",
+ redirectTo: "people",
+ },
+ {
+ path: "collections",
+ component: CollectionsComponent,
+ canActivate: [PermissionsGuard],
+ data: {
+ titleId: "collections",
+ permissions: [
+ Permissions.CreateNewCollections,
+ Permissions.EditAnyCollection,
+ Permissions.DeleteAnyCollection,
+ Permissions.EditAssignedCollections,
+ Permissions.DeleteAssignedCollections,
+ ],
+ },
+ },
+ {
+ path: "events",
+ component: EventsComponent,
+ canActivate: [PermissionsGuard],
+ data: {
+ titleId: "eventLogs",
+ permissions: [Permissions.AccessEventLogs],
+ },
+ },
+ {
+ path: "groups",
+ component: GroupsComponent,
+ canActivate: [PermissionsGuard],
+ data: {
+ titleId: "groups",
+ permissions: [Permissions.ManageGroups],
+ },
+ },
+ {
+ path: "people",
+ component: PeopleComponent,
+ canActivate: [PermissionsGuard],
+ data: {
+ titleId: "people",
+ permissions: [Permissions.ManageUsers, Permissions.ManageUsersPassword],
+ },
+ },
+ {
+ path: "policies",
+ component: PoliciesComponent,
+ canActivate: [PermissionsGuard],
+ data: {
+ titleId: "policies",
+ permissions: [Permissions.ManagePolicies],
+ },
+ },
+ ],
+ },
+ {
+ path: "settings",
+ component: SettingsComponent,
+ canActivate: [PermissionsGuard],
+ data: { permissions: NavigationPermissionsService.getPermissions("settings") },
+ children: [
+ { path: "", pathMatch: "full", redirectTo: "account" },
+ { path: "account", component: AccountComponent, data: { titleId: "myOrganization" } },
+ {
+ path: "two-factor",
+ component: TwoFactorSetupComponent,
+ data: { titleId: "twoStepLogin" },
+ },
+ {
+ path: "billing",
+ component: OrganizationBillingComponent,
+ data: { titleId: "billing" },
+ },
+ {
+ path: "subscription",
+ component: OrganizationSubscriptionComponent,
+ data: { titleId: "subscription" },
+ },
+ ],
+ },
+ ],
+ },
+];
+
+@NgModule({
+ imports: [RouterModule.forChild(routes)],
+ exports: [RouterModule],
+})
+export class OrganizationsRoutingModule {}
diff --git a/src/app/organizations/services/navigation-permissions.service.ts b/src/app/organizations/services/navigation-permissions.service.ts
new file mode 100644
index 00000000..dd47d2c5
--- /dev/null
+++ b/src/app/organizations/services/navigation-permissions.service.ts
@@ -0,0 +1,48 @@
+import { Permissions } from "jslib-common/enums/permissions";
+import { Organization } from "jslib-common/models/domain/organization";
+
+const permissions = {
+ manage: [
+ Permissions.CreateNewCollections,
+ Permissions.EditAnyCollection,
+ Permissions.DeleteAnyCollection,
+ Permissions.EditAssignedCollections,
+ Permissions.DeleteAssignedCollections,
+ Permissions.AccessEventLogs,
+ Permissions.ManageGroups,
+ Permissions.ManageUsers,
+ Permissions.ManagePolicies,
+ ],
+ tools: [Permissions.AccessImportExport, Permissions.AccessReports],
+ settings: [Permissions.ManageOrganization],
+};
+
+export class NavigationPermissionsService {
+ static getPermissions(route: keyof typeof permissions | "admin") {
+ if (route === "admin") {
+ return Object.values(permissions).reduce((previous, current) => previous.concat(current), []);
+ }
+
+ return permissions[route];
+ }
+
+ static canAccessAdmin(organization: Organization): boolean {
+ return (
+ this.canAccessTools(organization) ||
+ this.canAccessSettings(organization) ||
+ this.canAccessManage(organization)
+ );
+ }
+
+ static canAccessTools(organization: Organization): boolean {
+ return organization.hasAnyPermission(NavigationPermissionsService.getPermissions("tools"));
+ }
+
+ static canAccessSettings(organization: Organization): boolean {
+ return organization.hasAnyPermission(NavigationPermissionsService.getPermissions("settings"));
+ }
+
+ static canAccessManage(organization: Organization): boolean {
+ return organization.hasAnyPermission(NavigationPermissionsService.getPermissions("manage"));
+ }
+}
diff --git a/src/app/oss-routing.module.ts b/src/app/oss-routing.module.ts
index f93a7090..d194cd2f 100644
--- a/src/app/oss-routing.module.ts
+++ b/src/app/oss-routing.module.ts
@@ -4,7 +4,6 @@ import { RouterModule, Routes } from "@angular/router";
import { AuthGuard } from "jslib-angular/guards/auth.guard";
import { LockGuard } from "jslib-angular/guards/lock.guard";
import { UnauthGuard } from "jslib-angular/guards/unauth.guard";
-import { Permissions } from "jslib-common/enums/permissions";
import { AcceptEmergencyComponent } from "./accounts/accept-emergency.component";
import { AcceptOrganizationComponent } from "./accounts/accept-organization.component";
@@ -23,33 +22,10 @@ import { UpdateTempPasswordComponent } from "./accounts/update-temp-password.com
import { VerifyEmailTokenComponent } from "./accounts/verify-email-token.component";
import { VerifyRecoverDeleteComponent } from "./accounts/verify-recover-delete.component";
import { FrontendLayoutComponent } from "./layouts/frontend-layout.component";
-import { OrganizationLayoutComponent } from "./layouts/organization-layout.component";
import { UserLayoutComponent } from "./layouts/user-layout.component";
-import { CollectionsComponent as OrgManageCollectionsComponent } from "./organizations/manage/collections.component";
-import { EventsComponent as OrgEventsComponent } from "./organizations/manage/events.component";
-import { GroupsComponent as OrgGroupsComponent } from "./organizations/manage/groups.component";
-import { ManageComponent as OrgManageComponent } from "./organizations/manage/manage.component";
-import { PeopleComponent as OrgPeopleComponent } from "./organizations/manage/people.component";
-import { PoliciesComponent as OrgPoliciesComponent } from "./organizations/manage/policies.component";
-import { AccountComponent as OrgAccountComponent } from "./organizations/settings/account.component";
-import { OrganizationBillingComponent } from "./organizations/settings/organization-billing.component";
-import { OrganizationSubscriptionComponent } from "./organizations/settings/organization-subscription.component";
-import { SettingsComponent as OrgSettingsComponent } from "./organizations/settings/settings.component";
-import { TwoFactorSetupComponent as OrgTwoFactorSetupComponent } from "./organizations/settings/two-factor-setup.component";
import { FamiliesForEnterpriseSetupComponent } from "./organizations/sponsorships/families-for-enterprise-setup.component";
-import { ExportComponent as OrgExportComponent } from "./organizations/tools/export.component";
-import { ExposedPasswordsReportComponent as OrgExposedPasswordsReportComponent } from "./organizations/tools/exposed-passwords-report.component";
-import { ImportComponent as OrgImportComponent } from "./organizations/tools/import.component";
-import { InactiveTwoFactorReportComponent as OrgInactiveTwoFactorReportComponent } from "./organizations/tools/inactive-two-factor-report.component";
-import { ReusedPasswordsReportComponent as OrgReusedPasswordsReportComponent } from "./organizations/tools/reused-passwords-report.component";
-import { ToolsComponent as OrgToolsComponent } from "./organizations/tools/tools.component";
-import { UnsecuredWebsitesReportComponent as OrgUnsecuredWebsitesReportComponent } from "./organizations/tools/unsecured-websites-report.component";
-import { WeakPasswordsReportComponent as OrgWeakPasswordsReportComponent } from "./organizations/tools/weak-passwords-report.component";
-import { VaultComponent as OrgVaultComponent } from "./organizations/vault/vault.component";
import { AccessComponent } from "./send/access.component";
import { SendComponent } from "./send/send.component";
-import { OrganizationGuardService } from "./services/organization-guard.service";
-import { OrganizationTypeGuardService } from "./services/organization-type-guard.service";
import { AccountComponent } from "./settings/account.component";
import { CreateOrganizationComponent } from "./settings/create-organization.component";
import { DomainRulesComponent } from "./settings/domain-rules.component";
@@ -245,191 +221,12 @@ const routes: Routes = [
loadChildren: async () => (await import("./reports/reports.module")).ReportsModule,
},
{ path: "setup/families-for-enterprise", component: FamiliesForEnterpriseSetupComponent },
- ],
- },
- {
- path: "organizations/:organizationId",
- component: OrganizationLayoutComponent,
- canActivate: [AuthGuard, OrganizationGuardService],
- children: [
- { path: "", pathMatch: "full", redirectTo: "vault" },
- { path: "vault", component: OrgVaultComponent, data: { titleId: "vault" } },
{
- path: "tools",
- component: OrgToolsComponent,
- canActivate: [OrganizationTypeGuardService],
- data: { permissions: [Permissions.AccessImportExport, Permissions.AccessReports] },
- children: [
- {
- path: "",
- pathMatch: "full",
- redirectTo: "import",
- },
- {
- path: "import",
- component: OrgImportComponent,
- canActivate: [OrganizationTypeGuardService],
- data: {
- titleId: "importData",
- permissions: [Permissions.AccessImportExport],
- },
- },
- {
- path: "export",
- component: OrgExportComponent,
- canActivate: [OrganizationTypeGuardService],
- data: {
- titleId: "exportVault",
- permissions: [Permissions.AccessImportExport],
- },
- },
- {
- path: "exposed-passwords-report",
- component: OrgExposedPasswordsReportComponent,
- canActivate: [OrganizationTypeGuardService],
- data: {
- titleId: "exposedPasswordsReport",
- permissions: [Permissions.AccessReports],
- },
- },
- {
- path: "inactive-two-factor-report",
- component: OrgInactiveTwoFactorReportComponent,
- canActivate: [OrganizationTypeGuardService],
- data: {
- titleId: "inactive2faReport",
- permissions: [Permissions.AccessReports],
- },
- },
- {
- path: "reused-passwords-report",
- component: OrgReusedPasswordsReportComponent,
- canActivate: [OrganizationTypeGuardService],
- data: {
- titleId: "reusedPasswordsReport",
- permissions: [Permissions.AccessReports],
- },
- },
- {
- path: "unsecured-websites-report",
- component: OrgUnsecuredWebsitesReportComponent,
- canActivate: [OrganizationTypeGuardService],
- data: {
- titleId: "unsecuredWebsitesReport",
- permissions: [Permissions.AccessReports],
- },
- },
- {
- path: "weak-passwords-report",
- component: OrgWeakPasswordsReportComponent,
- canActivate: [OrganizationTypeGuardService],
- data: {
- titleId: "weakPasswordsReport",
- permissions: [Permissions.AccessReports],
- },
- },
- ],
- },
- {
- path: "manage",
- component: OrgManageComponent,
- canActivate: [OrganizationTypeGuardService],
- data: {
- permissions: [
- Permissions.CreateNewCollections,
- Permissions.EditAnyCollection,
- Permissions.DeleteAnyCollection,
- Permissions.EditAssignedCollections,
- Permissions.DeleteAssignedCollections,
- Permissions.AccessEventLogs,
- Permissions.ManageGroups,
- Permissions.ManageUsers,
- Permissions.ManagePolicies,
- ],
- },
- children: [
- {
- path: "",
- pathMatch: "full",
- redirectTo: "people",
- },
- {
- path: "collections",
- component: OrgManageCollectionsComponent,
- canActivate: [OrganizationTypeGuardService],
- data: {
- titleId: "collections",
- permissions: [
- Permissions.CreateNewCollections,
- Permissions.EditAnyCollection,
- Permissions.DeleteAnyCollection,
- Permissions.EditAssignedCollections,
- Permissions.DeleteAssignedCollections,
- ],
- },
- },
- {
- path: "events",
- component: OrgEventsComponent,
- canActivate: [OrganizationTypeGuardService],
- data: {
- titleId: "eventLogs",
- permissions: [Permissions.AccessEventLogs],
- },
- },
- {
- path: "groups",
- component: OrgGroupsComponent,
- canActivate: [OrganizationTypeGuardService],
- data: {
- titleId: "groups",
- permissions: [Permissions.ManageGroups],
- },
- },
- {
- path: "people",
- component: OrgPeopleComponent,
- canActivate: [OrganizationTypeGuardService],
- data: {
- titleId: "people",
- permissions: [Permissions.ManageUsers, Permissions.ManageUsersPassword],
- },
- },
- {
- path: "policies",
- component: OrgPoliciesComponent,
- canActivate: [OrganizationTypeGuardService],
- data: {
- titleId: "policies",
- permissions: [Permissions.ManagePolicies],
- },
- },
- ],
- },
- {
- path: "settings",
- component: OrgSettingsComponent,
- canActivate: [OrganizationTypeGuardService],
- data: { permissions: [Permissions.ManageOrganization] },
- children: [
- { path: "", pathMatch: "full", redirectTo: "account" },
- { path: "account", component: OrgAccountComponent, data: { titleId: "myOrganization" } },
- {
- path: "two-factor",
- component: OrgTwoFactorSetupComponent,
- data: { titleId: "twoStepLogin" },
- },
- {
- path: "billing",
- component: OrganizationBillingComponent,
- data: { titleId: "billing" },
- },
- {
- path: "subscription",
- component: OrganizationSubscriptionComponent,
- data: { titleId: "subscription" },
- },
- ],
+ path: "organizations",
+ loadChildren: () =>
+ import("./organizations/organization-routing.module").then(
+ (m) => m.OrganizationsRoutingModule
+ ),
},
],
},
@@ -440,7 +237,7 @@ const routes: Routes = [
RouterModule.forRoot(routes, {
useHash: true,
paramsInheritanceStrategy: "always",
- /*enableTracing: true,*/
+ // enableTracing: true,
}),
],
exports: [RouterModule],
diff --git a/src/app/oss.module.ts b/src/app/oss.module.ts
index c52b6ba4..f373d6b0 100644
--- a/src/app/oss.module.ts
+++ b/src/app/oss.module.ts
@@ -83,8 +83,8 @@ import { PremiumBadgeComponent } from "./components/premium-badge.component";
import { FooterComponent } from "./layouts/footer.component";
import { FrontendLayoutComponent } from "./layouts/frontend-layout.component";
import { NavbarComponent } from "./layouts/navbar.component";
-import { OrganizationLayoutComponent } from "./layouts/organization-layout.component";
import { UserLayoutComponent } from "./layouts/user-layout.component";
+import { OrganizationLayoutComponent } from "./organizations/layouts/organization-layout.component";
import { BulkConfirmComponent as OrgBulkConfirmComponent } from "./organizations/manage/bulk/bulk-confirm.component";
import { BulkRemoveComponent as OrgBulkRemoveComponent } from "./organizations/manage/bulk/bulk-remove.component";
import { BulkStatusComponent as OrgBulkStatusComponent } from "./organizations/manage/bulk/bulk-status.component";
diff --git a/src/app/services/organization-type-guard.service.ts b/src/app/services/organization-type-guard.service.ts
deleted file mode 100644
index c9a0f9b8..00000000
--- a/src/app/services/organization-type-guard.service.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-import { Injectable } from "@angular/core";
-import { ActivatedRouteSnapshot, CanActivate, Router } from "@angular/router";
-
-import { OrganizationService } from "jslib-common/abstractions/organization.service";
-import { Permissions } from "jslib-common/enums/permissions";
-
-@Injectable()
-export class OrganizationTypeGuardService implements CanActivate {
- constructor(private organizationService: OrganizationService, private router: Router) {}
-
- async canActivate(route: ActivatedRouteSnapshot) {
- const org = await this.organizationService.get(route.params.organizationId);
- const permissions = route.data == null ? null : (route.data.permissions as Permissions[]);
-
- if (
- (permissions.indexOf(Permissions.AccessEventLogs) !== -1 && org.canAccessEventLogs) ||
- (permissions.indexOf(Permissions.AccessImportExport) !== -1 && org.canAccessImportExport) ||
- (permissions.indexOf(Permissions.AccessReports) !== -1 && org.canAccessReports) ||
- (permissions.indexOf(Permissions.CreateNewCollections) !== -1 &&
- org.canCreateNewCollections) ||
- (permissions.indexOf(Permissions.EditAnyCollection) !== -1 && org.canEditAnyCollection) ||
- (permissions.indexOf(Permissions.DeleteAnyCollection) !== -1 && org.canDeleteAnyCollection) ||
- (permissions.indexOf(Permissions.EditAssignedCollections) !== -1 &&
- org.canEditAssignedCollections) ||
- (permissions.indexOf(Permissions.DeleteAssignedCollections) !== -1 &&
- org.canDeleteAssignedCollections) ||
- (permissions.indexOf(Permissions.ManageGroups) !== -1 && org.canManageGroups) ||
- (permissions.indexOf(Permissions.ManageOrganization) !== -1 && org.isOwner) ||
- (permissions.indexOf(Permissions.ManagePolicies) !== -1 && org.canManagePolicies) ||
- (permissions.indexOf(Permissions.ManageUsers) !== -1 && org.canManageUsers) ||
- (permissions.indexOf(Permissions.ManageUsersPassword) !== -1 && org.canManageUsersPassword) ||
- (permissions.indexOf(Permissions.ManageSso) !== -1 && org.canManageSso)
- ) {
- return true;
- }
-
- this.router.navigate(["/organizations", org.id]);
- return false;
- }
-}
diff --git a/src/app/services/services.module.ts b/src/app/services/services.module.ts
index c8e95320..cf8e4805 100644
--- a/src/app/services/services.module.ts
+++ b/src/app/services/services.module.ts
@@ -45,11 +45,11 @@ import { PasswordRepromptService } from "../../services/passwordReprompt.service
import { StateService } from "../../services/state.service";
import { StateMigrationService } from "../../services/stateMigration.service";
import { WebPlatformUtilsService } from "../../services/webPlatformUtils.service";
+import { PermissionsGuard as OrgPermissionsGuard } from "../organizations/guards/permissions.guard";
+import { NavigationPermissionsService as OrgPermissionsService } from "../organizations/services/navigation-permissions.service";
import { EventService } from "./event.service";
import { ModalService } from "./modal.service";
-import { OrganizationGuardService } from "./organization-guard.service";
-import { OrganizationTypeGuardService } from "./organization-type-guard.service";
import { PolicyListService } from "./policy-list.service";
import { RouterService } from "./router.service";
@@ -100,6 +100,7 @@ export function initFactory(
imports: [ToastrModule, JslibServicesModule],
declarations: [],
providers: [
+ OrgPermissionsService,
{
provide: APP_INITIALIZER,
useFactory: initFactory,
@@ -117,8 +118,7 @@ export function initFactory(
],
multi: true,
},
- OrganizationGuardService,
- OrganizationTypeGuardService,
+ OrgPermissionsGuard,
RouterService,
EventService,
PolicyListService,
diff --git a/src/locales/en/messages.json b/src/locales/en/messages.json
index 29be1458..c0936f0d 100644
--- a/src/locales/en/messages.json
+++ b/src/locales/en/messages.json
@@ -4833,6 +4833,9 @@
}
}
},
+ "accessDenied": {
+ "message": "Access Denied. You do not have permission to view this page."
+ },
"backToReports": {
"message": "Back to Reports"
},