1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-06-25 10:25:36 +02:00

[EC-678] [EC-673] Fix active tab not showing selected while in child route (#3964)

* [PS-1114] hide reporting sidebar if only events

* [PS-1114] add orgRedirectGuard

* [PS-1114] highlight tabs based on route subset

* [PS-1114] redirect to correct child route on tab
- Use new OrgRedirectGuard

* [PS-1114] add settings redirect using guard
- refactored guard to accept array of strings

* [EC-678] [EC-673] remove remaining methods

* [EC-678][EC-673] address PR feedback
- change switch to if statements
- remove ternary
This commit is contained in:
Jake Fink 2022-11-07 09:21:16 -05:00 committed by GitHub
parent 4afe0f5d89
commit 6f4771da6c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 129 additions and 33 deletions

View File

@ -0,0 +1,32 @@
import { Injectable } from "@angular/core";
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from "@angular/router";
import {
canAccessOrgAdmin,
OrganizationService,
} from "@bitwarden/common/abstractions/organization/organization.service.abstraction";
@Injectable({
providedIn: "root",
})
export class OrganizationRedirectGuard implements CanActivate {
constructor(private router: Router, private organizationService: OrganizationService) {}
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
const org = this.organizationService.get(route.params.organizationId);
const customRedirect = route.data?.autoRedirectCallback;
if (customRedirect) {
let redirectPath = customRedirect(org);
if (typeof redirectPath === "string") {
redirectPath = [redirectPath];
}
return this.router.createUrlTree([state.url, ...redirectPath]);
}
if (canAccessOrgAdmin(org)) {
return this.router.createUrlTree(["/organizations", org.id]);
}
return this.router.createUrlTree(["/"]);
}
}

View File

