From cb2e5a04d0c12d274bc871e3560ad3de3fe187ae Mon Sep 17 00:00:00 2001 From: Vijay Oommen Date: Fri, 6 Feb 2026 12:36:44 -0600 Subject: [PATCH] [PM-29621] Changed error message to indicate lack of permissions (#18528) --- apps/web/src/locales/en/messages.json | 3 ++ .../password-change-metric.component.ts | 12 ++++- .../new-applications-dialog.component.ts | 41 ++++++++++------ .../critical-applications.component.ts | 47 +++++++++++-------- 4 files changed, 68 insertions(+), 35 deletions(-) diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index 4d69bf45311..97bb46029a7 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -10499,6 +10499,9 @@ "failedToSaveIntegration": { "message": "Failed to save integration. Please try again later." }, + "mustBeOrganizationOwnerAdmin": { + "message": "You must be an Organization Owner or Admin to perform this action." + }, "mustBeOrgOwnerToPerformAction": { "message": "You must be the organization owner to perform this action." }, diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/activity-cards/password-change-metric.component.ts b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/activity-cards/password-change-metric.component.ts index c1a00731100..df47adb4635 100644 --- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/activity-cards/password-change-metric.component.ts +++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/activity-cards/password-change-metric.component.ts @@ -18,6 +18,7 @@ import { AllActivitiesService, RiskInsightsDataService, } from "@bitwarden/bit-common/dirt/reports/risk-insights"; +import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { CipherId, OrganizationId } from "@bitwarden/common/types/guid"; import { SecurityTask, SecurityTaskStatus } from "@bitwarden/common/vault/tasks"; @@ -170,7 +171,16 @@ export class PasswordChangeMetricComponent implements OnInit { variant: "success", title: this.i18nService.t("success"), }); - } catch { + } catch (error) { + if (error instanceof ErrorResponse && error.statusCode === 404) { + this.toastService.showToast({ + message: this.i18nService.t("mustBeOrganizationOwnerAdmin"), + variant: "error", + title: this.i18nService.t("error"), + }); + return; + } + this.toastService.showToast({ message: this.i18nService.t("unexpectedError"), variant: "error", diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/application-review-dialog/new-applications-dialog.component.ts b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/application-review-dialog/new-applications-dialog.component.ts index 796c0acf220..5b9cea436a0 100644 --- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/application-review-dialog/new-applications-dialog.component.ts +++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/application-review-dialog/new-applications-dialog.component.ts @@ -10,13 +10,14 @@ import { signal, } from "@angular/core"; import { takeUntilDestroyed, toSignal } from "@angular/core/rxjs-interop"; -import { from, switchMap, take } from "rxjs"; +import { catchError, EMPTY, from, switchMap, take } from "rxjs"; import { ApplicationHealthReportDetail, RiskInsightsDataService, } from "@bitwarden/bit-common/dirt/reports/risk-insights"; import { getUniqueMembers } from "@bitwarden/bit-common/dirt/reports/risk-insights/helpers"; +import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { CipherId, OrganizationId } from "@bitwarden/common/types/guid"; @@ -289,18 +290,18 @@ export class NewApplicationsDialogComponent { ), ); }), - ) - .subscribe({ - next: () => { - this.toastService.showToast({ - variant: "success", - title: this.i18nService.t("applicationReviewSaved"), - message: this.i18nService.t("newApplicationsReviewed"), - }); - this.saving.set(false); - this.handleAssigningCompleted(); - }, - error: (error: unknown) => { + catchError((error: unknown) => { + if (error instanceof ErrorResponse && error.statusCode === 404) { + this.toastService.showToast({ + message: this.i18nService.t("mustBeOrganizationOwnerAdmin"), + variant: "error", + title: this.i18nService.t("error"), + }); + + this.saving.set(false); + return EMPTY; + } + this.logService.error( "[NewApplicationsDialog] Failed to save application review or assign tasks", error, @@ -311,7 +312,19 @@ export class NewApplicationsDialogComponent { title: this.i18nService.t("errorSavingReviewStatus"), message: this.i18nService.t("pleaseTryAgain"), }); - }, + + this.saving.set(false); + return EMPTY; + }), + ) + .subscribe(() => { + this.toastService.showToast({ + variant: "success", + title: this.i18nService.t("applicationReviewSaved"), + message: this.i18nService.t("newApplicationsReviewed"), + }); + this.saving.set(false); + this.handleAssigningCompleted(); }); } diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/critical-applications/critical-applications.component.ts b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/critical-applications/critical-applications.component.ts index b61190df660..3033bf139c3 100644 --- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/critical-applications/critical-applications.component.ts +++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/critical-applications/critical-applications.component.ts @@ -1,10 +1,8 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { Component, DestroyRef, inject, OnInit, ChangeDetectionStrategy } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { FormControl } from "@angular/forms"; import { ActivatedRoute, Router } from "@angular/router"; -import { debounceTime, EMPTY, from, map, switchMap, take } from "rxjs"; +import { catchError, debounceTime, EMPTY, from, map, switchMap, take } from "rxjs"; import { Security } from "@bitwarden/assets/svg"; import { @@ -14,6 +12,7 @@ import { } from "@bitwarden/bit-common/dirt/reports/risk-insights"; import { createNewSummaryData } from "@bitwarden/bit-common/dirt/reports/risk-insights/helpers"; import { OrganizationReportSummary } from "@bitwarden/bit-common/dirt/reports/risk-insights/models/report-models"; +import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { OrganizationId } from "@bitwarden/common/types/guid"; import { @@ -53,7 +52,7 @@ import { AccessIntelligenceSecurityTasksService } from "../shared/security-tasks export class CriticalApplicationsComponent implements OnInit { private destroyRef = inject(DestroyRef); protected enableRequestPasswordChange = false; - protected organizationId: OrganizationId; + protected organizationId: OrganizationId = "" as OrganizationId; noItemsIcon = Security; protected dataSource = new TableDataSource(); @@ -151,35 +150,43 @@ export class CriticalApplicationsComponent implements OnInit { }); }; - async requestPasswordChange() { + requestPasswordChange(): void { this.dataService.criticalApplicationAtRiskCipherIds$ .pipe( takeUntilDestroyed(this.destroyRef), // Satisfy eslint rule take(1), // Handle unsubscribe for one off operation - switchMap((cipherIds) => { - return from( + switchMap((cipherIds) => + from( this.securityTasksService.requestPasswordChangeForCriticalApplications( this.organizationId, cipherIds, ), - ); - }), - ) - .subscribe({ - next: () => { - this.toastService.showToast({ - message: this.i18nService.t("notifiedMembers"), - variant: "success", - title: this.i18nService.t("success"), - }); - }, - error: () => { + ), + ), + catchError((error: unknown) => { + if (error instanceof ErrorResponse && error.statusCode === 404) { + this.toastService.showToast({ + message: this.i18nService.t("mustBeOrganizationOwnerAdmin"), + variant: "error", + title: this.i18nService.t("error"), + }); + return EMPTY; + } + this.toastService.showToast({ message: this.i18nService.t("unexpectedError"), variant: "error", title: this.i18nService.t("error"), }); - }, + return EMPTY; + }), + ) + .subscribe(() => { + this.toastService.showToast({ + message: this.i18nService.t("notifiedMembers"), + variant: "success", + title: this.i18nService.t("success"), + }); }); }