mirror of
https://github.com/bitwarden/browser.git
synced 2024-11-09 09:51:02 +01:00
[AC-2509] add member access component (#9411)
* Added new report card and FeatureFlag for MemberAccessReport * Add new "isEnterpriseOrgGuard" * Add member access icon * Show upgrade organization dialog for enterprise on member access report click * verify member access featureflag on enterprise org guard * add comment with TODO information for follow up task * Initial member access report component * Improved readability, removed path to wrong component and refactored buildReports to use the productType * finished MemberAccessReport layout and added temporary service to provide mock data * Moved member-access-report files to bitwarden_license/ Removed unnecessary files * Added new tools path on bitwarden_license to the CODEOWNERS file * added member access description to the messages.json * layout changes to member access report * Created new reports-routing under bitwarden_license Moved member-access-report files to corresponding subfolder * Added search logic * Removed routing from member-access-report BL component on OSS. Added member-access-report navigation to organizations-routing on BL * removed unnecessary ng-container * Added OrganizationPermissionsGuard and canAccessReports validation to member-access-report navigation * replaced deprecated search code with searchControl * Address PR feedback * removed unnecessary canAccessReports method
This commit is contained in:
parent
dd40faf72e
commit
0e2c486a38
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
@ -29,6 +29,7 @@ libs/common/src/models/export @bitwarden/team-tools-dev
|
|||||||
libs/common/src/tools @bitwarden/team-tools-dev
|
libs/common/src/tools @bitwarden/team-tools-dev
|
||||||
libs/importer @bitwarden/team-tools-dev
|
libs/importer @bitwarden/team-tools-dev
|
||||||
libs/tools @bitwarden/team-tools-dev
|
libs/tools @bitwarden/team-tools-dev
|
||||||
|
bitwarden_license/bit-web/src/app/tools @bitwarden/team-tools-dev
|
||||||
|
|
||||||
## Localization/Crowdin (Tools team)
|
## Localization/Crowdin (Tools team)
|
||||||
apps/browser/src/_locales @bitwarden/team-tools-dev
|
apps/browser/src/_locales @bitwarden/team-tools-dev
|
||||||
|
@ -8445,6 +8445,9 @@
|
|||||||
"memberAccessReportDesc": {
|
"memberAccessReportDesc": {
|
||||||
"message": "Ensure members have access to the right credentials and their accounts are secure. Use this report to obtain a CSV of member access and account configurations."
|
"message": "Ensure members have access to the right credentials and their accounts are secure. Use this report to obtain a CSV of member access and account configurations."
|
||||||
},
|
},
|
||||||
|
"memberAccessReportPageDesc": {
|
||||||
|
"message": "Audit organization member access across groups, collections, and collection items. The CSV export provides a detailed breakdown per member, including information on collection permissions and account configurations."
|
||||||
|
},
|
||||||
"higherKDFIterations": {
|
"higherKDFIterations": {
|
||||||
"message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker."
|
"message": "Higher KDF iterations can help protect your master password from being brute forced by an attacker."
|
||||||
},
|
},
|
||||||
|
@ -3,6 +3,7 @@ import { RouterModule, Routes } from "@angular/router";
|
|||||||
|
|
||||||
import { AuthGuard } from "@bitwarden/angular/auth/guards";
|
import { AuthGuard } from "@bitwarden/angular/auth/guards";
|
||||||
import { canAccessSettingsTab } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
import { canAccessSettingsTab } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||||
|
import { IsEnterpriseOrgGuard } from "@bitwarden/web-vault/app/admin-console/organizations/guards/is-enterprise-org.guard";
|
||||||
import { organizationPermissionsGuard } from "@bitwarden/web-vault/app/admin-console/organizations/guards/org-permissions.guard";
|
import { organizationPermissionsGuard } from "@bitwarden/web-vault/app/admin-console/organizations/guards/org-permissions.guard";
|
||||||
import { OrganizationLayoutComponent } from "@bitwarden/web-vault/app/admin-console/organizations/layouts/organization-layout.component";
|
import { OrganizationLayoutComponent } from "@bitwarden/web-vault/app/admin-console/organizations/layouts/organization-layout.component";
|
||||||
|
|
||||||
@ -58,6 +59,23 @@ const routes: Routes = [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "reporting/reports",
|
||||||
|
canActivate: [AuthGuard, organizationPermissionsGuard((org) => org.canAccessReports)],
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "member-access-report",
|
||||||
|
loadComponent: () =>
|
||||||
|
import(
|
||||||
|
"../../tools/reports/member-access-report/member-access-report.component"
|
||||||
|
).then((mod) => mod.MemberAccessReportComponent),
|
||||||
|
data: {
|
||||||
|
titleId: "memberAccessReport",
|
||||||
|
},
|
||||||
|
canActivate: [IsEnterpriseOrgGuard],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@ -0,0 +1,50 @@
|
|||||||
|
<app-header>
|
||||||
|
<bit-search
|
||||||
|
[formControl]="searchControl"
|
||||||
|
[placeholder]="'searchMembers' | i18n"
|
||||||
|
class="tw-grow"
|
||||||
|
></bit-search>
|
||||||
|
|
||||||
|
<button type="button" bitButton buttonType="primary">
|
||||||
|
<span>{{ "export" | i18n }}</span>
|
||||||
|
<i class="bwi bwi-fw bwi-sign-in" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
</app-header>
|
||||||
|
|
||||||
|
<div class="tw-max-w-4xl">
|
||||||
|
<p bitTypography="body1">
|
||||||
|
{{ "memberAccessReportPageDesc" | i18n }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<bit-table [dataSource]="dataSource" class="tw-mt-2">
|
||||||
|
<ng-container header>
|
||||||
|
<tr>
|
||||||
|
<th bitCell bitSortable="name" default>Members</th>
|
||||||
|
<th bitCell bitSortable="groups">Groups</th>
|
||||||
|
<th bitCell bitSortable="collections">Collections</th>
|
||||||
|
<th bitCell bitSortable="items">Items</th>
|
||||||
|
</tr>
|
||||||
|
</ng-container>
|
||||||
|
<ng-template body let-rows$>
|
||||||
|
<tr bitRow *ngFor="let r of rows$ | async">
|
||||||
|
<td bitCell>
|
||||||
|
<div class="tw-flex tw-items-center">
|
||||||
|
<bit-avatar size="small" [text]="r.name" class="tw-mr-3"></bit-avatar>
|
||||||
|
<div class="tw-flex tw-flex-col">
|
||||||
|
<button type="button" bitLink>
|
||||||
|
{{ r.name }}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="tw-text-sm tw-mt-1 tw-text-muted">
|
||||||
|
{{ r.email }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td bitCell class="tw-text-muted tw-w-[278px] tw-p-4">{{ r.groups }}</td>
|
||||||
|
<td bitCell class="tw-text-muted tw-w-[278px] tw-p-4">{{ r.collections }}</td>
|
||||||
|
<td bitCell class="tw-text-muted tw-w-[278px] tw-p-4">{{ r.items }}</td>
|
||||||
|
</tr>
|
||||||
|
</ng-template>
|
||||||
|
</bit-table>
|
@ -0,0 +1,33 @@
|
|||||||
|
import { Component, OnInit } from "@angular/core";
|
||||||
|
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
||||||
|
import { FormControl } from "@angular/forms";
|
||||||
|
import { debounceTime } from "rxjs";
|
||||||
|
|
||||||
|
import { SearchModule, TableDataSource } from "@bitwarden/components";
|
||||||
|
import { HeaderModule } from "@bitwarden/web-vault/app/layouts/header/header.module";
|
||||||
|
import { SharedModule } from "@bitwarden/web-vault/app/shared";
|
||||||
|
|
||||||
|
import { MemberAccessReportService } from "./member-access-report.service";
|
||||||
|
import { MemberAccessReportView } from "./view/member-access-report.view";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: "member-access-report",
|
||||||
|
templateUrl: "member-access-report.component.html",
|
||||||
|
imports: [SharedModule, SearchModule, HeaderModule],
|
||||||
|
standalone: true,
|
||||||
|
})
|
||||||
|
export class MemberAccessReportComponent implements OnInit {
|
||||||
|
protected dataSource = new TableDataSource<MemberAccessReportView>();
|
||||||
|
protected searchControl = new FormControl("", { nonNullable: true });
|
||||||
|
|
||||||
|
constructor(protected reportService: MemberAccessReportService) {
|
||||||
|
// Connect the search input to the table dataSource filter input
|
||||||
|
this.searchControl.valueChanges
|
||||||
|
.pipe(debounceTime(200), takeUntilDestroyed())
|
||||||
|
.subscribe((v) => (this.dataSource.filter = v));
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.dataSource.data = this.reportService.getMemberAccessMockData();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
|
||||||
|
import { MemberAccessReportView } from "./view/member-access-report.view";
|
||||||
|
|
||||||
|
@Injectable({ providedIn: "root" })
|
||||||
|
export class MemberAccessReportService {
|
||||||
|
//Temporary method to provide mock data for test purposes only
|
||||||
|
getMemberAccessMockData(): MemberAccessReportView[] {
|
||||||
|
const memberAccess = new MemberAccessReportView();
|
||||||
|
memberAccess.email = "sjohnson@email.com";
|
||||||
|
memberAccess.name = "Sarah Johnson";
|
||||||
|
memberAccess.groups = 3;
|
||||||
|
memberAccess.collections = 12;
|
||||||
|
memberAccess.items = 3;
|
||||||
|
|
||||||
|
const memberAccess2 = new MemberAccessReportView();
|
||||||
|
memberAccess2.email = "jlull@email.com";
|
||||||
|
memberAccess2.name = "James Lull";
|
||||||
|
memberAccess2.groups = 2;
|
||||||
|
memberAccess2.collections = 24;
|
||||||
|
memberAccess2.items = 2;
|
||||||
|
|
||||||
|
const memberAccess3 = new MemberAccessReportView();
|
||||||
|
memberAccess3.email = "bwilliams@email.com";
|
||||||
|
memberAccess3.name = "Beth Williams";
|
||||||
|
memberAccess3.groups = 6;
|
||||||
|
memberAccess3.collections = 12;
|
||||||
|
memberAccess3.items = 1;
|
||||||
|
|
||||||
|
const memberAccess4 = new MemberAccessReportView();
|
||||||
|
memberAccess4.email = "rwilliams@email.com";
|
||||||
|
memberAccess4.name = "Ray Williams";
|
||||||
|
memberAccess4.groups = 5;
|
||||||
|
memberAccess4.collections = 21;
|
||||||
|
memberAccess4.items = 2;
|
||||||
|
|
||||||
|
return [memberAccess, memberAccess2, memberAccess3, memberAccess4];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
export class MemberAccessReportView {
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
collections: number;
|
||||||
|
groups: number;
|
||||||
|
items: number;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user