@ -8,13 +8,10 @@
></app-organization-switcher>
<bit-tab-nav-bar class="-tw-mb-px">
<bit-tab-link route="vault">{{ "vault" | i18n }}</bit-tab-link>
<bit-tab-link *ngIf="canShowManageTab(organization)" [route]="getManageRoute(organization)">
<bit-tab-link *ngIf="canShowManageTab(organization)" route="manage">
{{ "manage" | i18n }}
</bit-tab-link>
<bit-tab-link
*ngIf="canShowReportsTab(organization)"
[route]="getReportRoute(organization)"
>
<bit-tab-link *ngIf="canShowReportsTab(organization)" route="reporting">
{{ getReportTabLabel(organization) | i18n }}
</bit-tab-link>
<bit-tab-link *ngIf="canShowBillingTab(organization)" route="billing">{{

View File

@ -72,24 +72,4 @@ export class OrganizationLayoutComponent implements OnInit, OnDestroy {
getReportTabLabel(organization: Organization): string {
return organization.useEvents ? "reporting" : "reports";
}
getReportRoute(organization: Organization): string {
return organization.useEvents ? "reporting/events" : "reporting/reports";
}
getManageRoute(organization: Organization): string {
let route: string;
switch (true) {
case organization.canManageUsers:
route = "manage/members";
break;
case organization.canViewAssignedCollections || organization.canViewAllCollections:
route = "manage/collections";
break;
case organization.canManageGroups:
route = "manage/groups";
break;
}
return route;
}
}

View File

@ -9,8 +9,10 @@ import {
canAccessOrgAdmin,
canManageCollections,
} from "@bitwarden/common/abstractions/organization/organization.service.abstraction";
import { Organization } from "@bitwarden/common/models/domain/organization";
import { OrganizationPermissionsGuard } from "./guards/org-permissions.guard";
import { OrganizationRedirectGuard } from "./guards/org-redirect.guard";
import { OrganizationLayoutComponent } from "./layouts/organization-layout.component";
import { CollectionsComponent } from "./manage/collections.component";
import { GroupsComponent } from "./manage/groups.component";
@ -47,7 +49,11 @@ const routes: Routes = [
{
path: "",
pathMatch: "full",
redirectTo: "members",
canActivate: [OrganizationRedirectGuard],
data: {
autoRedirectCallback: getManageRoute,
},
children: [], // This is required to make the auto redirect work
},
{
path: "collections",
@ -94,6 +100,19 @@ const routes: Routes = [
},
];
function getManageRoute(organization: Organization): string {
if (organization.canManageUsers) {
return "members";
}
if (organization.canViewAssignedCollections || organization.canViewAllCollections) {
return "collections";
}
if (organization.canManageGroups) {
return "groups";
}
return undefined;
}
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],

View File

@ -5,6 +5,7 @@ import { canAccessReportingTab } from "@bitwarden/common/abstractions/organizati
import { Organization } from "@bitwarden/common/models/domain/organization";
import { OrganizationPermissionsGuard } from "../guards/org-permissions.guard";
import { OrganizationRedirectGuard } from "../guards/org-redirect.guard";
import { EventsComponent } from "../manage/events.component";
import { ExposedPasswordsReportComponent } from "../tools/exposed-passwords-report.component";
import { InactiveTwoFactorReportComponent } from "../tools/inactive-two-factor-report.component";
@ -22,7 +23,15 @@ const routes: Routes = [
canActivate: [OrganizationPermissionsGuard],
data: { organizationPermissions: canAccessReportingTab },
children: [
{ path: "", pathMatch: "full", redirectTo: "reports" },
{
path: "",
pathMatch: "full",
canActivate: [OrganizationRedirectGuard],
data: {
autoRedirectCallback: getReportRoute,
},
children: [], // This is required to make the auto redirect work,
},
{
path: "reports",
component: ReportsHomeComponent,
@ -80,6 +89,17 @@ const routes: Routes = [
],
},
];
function getReportRoute(organization: Organization): string {
if (organization.canAccessEventLogs) {
return "events";
}
if (organization.canAccessReports) {
return "reports";
}
return undefined;
}
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],

View File

@ -22,7 +22,8 @@ export class ReportingComponent implements OnInit, OnDestroy {
.pipe(
concatMap(async (params) => {
this.organization = await this.organizationService.get(params.organizationId);
this.showLeftNav = this.organization.canAccessEventLogs;
this.showLeftNav =
this.organization.canAccessEventLogs && this.organization.canAccessReports;
}),
takeUntil(this.destroy$)
)

View File

@ -5,6 +5,7 @@ import { canAccessSettingsTab } from "@bitwarden/common/abstractions/organizatio
import { Organization } from "@bitwarden/common/models/domain/organization";
import { OrganizationPermissionsGuard } from "../guards/org-permissions.guard";
import { OrganizationRedirectGuard } from "../guards/org-redirect.guard";
import { PoliciesComponent } from "../policies";
import { AccountComponent } from "./account.component";
@ -18,7 +19,15 @@ const routes: Routes = [
canActivate: [OrganizationPermissionsGuard],
data: { organizationPermissions: canAccessSettingsTab },
children: [
{ path: "", pathMatch: "full", redirectTo: "account" },
{
path: "",
pathMatch: "full",
canActivate: [OrganizationRedirectGuard],
data: {
autoRedirectCallback: getSettingsRoute,
},
children: [], // This is required to make the auto redirect work,
},
{ path: "account", component: AccountComponent, data: { titleId: "organizationInfo" } },
{
path: "two-factor",
@ -45,6 +54,25 @@ const routes: Routes = [
},
];
function getSettingsRoute(organization: Organization) {
if (organization.isOwner) {
return "account";
}
if (organization.canManagePolicies) {
return "policies";
}
if (organization.canAccessImportExport) {
return ["tools", "import"];
}
if (organization.canManageSso) {
return "sso";
}
if (organization.canManageScim) {
return "scim";
}
return undefined;
}
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],

View File

@ -4,7 +4,12 @@
<div class="card">
<div class="card-header">{{ "settings" | i18n }}</div>
<div class="list-group list-group-flush">
<a routerLink="account" class="list-group-item" routerLinkActive="active">
<a
routerLink="account"
class="list-group-item"
routerLinkActive="active"
*ngIf="organization?.isOwner"
>
{{ "organizationInfo" | i18n }}
</a>
<a
@ -19,7 +24,7 @@
routerLink="two-factor"
class="list-group-item"
routerLinkActive="active"
*ngIf="organization?.use2fa"
*ngIf="organization?.use2fa && organization?.isOwner"
>
{{ "twoStepLogin" | i18n }}
</a>

View File

@ -9,7 +9,13 @@ export function canAccessVaultTab(org: Organization): boolean {
}
export function canAccessSettingsTab(org: Organization): boolean {
return org.isOwner;
return (
org.isOwner ||
org.canManagePolicies ||
org.canManageSso ||
org.canManageScim ||
org.canAccessImportExport
);
}
export function canAccessMembersTab(org: Organization): boolean {

View File

@ -2,6 +2,7 @@
bitTabListItem
[routerLink]="disabled ? null : route"
routerLinkActive
[routerLinkActiveOptions]="routerLinkMatchOptions"
#rla="routerLinkActive"
[active]="rla.isActive"
[disabled]="disabled"

View File

@ -1,6 +1,6 @@
import { FocusableOption } from "@angular/cdk/a11y";
import { AfterViewInit, Component, HostListener, Input, OnDestroy, ViewChild } from "@angular/core";
import { RouterLinkActive } from "@angular/router";
import { IsActiveMatchOptions, RouterLinkActive } from "@angular/router";
import { Subject, takeUntil } from "rxjs";
import { TabListItemDirective } from "../shared/tab-list-item.directive";
@ -17,6 +17,13 @@ export class TabLinkComponent implements FocusableOption, AfterViewInit, OnDestr
@ViewChild(TabListItemDirective) tabItem: TabListItemDirective;
@ViewChild("rla") routerLinkActive: RouterLinkActive;
readonly routerLinkMatchOptions: IsActiveMatchOptions = {
queryParams: "ignored",
matrixParams: "ignored",
paths: "subset",
fragment: "ignored",
};
@Input() route: string;
@Input() disabled = false;