diff --git a/bitwarden_license/bit-web/src/app/tools/access-intelligence/critical-applications.component.html b/bitwarden_license/bit-web/src/app/tools/access-intelligence/critical-applications.component.html index 4dc4b7ffb1a..3135cc8ac46 100644 --- a/bitwarden_license/bit-web/src/app/tools/access-intelligence/critical-applications.component.html +++ b/bitwarden_license/bit-web/src/app/tools/access-intelligence/critical-applications.component.html @@ -28,7 +28,14 @@

{{ "criticalApplications" | i18n }}

- diff --git a/bitwarden_license/bit-web/src/app/tools/access-intelligence/critical-applications.component.ts b/bitwarden_license/bit-web/src/app/tools/access-intelligence/critical-applications.component.ts index f1fa38dd28f..42c1c62a437 100644 --- a/bitwarden_license/bit-web/src/app/tools/access-intelligence/critical-applications.component.ts +++ b/bitwarden_license/bit-web/src/app/tools/access-intelligence/critical-applications.component.ts @@ -18,7 +18,7 @@ import { import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.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 { Icons, NoItemsModule, @@ -27,10 +27,14 @@ import { ToastService, } from "@bitwarden/components"; import { CardComponent } from "@bitwarden/tools-card"; +import { SecurityTaskType } from "@bitwarden/vault"; import { HeaderModule } from "@bitwarden/web-vault/app/layouts/header/header.module"; import { SharedModule } from "@bitwarden/web-vault/app/shared"; 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"; @Component({ @@ -38,7 +42,7 @@ import { RiskInsightsTabType } from "./risk-insights.component"; selector: "tools-critical-applications", templateUrl: "./critical-applications.component.html", imports: [CardComponent, HeaderModule, SearchModule, NoItemsModule, PipesModule, SharedModule], - providers: [], + providers: [DefaultAdminTaskService], }) export class CriticalApplicationsComponent implements OnInit { protected dataSource = new TableDataSource(); @@ -50,6 +54,7 @@ export class CriticalApplicationsComponent implements OnInit { protected applicationSummary = {} as ApplicationHealthReportSummary; noItemsIcon = Icons.Security; isNotificationsFeatureEnabled: boolean = false; + enableRequestPasswordChange = false; async ngOnInit() { this.isNotificationsFeatureEnabled = await this.configService.getFeatureFlag( @@ -75,6 +80,7 @@ export class CriticalApplicationsComponent implements OnInit { if (applications) { this.dataSource.data = 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); }; + 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( protected activatedRoute: ActivatedRoute, protected router: Router, @@ -118,6 +151,7 @@ export class CriticalApplicationsComponent implements OnInit { protected reportService: RiskInsightsReportService, protected i18nService: I18nService, private configService: ConfigService, + private adminTaskService: DefaultAdminTaskService, ) { this.searchControl.valueChanges .pipe(debounceTime(200), takeUntilDestroyed())