From 361f90a488465ee7f8a32a15904eb659988fbd25 Mon Sep 17 00:00:00 2001 From: Cesar Gonzalez Date: Fri, 30 Aug 2024 08:59:04 -0500 Subject: [PATCH 01/24] [PM-11519] `browser` global reference triggering an error when sending an extension message (#10819) --- apps/browser/src/autofill/utils/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/browser/src/autofill/utils/index.ts b/apps/browser/src/autofill/utils/index.ts index d62013b1611..ad9fa3a9a8d 100644 --- a/apps/browser/src/autofill/utils/index.ts +++ b/apps/browser/src/autofill/utils/index.ts @@ -105,7 +105,7 @@ export async function sendExtensionMessage( command: string, options: Record = {}, ): Promise { - if (typeof browser !== "undefined") { + if (typeof browser?.runtime?.sendMessage !== "undefined") { return browser.runtime.sendMessage({ command, ...options }); } From 4453a5c114c12fa0f86e527a9b0d34b78b8803eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rui=20Tom=C3=A9?= <108268980+r-tome@users.noreply.github.com> Date: Fri, 30 Aug 2024 15:01:29 +0100 Subject: [PATCH 02/24] [PM-11333] Rename deleteOrganizationUser to removeOrganizationUser in BaseMembersComponent, OrganizationUserService and related files --- .../admin-console/common/base-members.component.ts | 4 ++-- .../members/components/bulk/bulk-remove.component.ts | 6 +++--- .../member-dialog/member-dialog.component.ts | 2 +- .../organizations/members/members.component.ts | 4 ++-- .../providers/manage/members.component.ts | 2 +- .../organization-user/organization-user.service.ts | 12 ++++++------ .../organization-user.service.implementation.ts | 4 ++-- 7 files changed, 17 insertions(+), 17 deletions(-) diff --git a/apps/web/src/app/admin-console/common/base-members.component.ts b/apps/web/src/app/admin-console/common/base-members.component.ts index c13bec78c56..2d0d66e2930 100644 --- a/apps/web/src/app/admin-console/common/base-members.component.ts +++ b/apps/web/src/app/admin-console/common/base-members.component.ts @@ -96,7 +96,7 @@ export abstract class BaseMembersComponent { abstract edit(user: UserView): void; abstract getUsers(): Promise | UserView[]>; - abstract deleteUser(id: string): Promise; + abstract removeUser(id: string): Promise; abstract reinviteUser(id: string): Promise; abstract confirmUser(user: UserView, publicKey: Uint8Array): Promise; @@ -132,7 +132,7 @@ export abstract class BaseMembersComponent { return false; } - this.actionPromise = this.deleteUser(user.id); + this.actionPromise = this.removeUser(user.id); try { await this.actionPromise; this.toastService.showToast({ diff --git a/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-remove.component.ts b/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-remove.component.ts index 15a24eb25eb..60d1aec41c6 100644 --- a/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-remove.component.ts +++ b/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-remove.component.ts @@ -45,7 +45,7 @@ export class BulkRemoveComponent { submit = async () => { this.loading = true; try { - const response = await this.deleteUsers(); + const response = await this.removeUsers(); response.data.forEach((entry) => { const error = entry.error !== "" ? entry.error : this.i18nService.t("bulkRemovedMessage"); @@ -59,8 +59,8 @@ export class BulkRemoveComponent { this.loading = false; }; - protected async deleteUsers() { - return await this.organizationUserService.deleteManyOrganizationUsers( + protected async removeUsers() { + return await this.organizationUserService.removeManyOrganizationUsers( this.organizationId, this.users.map((user) => user.id), ); diff --git a/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts b/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts index ef36f2b80ba..9f02c63f07a 100644 --- a/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts +++ b/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts @@ -487,7 +487,7 @@ export class MemberDialogComponent implements OnDestroy { } } - await this.organizationUserService.deleteOrganizationUser( + await this.organizationUserService.removeOrganizationUser( this.params.organizationId, this.params.organizationUserId, ); diff --git a/apps/web/src/app/admin-console/organizations/members/members.component.ts b/apps/web/src/app/admin-console/organizations/members/members.component.ts index 66a62f7acc7..c1bc970d5df 100644 --- a/apps/web/src/app/admin-console/organizations/members/members.component.ts +++ b/apps/web/src/app/admin-console/organizations/members/members.component.ts @@ -269,8 +269,8 @@ export class MembersComponent extends BaseMembersComponent return collectionMap; } - deleteUser(id: string): Promise { - return this.organizationUserService.deleteOrganizationUser(this.organization.id, id); + removeUser(id: string): Promise { + return this.organizationUserService.removeOrganizationUser(this.organization.id, id); } revokeUser(id: string): Promise { diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/members.component.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/members.component.ts index 3e9856638e8..f78bccd3548 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/members.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/members.component.ts @@ -190,7 +190,7 @@ export class MembersComponent extends BaseMembersComponent { await this.apiService.postProviderUserConfirm(this.providerId, user.id, request); } - deleteUser = (id: string): Promise => + removeUser = (id: string): Promise => this.apiService.deleteProviderUser(this.providerId, id); edit = async (user: ProviderUser | null): Promise => { diff --git a/libs/common/src/admin-console/abstractions/organization-user/organization-user.service.ts b/libs/common/src/admin-console/abstractions/organization-user/organization-user.service.ts index 7058be08037..aada830f954 100644 --- a/libs/common/src/admin-console/abstractions/organization-user/organization-user.service.ts +++ b/libs/common/src/admin-console/abstractions/organization-user/organization-user.service.ts @@ -210,19 +210,19 @@ export abstract class OrganizationUserService { ): Promise; /** - * Delete an organization user + * Remove an organization user * @param organizationId - Identifier for the organization the user belongs to * @param id - Organization user identifier */ - abstract deleteOrganizationUser(organizationId: string, id: string): Promise; + abstract removeOrganizationUser(organizationId: string, id: string): Promise; /** - * Delete many organization users + * Remove many organization users * @param organizationId - Identifier for the organization the users belongs to - * @param ids - List of organization user identifiers to delete - * @return List of user ids, including both those that were successfully deleted and those that had an error + * @param ids - List of organization user identifiers to remove + * @return List of user ids, including both those that were successfully removed and those that had an error */ - abstract deleteManyOrganizationUsers( + abstract removeManyOrganizationUsers( organizationId: string, ids: string[], ): Promise>; diff --git a/libs/common/src/admin-console/services/organization-user/organization-user.service.implementation.ts b/libs/common/src/admin-console/services/organization-user/organization-user.service.implementation.ts index b66805a20b9..e3687691b6b 100644 --- a/libs/common/src/admin-console/services/organization-user/organization-user.service.implementation.ts +++ b/libs/common/src/admin-console/services/organization-user/organization-user.service.implementation.ts @@ -274,7 +274,7 @@ export class OrganizationUserServiceImplementation implements OrganizationUserSe ); } - deleteOrganizationUser(organizationId: string, id: string): Promise { + removeOrganizationUser(organizationId: string, id: string): Promise { return this.apiService.send( "DELETE", "/organizations/" + organizationId + "/users/" + id, @@ -284,7 +284,7 @@ export class OrganizationUserServiceImplementation implements OrganizationUserSe ); } - async deleteManyOrganizationUsers( + async removeManyOrganizationUsers( organizationId: string, ids: string[], ): Promise> { From ba42f10ea3a3f183c3e9576b8a88a7accc3cd943 Mon Sep 17 00:00:00 2001 From: Cesar Gonzalez Date: Fri, 30 Aug 2024 09:32:40 -0500 Subject: [PATCH 03/24] [PM-11519] browser global variable triggering error fix jest (#10821) * [PM-11519] `browser` global reference triggering an error when sending an extension message * [PM-11519] `browser` global reference triggering an error when sending an extension message --- apps/browser/src/autofill/utils/index.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/browser/src/autofill/utils/index.ts b/apps/browser/src/autofill/utils/index.ts index ad9fa3a9a8d..98e58a022e4 100644 --- a/apps/browser/src/autofill/utils/index.ts +++ b/apps/browser/src/autofill/utils/index.ts @@ -105,7 +105,11 @@ export async function sendExtensionMessage( command: string, options: Record = {}, ): Promise { - if (typeof browser?.runtime?.sendMessage !== "undefined") { + if ( + typeof browser !== "undefined" && + typeof browser.runtime !== "undefined" && + typeof browser.runtime.sendMessage !== "undefined" + ) { return browser.runtime.sendMessage({ command, ...options }); } From 6bc74546e4a27fb5088a4808cc049113722c4848 Mon Sep 17 00:00:00 2001 From: Jon Mulder Date: Fri, 30 Aug 2024 11:44:12 -0400 Subject: [PATCH 04/24] [chore] Add "otp" TotpFieldNames array in autofill-constants.ts (#10717) Add "otp" TotpFieldNames array in autofill-constants.ts. Noticed on https://id.fedoraproject.org/login/ --- apps/browser/src/autofill/services/autofill-constants.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/browser/src/autofill/services/autofill-constants.ts b/apps/browser/src/autofill/services/autofill-constants.ts index 992551256bb..1bab7210672 100644 --- a/apps/browser/src/autofill/services/autofill-constants.ts +++ b/apps/browser/src/autofill/services/autofill-constants.ts @@ -38,6 +38,7 @@ export class AutoFillConstants { "mfacode", "otc", "otc-code", + "otp", "otp-code", "otpcode", "pin", From c0fbb5ce39db6cb3f387532451f549102c1836cb Mon Sep 17 00:00:00 2001 From: cyprain-okeke <108260115+cyprain-okeke@users.noreply.github.com> Date: Fri, 30 Aug 2024 17:26:25 +0100 Subject: [PATCH 05/24] Hide the selfHost for teams and free plan (#10692) --- .../organization-subscription-cloud.component.html | 8 +++++--- .../organization-subscription-cloud.component.ts | 7 +++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.html b/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.html index 35cb0c2ac79..656796b443e 100644 --- a/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.html +++ b/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.html @@ -222,8 +222,10 @@ -

{{ "selfHostingTitle" | i18n }}

-

+

+ {{ "selfHostingTitle" | i18n }} +

+

{{ "selfHostingEnterpriseOrganizationSectionCopy" | i18n }} {{ "downloadLicense" | i18n }} diff --git a/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.ts b/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.ts index b8616ae1b42..f28933a4ecc 100644 --- a/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.ts +++ b/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.ts @@ -345,6 +345,13 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy ); } + shownSelfHost(): boolean { + return ( + this.sub?.plan.productTier !== ProductTierType.Teams && + this.sub?.plan.productTier !== ProductTierType.Free + ); + } + cancelSubscription = async () => { const reference = openOffboardingSurvey(this.dialogService, { data: { From d9ff8b0944e095ed803f15d26183885045469cfd Mon Sep 17 00:00:00 2001 From: Jason Ng Date: Fri, 30 Aug 2024 12:27:56 -0400 Subject: [PATCH 06/24] PM-11427 Copy Totp Code without space (#10800) --- .../login-credentials-view.component.html | 4 ++-- .../login-credentials-view.component.ts | 11 ++++++++--- .../totp-countdown/totp-countdown.component.ts | 7 +++++-- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/libs/vault/src/cipher-view/login-credentials/login-credentials-view.component.html b/libs/vault/src/cipher-view/login-credentials/login-credentials-view.component.html index 828ac25a14f..78b5becfc4a 100644 --- a/libs/vault/src/cipher-view/login-credentials/login-credentials-view.component.html +++ b/libs/vault/src/cipher-view/login-credentials/login-credentials-view.component.html @@ -106,7 +106,7 @@ readonly bitInput [type]="!(isPremium$ | async) ? 'password' : 'text'" - [value]="totpCopyCode || '*** ***'" + [value]="totpCodeCopyObj?.totpCodeFormatted || '*** ***'" aria-readonly="true" data-testid="login-totp" [disabled]="!(isPremium$ | async)" @@ -124,7 +124,7 @@ bitIconButton="bwi-clone" bitSuffix type="button" - [appCopyClick]="totpCopyCode" + [appCopyClick]="totpCodeCopyObj?.totpCode" [valueLabel]="'verificationCodeTotp' | i18n" showToast [appA11yTitle]="'copyValue' | i18n" diff --git a/libs/vault/src/cipher-view/login-credentials/login-credentials-view.component.ts b/libs/vault/src/cipher-view/login-credentials/login-credentials-view.component.ts index d8683a44073..3973a666847 100644 --- a/libs/vault/src/cipher-view/login-credentials/login-credentials-view.component.ts +++ b/libs/vault/src/cipher-view/login-credentials/login-credentials-view.component.ts @@ -20,6 +20,11 @@ import { import { BitTotpCountdownComponent } from "../../components/totp-countdown/totp-countdown.component"; +type TotpCodeValues = { + totpCode: string; + totpCodeFormatted?: string; +}; + @Component({ selector: "app-login-credentials-view", templateUrl: "login-credentials-view.component.html", @@ -47,7 +52,7 @@ export class LoginCredentialsViewComponent { ); showPasswordCount: boolean = false; passwordRevealed: boolean = false; - totpCopyCode: string; + totpCodeCopyObj: TotpCodeValues; private datePipe = inject(DatePipe); constructor( @@ -77,7 +82,7 @@ export class LoginCredentialsViewComponent { this.showPasswordCount = !this.showPasswordCount; } - setTotpCopyCode(e: any) { - this.totpCopyCode = e; + setTotpCopyCode(e: TotpCodeValues) { + this.totpCodeCopyObj = e; } } diff --git a/libs/vault/src/components/totp-countdown/totp-countdown.component.ts b/libs/vault/src/components/totp-countdown/totp-countdown.component.ts index aeb7d306540..f93e8400b0e 100644 --- a/libs/vault/src/components/totp-countdown/totp-countdown.component.ts +++ b/libs/vault/src/components/totp-countdown/totp-countdown.component.ts @@ -44,13 +44,16 @@ export class BitTotpCountdownComponent implements OnInit { if (this.totpCode != null) { if (this.totpCode.length > 4) { this.totpCodeFormatted = this.formatTotpCode(); - this.sendCopyCode.emit(this.totpCodeFormatted); + this.sendCopyCode.emit({ + totpCode: this.totpCode, + totpCodeFormatted: this.totpCodeFormatted, + }); } else { this.totpCodeFormatted = this.totpCode; } } else { this.totpCodeFormatted = null; - this.sendCopyCode.emit(this.totpCodeFormatted); + this.sendCopyCode.emit({ totpCode: null, totpCodeFormatted: null }); this.clearTotp(); } } From 2882fa30778ebff67de7d203db9f1871e246b3d3 Mon Sep 17 00:00:00 2001 From: Jordan Aasen <166539328+jaasen-livefront@users.noreply.github.com> Date: Fri, 30 Aug 2024 11:16:06 -0700 Subject: [PATCH 07/24] [AC-2268] - migrate toast to CL service for admin-console (#10663) * migrate toast to CL service for admin-console * fix spec * add missing dep for toastService * fix toastService args * fix toastService args * fix toastService args --- .../components/vault/collections.component.ts | 3 + .../vault/app/vault/collections.component.ts | 3 + .../common/base.events.component.ts | 12 +-- .../common/base.people.component.ts | 53 ++++++------- .../manage/entity-events.component.ts | 13 ++-- .../organizations/manage/events.component.ts | 3 + .../manage/group-add-edit.component.ts | 39 ++++++---- .../verify-recover-delete-org.component.ts | 12 +-- .../bulk/bulk-enable-sm-dialog.component.ts | 13 ++-- .../member-dialog/member-dialog.component.ts | 76 ++++++++++--------- .../components/reset-password.component.ts | 53 ++++++------- .../policies/policy-edit.component.ts | 15 ++-- .../settings/account.component.ts | 19 +++-- .../delete-organization-dialog.component.ts | 13 ++-- ...families-for-enterprise-setup.component.ts | 25 +++--- .../enroll-master-password-reset.component.ts | 9 ++- ...erify-recover-delete-provider.component.ts | 12 +-- .../individual-vault/collections.component.ts | 4 +- .../organization-options.component.ts | 4 +- .../vault/org-vault/collections.component.ts | 4 +- .../device-approvals.component.ts | 49 ++++++------ .../domain-add-edit-dialog.component.ts | 33 ++++++-- .../domain-verification.component.ts | 35 +++++---- .../organizations/manage/scim.component.ts | 15 +++- .../clients/add-organization.component.ts | 13 ++-- .../guards/provider-permissions.guard.spec.ts | 4 +- .../guards/provider-permissions.guard.ts | 16 +++- .../providers/manage/events.component.ts | 3 + .../providers/manage/people.component.ts | 14 ++-- .../manage/user-add-edit.component.ts | 23 +++--- .../providers/settings/account.component.ts | 19 +++-- .../service-accounts-events.component.ts | 3 + .../components/collections.component.ts | 24 ++++-- 33 files changed, 381 insertions(+), 255 deletions(-) diff --git a/apps/browser/src/vault/popup/components/vault/collections.component.ts b/apps/browser/src/vault/popup/components/vault/collections.component.ts index 0a420192c88..1204a436f27 100644 --- a/apps/browser/src/vault/popup/components/vault/collections.component.ts +++ b/apps/browser/src/vault/popup/components/vault/collections.component.ts @@ -12,6 +12,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service"; +import { ToastService } from "@bitwarden/components"; @Component({ selector: "app-vault-collections", @@ -30,6 +31,7 @@ export class CollectionsComponent extends BaseCollectionsComponent implements On logService: LogService, configService: ConfigService, accountService: AccountService, + toastService: ToastService, ) { super( collectionService, @@ -40,6 +42,7 @@ export class CollectionsComponent extends BaseCollectionsComponent implements On logService, configService, accountService, + toastService, ); } diff --git a/apps/desktop/src/vault/app/vault/collections.component.ts b/apps/desktop/src/vault/app/vault/collections.component.ts index f659d4352c0..7183e4bd5c7 100644 --- a/apps/desktop/src/vault/app/vault/collections.component.ts +++ b/apps/desktop/src/vault/app/vault/collections.component.ts @@ -9,6 +9,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service"; +import { ToastService } from "@bitwarden/components"; @Component({ selector: "app-vault-collections", @@ -24,6 +25,7 @@ export class CollectionsComponent extends BaseCollectionsComponent { logService: LogService, configService: ConfigService, accountService: AccountService, + toastService: ToastService, ) { super( collectionService, @@ -34,6 +36,7 @@ export class CollectionsComponent extends BaseCollectionsComponent { logService, configService, accountService, + toastService, ); } } diff --git a/apps/web/src/app/admin-console/common/base.events.component.ts b/apps/web/src/app/admin-console/common/base.events.component.ts index 12c051271e1..578a9e551de 100644 --- a/apps/web/src/app/admin-console/common/base.events.component.ts +++ b/apps/web/src/app/admin-console/common/base.events.component.ts @@ -8,6 +8,7 @@ import { FileDownloadService } from "@bitwarden/common/platform/abstractions/fil import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { ToastService } from "@bitwarden/components"; import { EventService } from "../../core"; import { EventExportService } from "../../tools/event-export"; @@ -34,6 +35,7 @@ export abstract class BaseEventsComponent { protected platformUtilsService: PlatformUtilsService, protected logService: LogService, protected fileDownloadService: FileDownloadService, + private toastService: ToastService, ) { const defaultDates = this.eventService.getDefaultDateFilters(); this.start = defaultDates[0]; @@ -164,11 +166,11 @@ export abstract class BaseEventsComponent { try { dates = this.eventService.formatDateFilters(this.start, this.end); } catch (e) { - this.platformUtilsService.showToast( - "error", - this.i18nService.t("errorOccurred"), - this.i18nService.t("invalidDateRange"), - ); + this.toastService.showToast({ + variant: "error", + title: this.i18nService.t("errorOccurred"), + message: this.i18nService.t("invalidDateRange"), + }); return null; } return dates; diff --git a/apps/web/src/app/admin-console/common/base.people.component.ts b/apps/web/src/app/admin-console/common/base.people.component.ts index 0dad7ab7b13..e24f3ac78e7 100644 --- a/apps/web/src/app/admin-console/common/base.people.component.ts +++ b/apps/web/src/app/admin-console/common/base.people.component.ts @@ -22,7 +22,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; -import { DialogService } from "@bitwarden/components"; +import { DialogService, ToastService } from "@bitwarden/components"; import { OrganizationUserView } from "../organizations/core/views/organization-user.view"; import { UserConfirmComponent } from "../organizations/manage/user-confirm.component"; @@ -127,6 +127,7 @@ export abstract class BasePeopleComponent< protected userNamePipe: UserNamePipe, protected dialogService: DialogService, protected organizationManagementPreferencesService: OrganizationManagementPreferencesService, + protected toastService: ToastService, ) {} abstract edit(user: UserType): void; @@ -251,11 +252,11 @@ export abstract class BasePeopleComponent< this.actionPromise = this.deleteUser(user.id); try { await this.actionPromise; - this.platformUtilsService.showToast( - "success", - null, - this.i18nService.t("removedUserId", this.userNamePipe.transform(user)), - ); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("removedUserId", this.userNamePipe.transform(user)), + }); this.removeUser(user); } catch (e) { this.validationService.showError(e); @@ -282,11 +283,11 @@ export abstract class BasePeopleComponent< this.actionPromise = this.revokeUser(user.id); try { await this.actionPromise; - this.platformUtilsService.showToast( - "success", - null, - this.i18nService.t("revokedUserId", this.userNamePipe.transform(user)), - ); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("revokedUserId", this.userNamePipe.transform(user)), + }); await this.load(); } catch (e) { this.validationService.showError(e); @@ -298,11 +299,11 @@ export abstract class BasePeopleComponent< this.actionPromise = this.restoreUser(user.id); try { await this.actionPromise; - this.platformUtilsService.showToast( - "success", - null, - this.i18nService.t("restoredUserId", this.userNamePipe.transform(user)), - ); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("restoredUserId", this.userNamePipe.transform(user)), + }); await this.load(); } catch (e) { this.validationService.showError(e); @@ -318,11 +319,11 @@ export abstract class BasePeopleComponent< this.actionPromise = this.reinviteUser(user.id); try { await this.actionPromise; - this.platformUtilsService.showToast( - "success", - null, - this.i18nService.t("hasBeenReinvited", this.userNamePipe.transform(user)), - ); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("hasBeenReinvited", this.userNamePipe.transform(user)), + }); } catch (e) { this.validationService.showError(e); } @@ -344,11 +345,11 @@ export abstract class BasePeopleComponent< this.actionPromise = this.confirmUser(user, publicKey); await this.actionPromise; updateUser(this); - this.platformUtilsService.showToast( - "success", - null, - this.i18nService.t("hasBeenConfirmed", this.userNamePipe.transform(user)), - ); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("hasBeenConfirmed", this.userNamePipe.transform(user)), + }); } catch (e) { this.validationService.showError(e); throw e; diff --git a/apps/web/src/app/admin-console/organizations/manage/entity-events.component.ts b/apps/web/src/app/admin-console/organizations/manage/entity-events.component.ts index 0c503e81bcc..c9af2c8b5de 100644 --- a/apps/web/src/app/admin-console/organizations/manage/entity-events.component.ts +++ b/apps/web/src/app/admin-console/organizations/manage/entity-events.component.ts @@ -12,7 +12,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; -import { DialogService, TableDataSource } from "@bitwarden/components"; +import { DialogService, TableDataSource, ToastService } from "@bitwarden/components"; import { EventService } from "../../../core"; import { SharedModule } from "../../../shared"; @@ -63,6 +63,7 @@ export class EntityEventsComponent implements OnInit { private organizationUserService: OrganizationUserService, private formBuilder: FormBuilder, private validationService: ValidationService, + private toastService: ToastService, ) {} async ngOnInit() { @@ -109,11 +110,11 @@ export class EntityEventsComponent implements OnInit { this.filterFormGroup.value.end, ); } catch (e) { - this.platformUtilsService.showToast( - "error", - this.i18nService.t("errorOccurred"), - this.i18nService.t("invalidDateRange"), - ); + this.toastService.showToast({ + variant: "error", + title: this.i18nService.t("errorOccurred"), + message: this.i18nService.t("invalidDateRange"), + }); return; } diff --git a/apps/web/src/app/admin-console/organizations/manage/events.component.ts b/apps/web/src/app/admin-console/organizations/manage/events.component.ts index ec0a43eeda7..0b7e3d42295 100644 --- a/apps/web/src/app/admin-console/organizations/manage/events.component.ts +++ b/apps/web/src/app/admin-console/organizations/manage/events.component.ts @@ -14,6 +14,7 @@ import { FileDownloadService } from "@bitwarden/common/platform/abstractions/fil import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { ToastService } from "@bitwarden/components"; import { EventService } from "../../../core"; import { EventExportService } from "../../../tools/event-export"; @@ -51,6 +52,7 @@ export class EventsComponent extends BaseEventsComponent implements OnInit, OnDe private organizationUserService: OrganizationUserService, private providerService: ProviderService, fileDownloadService: FileDownloadService, + toastService: ToastService, ) { super( eventService, @@ -59,6 +61,7 @@ export class EventsComponent extends BaseEventsComponent implements OnInit, OnDe platformUtilsService, logService, fileDownloadService, + toastService, ); } diff --git a/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.ts b/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.ts index c12d133f37b..82fa85476f2 100644 --- a/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.ts +++ b/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.ts @@ -24,7 +24,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { UserId } from "@bitwarden/common/types/guid"; -import { DialogService } from "@bitwarden/components"; +import { DialogService, ToastService } from "@bitwarden/components"; import { CollectionAdminService } from "../../../vault/core/collection-admin.service"; import { CollectionAdminView } from "../../../vault/core/views/collection-admin.view"; @@ -213,6 +213,7 @@ export class GroupAddEditComponent implements OnInit, OnDestroy { private organizationService: OrganizationService, private accountService: AccountService, private collectionAdminService: CollectionAdminService, + private toastService: ToastService, ) { this.tabIndex = params.initialTab ?? GroupAddEditTabType.Info; } @@ -280,11 +281,14 @@ export class GroupAddEditComponent implements OnInit, OnDestroy { if (this.groupForm.invalid) { if (this.tabIndex !== GroupAddEditTabType.Info) { - this.platformUtilsService.showToast( - "error", - null, - this.i18nService.t("fieldOnTabRequiresAttention", this.i18nService.t("groupInfo")), - ); + this.toastService.showToast({ + variant: "error", + title: null, + message: this.i18nService.t( + "fieldOnTabRequiresAttention", + this.i18nService.t("groupInfo"), + ), + }); } return; } @@ -300,11 +304,14 @@ export class GroupAddEditComponent implements OnInit, OnDestroy { await this.groupService.save(groupView); - this.platformUtilsService.showToast( - "success", - null, - this.i18nService.t(this.editMode ? "editedGroupId" : "createdGroupId", formValue.name), - ); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t( + this.editMode ? "editedGroupId" : "createdGroupId", + formValue.name, + ), + }); this.dialogRef.close(GroupAddEditDialogResultType.Saved); }; @@ -325,11 +332,11 @@ export class GroupAddEditComponent implements OnInit, OnDestroy { await this.groupService.delete(this.organizationId, this.groupId); - this.platformUtilsService.showToast( - "success", - null, - this.i18nService.t("deletedGroupId", this.group.name), - ); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("deletedGroupId", this.group.name), + }); this.dialogRef.close(GroupAddEditDialogResultType.Deleted); }; } diff --git a/apps/web/src/app/admin-console/organizations/manage/verify-recover-delete-org.component.ts b/apps/web/src/app/admin-console/organizations/manage/verify-recover-delete-org.component.ts index 0039347dc61..10969350695 100644 --- a/apps/web/src/app/admin-console/organizations/manage/verify-recover-delete-org.component.ts +++ b/apps/web/src/app/admin-console/organizations/manage/verify-recover-delete-org.component.ts @@ -6,6 +6,7 @@ import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-conso import { OrganizationVerifyDeleteRecoverRequest } from "@bitwarden/common/admin-console/models/request/organization-verify-delete-recover.request"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { ToastService } from "@bitwarden/components"; import { SharedModule } from "../../../shared/shared.module"; @@ -27,6 +28,7 @@ export class VerifyRecoverDeleteOrgComponent implements OnInit { private platformUtilsService: PlatformUtilsService, private i18nService: I18nService, private route: ActivatedRoute, + private toastService: ToastService, ) {} async ngOnInit() { @@ -44,11 +46,11 @@ export class VerifyRecoverDeleteOrgComponent implements OnInit { submit = async () => { const request = new OrganizationVerifyDeleteRecoverRequest(this.token); await this.apiService.deleteUsingToken(this.orgId, request); - this.platformUtilsService.showToast( - "success", - this.i18nService.t("organizationDeleted"), - this.i18nService.t("organizationDeletedDesc"), - ); + this.toastService.showToast({ + variant: "success", + title: this.i18nService.t("organizationDeleted"), + message: this.i18nService.t("organizationDeletedDesc"), + }); await this.router.navigate(["/"]); }; } diff --git a/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-enable-sm-dialog.component.ts b/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-enable-sm-dialog.component.ts index 95dd180f45b..de12d4f26d7 100644 --- a/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-enable-sm-dialog.component.ts +++ b/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-enable-sm-dialog.component.ts @@ -4,7 +4,7 @@ import { Component, Inject, OnInit } from "@angular/core"; import { OrganizationUserService } from "@bitwarden/common/admin-console/abstractions/organization-user/organization-user.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { DialogService, TableDataSource } from "@bitwarden/components"; +import { DialogService, TableDataSource, ToastService } from "@bitwarden/components"; import { OrganizationUserView } from "../../../core"; @@ -24,6 +24,7 @@ export class BulkEnableSecretsManagerDialogComponent implements OnInit { private organizationUserService: OrganizationUserService, private platformUtilsService: PlatformUtilsService, private i18nService: I18nService, + private toastService: ToastService, ) {} ngOnInit(): void { @@ -35,11 +36,11 @@ export class BulkEnableSecretsManagerDialogComponent implements OnInit { this.data.orgId, this.dataSource.data.map((u) => u.id), ); - this.platformUtilsService.showToast( - "success", - null, - this.i18nService.t("activatedAccessToSecretsManager"), - ); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("activatedAccessToSecretsManager"), + }); this.dialogRef.close(); }; diff --git a/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts b/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts index 9f02c63f07a..d39e7784b8d 100644 --- a/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts +++ b/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts @@ -26,7 +26,7 @@ import { ProductTierType } from "@bitwarden/common/billing/enums"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view"; -import { DialogService } from "@bitwarden/components"; +import { DialogService, ToastService } from "@bitwarden/components"; import { CollectionAdminService } from "../../../../../vault/core/collection-admin.service"; import { CollectionAdminView } from "../../../../../vault/core/views/collection-admin.view"; @@ -143,6 +143,7 @@ export class MemberDialogComponent implements OnDestroy { private dialogService: DialogService, private accountService: AccountService, organizationService: OrganizationService, + private toastService: ToastService, ) { this.organization$ = organizationService .get$(this.params.organizationId) @@ -376,11 +377,11 @@ export class MemberDialogComponent implements OnDestroy { ) { this.permissionsGroup.value.manageUsers = true; (document.getElementById("manageUsers") as HTMLInputElement).checked = true; - this.platformUtilsService.showToast( - "info", - null, - this.i18nService.t("accountRecoveryManageUsers"), - ); + this.toastService.showToast({ + variant: "info", + title: null, + message: this.i18nService.t("accountRecoveryManageUsers"), + }); } } @@ -389,11 +390,11 @@ export class MemberDialogComponent implements OnDestroy { if (this.formGroup.invalid) { if (this.tabIndex !== MemberDialogTab.Role) { - this.platformUtilsService.showToast( - "error", - null, - this.i18nService.t("fieldOnTabRequiresAttention", this.i18nService.t("role")), - ); + this.toastService.showToast({ + variant: "error", + title: null, + message: this.i18nService.t("fieldOnTabRequiresAttention", this.i18nService.t("role")), + }); } return; } @@ -401,11 +402,11 @@ export class MemberDialogComponent implements OnDestroy { const organization = await firstValueFrom(this.organization$); if (!organization.useCustomPermissions && this.customUserTypeSelected) { - this.platformUtilsService.showToast( - "error", - null, - this.i18nService.t("customNonEnterpriseError"), - ); + this.toastService.showToast({ + variant: "error", + title: null, + message: this.i18nService.t("customNonEnterpriseError"), + }); return; } @@ -452,11 +453,14 @@ export class MemberDialogComponent implements OnDestroy { await this.userService.invite(emails, userView); } - this.platformUtilsService.showToast( - "success", - null, - this.i18nService.t(this.editMode ? "editedUserId" : "invitedUsers", this.params.name), - ); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t( + this.editMode ? "editedUserId" : "invitedUsers", + this.params.name, + ), + }); this.close(MemberDialogResult.Saved); }; @@ -492,11 +496,11 @@ export class MemberDialogComponent implements OnDestroy { this.params.organizationUserId, ); - this.platformUtilsService.showToast( - "success", - null, - this.i18nService.t("removedUserId", this.params.name), - ); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("removedUserId", this.params.name), + }); this.close(MemberDialogResult.Deleted); }; @@ -529,11 +533,11 @@ export class MemberDialogComponent implements OnDestroy { this.params.organizationUserId, ); - this.platformUtilsService.showToast( - "success", - null, - this.i18nService.t("revokedUserId", this.params.name), - ); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("revokedUserId", this.params.name), + }); this.isRevoked = true; this.close(MemberDialogResult.Revoked); }; @@ -548,11 +552,11 @@ export class MemberDialogComponent implements OnDestroy { this.params.organizationUserId, ); - this.platformUtilsService.showToast( - "success", - null, - this.i18nService.t("restoredUserId", this.params.name), - ); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("restoredUserId", this.params.name), + }); this.isRevoked = false; this.close(MemberDialogResult.Restored); }; diff --git a/apps/web/src/app/admin-console/organizations/members/components/reset-password.component.ts b/apps/web/src/app/admin-console/organizations/members/components/reset-password.component.ts index cbda3b2bdf3..ce605a6f5aa 100644 --- a/apps/web/src/app/admin-console/organizations/members/components/reset-password.component.ts +++ b/apps/web/src/app/admin-console/organizations/members/components/reset-password.component.ts @@ -17,7 +17,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; -import { DialogService } from "@bitwarden/components"; +import { DialogService, ToastService } from "@bitwarden/components"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy"; import { OrganizationUserResetPasswordService } from "../services/organization-user-reset-password/organization-user-reset-password.service"; @@ -50,6 +50,7 @@ export class ResetPasswordComponent implements OnInit, OnDestroy { private policyService: PolicyService, private logService: LogService, private dialogService: DialogService, + private toastService: ToastService, ) {} async ngOnInit() { @@ -88,30 +89,30 @@ export class ResetPasswordComponent implements OnInit, OnDestroy { } this.platformUtilsService.copyToClipboard(value, { window: window }); - this.platformUtilsService.showToast( - "info", - null, - this.i18nService.t("valueCopied", this.i18nService.t("password")), - ); + this.toastService.showToast({ + variant: "info", + title: null, + message: this.i18nService.t("valueCopied", this.i18nService.t("password")), + }); } async submit() { // Validation if (this.newPassword == null || this.newPassword === "") { - this.platformUtilsService.showToast( - "error", - this.i18nService.t("errorOccurred"), - this.i18nService.t("masterPasswordRequired"), - ); + this.toastService.showToast({ + variant: "error", + title: this.i18nService.t("errorOccurred"), + message: this.i18nService.t("masterPasswordRequired"), + }); return false; } if (this.newPassword.length < Utils.minimumPasswordLength) { - this.platformUtilsService.showToast( - "error", - this.i18nService.t("errorOccurred"), - this.i18nService.t("masterPasswordMinlength", Utils.minimumPasswordLength), - ); + this.toastService.showToast({ + variant: "error", + title: this.i18nService.t("errorOccurred"), + message: this.i18nService.t("masterPasswordMinlength", Utils.minimumPasswordLength), + }); return false; } @@ -123,11 +124,11 @@ export class ResetPasswordComponent implements OnInit, OnDestroy { this.enforcedPolicyOptions, ) ) { - this.platformUtilsService.showToast( - "error", - this.i18nService.t("errorOccurred"), - this.i18nService.t("masterPasswordPolicyRequirementsNotMet"), - ); + this.toastService.showToast({ + variant: "error", + title: this.i18nService.t("errorOccurred"), + message: this.i18nService.t("masterPasswordPolicyRequirementsNotMet"), + }); return; } @@ -151,11 +152,11 @@ export class ResetPasswordComponent implements OnInit, OnDestroy { this.organizationId, ); await this.formPromise; - this.platformUtilsService.showToast( - "success", - null, - this.i18nService.t("resetPasswordSuccess"), - ); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("resetPasswordSuccess"), + }); this.passwordReset.emit(); } catch (e) { this.logService.error(e); diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit.component.ts index d84a7dec997..8f855e3c8c2 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit.component.ts @@ -15,7 +15,7 @@ import { PolicyRequest } from "@bitwarden/common/admin-console/models/request/po import { PolicyResponse } from "@bitwarden/common/admin-console/models/response/policy.response"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { DialogService } from "@bitwarden/components"; +import { DialogService, ToastService } from "@bitwarden/components"; import { BasePolicy, BasePolicyComponent } from "../policies"; @@ -58,6 +58,7 @@ export class PolicyEditComponent implements AfterViewInit { private cdr: ChangeDetectorRef, private formBuilder: FormBuilder, private dialogRef: DialogRef, + private toastService: ToastService, ) {} get policy(): BasePolicy { return this.data.policy; @@ -95,7 +96,7 @@ export class PolicyEditComponent implements AfterViewInit { try { request = await this.policyComponent.buildRequest(this.data.policiesEnabledMap); } catch (e) { - this.platformUtilsService.showToast("error", null, e.message); + this.toastService.showToast({ variant: "error", title: null, message: e.message }); return; } this.formPromise = this.policyApiService.putPolicy( @@ -104,11 +105,11 @@ export class PolicyEditComponent implements AfterViewInit { request, ); await this.formPromise; - this.platformUtilsService.showToast( - "success", - null, - this.i18nService.t("editedPolicyId", this.i18nService.t(this.data.policy.name)), - ); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("editedPolicyId", this.i18nService.t(this.data.policy.name)), + }); this.dialogRef.close(PolicyEditDialogResult.Saved); }; diff --git a/apps/web/src/app/admin-console/organizations/settings/account.component.ts b/apps/web/src/app/admin-console/organizations/settings/account.component.ts index 7cfbee166e3..8c97a176fb8 100644 --- a/apps/web/src/app/admin-console/organizations/settings/account.component.ts +++ b/apps/web/src/app/admin-console/organizations/settings/account.component.ts @@ -14,7 +14,7 @@ import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.se import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; -import { DialogService } from "@bitwarden/components"; +import { DialogService, ToastService } from "@bitwarden/components"; import { ApiKeyComponent } from "../../../auth/settings/security/api-key.component"; import { PurgeVaultComponent } from "../../../vault/settings/purge-vault.component"; @@ -77,6 +77,7 @@ export class AccountComponent implements OnInit, OnDestroy { private organizationApiService: OrganizationApiServiceAbstraction, private dialogService: DialogService, private formBuilder: FormBuilder, + private toastService: ToastService, ) {} async ngOnInit() { @@ -167,7 +168,11 @@ export class AccountComponent implements OnInit, OnDestroy { await this.organizationApiService.save(this.organizationId, request); - this.platformUtilsService.showToast("success", null, this.i18nService.t("organizationUpdated")); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("organizationUpdated"), + }); }; submitCollectionManagement = async () => { @@ -184,11 +189,11 @@ export class AccountComponent implements OnInit, OnDestroy { await this.organizationApiService.updateCollectionManagement(this.organizationId, request); - this.platformUtilsService.showToast( - "success", - null, - this.i18nService.t("updatedCollectionManagement"), - ); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("updatedCollectionManagement"), + }); }; async deleteOrganization() { diff --git a/apps/web/src/app/admin-console/organizations/settings/components/delete-organization-dialog.component.ts b/apps/web/src/app/admin-console/organizations/settings/components/delete-organization-dialog.component.ts index 7a88905449d..b5e1baf70f1 100644 --- a/apps/web/src/app/admin-console/organizations/settings/components/delete-organization-dialog.component.ts +++ b/apps/web/src/app/admin-console/organizations/settings/components/delete-organization-dialog.component.ts @@ -14,7 +14,7 @@ import { Utils } from "@bitwarden/common/platform/misc/utils"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; -import { DialogService } from "@bitwarden/components"; +import { DialogService, ToastService } from "@bitwarden/components"; import { UserVerificationModule } from "../../../../auth/shared/components/user-verification"; import { SharedModule } from "../../../../shared/shared.module"; @@ -94,6 +94,7 @@ export class DeleteOrganizationDialogComponent implements OnInit, OnDestroy { private organizationService: OrganizationService, private organizationApiService: OrganizationApiServiceAbstraction, private formBuilder: FormBuilder, + private toastService: ToastService, ) {} ngOnDestroy(): void { @@ -121,11 +122,11 @@ export class DeleteOrganizationDialogComponent implements OnInit, OnDestroy { .buildRequest(this.formGroup.value.secret) .then((request) => this.organizationApiService.delete(this.organization.id, request)); - this.platformUtilsService.showToast( - "success", - this.i18nService.t("organizationDeleted"), - this.i18nService.t("organizationDeletedDesc"), - ); + this.toastService.showToast({ + variant: "success", + title: this.i18nService.t("organizationDeleted"), + message: this.i18nService.t("organizationDeletedDesc"), + }); this.dialogRef.close(DeleteOrganizationDialogResult.Deleted); }; diff --git a/apps/web/src/app/admin-console/organizations/sponsorships/families-for-enterprise-setup.component.ts b/apps/web/src/app/admin-console/organizations/sponsorships/families-for-enterprise-setup.component.ts index f7ca9b5c75c..f857eb63764 100644 --- a/apps/web/src/app/admin-console/organizations/sponsorships/families-for-enterprise-setup.component.ts +++ b/apps/web/src/app/admin-console/organizations/sponsorships/families-for-enterprise-setup.component.ts @@ -14,7 +14,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; -import { DialogService } from "@bitwarden/components"; +import { DialogService, ToastService } from "@bitwarden/components"; import { OrganizationPlansComponent } from "../../../billing"; import { SharedModule } from "../../../shared"; @@ -68,6 +68,7 @@ export class FamiliesForEnterpriseSetupComponent implements OnInit, OnDestroy { private organizationService: OrganizationService, private dialogService: DialogService, private formBuilder: FormBuilder, + private toastService: ToastService, ) {} async ngOnInit() { @@ -76,12 +77,12 @@ export class FamiliesForEnterpriseSetupComponent implements OnInit, OnDestroy { this.route.queryParams.pipe(first()).subscribe(async (qParams) => { const error = qParams.token == null; if (error) { - this.platformUtilsService.showToast( - "error", - null, - this.i18nService.t("sponsoredFamiliesAcceptFailed"), - { timeout: 10000 }, - ); + this.toastService.showToast({ + variant: "error", + title: null, + message: this.i18nService.t("sponsoredFamiliesAcceptFailed"), + timeout: 10000, + }); // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. // eslint-disable-next-line @typescript-eslint/no-floating-promises this.router.navigate(["/"]); @@ -139,11 +140,11 @@ export class FamiliesForEnterpriseSetupComponent implements OnInit, OnDestroy { request.sponsoredOrganizationId = organizationId; await this.apiService.postRedeemSponsorship(this.token, request); - this.platformUtilsService.showToast( - "success", - null, - this.i18nService.t("sponsoredFamiliesOfferRedeemed"), - ); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("sponsoredFamiliesOfferRedeemed"), + }); await this.syncService.fullSync(true); // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. diff --git a/apps/web/src/app/admin-console/organizations/users/enroll-master-password-reset.component.ts b/apps/web/src/app/admin-console/organizations/users/enroll-master-password-reset.component.ts index e262fa51ffe..bbd344e289e 100644 --- a/apps/web/src/app/admin-console/organizations/users/enroll-master-password-reset.component.ts +++ b/apps/web/src/app/admin-console/organizations/users/enroll-master-password-reset.component.ts @@ -8,7 +8,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; -import { DialogService } from "@bitwarden/components"; +import { DialogService, ToastService } from "@bitwarden/components"; import { OrganizationUserResetPasswordService } from "../members/services/organization-user-reset-password/organization-user-reset-password.service"; @@ -29,6 +29,7 @@ export class EnrollMasterPasswordReset { syncService: SyncService, logService: LogService, userVerificationService: UserVerificationService, + toastService: ToastService, ) { const result = await UserVerificationDialogComponent.open(dialogService, { title: "enrollAccountRecovery", @@ -71,7 +72,11 @@ export class EnrollMasterPasswordReset { // Enrollment succeeded try { - platformUtilsService.showToast("success", null, i18nService.t("enrollPasswordResetSuccess")); + toastService.showToast({ + variant: "success", + title: null, + message: i18nService.t("enrollPasswordResetSuccess"), + }); await syncService.fullSync(true); } catch (e) { logService.error(e); diff --git a/apps/web/src/app/admin-console/providers/verify-recover-delete-provider.component.ts b/apps/web/src/app/admin-console/providers/verify-recover-delete-provider.component.ts index 0550820cda4..dc6fa099610 100644 --- a/apps/web/src/app/admin-console/providers/verify-recover-delete-provider.component.ts +++ b/apps/web/src/app/admin-console/providers/verify-recover-delete-provider.component.ts @@ -7,6 +7,7 @@ import { ProviderVerifyRecoverDeleteRequest } from "@bitwarden/common/admin-cons import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { ToastService } from "@bitwarden/components"; @Component({ selector: "app-verify-recover-delete-provider", @@ -27,6 +28,7 @@ export class VerifyRecoverDeleteProviderComponent implements OnInit { private i18nService: I18nService, private route: ActivatedRoute, private logService: LogService, + private toastService: ToastService, ) {} async ngOnInit() { @@ -48,11 +50,11 @@ export class VerifyRecoverDeleteProviderComponent implements OnInit { request, ); await this.formPromise; - this.platformUtilsService.showToast( - "success", - this.i18nService.t("providerDeleted"), - this.i18nService.t("providerDeletedDesc"), - ); + this.toastService.showToast({ + variant: "success", + title: this.i18nService.t("providerDeleted"), + message: this.i18nService.t("providerDeletedDesc"), + }); await this.router.navigate(["/"]); } catch (e) { this.logService.error(e); diff --git a/apps/web/src/app/vault/individual-vault/collections.component.ts b/apps/web/src/app/vault/individual-vault/collections.component.ts index ed9dfbaf271..cd52e41e38d 100644 --- a/apps/web/src/app/vault/individual-vault/collections.component.ts +++ b/apps/web/src/app/vault/individual-vault/collections.component.ts @@ -11,7 +11,7 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service"; import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view"; -import { DialogService } from "@bitwarden/components"; +import { DialogService, ToastService } from "@bitwarden/components"; @Component({ selector: "app-vault-collections", @@ -29,6 +29,7 @@ export class CollectionsComponent extends BaseCollectionsComponent implements On accountService: AccountService, protected dialogRef: DialogRef, @Inject(DIALOG_DATA) params: CollectionsDialogParams, + toastService: ToastService, ) { super( collectionService, @@ -39,6 +40,7 @@ export class CollectionsComponent extends BaseCollectionsComponent implements On logService, configService, accountService, + toastService, ); this.cipherId = params?.cipherId; } diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/components/organization-options.component.ts b/apps/web/src/app/vault/individual-vault/vault-filter/components/organization-options.component.ts index bdcd409dbf8..2ff3e953ae7 100644 --- a/apps/web/src/app/vault/individual-vault/vault-filter/components/organization-options.component.ts +++ b/apps/web/src/app/vault/individual-vault/vault-filter/components/organization-options.component.ts @@ -15,7 +15,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { SyncService } from "@bitwarden/common/platform/sync"; -import { DialogService } from "@bitwarden/components"; +import { DialogService, ToastService } from "@bitwarden/components"; import { OrganizationUserResetPasswordService } from "../../../../admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service"; import { EnrollMasterPasswordReset } from "../../../../admin-console/organizations/users/enroll-master-password-reset.component"; @@ -50,6 +50,7 @@ export class OrganizationOptionsComponent implements OnInit, OnDestroy { private dialogService: DialogService, private resetPasswordService: OrganizationUserResetPasswordService, private userVerificationService: UserVerificationService, + private toastService: ToastService, ) {} async ngOnInit() { @@ -158,6 +159,7 @@ export class OrganizationOptionsComponent implements OnInit, OnDestroy { this.syncService, this.logService, this.userVerificationService, + this.toastService, ); } else { // Remove reset password diff --git a/apps/web/src/app/vault/org-vault/collections.component.ts b/apps/web/src/app/vault/org-vault/collections.component.ts index e0c0ce91a7b..72816d53214 100644 --- a/apps/web/src/app/vault/org-vault/collections.component.ts +++ b/apps/web/src/app/vault/org-vault/collections.component.ts @@ -15,7 +15,7 @@ import { CipherData } from "@bitwarden/common/vault/models/data/cipher.data"; import { Cipher } from "@bitwarden/common/vault/models/domain/cipher"; import { CipherCollectionsRequest } from "@bitwarden/common/vault/models/request/cipher-collections.request"; import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view"; -import { DialogService } from "@bitwarden/components"; +import { DialogService, ToastService } from "@bitwarden/components"; import { CollectionsComponent as BaseCollectionsComponent, @@ -41,6 +41,7 @@ export class CollectionsComponent extends BaseCollectionsComponent { accountService: AccountService, protected dialogRef: DialogRef, @Inject(DIALOG_DATA) params: OrgVaultCollectionsDialogParams, + toastService: ToastService, ) { super( collectionService, @@ -53,6 +54,7 @@ export class CollectionsComponent extends BaseCollectionsComponent { accountService, dialogRef, params, + toastService, ); this.allowSelectNone = true; this.collectionIds = params?.collectionIds; diff --git a/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/device-approvals/device-approvals.component.ts b/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/device-approvals/device-approvals.component.ts index 5f000142527..de7642aa347 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/device-approvals/device-approvals.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/device-approvals/device-approvals.component.ts @@ -14,7 +14,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; -import { TableDataSource, NoItemsModule } from "@bitwarden/components"; +import { TableDataSource, NoItemsModule, ToastService } from "@bitwarden/components"; import { Devices } from "@bitwarden/web-vault/app/admin-console/icons"; import { LooseComponentsModule } from "@bitwarden/web-vault/app/shared"; import { SharedModule } from "@bitwarden/web-vault/app/shared/shared.module"; @@ -54,6 +54,7 @@ export class DeviceApprovalsComponent implements OnInit, OnDestroy { private logService: LogService, private validationService: ValidationService, private configService: ConfigService, + private toastService: ToastService, ) {} async ngOnInit() { @@ -84,17 +85,17 @@ export class DeviceApprovalsComponent implements OnInit, OnDestroy { authRequest, ); - this.platformUtilsService.showToast( - "success", - null, - this.i18nService.t("loginRequestApproved"), - ); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("loginRequestApproved"), + }); } catch (error) { - this.platformUtilsService.showToast( - "error", - null, - this.i18nService.t("resetPasswordDetailsError"), - ); + this.toastService.showToast({ + variant: "error", + title: null, + message: this.i18nService.t("resetPasswordDetailsError"), + }); } }); } @@ -109,18 +110,22 @@ export class DeviceApprovalsComponent implements OnInit, OnDestroy { this.organizationId, this.tableDataSource.data, ); - this.platformUtilsService.showToast( - "success", - null, - this.i18nService.t("allLoginRequestsApproved"), - ); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("allLoginRequestsApproved"), + }); }); } async denyRequest(requestId: string) { await this.performAsyncAction(async () => { await this.organizationAuthRequestService.denyPendingRequests(this.organizationId, requestId); - this.platformUtilsService.showToast("error", null, this.i18nService.t("loginRequestDenied")); + this.toastService.showToast({ + variant: "error", + title: null, + message: this.i18nService.t("loginRequestDenied"), + }); }); } @@ -134,11 +139,11 @@ export class DeviceApprovalsComponent implements OnInit, OnDestroy { this.organizationId, ...this.tableDataSource.data.map((r) => r.id), ); - this.platformUtilsService.showToast( - "error", - null, - this.i18nService.t("allLoginRequestsDenied"), - ); + this.toastService.showToast({ + variant: "error", + title: null, + message: this.i18nService.t("allLoginRequestsDenied"), + }); }); } diff --git a/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/domain-verification/domain-add-edit-dialog/domain-add-edit-dialog.component.ts b/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/domain-verification/domain-add-edit-dialog/domain-add-edit-dialog.component.ts index 52e46915e9f..e0b76c7f5c3 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/domain-verification/domain-add-edit-dialog/domain-add-edit-dialog.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/domain-verification/domain-add-edit-dialog/domain-add-edit-dialog.component.ts @@ -13,7 +13,7 @@ import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@bitw import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; -import { DialogService } from "@bitwarden/components"; +import { DialogService, ToastService } from "@bitwarden/components"; import { domainNameValidator } from "./validators/domain-name.validator"; import { uniqueInArrayValidator } from "./validators/unique-in-array.validator"; @@ -66,6 +66,7 @@ export class DomainAddEditDialogComponent implements OnInit, OnDestroy { private orgDomainService: OrgDomainServiceAbstraction, private validationService: ValidationService, private dialogService: DialogService, + private toastService: ToastService, ) {} // Angular Method Implementations @@ -112,7 +113,11 @@ export class DomainAddEditDialogComponent implements OnInit, OnDestroy { // Creates a new domain record. The DNS TXT Record will be generated server-side and returned in the response. saveDomain = async (): Promise => { if (this.domainForm.invalid) { - this.platformUtilsService.showToast("error", null, this.i18nService.t("domainFormInvalid")); + this.toastService.showToast({ + variant: "error", + title: null, + message: this.i18nService.t("domainFormInvalid"), + }); return; } @@ -126,7 +131,11 @@ export class DomainAddEditDialogComponent implements OnInit, OnDestroy { this.data.orgDomain = await this.orgDomainApiService.post(this.data.organizationId, request); // Patch the DNS TXT Record that was generated server-side this.domainForm.controls.txt.patchValue(this.data.orgDomain.txt); - this.platformUtilsService.showToast("success", null, this.i18nService.t("domainSaved")); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("domainSaved"), + }); } catch (e) { this.handleDomainSaveError(e); } @@ -177,7 +186,11 @@ export class DomainAddEditDialogComponent implements OnInit, OnDestroy { verifyDomain = async (): Promise => { if (this.domainForm.invalid) { // Note: shouldn't be possible, but going to leave this to be safe. - this.platformUtilsService.showToast("error", null, this.i18nService.t("domainFormInvalid")); + this.toastService.showToast({ + variant: "error", + title: null, + message: this.i18nService.t("domainFormInvalid"), + }); return; } @@ -188,7 +201,11 @@ export class DomainAddEditDialogComponent implements OnInit, OnDestroy { ); if (this.data.orgDomain.verifiedDate) { - this.platformUtilsService.showToast("success", null, this.i18nService.t("domainVerified")); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("domainVerified"), + }); this.dialogRef.close(); } else { this.domainNameCtrl.setErrors({ @@ -250,7 +267,11 @@ export class DomainAddEditDialogComponent implements OnInit, OnDestroy { } await this.orgDomainApiService.delete(this.data.organizationId, this.data.orgDomain.id); - this.platformUtilsService.showToast("success", null, this.i18nService.t("domainRemoved")); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("domainRemoved"), + }); this.dialogRef.close(); }; diff --git a/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/domain-verification/domain-verification.component.ts b/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/domain-verification/domain-verification.component.ts index 27fede30f18..bc68bdaaf54 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/domain-verification/domain-verification.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/domain-verification/domain-verification.component.ts @@ -10,7 +10,7 @@ import { ErrorResponse } from "@bitwarden/common/models/response/error.response" import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; -import { DialogService } from "@bitwarden/components"; +import { DialogService, ToastService } from "@bitwarden/components"; import { DomainAddEditDialogComponent, @@ -37,6 +37,7 @@ export class DomainVerificationComponent implements OnInit, OnDestroy { private orgDomainService: OrgDomainServiceAbstraction, private dialogService: DialogService, private validationService: ValidationService, + private toastService: ToastService, ) {} // eslint-disable-next-line @typescript-eslint/no-empty-function @@ -110,13 +111,17 @@ export class DomainVerificationComponent implements OnInit, OnDestroy { ); if (orgDomain.verifiedDate) { - this.platformUtilsService.showToast("success", null, this.i18nService.t("domainVerified")); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("domainVerified"), + }); } else { - this.platformUtilsService.showToast( - "error", - null, - this.i18nService.t("domainNotVerified", domainName), - ); + this.toastService.showToast({ + variant: "error", + title: null, + message: this.i18nService.t("domainNotVerified", domainName), + }); // Update this item so the last checked date gets updated. await this.updateOrgDomain(orgDomainId); } @@ -138,11 +143,11 @@ export class DomainVerificationComponent implements OnInit, OnDestroy { switch (errorResponse.statusCode) { case HttpStatusCode.Conflict: if (errorResponse.message.includes("The domain is not available to be claimed")) { - this.platformUtilsService.showToast( - "error", - null, - this.i18nService.t("domainNotAvailable", domainName), - ); + this.toastService.showToast({ + variant: "error", + title: null, + message: this.i18nService.t("domainNotAvailable", domainName), + }); } break; @@ -166,7 +171,11 @@ export class DomainVerificationComponent implements OnInit, OnDestroy { await this.orgDomainApiService.delete(this.organizationId, orgDomainId); - this.platformUtilsService.showToast("success", null, this.i18nService.t("domainRemoved")); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("domainRemoved"), + }); } ngOnDestroy(): void { diff --git a/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/scim.component.ts b/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/scim.component.ts index 55ae318e982..76e3caa145f 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/scim.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/scim.component.ts @@ -17,7 +17,7 @@ import { OrganizationConnectionResponse } from "@bitwarden/common/admin-console/ import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { DialogService } from "@bitwarden/components"; +import { DialogService, ToastService } from "@bitwarden/components"; @Component({ selector: "app-org-manage-scim", @@ -46,6 +46,7 @@ export class ScimComponent implements OnInit { private environmentService: EnvironmentService, private organizationApiService: OrganizationApiServiceAbstraction, private dialogService: DialogService, + private toastService: ToastService, ) {} async ngOnInit() { @@ -104,7 +105,11 @@ export class ScimComponent implements OnInit { endpointUrl: await this.getScimEndpointUrl(), clientSecret: response.apiKey, }); - this.platformUtilsService.showToast("success", null, this.i18nService.t("scimApiKeyRotated")); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("scimApiKeyRotated"), + }); }; copyScimKey = async () => { @@ -131,7 +136,11 @@ export class ScimComponent implements OnInit { } await this.setConnectionFormValues(response); - this.platformUtilsService.showToast("success", null, this.i18nService.t("scimSettingsSaved")); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("scimSettingsSaved"), + }); }; async getScimEndpointUrl() { diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/clients/add-organization.component.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/clients/add-organization.component.ts index b255593ed20..dfbf1794f15 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/clients/add-organization.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/clients/add-organization.component.ts @@ -7,7 +7,7 @@ import { Provider } from "@bitwarden/common/admin-console/models/domain/provider import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; -import { DialogService } from "@bitwarden/components"; +import { DialogService, ToastService } from "@bitwarden/components"; import { WebProviderService } from "../services/web-provider.service"; @@ -32,6 +32,7 @@ export class AddOrganizationComponent implements OnInit { private platformUtilsService: PlatformUtilsService, private validationService: ValidationService, private dialogService: DialogService, + private toastService: ToastService, ) {} async ngOnInit() { @@ -73,11 +74,11 @@ export class AddOrganizationComponent implements OnInit { return; } - this.platformUtilsService.showToast( - "success", - null, - this.i18nService.t("organizationJoinedProvider"), - ); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("organizationJoinedProvider"), + }); this.dialogRef.close(true); }; diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/guards/provider-permissions.guard.spec.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/guards/provider-permissions.guard.spec.ts index 7f49c42fb8a..a92db7edd14 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/guards/provider-permissions.guard.spec.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/guards/provider-permissions.guard.spec.ts @@ -6,7 +6,7 @@ import { ProviderService } from "@bitwarden/common/admin-console/abstractions/pr import { ProviderUserType } from "@bitwarden/common/admin-console/enums"; import { Provider } from "@bitwarden/common/admin-console/models/domain/provider"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { ToastService } from "@bitwarden/components"; import { providerPermissionsGuard } from "./provider-permissions.guard"; @@ -39,7 +39,7 @@ describe("Provider Permissions Guard", () => { TestBed.configureTestingModule({ providers: [ { provide: ProviderService, useValue: providerService }, - { provide: PlatformUtilsService, useValue: mock() }, + { provide: ToastService, useValue: mock() }, { provide: I18nService, useValue: mock() }, { provide: Router, useValue: mock() }, ], diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/guards/provider-permissions.guard.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/guards/provider-permissions.guard.ts index 6dcaf604666..dd4db2528b6 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/guards/provider-permissions.guard.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/guards/provider-permissions.guard.ts @@ -9,7 +9,7 @@ import { import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service"; import { Provider } from "@bitwarden/common/admin-console/models/domain/provider"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { ToastService } from "@bitwarden/components"; /** * `CanActivateFn` that asserts the logged in user has permission to access @@ -36,8 +36,8 @@ export function providerPermissionsGuard( return async (route: ActivatedRouteSnapshot, _state: RouterStateSnapshot) => { const providerService = inject(ProviderService); const router = inject(Router); - const platformUtilsService = inject(PlatformUtilsService); const i18nService = inject(I18nService); + const toastService = inject(ToastService); const provider = await providerService.get(route.params.providerId); if (provider == null) { @@ -45,14 +45,22 @@ export function providerPermissionsGuard( } if (!provider.isProviderAdmin && !provider.enabled) { - platformUtilsService.showToast("error", null, i18nService.t("providerIsDisabled")); + toastService.showToast({ + variant: "error", + title: null, + message: i18nService.t("providerIsDisabled"), + }); return router.createUrlTree(["/"]); } const hasSpecifiedPermissions = permissionsCallback == null || permissionsCallback(provider); if (!hasSpecifiedPermissions) { - platformUtilsService.showToast("error", null, i18nService.t("accessDenied")); + toastService.showToast({ + variant: "error", + title: null, + message: i18nService.t("accessDenied"), + }); return router.createUrlTree(["/providers", provider.id]); } diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/events.component.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/events.component.ts index 7db8d1e226f..0517715d425 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/events.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/events.component.ts @@ -9,6 +9,7 @@ import { FileDownloadService } from "@bitwarden/common/platform/abstractions/fil import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { ToastService } from "@bitwarden/components"; import { BaseEventsComponent } from "@bitwarden/web-vault/app/admin-console/common/base.events.component"; import { EventService } from "@bitwarden/web-vault/app/core"; import { EventExportService } from "@bitwarden/web-vault/app/tools/event-export"; @@ -37,6 +38,7 @@ export class EventsComponent extends BaseEventsComponent implements OnInit { logService: LogService, private userNamePipe: UserNamePipe, fileDownloadService: FileDownloadService, + toastService: ToastService, ) { super( eventService, @@ -45,6 +47,7 @@ export class EventsComponent extends BaseEventsComponent implements OnInit { platformUtilsService, logService, fileDownloadService, + toastService, ); } diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/people.component.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/people.component.ts index 1849809df5f..49961e0c7fc 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/people.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/people.component.ts @@ -21,7 +21,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; -import { DialogService } from "@bitwarden/components"; +import { DialogService, ToastService } from "@bitwarden/components"; import { BasePeopleComponent } from "@bitwarden/web-vault/app/admin-console/common/base.people.component"; import { openEntityEventsDialog } from "@bitwarden/web-vault/app/admin-console/organizations/manage/entity-events.component"; import { BulkStatusComponent } from "@bitwarden/web-vault/app/admin-console/organizations/members/components/bulk/bulk-status.component"; @@ -75,6 +75,7 @@ export class PeopleComponent dialogService: DialogService, organizationManagementPreferencesService: OrganizationManagementPreferencesService, private configService: ConfigService, + protected toastService: ToastService, ) { super( apiService, @@ -89,6 +90,7 @@ export class PeopleComponent userNamePipe, dialogService, organizationManagementPreferencesService, + toastService, ); } @@ -213,11 +215,11 @@ export class PeopleComponent const filteredUsers = users.filter((u) => u.status === ProviderUserStatusType.Invited); if (filteredUsers.length <= 0) { - this.platformUtilsService.showToast( - "error", - this.i18nService.t("errorOccurred"), - this.i18nService.t("noSelectedUsersApplicable"), - ); + this.toastService.showToast({ + variant: "error", + title: this.i18nService.t("errorOccurred"), + message: this.i18nService.t("noSelectedUsersApplicable"), + }); return; } diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/user-add-edit.component.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/user-add-edit.component.ts index 7406098ee4f..fde45224ab4 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/user-add-edit.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/user-add-edit.component.ts @@ -8,7 +8,7 @@ import { ProviderUserUpdateRequest } from "@bitwarden/common/admin-console/model import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { DialogService } from "@bitwarden/components"; +import { DialogService, ToastService } from "@bitwarden/components"; /** * @deprecated Please use the {@link MembersDialogComponent} instead. @@ -42,6 +42,7 @@ export class UserAddEditComponent implements OnInit { private platformUtilsService: PlatformUtilsService, private logService: LogService, private dialogService: DialogService, + private toastService: ToastService, ) {} async ngOnInit() { @@ -80,11 +81,11 @@ export class UserAddEditComponent implements OnInit { this.formPromise = this.apiService.postProviderUserInvite(this.providerId, request); } await this.formPromise; - this.platformUtilsService.showToast( - "success", - null, - this.i18nService.t(this.editMode ? "editedUserId" : "invitedUsers", this.name), - ); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t(this.editMode ? "editedUserId" : "invitedUsers", this.name), + }); this.savedUser.emit(); } catch (e) { this.logService.error(e); @@ -109,11 +110,11 @@ export class UserAddEditComponent implements OnInit { try { this.deletePromise = this.apiService.deleteProviderUser(this.providerId, this.providerUserId); await this.deletePromise; - this.platformUtilsService.showToast( - "success", - null, - this.i18nService.t("removedUserId", this.name), - ); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("removedUserId", this.name), + }); this.deletedUser.emit(); } catch (e) { this.logService.error(e); diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/settings/account.component.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/settings/account.component.ts index 01e863a826a..d5d7634db4c 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/settings/account.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/settings/account.component.ts @@ -14,7 +14,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; -import { DialogService } from "@bitwarden/components"; +import { DialogService, ToastService } from "@bitwarden/components"; @Component({ selector: "provider-account", @@ -49,6 +49,7 @@ export class AccountComponent implements OnDestroy, OnInit { private providerApiService: ProviderApiServiceAbstraction, private formBuilder: FormBuilder, private router: Router, + private toastService: ToastService, ) {} async ngOnInit() { @@ -86,7 +87,11 @@ export class AccountComponent implements OnDestroy, OnInit { await this.providerApiService.putProvider(this.providerId, request); await this.syncService.fullSync(true); this.provider = await this.providerApiService.getProvider(this.providerId); - this.platformUtilsService.showToast("success", null, this.i18nService.t("providerUpdated")); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("providerUpdated"), + }); }; async deleteProvider() { @@ -110,11 +115,11 @@ export class AccountComponent implements OnDestroy, OnInit { try { await this.providerApiService.deleteProvider(this.providerId); - this.platformUtilsService.showToast( - "success", - this.i18nService.t("providerDeleted"), - this.i18nService.t("providerDeletedDesc"), - ); + this.toastService.showToast({ + variant: "success", + title: this.i18nService.t("providerDeleted"), + message: this.i18nService.t("providerDeletedDesc"), + }); } catch (e) { this.logService.error(e); } diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/event-logs/service-accounts-events.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/event-logs/service-accounts-events.component.ts index 72b1d33b772..f231a273fc5 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/event-logs/service-accounts-events.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/event-logs/service-accounts-events.component.ts @@ -6,6 +6,7 @@ import { FileDownloadService } from "@bitwarden/common/platform/abstractions/fil import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { ToastService } from "@bitwarden/components"; import { BaseEventsComponent } from "@bitwarden/web-vault/app/admin-console/common/base.events.component"; import { EventService } from "@bitwarden/web-vault/app/core"; import { EventExportService } from "@bitwarden/web-vault/app/tools/event-export"; @@ -33,6 +34,7 @@ export class ServiceAccountEventsComponent platformUtilsService: PlatformUtilsService, logService: LogService, fileDownloadService: FileDownloadService, + toastService: ToastService, ) { super( eventService, @@ -41,6 +43,7 @@ export class ServiceAccountEventsComponent platformUtilsService, logService, fileDownloadService, + toastService, ); } diff --git a/libs/angular/src/admin-console/components/collections.component.ts b/libs/angular/src/admin-console/components/collections.component.ts index f185bed7e4a..4f166286184 100644 --- a/libs/angular/src/admin-console/components/collections.component.ts +++ b/libs/angular/src/admin-console/components/collections.component.ts @@ -14,6 +14,7 @@ import { CollectionService } from "@bitwarden/common/vault/abstractions/collecti import { Cipher } from "@bitwarden/common/vault/models/domain/cipher"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view"; +import { ToastService } from "@bitwarden/components"; @Directive() export class CollectionsComponent implements OnInit { @@ -39,6 +40,7 @@ export class CollectionsComponent implements OnInit { private logService: LogService, private configService: ConfigService, private accountService: AccountService, + private toastService: ToastService, ) {} async ngOnInit() { @@ -82,11 +84,11 @@ export class CollectionsComponent implements OnInit { }) .map((c) => c.id); if (!this.allowSelectNone && selectedCollectionIds.length === 0) { - this.platformUtilsService.showToast( - "error", - this.i18nService.t("errorOccurred"), - this.i18nService.t("selectOneCollection"), - ); + this.toastService.showToast({ + variant: "error", + title: this.i18nService.t("errorOccurred"), + message: this.i18nService.t("selectOneCollection"), + }); return false; } this.cipherDomain.collectionIds = selectedCollectionIds; @@ -94,10 +96,18 @@ export class CollectionsComponent implements OnInit { this.formPromise = this.saveCollections(); await this.formPromise; this.onSavedCollections.emit(); - this.platformUtilsService.showToast("success", null, this.i18nService.t("editedItem")); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("editedItem"), + }); return true; } catch (e) { - this.platformUtilsService.showToast("error", this.i18nService.t("errorOccurred"), e.message); + this.toastService.showToast({ + variant: "error", + title: this.i18nService.t("errorOccurred"), + message: e.message, + }); return false; } } From 95d04f264fdce58b71abb0dd346102b2a65ce22e Mon Sep 17 00:00:00 2001 From: Alex Morask <144709477+amorask-bitwarden@users.noreply.github.com> Date: Fri, 30 Aug 2024 14:18:07 -0400 Subject: [PATCH 08/24] Remove unused payment.component from adjust-subscription.component.html (#10820) --- .../adjust-subscription.component.html | 1 - .../adjust-subscription.component.ts | 36 ++++++------------- 2 files changed, 11 insertions(+), 26 deletions(-) diff --git a/apps/web/src/app/billing/organizations/adjust-subscription.component.html b/apps/web/src/app/billing/organizations/adjust-subscription.component.html index 61197e86d22..60d57a3b199 100644 --- a/apps/web/src/app/billing/organizations/adjust-subscription.component.html +++ b/apps/web/src/app/billing/organizations/adjust-subscription.component.html @@ -56,4 +56,3 @@ {{ "save" | i18n }} - diff --git a/apps/web/src/app/billing/organizations/adjust-subscription.component.ts b/apps/web/src/app/billing/organizations/adjust-subscription.component.ts index 226c92b45e3..436372c049b 100644 --- a/apps/web/src/app/billing/organizations/adjust-subscription.component.ts +++ b/apps/web/src/app/billing/organizations/adjust-subscription.component.ts @@ -1,43 +1,36 @@ -import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from "@angular/core"; +import { Component, EventEmitter, Input, Output } from "@angular/core"; +import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { FormBuilder, Validators } from "@angular/forms"; -import { Subject, takeUntil } from "rxjs"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; import { OrganizationSubscriptionUpdateRequest } from "@bitwarden/common/billing/models/request/organization-subscription-update.request"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { ToastService } from "@bitwarden/components"; @Component({ selector: "app-adjust-subscription", templateUrl: "adjust-subscription.component.html", }) -export class AdjustSubscription implements OnInit, OnDestroy { +export class AdjustSubscription { @Input() organizationId: string; @Input() maxAutoscaleSeats: number; @Input() currentSeatCount: number; @Input() seatPrice = 0; @Input() interval = "year"; @Output() onAdjusted = new EventEmitter(); - private destroy$ = new Subject(); adjustSubscriptionForm = this.formBuilder.group({ newSeatCount: [0, [Validators.min(0)]], limitSubscription: [false], newMaxSeats: [0, [Validators.min(0)]], }); - get limitSubscription(): boolean { - return this.adjustSubscriptionForm.value.limitSubscription; - } + constructor( private i18nService: I18nService, - private platformUtilsService: PlatformUtilsService, private organizationApiService: OrganizationApiServiceAbstraction, private formBuilder: FormBuilder, private toastService: ToastService, - ) {} - - ngOnInit() { + ) { this.adjustSubscriptionForm.patchValue({ newSeatCount: this.currentSeatCount, limitSubscription: this.maxAutoscaleSeats != null, @@ -45,7 +38,7 @@ export class AdjustSubscription implements OnInit, OnDestroy { }); this.adjustSubscriptionForm .get("limitSubscription") - .valueChanges.pipe(takeUntil(this.destroy$)) + .valueChanges.pipe(takeUntilDestroyed()) .subscribe((value: boolean) => { if (value) { this.adjustSubscriptionForm @@ -63,10 +56,6 @@ export class AdjustSubscription implements OnInit, OnDestroy { }); } - ngOnDestroy() { - this.destroy$.next(); - this.destroy$.complete(); - } submit = async () => { this.adjustSubscriptionForm.markAllAsTouched(); if (this.adjustSubscriptionForm.invalid) { @@ -99,18 +88,15 @@ export class AdjustSubscription implements OnInit, OnDestroy { : 0; } - get additionalMaxSeatCount(): number { - return this.adjustSubscriptionForm.value.newMaxSeats - ? this.adjustSubscriptionForm.value.newMaxSeats - this.currentSeatCount - : 0; - } - get maxSeatTotal(): number { return Math.abs((this.adjustSubscriptionForm.value.newMaxSeats ?? 0) * this.seatPrice); } get seatTotalCost(): number { - const totalSeat = Math.abs(this.adjustSubscriptionForm.value.newSeatCount * this.seatPrice); - return totalSeat; + return Math.abs(this.adjustSubscriptionForm.value.newSeatCount * this.seatPrice); + } + + get limitSubscription(): boolean { + return this.adjustSubscriptionForm.value.limitSubscription; } } From 963e339e4f55fbe407ab419f9a948765bc73491e Mon Sep 17 00:00:00 2001 From: Jason Ng Date: Fri, 30 Aug 2024 15:38:37 -0400 Subject: [PATCH 09/24] PM-11429 Users with Except PW permissions will not get Copy Password Option Vault V2 (#10831) --- .../item-copy-action/item-copy-actions.component.html | 8 +++++++- libs/vault/src/services/copy-cipher-field.service.spec.ts | 6 ------ libs/vault/src/services/copy-cipher-field.service.ts | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/browser/src/vault/popup/components/vault-v2/item-copy-action/item-copy-actions.component.html b/apps/browser/src/vault/popup/components/vault-v2/item-copy-action/item-copy-actions.component.html index 487168539b9..f4444a10aeb 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/item-copy-action/item-copy-actions.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/item-copy-action/item-copy-actions.component.html @@ -13,7 +13,13 @@ - + + + + + + + + + + + + diff --git a/apps/browser/src/vault/popup/settings/trash-list-items-container/trash-list-items-container.component.ts b/apps/browser/src/vault/popup/settings/trash-list-items-container/trash-list-items-container.component.ts new file mode 100644 index 00000000000..1ec0f52aa6d --- /dev/null +++ b/apps/browser/src/vault/popup/settings/trash-list-items-container/trash-list-items-container.component.ts @@ -0,0 +1,107 @@ +import { CommonModule } from "@angular/common"; +import { Component, Input } from "@angular/core"; +import { Router } from "@angular/router"; + +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; +import { + DialogService, + IconButtonModule, + ItemModule, + MenuModule, + SectionComponent, + SectionHeaderComponent, + ToastService, +} from "@bitwarden/components"; +import { PasswordRepromptService } from "@bitwarden/vault"; + +@Component({ + selector: "app-trash-list-items-container", + templateUrl: "trash-list-items-container.component.html", + standalone: true, + imports: [ + CommonModule, + ItemModule, + JslibModule, + SectionComponent, + SectionHeaderComponent, + MenuModule, + IconButtonModule, + ], +}) +export class TrashListItemsContainerComponent { + /** + * The list of trashed items to display. + */ + @Input() + ciphers: CipherView[] = []; + + @Input() + headerText: string; + + constructor( + private cipherService: CipherService, + private logService: LogService, + private toastService: ToastService, + private i18nService: I18nService, + private dialogService: DialogService, + private passwordRepromptService: PasswordRepromptService, + private router: Router, + ) {} + + async restore(cipher: CipherView) { + try { + await this.cipherService.restoreWithServer(cipher.id); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("restoredItem"), + }); + } catch (e) { + this.logService.error(e); + } + } + + async delete(cipher: CipherView) { + const repromptPassed = await this.passwordRepromptService.passwordRepromptCheck(cipher); + + if (!repromptPassed) { + return; + } + + const confirmed = await this.dialogService.openSimpleDialog({ + title: { key: "deleteItem" }, + content: { key: "permanentlyDeleteItemConfirmation" }, + type: "warning", + }); + + if (!confirmed) { + return; + } + + try { + await this.cipherService.deleteWithServer(cipher.id); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("deletedItem"), + }); + } catch (e) { + this.logService.error(e); + } + } + + async onViewCipher(cipher: CipherView) { + const repromptPassed = await this.passwordRepromptService.passwordRepromptCheck(cipher); + if (!repromptPassed) { + return; + } + + await this.router.navigate(["/view-cipher"], { + queryParams: { cipherId: cipher.id, type: cipher.type }, + }); + } +} diff --git a/apps/browser/src/vault/popup/settings/trash.component.html b/apps/browser/src/vault/popup/settings/trash.component.html new file mode 100644 index 00000000000..ab3b6716504 --- /dev/null +++ b/apps/browser/src/vault/popup/settings/trash.component.html @@ -0,0 +1,33 @@ + + + + + + + + + {{ "trashWarning" | i18n }} + + + + + + + + + + {{ "noItemsInTrash" | i18n }} + + + {{ "noItemsInTrashDesc" | i18n }} + + + + + diff --git a/apps/browser/src/vault/popup/settings/trash.component.ts b/apps/browser/src/vault/popup/settings/trash.component.ts new file mode 100644 index 00000000000..b6f77ef6a52 --- /dev/null +++ b/apps/browser/src/vault/popup/settings/trash.component.ts @@ -0,0 +1,37 @@ +import { CommonModule } from "@angular/common"; +import { Component } from "@angular/core"; + +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { CalloutModule, NoItemsModule } from "@bitwarden/components"; +import { VaultIcons } from "@bitwarden/vault"; + +import { PopOutComponent } from "../../../platform/popup/components/pop-out.component"; +import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component"; +import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.component"; +import { VaultListItemsContainerComponent } from "../components/vault-v2/vault-list-items-container/vault-list-items-container.component"; +import { VaultPopupItemsService } from "../services/vault-popup-items.service"; + +import { TrashListItemsContainerComponent } from "./trash-list-items-container/trash-list-items-container.component"; + +@Component({ + templateUrl: "trash.component.html", + standalone: true, + imports: [ + CommonModule, + JslibModule, + PopupPageComponent, + PopupHeaderComponent, + PopOutComponent, + VaultListItemsContainerComponent, + TrashListItemsContainerComponent, + CalloutModule, + NoItemsModule, + ], +}) +export class TrashComponent { + protected deletedCiphers$ = this.vaultPopupItemsService.deletedCiphers$; + + protected emptyTrashIcon = VaultIcons.EmptyTrash; + + constructor(private vaultPopupItemsService: VaultPopupItemsService) {} +} diff --git a/apps/browser/src/vault/popup/settings/vault-settings-v2.component.html b/apps/browser/src/vault/popup/settings/vault-settings-v2.component.html index 10243bdaa9f..03dd1182fbb 100644 --- a/apps/browser/src/vault/popup/settings/vault-settings-v2.component.html +++ b/apps/browser/src/vault/popup/settings/vault-settings-v2.component.html @@ -24,6 +24,12 @@ + + + {{ "trash" | i18n }} + + + diff --git a/apps/web/src/app/vault/components/vault-items/vault-items.component.html b/apps/web/src/app/vault/components/vault-items/vault-items.component.html index 2f294a758db..c72daeffff4 100644 --- a/apps/web/src/app/vault/components/vault-items/vault-items.component.html +++ b/apps/web/src/app/vault/components/vault-items/vault-items.component.html @@ -47,7 +47,7 @@ (click)="bulkEditCollectionAccess()" > - {{ "access" | i18n }} + {{ "editAccess" | i18n }} diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index 76d16036579..293a8cd5052 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -9025,5 +9025,8 @@ }, "additionalContentAvailable": { "message": "Additional content is available" + }, + "editAccess": { + "message": "Edit access" } } From c5a267baadc70f294467a6d7f48809431e308a54 Mon Sep 17 00:00:00 2001 From: rr-bw <102181210+rr-bw@users.noreply.github.com> Date: Sat, 31 Aug 2024 12:26:11 -0700 Subject: [PATCH 14/24] [PM-11000] AnonLayout Icon/Logo theme refactor (#10549) * update base anon-layout logo/icon * update ExtensionAnonLayout logo/icon based on theme * remove hard-coded fill * remove solarizedDark class --------- Co-authored-by: Bernd Schoolmann --- ...extension-anon-layout-wrapper.component.ts | 19 ++------- .../extension-anon-layout-wrapper.stories.ts | 9 ---- .../extension-bitwarden-logo.icon.ts | 21 +--------- .../anon-layout-wrapper.stories.ts | 9 ---- .../anon-layout/anon-layout.component.ts | 42 ++++--------------- .../anon-layout/anon-layout.stories.ts | 9 +--- .../src/angular/icons/bitwarden-logo.icon.ts | 16 ++----- .../angular/icons/bitwarden-shield.icon.ts | 12 ++---- libs/components/src/tw-theme.css | 8 ++++ libs/components/tailwind.config.base.js | 1 + 10 files changed, 29 insertions(+), 117 deletions(-) diff --git a/apps/browser/src/auth/popup/extension-anon-layout-wrapper/extension-anon-layout-wrapper.component.ts b/apps/browser/src/auth/popup/extension-anon-layout-wrapper/extension-anon-layout-wrapper.component.ts index 7a5b156a506..350b4a8a84d 100644 --- a/apps/browser/src/auth/popup/extension-anon-layout-wrapper/extension-anon-layout-wrapper.component.ts +++ b/apps/browser/src/auth/popup/extension-anon-layout-wrapper/extension-anon-layout-wrapper.component.ts @@ -1,7 +1,7 @@ import { CommonModule } from "@angular/common"; import { Component, OnDestroy, OnInit } from "@angular/core"; import { ActivatedRoute, Data, NavigationEnd, Router, RouterModule } from "@angular/router"; -import { Subject, filter, firstValueFrom, switchMap, takeUntil, tap } from "rxjs"; +import { Subject, filter, switchMap, takeUntil, tap } from "rxjs"; import { AnonLayoutComponent, @@ -9,7 +9,6 @@ import { AnonLayoutWrapperDataService, } from "@bitwarden/auth/angular"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service"; import { Icon, IconModule } from "@bitwarden/components"; import { PopOutComponent } from "../../../platform/popup/components/pop-out.component"; @@ -17,10 +16,7 @@ import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-heade import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.component"; import { CurrentAccountComponent } from "../account-switching/current-account.component"; -import { - ExtensionBitwardenLogoPrimary, - ExtensionBitwardenLogoWhite, -} from "./extension-bitwarden-logo.icon"; +import { ExtensionBitwardenLogo } from "./extension-bitwarden-logo.icon"; export interface ExtensionAnonLayoutWrapperData extends AnonLayoutWrapperData { showAcctSwitcher?: boolean; @@ -56,14 +52,13 @@ export class ExtensionAnonLayoutWrapperComponent implements OnInit, OnDestroy { protected maxWidth: "md" | "3xl"; protected theme: string; - protected logo: Icon; + protected logo = ExtensionBitwardenLogo; constructor( private router: Router, private route: ActivatedRoute, private i18nService: I18nService, private extensionAnonLayoutWrapperDataService: AnonLayoutWrapperDataService, - private themeStateService: ThemeStateService, ) {} async ngOnInit(): Promise { @@ -73,14 +68,6 @@ export class ExtensionAnonLayoutWrapperComponent implements OnInit, OnDestroy { // Listen for page changes and update the page data appropriately this.listenForPageDataChanges(); this.listenForServiceDataChanges(); - - this.theme = await firstValueFrom(this.themeStateService.selectedTheme$); - - if (this.theme === "dark") { - this.logo = ExtensionBitwardenLogoWhite; - } else { - this.logo = ExtensionBitwardenLogoPrimary; - } } private listenForPageDataChanges() { diff --git a/apps/browser/src/auth/popup/extension-anon-layout-wrapper/extension-anon-layout-wrapper.stories.ts b/apps/browser/src/auth/popup/extension-anon-layout-wrapper/extension-anon-layout-wrapper.stories.ts index 44060f991ff..beb07f3523a 100644 --- a/apps/browser/src/auth/popup/extension-anon-layout-wrapper/extension-anon-layout-wrapper.stories.ts +++ b/apps/browser/src/auth/popup/extension-anon-layout-wrapper/extension-anon-layout-wrapper.stories.ts @@ -22,8 +22,6 @@ import { } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { ThemeType } from "@bitwarden/common/platform/enums"; -import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service"; import { UserId } from "@bitwarden/common/types/guid"; import { ButtonModule, I18nMockService } from "@bitwarden/components"; @@ -47,7 +45,6 @@ const decorators = (options: { applicationVersion?: string; clientType?: ClientType; hostName?: string; - themeType?: ThemeType; }) => { return [ componentWrapperDecorator( @@ -120,12 +117,6 @@ const decorators = (options: { getClientType: () => options.clientType || ClientType.Web, } as Partial, }, - { - provide: ThemeStateService, - useValue: { - selectedTheme$: of(options.themeType || ThemeType.Light), - } as Partial, - }, { provide: I18nService, useFactory: () => { diff --git a/apps/browser/src/auth/popup/extension-anon-layout-wrapper/extension-bitwarden-logo.icon.ts b/apps/browser/src/auth/popup/extension-anon-layout-wrapper/extension-bitwarden-logo.icon.ts index 569edaae978..51d748e1fbb 100644 --- a/apps/browser/src/auth/popup/extension-anon-layout-wrapper/extension-bitwarden-logo.icon.ts +++ b/apps/browser/src/auth/popup/extension-anon-layout-wrapper/extension-bitwarden-logo.icon.ts @@ -1,6 +1,6 @@ import { svgIcon } from "@bitwarden/components"; -export const ExtensionBitwardenLogoPrimary = svgIcon` +export const ExtensionBitwardenLogo = svgIcon` - -`; - -export const ExtensionBitwardenLogoWhite = svgIcon` - - `; diff --git a/libs/auth/src/angular/anon-layout/anon-layout-wrapper.stories.ts b/libs/auth/src/angular/anon-layout/anon-layout-wrapper.stories.ts index c05f491acd2..87e26bd2df1 100644 --- a/libs/auth/src/angular/anon-layout/anon-layout-wrapper.stories.ts +++ b/libs/auth/src/angular/anon-layout/anon-layout-wrapper.stories.ts @@ -15,8 +15,6 @@ import { Environment, } from "@bitwarden/common/platform/abstractions/environment.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { ThemeType } from "@bitwarden/common/platform/enums"; -import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service"; import { ButtonModule } from "@bitwarden/components"; // FIXME: remove `/apps` import from `/libs` @@ -40,7 +38,6 @@ const decorators = (options: { applicationVersion?: string; clientType?: ClientType; hostName?: string; - themeType?: ThemeType; }) => { return [ componentWrapperDecorator( @@ -84,12 +81,6 @@ const decorators = (options: { getClientType: () => options.clientType || ClientType.Web, } as Partial, }, - { - provide: ThemeStateService, - useValue: { - selectedTheme$: of(options.themeType || ThemeType.Light), - } as Partial, - }, ], }), applicationConfig({ diff --git a/libs/auth/src/angular/anon-layout/anon-layout.component.ts b/libs/auth/src/angular/anon-layout/anon-layout.component.ts index fc3026dad34..a40fafc5db9 100644 --- a/libs/auth/src/angular/anon-layout/anon-layout.component.ts +++ b/libs/auth/src/angular/anon-layout/anon-layout.component.ts @@ -5,13 +5,11 @@ import { firstValueFrom } from "rxjs"; import { ClientType } from "@bitwarden/common/enums"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service"; import { IconModule, Icon } from "../../../../components/src/icon"; import { SharedModule } from "../../../../components/src/shared"; import { TypographyModule } from "../../../../components/src/typography"; -import { BitwardenLogoPrimary, BitwardenLogoWhite } from "../icons"; -import { BitwardenShieldPrimary, BitwardenShieldWhite } from "../icons/bitwarden-shield.icon"; +import { BitwardenLogo, BitwardenShield } from "../icons"; @Component({ standalone: true, @@ -34,20 +32,17 @@ export class AnonLayoutComponent implements OnInit, OnChanges { */ @Input() maxWidth: "md" | "3xl" = "md"; - protected logo: Icon; - + protected logo = BitwardenLogo; protected year = "2024"; protected clientType: ClientType; protected hostname: string; protected version: string; - protected theme: string; protected hideYearAndVersion = false; constructor( private environmentService: EnvironmentService, private platformUtilsService: PlatformUtilsService, - private themeStateService: ThemeStateService, ) { this.year = new Date().getFullYear().toString(); this.clientType = this.platformUtilsService.getClientType(); @@ -56,41 +51,18 @@ export class AnonLayoutComponent implements OnInit, OnChanges { async ngOnInit() { this.maxWidth = this.maxWidth ?? "md"; - - this.theme = await firstValueFrom(this.themeStateService.selectedTheme$); - - if (this.theme === "dark") { - this.logo = BitwardenLogoWhite; - } else { - this.logo = BitwardenLogoPrimary; - } - - await this.updateIcon(this.theme); - this.hostname = (await firstValueFrom(this.environmentService.environment$)).getHostname(); this.version = await this.platformUtilsService.getApplicationVersion(); + + // If there is no icon input, then use the default icon + if (this.icon == null) { + this.icon = BitwardenShield; + } } async ngOnChanges(changes: SimpleChanges) { - if (changes.icon) { - const theme = await firstValueFrom(this.themeStateService.selectedTheme$); - await this.updateIcon(theme); - } - if (changes.maxWidth) { this.maxWidth = changes.maxWidth.currentValue ?? "md"; } } - - private async updateIcon(theme: string) { - if (this.icon == null) { - if (theme === "dark") { - this.icon = BitwardenShieldWhite; - } - - if (theme !== "dark") { - this.icon = BitwardenShieldPrimary; - } - } - } } diff --git a/libs/auth/src/angular/anon-layout/anon-layout.stories.ts b/libs/auth/src/angular/anon-layout/anon-layout.stories.ts index edf6c8d70a1..110bda7ce81 100644 --- a/libs/auth/src/angular/anon-layout/anon-layout.stories.ts +++ b/libs/auth/src/angular/anon-layout/anon-layout.stories.ts @@ -1,11 +1,10 @@ import { Meta, StoryObj, moduleMetadata } from "@storybook/angular"; -import { BehaviorSubject, of } from "rxjs"; +import { BehaviorSubject } from "rxjs"; import { ClientType } from "@bitwarden/common/enums"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service"; import { ButtonModule } from "../../../../components/src/button"; import { I18nMockService } from "../../../../components/src/utils/i18n-mock.service"; @@ -47,12 +46,6 @@ export default { }).asObservable(), }, }, - { - provide: ThemeStateService, - useValue: { - selectedTheme$: of("light"), - }, - }, ], }), ], diff --git a/libs/auth/src/angular/icons/bitwarden-logo.icon.ts b/libs/auth/src/angular/icons/bitwarden-logo.icon.ts index b9094befff1..2a1ae48526b 100644 --- a/libs/auth/src/angular/icons/bitwarden-logo.icon.ts +++ b/libs/auth/src/angular/icons/bitwarden-logo.icon.ts @@ -1,17 +1,9 @@ import { svgIcon } from "@bitwarden/components"; -export const BitwardenLogoPrimary = svgIcon` - +export const BitwardenLogo = svgIcon` + Bitwarden - - - -`; - -export const BitwardenLogoWhite = svgIcon` - - Bitwarden - - + + `; diff --git a/libs/auth/src/angular/icons/bitwarden-shield.icon.ts b/libs/auth/src/angular/icons/bitwarden-shield.icon.ts index a81a9906a6b..86e3a0bb1b2 100644 --- a/libs/auth/src/angular/icons/bitwarden-shield.icon.ts +++ b/libs/auth/src/angular/icons/bitwarden-shield.icon.ts @@ -1,13 +1,7 @@ import { svgIcon } from "@bitwarden/components"; -export const BitwardenShieldPrimary = svgIcon` - - - -`; - -export const BitwardenShieldWhite = svgIcon` - - +export const BitwardenShield = svgIcon` + + `; diff --git a/libs/components/src/tw-theme.css b/libs/components/src/tw-theme.css index 00ab2ff717e..6234ba380bc 100644 --- a/libs/components/src/tw-theme.css +++ b/libs/components/src/tw-theme.css @@ -49,6 +49,8 @@ --color-text-code: 192 17 118; --color-text-headers: 2 15 102; + --color-marketing-logo: 23 93 220; + --tw-ring-offset-color: #ffffff; } @@ -95,6 +97,8 @@ --color-text-code: 240 141 199; --color-text-headers: 226 227 228; + --color-marketing-logo: 255 255 255; + --tw-ring-offset-color: #1f242e; } @@ -134,6 +138,8 @@ --color-text-alt2: 255 255 255; --color-text-code: 219 177 211; + --color-marketing-logo: 255 255 255; + --tw-ring-offset-color: #434c5e; } @@ -173,6 +179,8 @@ --color-text-alt2: 255 255 255; --color-text-code: 240 141 199; + --color-marketing-logo: 255 255 255; + --tw-ring-offset-color: #002b36; } diff --git a/libs/components/tailwind.config.base.js b/libs/components/tailwind.config.base.js index 88e7549780f..236baed74c8 100644 --- a/libs/components/tailwind.config.base.js +++ b/libs/components/tailwind.config.base.js @@ -69,6 +69,7 @@ module.exports = { alt3: rgba("--color-background-alt3"), alt4: rgba("--color-background-alt4"), }, + "marketing-logo": rgba("--color-marketing-logo"), }, textColor: { main: rgba("--color-text-main"), From b339c01c20db6c6714e78294bfd90a43c9442087 Mon Sep 17 00:00:00 2001 From: rr-bw <102181210+rr-bw@users.noreply.github.com> Date: Sat, 31 Aug 2024 13:19:48 -0700 Subject: [PATCH 15/24] remove position on browser (#10810) --- .../auth/src/angular/anon-layout/anon-layout.component.html | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libs/auth/src/angular/anon-layout/anon-layout.component.html b/libs/auth/src/angular/anon-layout/anon-layout.component.html index 082edf40630..39570dabc8b 100644 --- a/libs/auth/src/angular/anon-layout/anon-layout.component.html +++ b/libs/auth/src/angular/anon-layout/anon-layout.component.html @@ -1,6 +1,10 @@
From 5436072a751e9dc785c7ba180697ea936480be7a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 07:21:17 -0400 Subject: [PATCH 16/24] [deps] Autofill: Update wait-on to v8 (#10849) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package-lock.json | 12 ++++++------ package.json | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index c3eeb5d529b..a9e95fec1f9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -179,7 +179,7 @@ "typescript": "5.1.6", "url": "0.11.3", "util": "0.12.5", - "wait-on": "7.2.0", + "wait-on": "8.0.0", "webpack": "5.94.0", "webpack-cli": "5.1.4", "webpack-dev-server": "5.0.4", @@ -38305,14 +38305,14 @@ } }, "node_modules/wait-on": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-7.2.0.tgz", - "integrity": "sha512-wCQcHkRazgjG5XoAq9jbTMLpNIjoSlZslrJ2+N9MxDsGEv1HnFoVjOCexL0ESva7Y9cu350j+DWADdk54s4AFQ==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-8.0.0.tgz", + "integrity": "sha512-fNE5SXinLr2Bt7cJvjvLg2PcXfqznlqRvtE3f8AqYdRZ9BhE+XpsCp1mwQbRoO7s1q7uhAuCw0Ro3mG/KdZjEw==", "dev": true, "license": "MIT", "dependencies": { - "axios": "^1.6.1", - "joi": "^17.11.0", + "axios": "^1.7.4", + "joi": "^17.13.3", "lodash": "^4.17.21", "minimist": "^1.2.8", "rxjs": "^7.8.1" diff --git a/package.json b/package.json index 478c93c52d3..722fd6c3e00 100644 --- a/package.json +++ b/package.json @@ -141,7 +141,7 @@ "typescript": "5.1.6", "url": "0.11.3", "util": "0.12.5", - "wait-on": "7.2.0", + "wait-on": "8.0.0", "webpack": "5.94.0", "webpack-cli": "5.1.4", "webpack-dev-server": "5.0.4", From 5dac4b94e154f3c0cd5e6d35bb176f43fad6ffed Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 14:13:53 +0200 Subject: [PATCH 17/24] [deps] Platform: Update argon2 to v0.41.1 (#9819) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- apps/cli/package.json | 2 +- apps/desktop/src/package-lock.json | 23 +++++++++++++---------- apps/desktop/src/package.json | 2 +- package-lock.json | 25 ++++++++++++++----------- package.json | 2 +- 5 files changed, 30 insertions(+), 24 deletions(-) diff --git a/apps/cli/package.json b/apps/cli/package.json index 8fe0284ed7d..2fe4f520611 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -58,7 +58,7 @@ "dependencies": { "@koa/multer": "3.0.2", "@koa/router": "12.0.1", - "argon2": "0.40.1", + "argon2": "0.41.1", "big-integer": "1.6.51", "browser-hrtime": "1.1.8", "chalk": "4.1.2", diff --git a/apps/desktop/src/package-lock.json b/apps/desktop/src/package-lock.json index a37c185d170..62ba98626f1 100644 --- a/apps/desktop/src/package-lock.json +++ b/apps/desktop/src/package-lock.json @@ -10,7 +10,7 @@ "license": "GPL-3.0", "dependencies": { "@bitwarden/desktop-napi": "file:../desktop_native/napi", - "argon2": "0.40.1" + "argon2": "0.41.1" } }, "../desktop_native/napi": { @@ -35,25 +35,28 @@ } }, "node_modules/argon2": { - "version": "0.40.1", - "resolved": "https://registry.npmjs.org/argon2/-/argon2-0.40.1.tgz", - "integrity": "sha512-DjtHDwd7pm12qeWyfihHoM8Bn5vGcgH6sKwgPqwNYroRmxlrzadHEvMyuvQxN/V8YSyRRKD5x6ito09q1e9OyA==", + "version": "0.41.1", + "resolved": "https://registry.npmjs.org/argon2/-/argon2-0.41.1.tgz", + "integrity": "sha512-dqCW8kJXke8Ik+McUcMDltrbuAWETPyU6iq+4AhxqKphWi7pChB/Zgd/Tp/o8xRLbg8ksMj46F/vph9wnxpTzQ==", "hasInstallScript": true, "license": "MIT", "dependencies": { "@phc/format": "^1.0.0", - "node-addon-api": "^7.1.0", - "node-gyp-build": "^4.8.0" + "node-addon-api": "^8.1.0", + "node-gyp-build": "^4.8.1" }, "engines": { "node": ">=16.17.0" } }, "node_modules/node-addon-api": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", - "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", - "license": "MIT" + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.1.0.tgz", + "integrity": "sha512-yBY+qqWSv3dWKGODD6OGE6GnTX7Q2r+4+DfpqxHSHh8x0B4EKP9+wVGLS6U/AM1vxSNNmUEuIV5EGhYwPpfOwQ==", + "license": "MIT", + "engines": { + "node": "^18 || ^20 || >= 21" + } }, "node_modules/node-gyp-build": { "version": "4.8.2", diff --git a/apps/desktop/src/package.json b/apps/desktop/src/package.json index df1f7f70d41..49eced14692 100644 --- a/apps/desktop/src/package.json +++ b/apps/desktop/src/package.json @@ -13,6 +13,6 @@ }, "dependencies": { "@bitwarden/desktop-napi": "file:../desktop_native/napi", - "argon2": "0.40.1" + "argon2": "0.41.1" } } diff --git a/package-lock.json b/package-lock.json index a9e95fec1f9..605362af059 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,7 +29,7 @@ "@microsoft/signalr": "8.0.7", "@microsoft/signalr-protocol-msgpack": "8.0.7", "@ng-select/ng-select": "11.2.0", - "argon2": "0.40.1", + "argon2": "0.41.1", "argon2-browser": "1.18.0", "big-integer": "1.6.51", "bootstrap": "4.6.0", @@ -201,7 +201,7 @@ "dependencies": { "@koa/multer": "3.0.2", "@koa/router": "12.0.1", - "argon2": "0.40.1", + "argon2": "0.41.1", "big-integer": "1.6.51", "browser-hrtime": "1.1.8", "chalk": "4.1.2", @@ -11792,15 +11792,15 @@ "license": "MIT" }, "node_modules/argon2": { - "version": "0.40.1", - "resolved": "https://registry.npmjs.org/argon2/-/argon2-0.40.1.tgz", - "integrity": "sha512-DjtHDwd7pm12qeWyfihHoM8Bn5vGcgH6sKwgPqwNYroRmxlrzadHEvMyuvQxN/V8YSyRRKD5x6ito09q1e9OyA==", + "version": "0.41.1", + "resolved": "https://registry.npmjs.org/argon2/-/argon2-0.41.1.tgz", + "integrity": "sha512-dqCW8kJXke8Ik+McUcMDltrbuAWETPyU6iq+4AhxqKphWi7pChB/Zgd/Tp/o8xRLbg8ksMj46F/vph9wnxpTzQ==", "hasInstallScript": true, "license": "MIT", "dependencies": { "@phc/format": "^1.0.0", - "node-addon-api": "^7.1.0", - "node-gyp-build": "^4.8.0" + "node-addon-api": "^8.1.0", + "node-gyp-build": "^4.8.1" }, "engines": { "node": ">=16.17.0" @@ -28464,10 +28464,13 @@ "license": "MIT" }, "node_modules/node-addon-api": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", - "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", - "license": "MIT" + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.1.0.tgz", + "integrity": "sha512-yBY+qqWSv3dWKGODD6OGE6GnTX7Q2r+4+DfpqxHSHh8x0B4EKP9+wVGLS6U/AM1vxSNNmUEuIV5EGhYwPpfOwQ==", + "license": "MIT", + "engines": { + "node": "^18 || ^20 || >= 21" + } }, "node_modules/node-api-version": { "version": "0.2.0", diff --git a/package.json b/package.json index 722fd6c3e00..b00afa49a07 100644 --- a/package.json +++ b/package.json @@ -162,7 +162,7 @@ "@microsoft/signalr": "8.0.7", "@microsoft/signalr-protocol-msgpack": "8.0.7", "@ng-select/ng-select": "11.2.0", - "argon2": "0.40.1", + "argon2": "0.41.1", "argon2-browser": "1.18.0", "big-integer": "1.6.51", "bootstrap": "4.6.0", From 76021b48171b5471a4872cb9d9404c50fbc2fe05 Mon Sep 17 00:00:00 2001 From: aj-rosado <109146700+aj-rosado@users.noreply.github.com> Date: Mon, 2 Sep 2024 14:59:28 +0100 Subject: [PATCH 18/24] Fix mSecure importer and added tests (#10698) --- .../spec/msecure-csv-importer.spec.ts | 113 ++++++++++++++++++ .../src/importers/msecure-csv-importer.ts | 51 ++++++-- 2 files changed, 157 insertions(+), 7 deletions(-) create mode 100644 libs/importer/spec/msecure-csv-importer.spec.ts diff --git a/libs/importer/spec/msecure-csv-importer.spec.ts b/libs/importer/spec/msecure-csv-importer.spec.ts new file mode 100644 index 00000000000..903be3bcb18 --- /dev/null +++ b/libs/importer/spec/msecure-csv-importer.spec.ts @@ -0,0 +1,113 @@ +import { CipherType } from "@bitwarden/common/vault/enums"; + +import { MSecureCsvImporter } from "../src/importers/msecure-csv-importer"; + +describe("MSecureCsvImporter.parse", () => { + let importer: MSecureCsvImporter; + beforeEach(() => { + importer = new MSecureCsvImporter(); + }); + + it("should correctly parse credit card entries as Secret Notes", async () => { + const mockCsvData = + `myCreditCard|155089404,Credit Card,,,Card Number|12|41111111111111111,Expiration Date|11|05/2026,Security Code|9|123,Name on Card|0|John Doe,PIN|9|1234,Issuing Bank|0|Visa,Phone Number|4|,Billing Address|0|,`.trim(); + const result = await importer.parse(mockCsvData); + + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(1); + const cipher = result.ciphers[0]; + expect(cipher.name).toBe("myCreditCard"); + expect(cipher.type).toBe(CipherType.Card); + expect(cipher.card.number).toBe("41111111111111111"); + expect(cipher.card.expiration).toBe("05 / 2026"); + expect(cipher.card.code).toBe("123"); + expect(cipher.card.cardholderName).toBe("John Doe"); + expect(cipher.card.brand).toBe("Visa"); + }); + + it("should correctly parse login entries", async () => { + const mockCsvData = ` + Bitwarden|810974637,Login,,,Website|2|bitwarden.com,Username|7|bitwarden user,Password|8|bitpassword, + `.trim(); + + const result = await importer.parse(mockCsvData); + + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(1); + const cipher = result.ciphers[0]; + expect(cipher.name).toBe("Bitwarden"); + expect(cipher.type).toBe(CipherType.Login); + expect(cipher.login.username).toBe("bitwarden user"); + expect(cipher.login.password).toBe("bitpassword"); + expect(cipher.login.uris[0].uri).toContain("bitwarden.com"); + }); + + it("should correctly parse login entries with notes", async () => { + const mockCsvData = + `Example|188987444,Login,,This is a note |,Website|2|example2.com,Username|7|username || lol,Password|8|this is a password,`.trim(); + + const result = await importer.parse(mockCsvData); + + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(1); + const cipher = result.ciphers[0]; + expect(cipher.name).toBe("Example"); + expect(cipher.type).toBe(CipherType.Login); + expect(cipher.login.username).toBe("username || lol"); + expect(cipher.login.password).toBe("this is a password"); + expect(cipher.login.uris[0].uri).toContain("example2.com"); + expect(cipher.notes).toBe("This is a note |"); + }); + + it("should correctly parse login entries with a tag", async () => { + const mockCsvData = ` + Website with a tag|1401978655,Login,tag holding it,,Website|2|johndoe.com,Username|7|JohnDoeWebsite,Password|8|JohnDoePassword, + `.trim(); + + const result = await importer.parse(mockCsvData); + + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(1); + const cipher = result.ciphers[0]; + expect(cipher.name).toBe("Website with a tag"); + expect(cipher.type).toBe(CipherType.Login); + expect(cipher.login.username).toBe("JohnDoeWebsite"); + expect(cipher.login.password).toBe("JohnDoePassword"); + expect(cipher.login.uris[0].uri).toContain("johndoe.com"); + expect(cipher.notes).toBeNull(); + expect(result.folders[0].name).toContain("tag holding it"); + }); + + it("should handle multiple entries correctly", async () => { + const mockCsvData = + `myCreditCard|155089404,Credit Card,,,Card Number|12|41111111111111111,Expiration Date|11|05/2026,Security Code|9|123,Name on Card|0|John Doe,PIN|9|1234,Issuing Bank|0|Visa,Phone Number|4|,Billing Address|0|, +Bitwarden|810974637,Login,,,Website|2|bitwarden.com,Username|7|bitwarden user,Password|8|bitpassword, +Example|188987444,Login,,This is a note |,Website|2|example2.com,Username|7|username || lol,Password|8|this is a password, +Website with a tag|1401978655,Login,tag holding it,,Website|2|johndoe.com,Username|7|JohnDoeWebsite,Password|8|JohnDoePassword,`.trim(); + + const result = await importer.parse(mockCsvData); + + expect(result.success).toBe(true); + expect(result.ciphers.length).toBe(4); + + // Check first entry (Credit Card) + const cipher1 = result.ciphers[0]; + expect(cipher1.name).toBe("myCreditCard"); + expect(cipher1.type).toBe(CipherType.Card); + + // Check second entry (Login - Bitwarden) + const cipher2 = result.ciphers[1]; + expect(cipher2.name).toBe("Bitwarden"); + expect(cipher2.type).toBe(CipherType.Login); + + // Check third entry (Login with note - Example) + const cipher3 = result.ciphers[2]; + expect(cipher3.name).toBe("Example"); + expect(cipher3.type).toBe(CipherType.Login); + + // Check fourth entry (Login with tag - Website with a tag) + const cipher4 = result.ciphers[3]; + expect(cipher4.name).toBe("Website with a tag"); + expect(cipher4.type).toBe(CipherType.Login); + }); +}); diff --git a/libs/importer/src/importers/msecure-csv-importer.ts b/libs/importer/src/importers/msecure-csv-importer.ts index 788dfd1d4e2..e953ce3a8db 100644 --- a/libs/importer/src/importers/msecure-csv-importer.ts +++ b/libs/importer/src/importers/msecure-csv-importer.ts @@ -9,7 +9,7 @@ import { Importer } from "./importer"; export class MSecureCsvImporter extends BaseImporter implements Importer { parse(data: string): Promise { const result = new ImportResult(); - const results = this.parseCsv(data, false); + const results = this.parseCsv(data, false, { delimiter: "," }); if (results == null) { result.success = false; return Promise.resolve(result); @@ -21,17 +21,43 @@ export class MSecureCsvImporter extends BaseImporter implements Importer { } const folderName = - this.getValueOrDefault(value[0], "Unassigned") !== "Unassigned" ? value[0] : null; + this.getValueOrDefault(value[2], "Unassigned") !== "Unassigned" ? value[2] : null; this.processFolder(result, folderName); const cipher = this.initLoginCipher(); - cipher.name = this.getValueOrDefault(value[2], "--"); + cipher.name = this.getValueOrDefault(value[0].split("|")[0], "--"); if (value[1] === "Web Logins" || value[1] === "Login") { - cipher.login.uris = this.makeUriArray(value[4]); - cipher.login.username = this.getValueOrDefault(value[5]); - cipher.login.password = this.getValueOrDefault(value[6]); + cipher.login.username = this.getValueOrDefault(this.splitValueRetainingLastPart(value[5])); + cipher.login.uris = this.makeUriArray(this.splitValueRetainingLastPart(value[4])); + cipher.login.password = this.getValueOrDefault(this.splitValueRetainingLastPart(value[6])); cipher.notes = !this.isNullOrWhitespace(value[3]) ? value[3].split("\\n").join("\n") : null; + } else if (value[1] === "Credit Card") { + cipher.type = CipherType.Card; + cipher.card.number = this.getValueOrDefault(this.splitValueRetainingLastPart(value[4])); + + const [month, year] = this.getValueOrDefault( + this.splitValueRetainingLastPart(value[5]), + ).split("/"); + cipher.card.expMonth = month.trim(); + cipher.card.expYear = year.trim(); + cipher.card.code = this.getValueOrDefault(this.splitValueRetainingLastPart(value[6])); + cipher.card.cardholderName = this.getValueOrDefault( + this.splitValueRetainingLastPart(value[7]), + ); + cipher.card.brand = this.getValueOrDefault(this.splitValueRetainingLastPart(value[9])); + cipher.notes = + this.getValueOrDefault(value[8].split("|")[0]) + + ": " + + this.getValueOrDefault(this.splitValueRetainingLastPart(value[8]), "") + + "\n" + + this.getValueOrDefault(value[10].split("|")[0]) + + ": " + + this.getValueOrDefault(this.splitValueRetainingLastPart(value[10]), "") + + "\n" + + this.getValueOrDefault(value[11].split("|")[0]) + + ": " + + this.getValueOrDefault(this.splitValueRetainingLastPart(value[11]), ""); } else if (value.length > 3) { cipher.type = CipherType.SecureNote; cipher.secureNote = new SecureNoteView(); @@ -43,7 +69,11 @@ export class MSecureCsvImporter extends BaseImporter implements Importer { } } - if (!this.isNullOrWhitespace(value[1]) && cipher.type !== CipherType.Login) { + if ( + !this.isNullOrWhitespace(value[1]) && + cipher.type !== CipherType.Login && + cipher.type !== CipherType.Card + ) { cipher.name = value[1] + ": " + cipher.name; } @@ -58,4 +88,11 @@ export class MSecureCsvImporter extends BaseImporter implements Importer { result.success = true; return Promise.resolve(result); } + + // mSecure returns values separated by "|" where after the second separator is the value + // like "Password|8|myPassword", we want to keep the "myPassword" but also ensure that if + // the value contains any "|" it works fine + private splitValueRetainingLastPart(value: string) { + return value.split("|").slice(0, 2).concat(value.split("|").slice(2).join("|")).pop(); + } } From 60fca9c11833c297b0f3f67f6ffa892c1dccb95b Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Tue, 3 Sep 2024 14:06:23 +0200 Subject: [PATCH 19/24] Revert "[deps] Platform: Update argon2 to v0.41.1 (#9819)" (#10858) This reverts commit 5dac4b94e154f3c0cd5e6d35bb176f43fad6ffed. --- apps/cli/package.json | 2 +- apps/desktop/src/package-lock.json | 23 ++++++++++------------- apps/desktop/src/package.json | 2 +- package-lock.json | 25 +++++++++++-------------- package.json | 2 +- 5 files changed, 24 insertions(+), 30 deletions(-) diff --git a/apps/cli/package.json b/apps/cli/package.json index 2fe4f520611..8fe0284ed7d 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -58,7 +58,7 @@ "dependencies": { "@koa/multer": "3.0.2", "@koa/router": "12.0.1", - "argon2": "0.41.1", + "argon2": "0.40.1", "big-integer": "1.6.51", "browser-hrtime": "1.1.8", "chalk": "4.1.2", diff --git a/apps/desktop/src/package-lock.json b/apps/desktop/src/package-lock.json index 62ba98626f1..a37c185d170 100644 --- a/apps/desktop/src/package-lock.json +++ b/apps/desktop/src/package-lock.json @@ -10,7 +10,7 @@ "license": "GPL-3.0", "dependencies": { "@bitwarden/desktop-napi": "file:../desktop_native/napi", - "argon2": "0.41.1" + "argon2": "0.40.1" } }, "../desktop_native/napi": { @@ -35,28 +35,25 @@ } }, "node_modules/argon2": { - "version": "0.41.1", - "resolved": "https://registry.npmjs.org/argon2/-/argon2-0.41.1.tgz", - "integrity": "sha512-dqCW8kJXke8Ik+McUcMDltrbuAWETPyU6iq+4AhxqKphWi7pChB/Zgd/Tp/o8xRLbg8ksMj46F/vph9wnxpTzQ==", + "version": "0.40.1", + "resolved": "https://registry.npmjs.org/argon2/-/argon2-0.40.1.tgz", + "integrity": "sha512-DjtHDwd7pm12qeWyfihHoM8Bn5vGcgH6sKwgPqwNYroRmxlrzadHEvMyuvQxN/V8YSyRRKD5x6ito09q1e9OyA==", "hasInstallScript": true, "license": "MIT", "dependencies": { "@phc/format": "^1.0.0", - "node-addon-api": "^8.1.0", - "node-gyp-build": "^4.8.1" + "node-addon-api": "^7.1.0", + "node-gyp-build": "^4.8.0" }, "engines": { "node": ">=16.17.0" } }, "node_modules/node-addon-api": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.1.0.tgz", - "integrity": "sha512-yBY+qqWSv3dWKGODD6OGE6GnTX7Q2r+4+DfpqxHSHh8x0B4EKP9+wVGLS6U/AM1vxSNNmUEuIV5EGhYwPpfOwQ==", - "license": "MIT", - "engines": { - "node": "^18 || ^20 || >= 21" - } + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "license": "MIT" }, "node_modules/node-gyp-build": { "version": "4.8.2", diff --git a/apps/desktop/src/package.json b/apps/desktop/src/package.json index 49eced14692..df1f7f70d41 100644 --- a/apps/desktop/src/package.json +++ b/apps/desktop/src/package.json @@ -13,6 +13,6 @@ }, "dependencies": { "@bitwarden/desktop-napi": "file:../desktop_native/napi", - "argon2": "0.41.1" + "argon2": "0.40.1" } } diff --git a/package-lock.json b/package-lock.json index 605362af059..a9e95fec1f9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,7 +29,7 @@ "@microsoft/signalr": "8.0.7", "@microsoft/signalr-protocol-msgpack": "8.0.7", "@ng-select/ng-select": "11.2.0", - "argon2": "0.41.1", + "argon2": "0.40.1", "argon2-browser": "1.18.0", "big-integer": "1.6.51", "bootstrap": "4.6.0", @@ -201,7 +201,7 @@ "dependencies": { "@koa/multer": "3.0.2", "@koa/router": "12.0.1", - "argon2": "0.41.1", + "argon2": "0.40.1", "big-integer": "1.6.51", "browser-hrtime": "1.1.8", "chalk": "4.1.2", @@ -11792,15 +11792,15 @@ "license": "MIT" }, "node_modules/argon2": { - "version": "0.41.1", - "resolved": "https://registry.npmjs.org/argon2/-/argon2-0.41.1.tgz", - "integrity": "sha512-dqCW8kJXke8Ik+McUcMDltrbuAWETPyU6iq+4AhxqKphWi7pChB/Zgd/Tp/o8xRLbg8ksMj46F/vph9wnxpTzQ==", + "version": "0.40.1", + "resolved": "https://registry.npmjs.org/argon2/-/argon2-0.40.1.tgz", + "integrity": "sha512-DjtHDwd7pm12qeWyfihHoM8Bn5vGcgH6sKwgPqwNYroRmxlrzadHEvMyuvQxN/V8YSyRRKD5x6ito09q1e9OyA==", "hasInstallScript": true, "license": "MIT", "dependencies": { "@phc/format": "^1.0.0", - "node-addon-api": "^8.1.0", - "node-gyp-build": "^4.8.1" + "node-addon-api": "^7.1.0", + "node-gyp-build": "^4.8.0" }, "engines": { "node": ">=16.17.0" @@ -28464,13 +28464,10 @@ "license": "MIT" }, "node_modules/node-addon-api": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.1.0.tgz", - "integrity": "sha512-yBY+qqWSv3dWKGODD6OGE6GnTX7Q2r+4+DfpqxHSHh8x0B4EKP9+wVGLS6U/AM1vxSNNmUEuIV5EGhYwPpfOwQ==", - "license": "MIT", - "engines": { - "node": "^18 || ^20 || >= 21" - } + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "license": "MIT" }, "node_modules/node-api-version": { "version": "0.2.0", diff --git a/package.json b/package.json index b00afa49a07..722fd6c3e00 100644 --- a/package.json +++ b/package.json @@ -162,7 +162,7 @@ "@microsoft/signalr": "8.0.7", "@microsoft/signalr-protocol-msgpack": "8.0.7", "@ng-select/ng-select": "11.2.0", - "argon2": "0.41.1", + "argon2": "0.40.1", "argon2-browser": "1.18.0", "big-integer": "1.6.51", "bootstrap": "4.6.0", From 5f5e4498e84391f8e848bbece7b43ec7a71cc8b2 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Tue, 3 Sep 2024 16:56:55 +0200 Subject: [PATCH 20/24] [PM-11593] Fix backgroundbrowserbiometricservice initialization (#10861) * Fix backgroundbrowserbiometricservice initialization * Cleanup according to review --- apps/browser/src/background/main.background.ts | 6 +++++- .../services/background-browser-biometrics.service.ts | 10 +++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index cf393e0a44c..0da55cbda5f 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -401,6 +401,8 @@ export default class MainBackground { const logoutCallback = async (logoutReason: LogoutReason, userId?: UserId) => await this.logout(logoutReason, userId); + const runtimeNativeMessagingBackground = () => this.nativeMessagingBackground; + const refreshAccessTokenErrorCallback = () => { // Send toast to popup this.messagingService.send("showToast", { @@ -616,7 +618,9 @@ export default class MainBackground { this.i18nService = new I18nService(BrowserApi.getUILanguage(), this.globalStateProvider); - this.biometricsService = new BackgroundBrowserBiometricsService(this.nativeMessagingBackground); + this.biometricsService = new BackgroundBrowserBiometricsService( + runtimeNativeMessagingBackground, + ); this.kdfConfigService = new KdfConfigService(this.stateProvider); diff --git a/apps/browser/src/platform/services/background-browser-biometrics.service.ts b/apps/browser/src/platform/services/background-browser-biometrics.service.ts index 41ae15972cd..0cd48c45938 100644 --- a/apps/browser/src/platform/services/background-browser-biometrics.service.ts +++ b/apps/browser/src/platform/services/background-browser-biometrics.service.ts @@ -6,20 +6,20 @@ import { BrowserBiometricsService } from "./browser-biometrics.service"; @Injectable() export class BackgroundBrowserBiometricsService extends BrowserBiometricsService { - constructor(private nativeMessagingBackground: NativeMessagingBackground) { + constructor(private nativeMessagingBackground: () => NativeMessagingBackground) { super(); } async authenticateBiometric(): Promise { - const responsePromise = this.nativeMessagingBackground.getResponse(); - await this.nativeMessagingBackground.send({ command: "biometricUnlock" }); + const responsePromise = this.nativeMessagingBackground().getResponse(); + await this.nativeMessagingBackground().send({ command: "biometricUnlock" }); const response = await responsePromise; return response.response === "unlocked"; } async isBiometricUnlockAvailable(): Promise { - const responsePromise = this.nativeMessagingBackground.getResponse(); - await this.nativeMessagingBackground.send({ command: "biometricUnlockAvailable" }); + const responsePromise = this.nativeMessagingBackground().getResponse(); + await this.nativeMessagingBackground().send({ command: "biometricUnlockAvailable" }); const response = await responsePromise; return response.response === "available"; } From 87a1742a433248c73056f78cd8754789a9fba4eb Mon Sep 17 00:00:00 2001 From: Nick Krantz <125900171+nick-livefront@users.noreply.github.com> Date: Tue, 3 Sep 2024 10:24:14 -0500 Subject: [PATCH 21/24] lowercase header for add/edit v2 (#10770) --- .../components/vault-v2/add-edit/add-edit-v2.component.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts b/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts index b1e95afb535..7664c7e0ca1 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts @@ -312,13 +312,13 @@ export class AddEditV2Component implements OnInit { switch (type) { case CipherType.Login: - return this.i18nService.t(partOne, this.i18nService.t("typeLogin")); + return this.i18nService.t(partOne, this.i18nService.t("typeLogin").toLocaleLowerCase()); case CipherType.Card: - return this.i18nService.t(partOne, this.i18nService.t("typeCard")); + return this.i18nService.t(partOne, this.i18nService.t("typeCard").toLocaleLowerCase()); case CipherType.Identity: - return this.i18nService.t(partOne, this.i18nService.t("typeIdentity")); + return this.i18nService.t(partOne, this.i18nService.t("typeIdentity").toLocaleLowerCase()); case CipherType.SecureNote: - return this.i18nService.t(partOne, this.i18nService.t("note")); + return this.i18nService.t(partOne, this.i18nService.t("note").toLocaleLowerCase()); } } } From 1951b70fd1c781937b256e1f71ed20c0d1ee0e88 Mon Sep 17 00:00:00 2001 From: Nick Krantz <125900171+nick-livefront@users.noreply.github.com> Date: Tue, 3 Sep 2024 10:25:10 -0500 Subject: [PATCH 22/24] implement bitLink for password history (#10769) --- .../src/cipher-view/item-history/item-history-v2.component.html | 1 + .../src/cipher-view/item-history/item-history-v2.component.ts | 2 ++ 2 files changed, 3 insertions(+) diff --git a/libs/vault/src/cipher-view/item-history/item-history-v2.component.html b/libs/vault/src/cipher-view/item-history/item-history-v2.component.html index 4d86645c8bc..a03639dee61 100644 --- a/libs/vault/src/cipher-view/item-history/item-history-v2.component.html +++ b/libs/vault/src/cipher-view/item-history/item-history-v2.component.html @@ -27,6 +27,7 @@

Date: Tue, 3 Sep 2024 10:30:46 -0500 Subject: [PATCH 23/24] Use alt background for view dialog. (#10763) --- apps/web/src/app/vault/individual-vault/view.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/src/app/vault/individual-vault/view.component.html b/apps/web/src/app/vault/individual-vault/view.component.html index a70f1be49d7..d1caf76192d 100644 --- a/apps/web/src/app/vault/individual-vault/view.component.html +++ b/apps/web/src/app/vault/individual-vault/view.component.html @@ -1,4 +1,4 @@ - + {{ cipherTypeString }} From b27dc44298436a8c6cc64d5681cd5a78b4a8fd95 Mon Sep 17 00:00:00 2001 From: rr-bw <102181210+rr-bw@users.noreply.github.com> Date: Tue, 3 Sep 2024 09:40:11 -0700 Subject: [PATCH 24/24] [PM-11136] Convert LoginEmailService email property to state provider (#10624) * convert email property to state provider * update tests * assign loginEmail to variable before passing in * remove nav logic in ngOnInit --- apps/browser/src/auth/popup/home.component.ts | 4 +-- .../browser/src/auth/popup/login.component.ts | 13 ++++---- apps/web/src/app/auth/hint.component.ts | 4 +-- ...base-login-decryption-options.component.ts | 4 +-- .../src/auth/components/hint.component.ts | 5 ++-- .../login-via-auth-request.component.ts | 10 ++----- .../src/auth/components/login.component.ts | 4 +-- .../abstractions/login-email.service.ts | 19 ++++++------ .../login-email/login-email.service.spec.ts | 18 +++++------ .../login-email/login-email.service.ts | 30 ++++++++++++------- .../src/platform/state/state-definitions.ts | 1 + 11 files changed, 58 insertions(+), 54 deletions(-) diff --git a/apps/browser/src/auth/popup/home.component.ts b/apps/browser/src/auth/popup/home.component.ts index 505931ad0f1..cd9dfc3702b 100644 --- a/apps/browser/src/auth/popup/home.component.ts +++ b/apps/browser/src/auth/popup/home.component.ts @@ -41,7 +41,7 @@ export class HomeComponent implements OnInit, OnDestroy { ) {} async ngOnInit(): Promise { - const email = this.loginEmailService.getEmail(); + const email = await firstValueFrom(this.loginEmailService.loginEmail$); const rememberEmail = this.loginEmailService.getRememberEmail(); if (email != null) { @@ -93,7 +93,7 @@ export class HomeComponent implements OnInit, OnDestroy { async setLoginEmailValues() { // Note: Browser saves email settings here instead of the login component this.loginEmailService.setRememberEmail(this.formGroup.value.rememberEmail); - this.loginEmailService.setEmail(this.formGroup.value.email); + await this.loginEmailService.setLoginEmail(this.formGroup.value.email); await this.loginEmailService.saveEmailSettings(); } } diff --git a/apps/browser/src/auth/popup/login.component.ts b/apps/browser/src/auth/popup/login.component.ts index 6e73199969a..09bfdbbc240 100644 --- a/apps/browser/src/auth/popup/login.component.ts +++ b/apps/browser/src/auth/popup/login.component.ts @@ -1,4 +1,4 @@ -import { Component, NgZone } from "@angular/core"; +import { Component, NgZone, OnInit } from "@angular/core"; import { FormBuilder } from "@angular/forms"; import { ActivatedRoute, Router } from "@angular/router"; import { firstValueFrom } from "rxjs"; @@ -31,7 +31,7 @@ import { flagEnabled } from "../../platform/flags"; selector: "app-login", templateUrl: "login.component.html", }) -export class LoginComponent extends BaseLoginComponent { +export class LoginComponent extends BaseLoginComponent implements OnInit { showPasswordless = false; constructor( devicesApiService: DevicesApiServiceAbstraction, @@ -83,13 +83,14 @@ export class LoginComponent extends BaseLoginComponent { }; super.successRoute = "/tabs/vault"; this.showPasswordless = flagEnabled("showPasswordless"); + } + async ngOnInit(): Promise { if (this.showPasswordless) { - this.formGroup.controls.email.setValue(this.loginEmailService.getEmail()); + const loginEmail = await firstValueFrom(this.loginEmailService.loginEmail$); + this.formGroup.controls.email.setValue(loginEmail); this.formGroup.controls.rememberEmail.setValue(this.loginEmailService.getRememberEmail()); - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.validateEmail(); + await this.validateEmail(); } } diff --git a/apps/web/src/app/auth/hint.component.ts b/apps/web/src/app/auth/hint.component.ts index 42744546234..753bdb342f9 100644 --- a/apps/web/src/app/auth/hint.component.ts +++ b/apps/web/src/app/auth/hint.component.ts @@ -44,8 +44,8 @@ export class HintComponent extends BaseHintComponent implements OnInit { ); } - ngOnInit(): void { - super.ngOnInit(); + async ngOnInit(): Promise { + await super.ngOnInit(); this.emailFormControl.setValue(this.email); } diff --git a/libs/angular/src/auth/components/base-login-decryption-options.component.ts b/libs/angular/src/auth/components/base-login-decryption-options.component.ts index 80088bf7f91..6487c0cf847 100644 --- a/libs/angular/src/auth/components/base-login-decryption-options.component.ts +++ b/libs/angular/src/auth/components/base-login-decryption-options.component.ts @@ -251,12 +251,12 @@ export class BaseLoginDecryptionOptionsComponent implements OnInit, OnDestroy { return; } - this.loginEmailService.setEmail(this.data.userEmail); + this.loginEmailService.setLoginEmail(this.data.userEmail); await this.router.navigate(["/login-with-device"]); } async requestAdminApproval() { - this.loginEmailService.setEmail(this.data.userEmail); + this.loginEmailService.setLoginEmail(this.data.userEmail); await this.router.navigate(["/admin-approval-requested"]); } diff --git a/libs/angular/src/auth/components/hint.component.ts b/libs/angular/src/auth/components/hint.component.ts index 7a152efbb9f..f7ae1e4c182 100644 --- a/libs/angular/src/auth/components/hint.component.ts +++ b/libs/angular/src/auth/components/hint.component.ts @@ -1,5 +1,6 @@ import { Directive, OnInit } from "@angular/core"; import { Router } from "@angular/router"; +import { firstValueFrom } from "rxjs"; import { LoginEmailServiceAbstraction } from "@bitwarden/auth/common"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; @@ -27,8 +28,8 @@ export class HintComponent implements OnInit { protected toastService: ToastService, ) {} - ngOnInit(): void { - this.email = this.loginEmailService.getEmail() ?? ""; + async ngOnInit(): Promise { + this.email = (await firstValueFrom(this.loginEmailService.loginEmail$)) ?? ""; } async submit() { diff --git a/libs/angular/src/auth/components/login-via-auth-request.component.ts b/libs/angular/src/auth/components/login-via-auth-request.component.ts index 452b5ceee1e..a89952e024f 100644 --- a/libs/angular/src/auth/components/login-via-auth-request.component.ts +++ b/libs/angular/src/auth/components/login-via-auth-request.component.ts @@ -93,13 +93,6 @@ export class LoginViaAuthRequestComponent ) { super(environmentService, i18nService, platformUtilsService, toastService); - // TODO: I don't know why this is necessary. - // Why would the existence of the email depend on the navigation? - const navigation = this.router.getCurrentNavigation(); - if (navigation) { - this.email = this.loginEmailService.getEmail(); - } - // Gets signalR push notification // Only fires on approval to prevent enumeration this.authRequestService.authRequestPushNotification$ @@ -118,6 +111,7 @@ export class LoginViaAuthRequestComponent } async ngOnInit() { + this.email = await firstValueFrom(this.loginEmailService.loginEmail$); this.userAuthNStatus = await this.authService.getAuthStatus(); const matchOptions: IsActiveMatchOptions = { @@ -165,7 +159,7 @@ export class LoginViaAuthRequestComponent } else { // Standard auth request // TODO: evaluate if we can remove the setting of this.email in the constructor - this.email = this.loginEmailService.getEmail(); + this.email = await firstValueFrom(this.loginEmailService.loginEmail$); if (!this.email) { this.toastService.showToast({ diff --git a/libs/angular/src/auth/components/login.component.ts b/libs/angular/src/auth/components/login.component.ts index 501d753a976..3b927a05716 100644 --- a/libs/angular/src/auth/components/login.component.ts +++ b/libs/angular/src/auth/components/login.component.ts @@ -304,7 +304,7 @@ export class LoginComponent extends CaptchaProtectedComponent implements OnInit, private async loadEmailSettings() { // Try to load from memory first - const email = this.loginEmailService.getEmail(); + const email = await firstValueFrom(this.loginEmailService.loginEmail$); const rememberEmail = this.loginEmailService.getRememberEmail(); if (email) { this.formGroup.controls.email.setValue(email); @@ -321,7 +321,7 @@ export class LoginComponent extends CaptchaProtectedComponent implements OnInit, } protected async saveEmailSettings() { - this.loginEmailService.setEmail(this.formGroup.value.email); + this.loginEmailService.setLoginEmail(this.formGroup.value.email); this.loginEmailService.setRememberEmail(this.formGroup.value.rememberEmail); await this.loginEmailService.saveEmailSettings(); } diff --git a/libs/auth/src/common/abstractions/login-email.service.ts b/libs/auth/src/common/abstractions/login-email.service.ts index d4fbbaff840..496d890f162 100644 --- a/libs/auth/src/common/abstractions/login-email.service.ts +++ b/libs/auth/src/common/abstractions/login-email.service.ts @@ -1,29 +1,28 @@ import { Observable } from "rxjs"; export abstract class LoginEmailServiceAbstraction { + /** + * An observable that monitors the loginEmail in memory. + * The loginEmail is the email that is being used in the current login process. + */ + loginEmail$: Observable; /** * An observable that monitors the storedEmail on disk. * This will return null if an account is being added. */ storedEmail$: Observable; /** - * Gets the current email being used in the login process from memory. - * @returns A string of the email. + * Sets the loginEmail in memory. + * The loginEmail is the email that is being used in the current login process. */ - getEmail: () => string; - /** - * Sets the current email being used in the login process in memory. - * @param email The email to be set. - */ - setEmail: (email: string) => void; + setLoginEmail: (email: string) => Promise; /** * Gets from memory whether or not the email should be stored on disk when `saveEmailSettings` is called. * @returns A boolean stating whether or not the email should be stored on disk. */ getRememberEmail: () => boolean; /** - * Sets in memory whether or not the email should be stored on disk when - * `saveEmailSettings` is called. + * Sets in memory whether or not the email should be stored on disk when `saveEmailSettings` is called. */ setRememberEmail: (value: boolean) => void; /** diff --git a/libs/auth/src/common/services/login-email/login-email.service.spec.ts b/libs/auth/src/common/services/login-email/login-email.service.spec.ts index 55e54c82f6e..8bb9b962eaf 100644 --- a/libs/auth/src/common/services/login-email/login-email.service.spec.ts +++ b/libs/auth/src/common/services/login-email/login-email.service.spec.ts @@ -43,7 +43,7 @@ describe("LoginEmailService", () => { describe("storedEmail$", () => { it("returns the stored email when not adding an account", async () => { - sut.setEmail("userEmail@bitwarden.com"); + await sut.setLoginEmail("userEmail@bitwarden.com"); sut.setRememberEmail(true); await sut.saveEmailSettings(); @@ -53,7 +53,7 @@ describe("LoginEmailService", () => { }); it("returns the stored email when not adding an account and the user has just logged in", async () => { - sut.setEmail("userEmail@bitwarden.com"); + await sut.setLoginEmail("userEmail@bitwarden.com"); sut.setRememberEmail(true); await sut.saveEmailSettings(); @@ -66,7 +66,7 @@ describe("LoginEmailService", () => { }); it("returns null when adding an account", async () => { - sut.setEmail("userEmail@bitwarden.com"); + await sut.setLoginEmail("userEmail@bitwarden.com"); sut.setRememberEmail(true); await sut.saveEmailSettings(); @@ -83,7 +83,7 @@ describe("LoginEmailService", () => { describe("saveEmailSettings", () => { it("saves the email when not adding an account", async () => { - sut.setEmail("userEmail@bitwarden.com"); + await sut.setLoginEmail("userEmail@bitwarden.com"); sut.setRememberEmail(true); await sut.saveEmailSettings(); @@ -95,7 +95,7 @@ describe("LoginEmailService", () => { it("clears the email when not adding an account and rememberEmail is false", async () => { storedEmailState.stateSubject.next("initialEmail@bitwarden.com"); - sut.setEmail("userEmail@bitwarden.com"); + await sut.setLoginEmail("userEmail@bitwarden.com"); sut.setRememberEmail(false); await sut.saveEmailSettings(); @@ -110,7 +110,7 @@ describe("LoginEmailService", () => { ["OtherUserId" as UserId]: AuthenticationStatus.Locked, }); - sut.setEmail("userEmail@bitwarden.com"); + await sut.setLoginEmail("userEmail@bitwarden.com"); sut.setRememberEmail(true); await sut.saveEmailSettings(); @@ -127,7 +127,7 @@ describe("LoginEmailService", () => { ["OtherUserId" as UserId]: AuthenticationStatus.Locked, }); - sut.setEmail("userEmail@bitwarden.com"); + await sut.setLoginEmail("userEmail@bitwarden.com"); sut.setRememberEmail(false); await sut.saveEmailSettings(); @@ -140,11 +140,11 @@ describe("LoginEmailService", () => { it("does not clear the email and rememberEmail after saving", async () => { // Browser uses these values to maintain the email between login and 2fa components so // we do not want to clear them too early. - sut.setEmail("userEmail@bitwarden.com"); + await sut.setLoginEmail("userEmail@bitwarden.com"); sut.setRememberEmail(true); await sut.saveEmailSettings(); - const result = sut.getEmail(); + const result = await firstValueFrom(sut.loginEmail$); expect(result).toBe("userEmail@bitwarden.com"); }); diff --git a/libs/auth/src/common/services/login-email/login-email.service.ts b/libs/auth/src/common/services/login-email/login-email.service.ts index 7793d3e7ff6..bb89b412c51 100644 --- a/libs/auth/src/common/services/login-email/login-email.service.ts +++ b/libs/auth/src/common/services/login-email/login-email.service.ts @@ -8,21 +8,28 @@ import { GlobalState, KeyDefinition, LOGIN_EMAIL_DISK, + LOGIN_EMAIL_MEMORY, StateProvider, } from "../../../../../common/src/platform/state"; import { LoginEmailServiceAbstraction } from "../../abstractions/login-email.service"; +export const LOGIN_EMAIL = new KeyDefinition(LOGIN_EMAIL_MEMORY, "loginEmail", { + deserializer: (value: string) => value, +}); + export const STORED_EMAIL = new KeyDefinition(LOGIN_EMAIL_DISK, "storedEmail", { deserializer: (value: string) => value, }); export class LoginEmailService implements LoginEmailServiceAbstraction { - private email: string | null; private rememberEmail: boolean; // True if an account is currently being added through account switching private readonly addingAccount$: Observable; + private readonly loginEmailState: GlobalState; + loginEmail$: Observable; + private readonly storedEmailState: GlobalState; storedEmail$: Observable; @@ -31,6 +38,7 @@ export class LoginEmailService implements LoginEmailServiceAbstraction { private authService: AuthService, private stateProvider: StateProvider, ) { + this.loginEmailState = this.stateProvider.getGlobal(LOGIN_EMAIL); this.storedEmailState = this.stateProvider.getGlobal(STORED_EMAIL); // In order to determine if an account is being added, we check if any account is not logged out @@ -46,6 +54,8 @@ export class LoginEmailService implements LoginEmailServiceAbstraction { }), ); + this.loginEmail$ = this.loginEmailState.state$; + this.storedEmail$ = this.storedEmailState.state$.pipe( switchMap(async (storedEmail) => { // When adding an account, we don't show the stored email @@ -57,12 +67,8 @@ export class LoginEmailService implements LoginEmailServiceAbstraction { ); } - getEmail() { - return this.email; - } - - setEmail(email: string) { - this.email = email; + async setLoginEmail(email: string) { + await this.loginEmailState.update((_) => email); } getRememberEmail() { @@ -76,25 +82,27 @@ export class LoginEmailService implements LoginEmailServiceAbstraction { // Note: only clear values on successful login or you are sure they are not needed. // Browser uses these values to maintain the email between login and 2fa components so // we do not want to clear them too early. - clearValues() { - this.email = null; + async clearValues() { + await this.setLoginEmail(null); this.rememberEmail = false; } async saveEmailSettings() { const addingAccount = await firstValueFrom(this.addingAccount$); + const email = await firstValueFrom(this.loginEmail$); + await this.storedEmailState.update((storedEmail) => { // If we're adding an account, only overwrite the stored email when rememberEmail is true if (addingAccount) { if (this.rememberEmail) { - return this.email; + return email; } return storedEmail; } // Saving with rememberEmail set to false will clear the stored email if (this.rememberEmail) { - return this.email; + return email; } return null; }); diff --git a/libs/common/src/platform/state/state-definitions.ts b/libs/common/src/platform/state/state-definitions.ts index 32307203b29..47b7199b940 100644 --- a/libs/common/src/platform/state/state-definitions.ts +++ b/libs/common/src/platform/state/state-definitions.ts @@ -53,6 +53,7 @@ export const KEY_CONNECTOR_DISK = new StateDefinition("keyConnector", "disk"); export const LOGIN_EMAIL_DISK = new StateDefinition("loginEmail", "disk", { web: "disk-local", }); +export const LOGIN_EMAIL_MEMORY = new StateDefinition("loginEmail", "memory"); export const LOGIN_STRATEGY_MEMORY = new StateDefinition("loginStrategy", "memory"); export const MASTER_PASSWORD_DISK = new StateDefinition("masterPassword", "disk"); export const MASTER_PASSWORD_MEMORY = new StateDefinition("masterPassword", "memory");