1
0
mirror of https://github.com/bitwarden/web synced 2025-12-23 11:43:15 +00:00

[End User Vault Refresh] Organizations - updated nav and route permissions (#1551)

* Add Organizations link to navbar

* Update route permissions and guards

* Use NavigationPermissionsService to unify route permissions
This commit is contained in:
Thomas Rittson
2022-03-25 08:02:33 +10:00
committed by GitHub
parent b64404863f
commit cf9df7bf98
18 changed files with 354 additions and 317 deletions

View File

@@ -19,17 +19,17 @@ import { MaximumVaultTimeoutPolicyComponent } from "./policies/maximum-vault-tim
@NgModule({ @NgModule({
imports: [ imports: [
AppRoutingModule,
BrowserAnimationsModule,
DragDropModule,
FormsModule,
InfiniteScrollModule,
JslibModule, JslibModule,
OrganizationsModule, BrowserAnimationsModule,
OssRoutingModule, FormsModule,
ReactiveFormsModule, ReactiveFormsModule,
RouterModule,
ServicesModule, 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 WildcardRoutingModule, // Needs to be last to catch all non-existing routes
], ],
declarations: [ declarations: [

View File

@@ -4,10 +4,10 @@ import { RouterModule, Routes } from "@angular/router";
import { AuthGuard } from "jslib-angular/guards/auth.guard"; import { AuthGuard } from "jslib-angular/guards/auth.guard";
import { Permissions } from "jslib-common/enums/permissions"; 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 { ManageComponent } from "src/app/organizations/manage/manage.component";
import { OrganizationGuardService } from "src/app/services/organization-guard.service"; import { NavigationPermissionsService } from "src/app/organizations/services/navigation-permissions.service";
import { OrganizationTypeGuardService } from "src/app/services/organization-type-guard.service";
import { SsoComponent } from "./manage/sso.component"; import { SsoComponent } from "./manage/sso.component";
@@ -15,24 +15,15 @@ const routes: Routes = [
{ {
path: "organizations/:organizationId", path: "organizations/:organizationId",
component: OrganizationLayoutComponent, component: OrganizationLayoutComponent,
canActivate: [AuthGuard, OrganizationGuardService], canActivate: [AuthGuard, PermissionsGuard],
children: [ children: [
{ {
path: "manage", path: "manage",
component: ManageComponent, component: ManageComponent,
canActivate: [OrganizationTypeGuardService], canActivate: [PermissionsGuard],
data: { data: {
permissions: [ permissions: [
Permissions.CreateNewCollections, NavigationPermissionsService.getPermissions("manage").concat(Permissions.ManageSso),
Permissions.EditAnyCollection,
Permissions.DeleteAnyCollection,
Permissions.EditAssignedCollections,
Permissions.DeleteAssignedCollections,
Permissions.AccessEventLogs,
Permissions.ManageGroups,
Permissions.ManageUsers,
Permissions.ManagePolicies,
Permissions.ManageSso,
], ],
}, },
children: [ children: [

View File

@@ -5,7 +5,7 @@ import { ProviderService } from "jslib-common/abstractions/provider.service";
import { Permissions } from "jslib-common/enums/permissions"; import { Permissions } from "jslib-common/enums/permissions";
@Injectable() @Injectable()
export class ProviderTypeGuardService implements CanActivate { export class PermissionsGuard implements CanActivate {
constructor(private providerService: ProviderService, private router: Router) {} constructor(private providerService: ProviderService, private router: Router) {}
async canActivate(route: ActivatedRouteSnapshot) { async canActivate(route: ActivatedRouteSnapshot) {

View File

@@ -6,7 +6,7 @@ import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.se
import { ProviderService } from "jslib-common/abstractions/provider.service"; import { ProviderService } from "jslib-common/abstractions/provider.service";
@Injectable() @Injectable()
export class ProviderGuardService implements CanActivate { export class ProviderGuard implements CanActivate {
constructor( constructor(
private router: Router, private router: Router,
private platformUtilsService: PlatformUtilsService, private platformUtilsService: PlatformUtilsService,

View File

@@ -9,13 +9,13 @@ import { ProvidersComponent } from "src/app/providers/providers.component";
import { ClientsComponent } from "./clients/clients.component"; import { ClientsComponent } from "./clients/clients.component";
import { CreateOrganizationComponent } from "./clients/create-organization.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 { AcceptProviderComponent } from "./manage/accept-provider.component";
import { EventsComponent } from "./manage/events.component"; import { EventsComponent } from "./manage/events.component";
import { ManageComponent } from "./manage/manage.component"; import { ManageComponent } from "./manage/manage.component";
import { PeopleComponent } from "./manage/people.component"; import { PeopleComponent } from "./manage/people.component";
import { ProvidersLayoutComponent } from "./providers-layout.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 { AccountComponent } from "./settings/account.component";
import { SettingsComponent } from "./settings/settings.component"; import { SettingsComponent } from "./settings/settings.component";
import { SetupProviderComponent } from "./setup/setup-provider.component"; import { SetupProviderComponent } from "./setup/setup-provider.component";
@@ -54,7 +54,7 @@ const routes: Routes = [
{ {
path: ":providerId", path: ":providerId",
component: ProvidersLayoutComponent, component: ProvidersLayoutComponent,
canActivate: [ProviderGuardService], canActivate: [ProviderGuard],
children: [ children: [
{ path: "", pathMatch: "full", redirectTo: "clients" }, { path: "", pathMatch: "full", redirectTo: "clients" },
{ path: "clients/create", component: CreateOrganizationComponent }, { path: "clients/create", component: CreateOrganizationComponent },
@@ -71,7 +71,7 @@ const routes: Routes = [
{ {
path: "people", path: "people",
component: PeopleComponent, component: PeopleComponent,
canActivate: [ProviderTypeGuardService], canActivate: [PermissionsGuard],
data: { data: {
titleId: "people", titleId: "people",
permissions: [Permissions.ManageUsers], permissions: [Permissions.ManageUsers],
@@ -80,7 +80,7 @@ const routes: Routes = [
{ {
path: "events", path: "events",
component: EventsComponent, component: EventsComponent,
canActivate: [ProviderTypeGuardService], canActivate: [PermissionsGuard],
data: { data: {
titleId: "eventLogs", titleId: "eventLogs",
permissions: [Permissions.AccessEventLogs], permissions: [Permissions.AccessEventLogs],
@@ -100,7 +100,7 @@ const routes: Routes = [
{ {
path: "account", path: "account",
component: AccountComponent, component: AccountComponent,
canActivate: [ProviderTypeGuardService], canActivate: [PermissionsGuard],
data: { data: {
titleId: "myProvider", titleId: "myProvider",
permissions: [Permissions.ManageProvider], permissions: [Permissions.ManageProvider],

View File

@@ -10,6 +10,8 @@ import { OssModule } from "src/app/oss.module";
import { AddOrganizationComponent } from "./clients/add-organization.component"; import { AddOrganizationComponent } from "./clients/add-organization.component";
import { ClientsComponent } from "./clients/clients.component"; import { ClientsComponent } from "./clients/clients.component";
import { CreateOrganizationComponent } from "./clients/create-organization.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 { AcceptProviderComponent } from "./manage/accept-provider.component";
import { BulkConfirmComponent } from "./manage/bulk/bulk-confirm.component"; import { BulkConfirmComponent } from "./manage/bulk/bulk-confirm.component";
import { BulkRemoveComponent } from "./manage/bulk/bulk-remove.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 { UserAddEditComponent } from "./manage/user-add-edit.component";
import { ProvidersLayoutComponent } from "./providers-layout.component"; import { ProvidersLayoutComponent } from "./providers-layout.component";
import { ProvidersRoutingModule } from "./providers-routing.module"; 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 { WebProviderService } from "./services/webProvider.service";
import { AccountComponent } from "./settings/account.component"; import { AccountComponent } from "./settings/account.component";
import { SettingsComponent } from "./settings/settings.component"; import { SettingsComponent } from "./settings/settings.component";
@@ -46,7 +46,7 @@ import { SetupComponent } from "./setup/setup.component";
SetupProviderComponent, SetupProviderComponent,
UserAddEditComponent, UserAddEditComponent,
], ],
providers: [WebProviderService, ProviderGuardService, ProviderTypeGuardService], providers: [WebProviderService, ProviderGuard, PermissionsGuard],
}) })
export class ProvidersModule { export class ProvidersModule {
constructor(modalService: ModalService, componentFactoryResolver: ComponentFactoryResolver) { constructor(modalService: ModalService, componentFactoryResolver: ComponentFactoryResolver) {

View File

@@ -27,6 +27,11 @@
<li class="nav-item" routerLinkActive="active"> <li class="nav-item" routerLinkActive="active">
<a class="nav-link" routerLink="/reports">{{ "reports" | i18n }}</a> <a class="nav-link" routerLink="/reports">{{ "reports" | i18n }}</a>
</li> </li>
<li *ngIf="organizations.length >= 1" class="nav-item" routerLinkActive="active">
<a class="nav-link" [routerLink]="['/organizations', organizations[0].id]">{{
"organizations" | i18n
}}</a>
</li>
</ul> </ul>
</div> </div>
<ul class="navbar-nav flex-row ml-md-auto d-none d-md-flex"> <ul class="navbar-nav flex-row ml-md-auto d-none d-md-flex">

View File

@@ -1,12 +1,18 @@
import { Component, OnInit } from "@angular/core"; import { Component, OnInit } from "@angular/core";
import { I18nService } from "jslib-common/abstractions/i18n.service";
import { MessagingService } from "jslib-common/abstractions/messaging.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 { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { ProviderService } from "jslib-common/abstractions/provider.service"; import { ProviderService } from "jslib-common/abstractions/provider.service";
import { SyncService } from "jslib-common/abstractions/sync.service"; import { SyncService } from "jslib-common/abstractions/sync.service";
import { TokenService } from "jslib-common/abstractions/token.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 { Provider } from "jslib-common/models/domain/provider";
import { NavigationPermissionsService as OrgNavigationPermissionsService } from "../organizations/services/navigation-permissions.service";
@Component({ @Component({
selector: "app-navbar", selector: "app-navbar",
templateUrl: "navbar.component.html", templateUrl: "navbar.component.html",
@@ -16,13 +22,16 @@ export class NavbarComponent implements OnInit {
name: string; name: string;
email: string; email: string;
providers: Provider[] = []; providers: Provider[] = [];
organizations: Organization[] = [];
constructor( constructor(
private messagingService: MessagingService, private messagingService: MessagingService,
private platformUtilsService: PlatformUtilsService, private platformUtilsService: PlatformUtilsService,
private tokenService: TokenService, private tokenService: TokenService,
private providerService: ProviderService, private providerService: ProviderService,
private syncService: SyncService private syncService: SyncService,
private organizationService: OrganizationService,
private i18nService: I18nService
) { ) {
this.selfHosted = this.platformUtilsService.isSelfHost(); this.selfHosted = this.platformUtilsService.isSelfHost();
} }
@@ -34,11 +43,16 @@ export class NavbarComponent implements OnInit {
this.name = this.email; this.name = this.email;
} }
// Ensure provides are loaded // Ensure providers and organizations are loaded
if ((await this.syncService.getLastSync()) == null) { if ((await this.syncService.getLastSync()) == null) {
await this.syncService.fullSync(false); await this.syncService.fullSync(false);
} }
this.providers = await this.providerService.getAll(); 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() { lock() {

View File

@@ -1,33 +1,42 @@
import { Injectable } from "@angular/core"; import { Injectable } from "@angular/core";
import { ActivatedRouteSnapshot, CanActivate, Router } from "@angular/router"; import { ActivatedRouteSnapshot, CanActivate, Router } from "@angular/router";
import { BaseGuard } from "jslib-angular/guards/base.guard";
import { I18nService } from "jslib-common/abstractions/i18n.service"; import { I18nService } from "jslib-common/abstractions/i18n.service";
import { OrganizationService } from "jslib-common/abstractions/organization.service"; import { OrganizationService } from "jslib-common/abstractions/organization.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { Permissions } from "jslib-common/enums/permissions";
@Injectable() @Injectable()
export class OrganizationGuardService implements CanActivate { export class PermissionsGuard extends BaseGuard implements CanActivate {
constructor( constructor(
private router: Router, router: Router,
private organizationService: OrganizationService,
private platformUtilsService: PlatformUtilsService, private platformUtilsService: PlatformUtilsService,
private i18nService: I18nService, private i18nService: I18nService
private organizationService: OrganizationService ) {
) {} super(router);
}
async canActivate(route: ActivatedRouteSnapshot) { async canActivate(route: ActivatedRouteSnapshot) {
const org = await this.organizationService.get(route.params.organizationId); const org = await this.organizationService.get(route.params.organizationId);
if (org == null) { if (org == null) {
this.router.navigate(["/"]); return this.redirect();
return false;
} }
if (!org.isOwner && !org.enabled) { if (!org.isOwner && !org.enabled) {
this.platformUtilsService.showToast( this.platformUtilsService.showToast(
"error", "error",
null, null,
this.i18nService.t("organizationIsDisabled") this.i18nService.t("organizationIsDisabled")
); );
this.router.navigate(["/"]); return this.redirect();
return false; }
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; return true;

View File

@@ -1,4 +1,3 @@
<app-navbar></app-navbar>
<div class="org-nav" *ngIf="organization"> <div class="org-nav" *ngIf="organization">
<div class="container d-flex"> <div class="container d-flex">
<div class="d-flex flex-column"> <div class="d-flex flex-column">
@@ -27,7 +26,7 @@
</div> </div>
</div> </div>
</div> </div>
<ul class="nav nav-tabs" *ngIf="showMenuBar"> <ul class="nav nav-tabs">
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" routerLink="vault" routerLinkActive="active"> <a class="nav-link" routerLink="vault" routerLinkActive="active">
<i class="bwi bwi-lock" aria-hidden="true"></i> <i class="bwi bwi-lock" aria-hidden="true"></i>
@@ -46,7 +45,7 @@
{{ "tools" | i18n }} {{ "tools" | i18n }}
</a> </a>
</li> </li>
<li class="nav-item" *ngIf="organization.isOwner"> <li class="nav-item" *ngIf="showSettingsTab">
<a class="nav-link" routerLink="settings" routerLinkActive="active"> <a class="nav-link" routerLink="settings" routerLinkActive="active">
<i class="bwi bwi-cogs" aria-hidden="true"></i> <i class="bwi bwi-cogs" aria-hidden="true"></i>
{{ "settings" | i18n }} {{ "settings" | i18n }}
@@ -57,4 +56,3 @@
</div> </div>
</div> </div>
<router-outlet></router-outlet> <router-outlet></router-outlet>
<app-footer></app-footer>

View File

@@ -5,6 +5,8 @@ import { BroadcasterService } from "jslib-common/abstractions/broadcaster.servic
import { OrganizationService } from "jslib-common/abstractions/organization.service"; import { OrganizationService } from "jslib-common/abstractions/organization.service";
import { Organization } from "jslib-common/models/domain/organization"; import { Organization } from "jslib-common/models/domain/organization";
import { NavigationPermissionsService } from "../services/navigation-permissions.service";
const BroadcasterSubscriptionId = "OrganizationLayoutComponent"; const BroadcasterSubscriptionId = "OrganizationLayoutComponent";
@Component({ @Component({
@@ -25,7 +27,7 @@ export class OrganizationLayoutComponent implements OnInit, OnDestroy {
ngOnInit() { ngOnInit() {
document.body.classList.remove("layout_frontend"); document.body.classList.remove("layout_frontend");
this.route.params.subscribe(async (params) => { this.route.params.subscribe(async (params: any) => {
this.organizationId = params.organizationId; this.organizationId = params.organizationId;
await this.load(); await this.load();
}); });
@@ -48,23 +50,16 @@ export class OrganizationLayoutComponent implements OnInit, OnDestroy {
this.organization = await this.organizationService.get(this.organizationId); this.organization = await this.organizationService.get(this.organizationId);
} }
get showMenuBar() {
return this.showManageTab || this.showToolsTab || this.organization.isOwner;
}
get showManageTab(): boolean { get showManageTab(): boolean {
return ( return NavigationPermissionsService.canAccessManage(this.organization);
this.organization.canManageUsers ||
this.organization.canViewAllCollections ||
this.organization.canViewAssignedCollections ||
this.organization.canManageGroups ||
this.organization.canManagePolicies ||
this.organization.canAccessEventLogs
);
} }
get showToolsTab(): boolean { 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 { get toolsRoute(): string {

View File

@@ -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 {}

View File

@@ -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"));
}
}

View File

@@ -4,7 +4,6 @@ import { RouterModule, Routes } from "@angular/router";
import { AuthGuard } from "jslib-angular/guards/auth.guard"; import { AuthGuard } from "jslib-angular/guards/auth.guard";
import { LockGuard } from "jslib-angular/guards/lock.guard"; import { LockGuard } from "jslib-angular/guards/lock.guard";
import { UnauthGuard } from "jslib-angular/guards/unauth.guard"; import { UnauthGuard } from "jslib-angular/guards/unauth.guard";
import { Permissions } from "jslib-common/enums/permissions";
import { AcceptEmergencyComponent } from "./accounts/accept-emergency.component"; import { AcceptEmergencyComponent } from "./accounts/accept-emergency.component";
import { AcceptOrganizationComponent } from "./accounts/accept-organization.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 { VerifyEmailTokenComponent } from "./accounts/verify-email-token.component";
import { VerifyRecoverDeleteComponent } from "./accounts/verify-recover-delete.component"; import { VerifyRecoverDeleteComponent } from "./accounts/verify-recover-delete.component";
import { FrontendLayoutComponent } from "./layouts/frontend-layout.component"; import { FrontendLayoutComponent } from "./layouts/frontend-layout.component";
import { OrganizationLayoutComponent } from "./layouts/organization-layout.component";
import { UserLayoutComponent } from "./layouts/user-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 { 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 { AccessComponent } from "./send/access.component";
import { SendComponent } from "./send/send.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 { AccountComponent } from "./settings/account.component";
import { CreateOrganizationComponent } from "./settings/create-organization.component"; import { CreateOrganizationComponent } from "./settings/create-organization.component";
import { DomainRulesComponent } from "./settings/domain-rules.component"; import { DomainRulesComponent } from "./settings/domain-rules.component";
@@ -245,191 +221,12 @@ const routes: Routes = [
loadChildren: async () => (await import("./reports/reports.module")).ReportsModule, loadChildren: async () => (await import("./reports/reports.module")).ReportsModule,
}, },
{ path: "setup/families-for-enterprise", component: FamiliesForEnterpriseSetupComponent }, { 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", path: "organizations",
component: OrgToolsComponent, loadChildren: () =>
canActivate: [OrganizationTypeGuardService], import("./organizations/organization-routing.module").then(
data: { permissions: [Permissions.AccessImportExport, Permissions.AccessReports] }, (m) => m.OrganizationsRoutingModule
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" },
},
],
}, },
], ],
}, },
@@ -440,7 +237,7 @@ const routes: Routes = [
RouterModule.forRoot(routes, { RouterModule.forRoot(routes, {
useHash: true, useHash: true,
paramsInheritanceStrategy: "always", paramsInheritanceStrategy: "always",
/*enableTracing: true,*/ // enableTracing: true,
}), }),
], ],
exports: [RouterModule], exports: [RouterModule],

View File

@@ -83,8 +83,8 @@ import { PremiumBadgeComponent } from "./components/premium-badge.component";
import { FooterComponent } from "./layouts/footer.component"; import { FooterComponent } from "./layouts/footer.component";
import { FrontendLayoutComponent } from "./layouts/frontend-layout.component"; import { FrontendLayoutComponent } from "./layouts/frontend-layout.component";
import { NavbarComponent } from "./layouts/navbar.component"; import { NavbarComponent } from "./layouts/navbar.component";
import { OrganizationLayoutComponent } from "./layouts/organization-layout.component";
import { UserLayoutComponent } from "./layouts/user-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 { BulkConfirmComponent as OrgBulkConfirmComponent } from "./organizations/manage/bulk/bulk-confirm.component";
import { BulkRemoveComponent as OrgBulkRemoveComponent } from "./organizations/manage/bulk/bulk-remove.component"; import { BulkRemoveComponent as OrgBulkRemoveComponent } from "./organizations/manage/bulk/bulk-remove.component";
import { BulkStatusComponent as OrgBulkStatusComponent } from "./organizations/manage/bulk/bulk-status.component"; import { BulkStatusComponent as OrgBulkStatusComponent } from "./organizations/manage/bulk/bulk-status.component";

View File

@@ -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;
}
}

View File

@@ -45,11 +45,11 @@ import { PasswordRepromptService } from "../../services/passwordReprompt.service
import { StateService } from "../../services/state.service"; import { StateService } from "../../services/state.service";
import { StateMigrationService } from "../../services/stateMigration.service"; import { StateMigrationService } from "../../services/stateMigration.service";
import { WebPlatformUtilsService } from "../../services/webPlatformUtils.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 { EventService } from "./event.service";
import { ModalService } from "./modal.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 { PolicyListService } from "./policy-list.service";
import { RouterService } from "./router.service"; import { RouterService } from "./router.service";
@@ -100,6 +100,7 @@ export function initFactory(
imports: [ToastrModule, JslibServicesModule], imports: [ToastrModule, JslibServicesModule],
declarations: [], declarations: [],
providers: [ providers: [
OrgPermissionsService,
{ {
provide: APP_INITIALIZER, provide: APP_INITIALIZER,
useFactory: initFactory, useFactory: initFactory,
@@ -117,8 +118,7 @@ export function initFactory(
], ],
multi: true, multi: true,
}, },
OrganizationGuardService, OrgPermissionsGuard,
OrganizationTypeGuardService,
RouterService, RouterService,
EventService, EventService,
PolicyListService, PolicyListService,

View File

@@ -4833,6 +4833,9 @@
} }
} }
}, },
"accessDenied": {
"message": "Access Denied. You do not have permission to view this page."
},
"backToReports": { "backToReports": {
"message": "Back to Reports" "message": "Back to Reports"
}, },