1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-21 10:43:35 +00:00

PM-17212 Invoke notifications API (#13377)

This commit is contained in:
Vijay Oommen
2025-02-18 08:36:37 -06:00
committed by GitHub
parent 117522f394
commit 6ea3e6e314
2 changed files with 44 additions and 3 deletions

View File

@@ -28,7 +28,14 @@
<div class="tw-mt-4 tw-flex tw-flex-col" *ngIf="!loading && dataSource.data.length"> <div class="tw-mt-4 tw-flex tw-flex-col" *ngIf="!loading && dataSource.data.length">
<div class="tw-flex tw-justify-between tw-mb-4"> <div class="tw-flex tw-justify-between tw-mb-4">
<h2 bitTypography="h2">{{ "criticalApplications" | i18n }}</h2> <h2 bitTypography="h2">{{ "criticalApplications" | i18n }}</h2>
<button *ngIf="isNotificationsFeatureEnabled" bitButton buttonType="primary" type="button"> <button
*ngIf="isNotificationsFeatureEnabled"
bitButton
buttonType="primary"
type="button"
[disabled]="!enableRequestPasswordChange"
(click)="requestPasswordChange()"
>
<i class="bwi bwi-envelope tw-mr-2"></i> <i class="bwi bwi-envelope tw-mr-2"></i>
{{ "requestPasswordChange" | i18n }} {{ "requestPasswordChange" | i18n }}
</button> </button>

View File

@@ -18,7 +18,7 @@ import {
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { OrganizationId } from "@bitwarden/common/types/guid"; import { CipherId, OrganizationId } from "@bitwarden/common/types/guid";
import { import {
Icons, Icons,
NoItemsModule, NoItemsModule,
@@ -27,10 +27,14 @@ import {
ToastService, ToastService,
} from "@bitwarden/components"; } from "@bitwarden/components";
import { CardComponent } from "@bitwarden/tools-card"; import { CardComponent } from "@bitwarden/tools-card";
import { SecurityTaskType } from "@bitwarden/vault";
import { HeaderModule } from "@bitwarden/web-vault/app/layouts/header/header.module"; import { HeaderModule } from "@bitwarden/web-vault/app/layouts/header/header.module";
import { SharedModule } from "@bitwarden/web-vault/app/shared"; 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 { CreateTasksRequest } from "../../vault/services/abstractions/admin-task.abstraction";
import { DefaultAdminTaskService } from "../../vault/services/default-admin-task.service";
import { RiskInsightsTabType } from "./risk-insights.component"; import { RiskInsightsTabType } from "./risk-insights.component";
@Component({ @Component({
@@ -38,7 +42,7 @@ import { RiskInsightsTabType } from "./risk-insights.component";
selector: "tools-critical-applications", selector: "tools-critical-applications",
templateUrl: "./critical-applications.component.html", templateUrl: "./critical-applications.component.html",
imports: [CardComponent, HeaderModule, SearchModule, NoItemsModule, PipesModule, SharedModule], imports: [CardComponent, HeaderModule, SearchModule, NoItemsModule, PipesModule, SharedModule],
providers: [], providers: [DefaultAdminTaskService],
}) })
export class CriticalApplicationsComponent implements OnInit { export class CriticalApplicationsComponent implements OnInit {
protected dataSource = new TableDataSource<ApplicationHealthReportDetailWithCriticalFlag>(); protected dataSource = new TableDataSource<ApplicationHealthReportDetailWithCriticalFlag>();
@@ -50,6 +54,7 @@ export class CriticalApplicationsComponent implements OnInit {
protected applicationSummary = {} as ApplicationHealthReportSummary; protected applicationSummary = {} as ApplicationHealthReportSummary;
noItemsIcon = Icons.Security; noItemsIcon = Icons.Security;
isNotificationsFeatureEnabled: boolean = false; isNotificationsFeatureEnabled: boolean = false;
enableRequestPasswordChange = false;
async ngOnInit() { async ngOnInit() {
this.isNotificationsFeatureEnabled = await this.configService.getFeatureFlag( this.isNotificationsFeatureEnabled = await this.configService.getFeatureFlag(
@@ -75,6 +80,7 @@ export class CriticalApplicationsComponent implements OnInit {
if (applications) { if (applications) {
this.dataSource.data = applications; this.dataSource.data = applications;
this.applicationSummary = this.reportService.generateApplicationsSummary(applications); this.applicationSummary = this.reportService.generateApplicationsSummary(applications);
this.enableRequestPasswordChange = this.applicationSummary.totalAtRiskMemberCount > 0;
} }
}); });
} }
@@ -109,6 +115,33 @@ export class CriticalApplicationsComponent implements OnInit {
this.dataSource.data = this.dataSource.data.filter((app) => app.applicationName !== hostname); this.dataSource.data = this.dataSource.data.filter((app) => app.applicationName !== hostname);
}; };
async requestPasswordChange() {
const apps = this.dataSource.data;
const cipherIds = apps
.filter((_) => _.atRiskPasswordCount > 0)
.flatMap((app) => app.atRiskMemberDetails.map((member) => member.cipherId));
const distinctCipherIds = Array.from(new Set(cipherIds));
const tasks: CreateTasksRequest[] = distinctCipherIds.map((cipherId) => ({
cipherId: cipherId as CipherId,
type: SecurityTaskType.UpdateAtRiskCredential,
}));
try {
await this.adminTaskService.bulkCreateTasks(this.organizationId as OrganizationId, tasks);
this.toastService.showToast({
message: this.i18nService.t("notifiedMembers"),
variant: "success",
title: this.i18nService.t("success"),
});
} catch {
this.toastService.showToast({
message: this.i18nService.t("unexpectedError"),
variant: "error",
title: this.i18nService.t("error"),
});
}
}
constructor( constructor(
protected activatedRoute: ActivatedRoute, protected activatedRoute: ActivatedRoute,
protected router: Router, protected router: Router,
@@ -118,6 +151,7 @@ export class CriticalApplicationsComponent implements OnInit {
protected reportService: RiskInsightsReportService, protected reportService: RiskInsightsReportService,
protected i18nService: I18nService, protected i18nService: I18nService,
private configService: ConfigService, private configService: ConfigService,
private adminTaskService: DefaultAdminTaskService,
) { ) {
this.searchControl.valueChanges this.searchControl.valueChanges
.pipe(debounceTime(200), takeUntilDestroyed()) .pipe(debounceTime(200), takeUntilDestroyed())