1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-15 15:53:27 +00:00

PM-16891 Applications at risk dialog (#12843)

* Org at risk members click on the card

* Fixing at risk member counts

* At risk member text modification

* Changing ok button to close

* PM-16891 added a dialog for at risk apps

* PM-16891 fixing order of imports (linting error)

* PM-16891 updated PR comments

---------

Co-authored-by: Tom <ttalty@bitwarden.com>
Co-authored-by: Tom <144813356+ttalty@users.noreply.github.com>
This commit is contained in:
Vijay Oommen
2025-01-14 13:58:57 -06:00
committed by GitHub
parent 27e8a1f27c
commit f2b6f05d3f
7 changed files with 125 additions and 1 deletions

View File

@@ -122,6 +122,39 @@
}
}
},
"atRiskApplicationsWithCount": {
"message": "At-risk applications ($COUNT$)",
"placeholders": {
"count": {
"content": "$1",
"example": "3"
}
}
},
"atRiskMembersDescription": {
"message": "These members are logging into applications with weak, exposed, or reused passwords."
},
"atRiskApplicationsDescription": {
"message": "These applications have weak, exposed, or reused passwords."
},
"atRiskMembersDescriptionWithApp": {
"message": "These members are logging into $APPNAME$ with weak, exposed, or reused passwords.",
"placeholders": {
"appname": {
"content": "$1",
"example": "Salesforce"
}
}
},
"atRiskMembersWithCount": {
"message": "At-risk members ($COUNT$)",
"placeholders": {
"count": {
"content": "$1",
"example": "3"
}
}
},
"atRiskMembersDescription": {
"message": "These members are logging into applications with weak, exposed, or reused passwords."
},

View File

@@ -100,3 +100,12 @@ export type AtRiskMemberDetail = {
email: string;
atRiskPasswordCount: number;
};
/*
* A list of applications and the count of
* at risk passwords for each application
*/
export type AtRiskApplicationDetail = {
applicationName: string;
atRiskPasswordCount: number;
};

View File

@@ -13,6 +13,7 @@ import {
ApplicationHealthReportDetail,
ApplicationHealthReportSummary,
AtRiskMemberDetail,
AtRiskApplicationDetail,
CipherHealthReportDetail,
CipherHealthReportUriDetail,
ExposedPasswordDetail,
@@ -114,6 +115,30 @@ export class RiskInsightsReportService {
}));
}
generateAtRiskApplicationList(
cipherHealthReportDetails: ApplicationHealthReportDetail[],
): AtRiskApplicationDetail[] {
const appsRiskMap = new Map<string, number>();
cipherHealthReportDetails
.filter((app) => app.atRiskPasswordCount > 0)
.forEach((app) => {
if (appsRiskMap.has(app.applicationName)) {
appsRiskMap.set(
app.applicationName,
appsRiskMap.get(app.applicationName) + app.atRiskPasswordCount,
);
} else {
appsRiskMap.set(app.applicationName, app.atRiskPasswordCount);
}
});
return Array.from(appsRiskMap.entries()).map(([applicationName, atRiskPasswordCount]) => ({
applicationName,
atRiskPasswordCount,
}));
}
/**
* Gets the summary from the application health report. Returns total members and applications as well
* as the total at risk members and at risk applications

View File

@@ -35,10 +35,11 @@
>
</tools-card>
<tools-card
class="tw-flex-1"
class="tw-flex-1 tw-cursor-pointer"
[title]="'atRiskApplications' | i18n"
[value]="applicationSummary.totalAtRiskApplicationCount"
[maxValue]="applicationSummary.totalApplicationCount"
(click)="showOrgAtRiskApps()"
>
</tools-card>
</div>

View File

@@ -32,6 +32,7 @@ import { SharedModule } from "@bitwarden/web-vault/app/shared";
import { PipesModule } from "@bitwarden/web-vault/app/vault/individual-vault/pipes/pipes.module";
import { openAppAtRiskMembersDialog } from "./app-at-risk-members-dialog.component";
import { OrgAtRiskAppsDialogComponent } from "./org-at-risk-apps-dialog.component";
import { OrgAtRiskMembersDialogComponent } from "./org-at-risk-members-dialog.component";
import { ApplicationsLoadingComponent } from "./risk-insights-loading.component";
@@ -154,6 +155,12 @@ export class AllApplicationsComponent implements OnInit, OnDestroy {
});
};
showOrgAtRiskApps = async () => {
this.dialogService.open(OrgAtRiskAppsDialogComponent, {
data: this.reportService.generateAtRiskApplicationList(this.dataSource.data),
});
};
onCheckboxChange(id: number, event: Event) {
const isChecked = (event.target as HTMLInputElement).checked;
if (isChecked) {

View File

@@ -0,0 +1,25 @@
<bit-dialog>
<ng-container bitDialogTitle>
<span bitDialogTitle>{{ "atRiskApplicationsWithCount" | i18n: atRiskApps.length }} </span>
</ng-container>
<ng-container bitDialogContent>
<div class="tw-flex tw-flex-col tw-gap-2">
<span bitTypography="body1">{{ "atRiskApplicationsDescription" | i18n }}</span>
<div class="tw-flex tw-justify-between tw-mt-2 tw-text-muted">
<div bitTypography="body2" class="tw-font-bold">{{ "application" | i18n }}</div>
<div bitTypography="body2" class="tw-font-bold">{{ "atRiskPasswords" | i18n }}</div>
</div>
<ng-container *ngFor="let app of atRiskApps">
<div class="tw-flex tw-justify-between tw-mt-2">
<div>{{ app.applicationName }}</div>
<div>{{ app.atRiskPasswordCount }}</div>
</div>
</ng-container>
</div>
</ng-container>
<ng-container bitDialogFooter>
<button bitButton bitDialogClose buttonType="secondary" type="button">
{{ "close" | i18n }}
</button>
</ng-container>
</bit-dialog>

View File

@@ -0,0 +1,24 @@
import { DIALOG_DATA } from "@angular/cdk/dialog";
import { CommonModule } from "@angular/common";
import { Component, Inject } from "@angular/core";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { AtRiskApplicationDetail } from "@bitwarden/bit-common/tools/reports/risk-insights/models/password-health";
import { ButtonModule, DialogModule, DialogService, TypographyModule } from "@bitwarden/components";
export const openOrgAtRiskMembersDialog = (
dialogService: DialogService,
dialogConfig: AtRiskApplicationDetail[],
) =>
dialogService.open<boolean, AtRiskApplicationDetail[]>(OrgAtRiskAppsDialogComponent, {
data: dialogConfig,
});
@Component({
standalone: true,
templateUrl: "./org-at-risk-apps-dialog.component.html",
imports: [ButtonModule, CommonModule, DialogModule, JslibModule, TypographyModule],
})
export class OrgAtRiskAppsDialogComponent {
constructor(@Inject(DIALOG_DATA) protected atRiskApps: AtRiskApplicationDetail[]) {}
}