mirror of
https://github.com/bitwarden/browser
synced 2025-12-16 16:23:44 +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:
@@ -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": {
|
"atRiskMembersDescription": {
|
||||||
"message": "These members are logging into applications with weak, exposed, or reused passwords."
|
"message": "These members are logging into applications with weak, exposed, or reused passwords."
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -100,3 +100,12 @@ export type AtRiskMemberDetail = {
|
|||||||
email: string;
|
email: string;
|
||||||
atRiskPasswordCount: number;
|
atRiskPasswordCount: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A list of applications and the count of
|
||||||
|
* at risk passwords for each application
|
||||||
|
*/
|
||||||
|
export type AtRiskApplicationDetail = {
|
||||||
|
applicationName: string;
|
||||||
|
atRiskPasswordCount: number;
|
||||||
|
};
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
ApplicationHealthReportDetail,
|
ApplicationHealthReportDetail,
|
||||||
ApplicationHealthReportSummary,
|
ApplicationHealthReportSummary,
|
||||||
AtRiskMemberDetail,
|
AtRiskMemberDetail,
|
||||||
|
AtRiskApplicationDetail,
|
||||||
CipherHealthReportDetail,
|
CipherHealthReportDetail,
|
||||||
CipherHealthReportUriDetail,
|
CipherHealthReportUriDetail,
|
||||||
ExposedPasswordDetail,
|
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
|
* 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
|
* as the total at risk members and at risk applications
|
||||||
|
|||||||
@@ -35,10 +35,11 @@
|
|||||||
>
|
>
|
||||||
</tools-card>
|
</tools-card>
|
||||||
<tools-card
|
<tools-card
|
||||||
class="tw-flex-1"
|
class="tw-flex-1 tw-cursor-pointer"
|
||||||
[title]="'atRiskApplications' | i18n"
|
[title]="'atRiskApplications' | i18n"
|
||||||
[value]="applicationSummary.totalAtRiskApplicationCount"
|
[value]="applicationSummary.totalAtRiskApplicationCount"
|
||||||
[maxValue]="applicationSummary.totalApplicationCount"
|
[maxValue]="applicationSummary.totalApplicationCount"
|
||||||
|
(click)="showOrgAtRiskApps()"
|
||||||
>
|
>
|
||||||
</tools-card>
|
</tools-card>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -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 { PipesModule } from "@bitwarden/web-vault/app/vault/individual-vault/pipes/pipes.module";
|
||||||
|
|
||||||
import { openAppAtRiskMembersDialog } from "./app-at-risk-members-dialog.component";
|
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 { OrgAtRiskMembersDialogComponent } from "./org-at-risk-members-dialog.component";
|
||||||
import { ApplicationsLoadingComponent } from "./risk-insights-loading.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) {
|
onCheckboxChange(id: number, event: Event) {
|
||||||
const isChecked = (event.target as HTMLInputElement).checked;
|
const isChecked = (event.target as HTMLInputElement).checked;
|
||||||
if (isChecked) {
|
if (isChecked) {
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -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[]) {}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user