From 917c5fff5bc151de6b36ccd65cf045e9ce74d1ae Mon Sep 17 00:00:00 2001 From: Jake Fink Date: Wed, 5 Jun 2024 16:05:12 -0400 Subject: [PATCH 01/14] remove extra unused method (#9500) --- .../abstractions/login-strategy.service.ts | 9 ---- .../login-strategy.service.ts | 44 ------------------- 2 files changed, 53 deletions(-) diff --git a/libs/auth/src/common/abstractions/login-strategy.service.ts b/libs/auth/src/common/abstractions/login-strategy.service.ts index eae6dc2a275..a46636532bf 100644 --- a/libs/auth/src/common/abstractions/login-strategy.service.ts +++ b/libs/auth/src/common/abstractions/login-strategy.service.ts @@ -3,7 +3,6 @@ import { Observable } from "rxjs"; import { AuthenticationType } from "@bitwarden/common/auth/enums/authentication-type"; import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result"; import { TokenTwoFactorRequest } from "@bitwarden/common/auth/models/request/identity-token/token-two-factor.request"; -import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response"; import { MasterKey } from "@bitwarden/common/types/key"; import { @@ -72,12 +71,4 @@ export abstract class LoginStrategyServiceAbstraction { * Creates a master key from the provided master password and email. */ makePreloginKey: (masterPassword: string, email: string) => Promise; - /** - * Sends a response to an auth request. - */ - passwordlessLogin: ( - id: string, - key: string, - requestApproved: boolean, - ) => Promise; } diff --git a/libs/auth/src/common/services/login-strategies/login-strategy.service.ts b/libs/auth/src/common/services/login-strategies/login-strategy.service.ts index f425bc697c5..7169fd69e93 100644 --- a/libs/auth/src/common/services/login-strategies/login-strategy.service.ts +++ b/libs/auth/src/common/services/login-strategies/login-strategy.service.ts @@ -24,8 +24,6 @@ import { PBKDF2KdfConfig, } from "@bitwarden/common/auth/models/domain/kdf-config"; import { TokenTwoFactorRequest } from "@bitwarden/common/auth/models/request/identity-token/token-two-factor.request"; -import { PasswordlessAuthRequest } from "@bitwarden/common/auth/models/request/passwordless-auth.request"; -import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { PreloginRequest } from "@bitwarden/common/models/request/prelogin.request"; import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; @@ -39,7 +37,6 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { KdfType } from "@bitwarden/common/platform/enums/kdf-type.enum"; -import { Utils } from "@bitwarden/common/platform/misc/utils"; import { GlobalState, GlobalStateProvider } from "@bitwarden/common/platform/state"; import { DeviceTrustServiceAbstraction } from "@bitwarden/common/src/auth/abstractions/device-trust.service.abstraction"; import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength"; @@ -263,47 +260,6 @@ export class LoginStrategyService implements LoginStrategyServiceAbstraction { return await this.cryptoService.makeMasterKey(masterPassword, email, kdfConfig); } - // TODO: move to auth request service - async passwordlessLogin( - id: string, - key: string, - requestApproved: boolean, - ): Promise { - const pubKey = Utils.fromB64ToArray(key); - - const userId = (await firstValueFrom(this.accountService.activeAccount$)).id; - const masterKey = await firstValueFrom(this.masterPasswordService.masterKey$(userId)); - let keyToEncrypt; - let encryptedMasterKeyHash = null; - - if (masterKey) { - keyToEncrypt = masterKey.encKey; - - // Only encrypt the master password hash if masterKey exists as - // we won't have a masterKeyHash without a masterKey - const masterKeyHash = await firstValueFrom(this.masterPasswordService.masterKeyHash$(userId)); - if (masterKeyHash != null) { - encryptedMasterKeyHash = await this.cryptoService.rsaEncrypt( - Utils.fromUtf8ToArray(masterKeyHash), - pubKey, - ); - } - } else { - const userKey = await this.cryptoService.getUserKey(); - keyToEncrypt = userKey.key; - } - - const encryptedKey = await this.cryptoService.rsaEncrypt(keyToEncrypt, pubKey); - - const request = new PasswordlessAuthRequest( - encryptedKey.encryptedString, - encryptedMasterKeyHash?.encryptedString, - await this.appIdService.getAppId(), - requestApproved, - ); - return await this.apiService.putAuthRequest(id, request); - } - private async clearCache(): Promise { await this.currentAuthnTypeState.update((_) => null); await this.loginStrategyCacheState.update((_) => null); From ce69b25d546fb4f5e6164aae674a146b3548b5a6 Mon Sep 17 00:00:00 2001 From: Cesar Gonzalez Date: Wed, 5 Jun 2024 15:14:34 -0500 Subject: [PATCH 02/14] [PM-8519] Inline menu fails to update credentials after saving credentials when unlocking extension (#9462) * Add Back Foreground & Background Derived State * Do Dependency Injection * Defend `map` Calls * Remove Folder Change * Add Helper For Preparing a Record For Use in `forkJoin` * Update & Test CryptoService Changes * Delete Unused Code * Update DeviceTrustService * Update CipherService * Make `userPublicKey$` Public * Rename convertValues File * Switch to `MonoTypeOperatorFunction` * Remove Unnecessary `async` * Remove Unnecessary Mock * Update libs/common/src/platform/abstractions/crypto.service.ts Co-authored-by: Andreas Coroiu * Add `convertValues` Tests * Add Doc Comments * Convert to `function`'s Co-authored-by: Andreas Coroiu * Fix Test Typos * Add param doc * Update Test Name * Add `@throws` Docs * [PM-8519] Inline Menu fails to Update Credentials After Saving New Cipher When Unlocking Extension * Revert "Merge branch 'reintroduce-foreground-background-derived' into autofill/pm-8519-inline-menu-fails-to-update-credentials-after-saving-credentials-when-unlocking-extension" This reverts commit 53b060dac108cf008ae1ab8f9a1199431496697b, reversing changes made to 15b9a237b82aa6276856596e02a032379e9fe73e. --------- Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com> Co-authored-by: Andreas Coroiu --- .../abstractions/overlay.background.ts | 2 ++ .../background/overlay.background.spec.ts | 34 ++++++++----------- .../autofill/background/overlay.background.ts | 2 ++ 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/apps/browser/src/autofill/background/abstractions/overlay.background.ts b/apps/browser/src/autofill/background/abstractions/overlay.background.ts index c9b230fe18c..aa62194af5c 100644 --- a/apps/browser/src/autofill/background/abstractions/overlay.background.ts +++ b/apps/browser/src/autofill/background/abstractions/overlay.background.ts @@ -77,7 +77,9 @@ type OverlayBackgroundExtensionMessageHandlers = { updateFocusedFieldData: ({ message, sender }: BackgroundOnMessageHandlerParams) => void; collectPageDetailsResponse: ({ message, sender }: BackgroundOnMessageHandlerParams) => void; unlockCompleted: ({ message }: BackgroundMessageParam) => void; + addedCipher: () => void; addEditCipherSubmitted: () => void; + editedCipher: () => void; deletedCipher: () => void; }; diff --git a/apps/browser/src/autofill/background/overlay.background.spec.ts b/apps/browser/src/autofill/background/overlay.background.spec.ts index c469eb7dbbc..df4867640f4 100644 --- a/apps/browser/src/autofill/background/overlay.background.spec.ts +++ b/apps/browser/src/autofill/background/overlay.background.spec.ts @@ -1000,29 +1000,23 @@ describe("OverlayBackground", () => { }); }); - describe("addEditCipherSubmitted message handler", () => { - it("updates the overlay ciphers", () => { - const message = { - command: "addEditCipherSubmitted", - }; - jest.spyOn(overlayBackground as any, "updateOverlayCiphers").mockImplementation(); + describe("extension messages that trigger an update of the inline menu ciphers", () => { + const extensionMessages = [ + "addedCipher", + "addEditCipherSubmitted", + "editedCipher", + "deletedCipher", + ]; - sendMockExtensionMessage(message); - - expect(overlayBackground["updateOverlayCiphers"]).toHaveBeenCalled(); + beforeEach(() => { + jest.spyOn(overlayBackground, "updateOverlayCiphers").mockImplementation(); }); - }); - describe("deletedCipher message handler", () => { - it("updates the overlay ciphers", () => { - const message = { - command: "deletedCipher", - }; - jest.spyOn(overlayBackground as any, "updateOverlayCiphers").mockImplementation(); - - sendMockExtensionMessage(message); - - expect(overlayBackground["updateOverlayCiphers"]).toHaveBeenCalled(); + extensionMessages.forEach((message) => { + it(`triggers an update of the overlay ciphers when the ${message} message is received`, () => { + sendMockExtensionMessage({ command: message }); + expect(overlayBackground.updateOverlayCiphers).toHaveBeenCalled(); + }); }); }); }); diff --git a/apps/browser/src/autofill/background/overlay.background.ts b/apps/browser/src/autofill/background/overlay.background.ts index 56f6a3a2158..bf954c3419f 100644 --- a/apps/browser/src/autofill/background/overlay.background.ts +++ b/apps/browser/src/autofill/background/overlay.background.ts @@ -72,7 +72,9 @@ class OverlayBackground implements OverlayBackgroundInterface { updateFocusedFieldData: ({ message, sender }) => this.setFocusedFieldData(message, sender), collectPageDetailsResponse: ({ message, sender }) => this.storePageDetails(message, sender), unlockCompleted: ({ message }) => this.unlockCompleted(message), + addedCipher: () => this.updateOverlayCiphers(), addEditCipherSubmitted: () => this.updateOverlayCiphers(), + editedCipher: () => this.updateOverlayCiphers(), deletedCipher: () => this.updateOverlayCiphers(), }; private readonly overlayButtonPortMessageHandlers: OverlayButtonPortMessageHandlers = { From 472a7a9d4dd88947f97bacfedd3e1ab783f9cf94 Mon Sep 17 00:00:00 2001 From: Will Martin Date: Wed, 5 Jun 2024 17:17:24 -0400 Subject: [PATCH 03/14] [CL-302] respect bottom padding in scrollable popup-layout (#9514) --- .../src/platform/popup/layout/popup-page.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/browser/src/platform/popup/layout/popup-page.component.html b/apps/browser/src/platform/popup/layout/popup-page.component.html index ece6be4c63e..b3dcd626ae3 100644 --- a/apps/browser/src/platform/popup/layout/popup-page.component.html +++ b/apps/browser/src/platform/popup/layout/popup-page.component.html @@ -1,6 +1,6 @@ -
-
+
+
From 37a07b9fd8ff4df534e88682c7c860966e0c63ce Mon Sep 17 00:00:00 2001 From: Jason Ng Date: Wed, 5 Jun 2024 18:05:40 -0400 Subject: [PATCH 04/14] [PM-8627] No Results Icon light/dark theme colors (#9522) * update no results icon for light/dark theme --- libs/components/src/icon/icons/no-results.ts | 22 ++++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/libs/components/src/icon/icons/no-results.ts b/libs/components/src/icon/icons/no-results.ts index f68e67f88ce..02e03f4c4ec 100644 --- a/libs/components/src/icon/icons/no-results.ts +++ b/libs/components/src/icon/icons/no-results.ts @@ -2,17 +2,17 @@ import { svgIcon } from "../icon"; export const NoResults = svgIcon` - - - - - - - - - - - + + + + + + + + + + + `; From 474a5a8c2237198ed9b5bac95f0ec3342e6b1227 Mon Sep 17 00:00:00 2001 From: Thomas Rittson <31796059+eliykat@users.noreply.github.com> Date: Thu, 6 Jun 2024 09:06:27 +1000 Subject: [PATCH 05/14] Add bit-cli directory to cli build workflow (#9491) --- .github/workflows/build-cli.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build-cli.yml b/.github/workflows/build-cli.yml index 6809d950c4e..5e333b3b58a 100644 --- a/.github/workflows/build-cli.yml +++ b/.github/workflows/build-cli.yml @@ -13,6 +13,7 @@ on: - '!*.md' - '!*.txt' - '.github/workflows/build-cli.yml' + - 'bitwarden_license/bit-cli/**' push: branches: - 'main' @@ -25,6 +26,7 @@ on: - '!*.md' - '!*.txt' - '.github/workflows/build-cli.yml' + - 'bitwarden_license/bit-cli/**' workflow_dispatch: inputs: {} From 323b59de7119401ce19b8f8d6b1e2423bb950a23 Mon Sep 17 00:00:00 2001 From: Maciej Zieniuk <167752252+mzieniukbw@users.noreply.github.com> Date: Thu, 6 Jun 2024 09:40:51 +0200 Subject: [PATCH 06/14] SM-1213 Add Bitwarden Secrets Manager tip to CLI help (#9307) * SM-1213 Add Bitwarden Secrets Manager tip to help * SM-1213: Moved SM tip before examples --- apps/cli/src/program.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/cli/src/program.ts b/apps/cli/src/program.ts index e0311beb247..597b388a05b 100644 --- a/apps/cli/src/program.ts +++ b/apps/cli/src/program.ts @@ -83,6 +83,11 @@ export class Program extends BaseProgram { }); program.on("--help", () => { + writeLn( + chalk.yellowBright( + "\n Tip: Managing and retrieving secrets for dev environments is easier with Bitwarden Secrets Manager. Learn more under https://bitwarden.com/products/secrets-manager/", + ), + ); writeLn("\n Examples:"); writeLn(""); writeLn(" bw login"); From d8a86cdb7ee07acfdf231cede2f71467319275a9 Mon Sep 17 00:00:00 2001 From: KiruthigaManivannan <162679756+KiruthigaManivannan@users.noreply.github.com> Date: Thu, 6 Jun 2024 18:18:13 +0530 Subject: [PATCH 07/14] PM-8478 Defect Visually more padding needed around bit avatar (#9481) --- apps/web/src/app/auth/settings/account/profile.component.html | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/web/src/app/auth/settings/account/profile.component.html b/apps/web/src/app/auth/settings/account/profile.component.html index db6ab2b6588..88f73945f26 100644 --- a/apps/web/src/app/auth/settings/account/profile.component.html +++ b/apps/web/src/app/auth/settings/account/profile.component.html @@ -23,6 +23,7 @@ + + + diff --git a/apps/web/src/app/auth/settings/account/change-avatar.component.ts b/apps/web/src/app/auth/settings/account/change-avatar-dialog.component.ts similarity index 78% rename from apps/web/src/app/auth/settings/account/change-avatar.component.ts rename to apps/web/src/app/auth/settings/account/change-avatar-dialog.component.ts index bbcbaf6820f..6946f8b94bb 100644 --- a/apps/web/src/app/auth/settings/account/change-avatar.component.ts +++ b/apps/web/src/app/auth/settings/account/change-avatar-dialog.component.ts @@ -1,11 +1,10 @@ +import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog"; import { Component, ElementRef, - EventEmitter, - Input, + Inject, OnDestroy, OnInit, - Output, ViewChild, ViewEncapsulation, } from "@angular/core"; @@ -14,20 +13,20 @@ import { BehaviorSubject, debounceTime, firstValueFrom, Subject, takeUntil } fro import { AvatarService } from "@bitwarden/common/auth/abstractions/avatar.service"; import { ProfileResponse } from "@bitwarden/common/models/response/profile.response"; 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 { Utils } from "@bitwarden/common/platform/misc/utils"; +import { DialogService } from "@bitwarden/components"; + +type ChangeAvatarDialogData = { + profile: ProfileResponse; +}; @Component({ - selector: "app-change-avatar", - templateUrl: "change-avatar.component.html", + templateUrl: "change-avatar-dialog.component.html", encapsulation: ViewEncapsulation.None, }) -export class ChangeAvatarComponent implements OnInit, OnDestroy { - @Input() profile: ProfileResponse; - - @Output() changeColor: EventEmitter = new EventEmitter(); - @Output() onSaved = new EventEmitter(); +export class ChangeAvatarDialogComponent implements OnInit, OnDestroy { + profile: ProfileResponse; @ViewChild("colorPicker") colorPickerElement: ElementRef; @@ -52,11 +51,14 @@ export class ChangeAvatarComponent implements OnInit, OnDestroy { private destroy$ = new Subject(); constructor( + @Inject(DIALOG_DATA) protected data: ChangeAvatarDialogData, private i18nService: I18nService, private platformUtilsService: PlatformUtilsService, - private logService: LogService, private avatarService: AvatarService, - ) {} + private dialogRef: DialogRef, + ) { + this.profile = data.profile; + } async ngOnInit() { //localize the default colors @@ -88,20 +90,15 @@ export class ChangeAvatarComponent implements OnInit, OnDestroy { Utils.stringToColor(this.profile.name.toString()); } - async submit() { - try { - if (Utils.validateHexColor(this.currentSelection) || this.currentSelection == null) { - await this.avatarService.setAvatarColor(this.currentSelection); - this.changeColor.emit(this.currentSelection); - this.platformUtilsService.showToast("success", null, this.i18nService.t("avatarUpdated")); - } else { - this.platformUtilsService.showToast("error", null, this.i18nService.t("errorOccurred")); - } - } catch (e) { - this.logService.error(e); + submit = async () => { + if (Utils.validateHexColor(this.currentSelection) || this.currentSelection == null) { + await this.avatarService.setAvatarColor(this.currentSelection); + this.dialogRef.close(); + this.platformUtilsService.showToast("success", null, this.i18nService.t("avatarUpdated")); + } else { this.platformUtilsService.showToast("error", null, this.i18nService.t("errorOccurred")); } - } + }; async ngOnDestroy() { this.destroy$.next(); @@ -131,6 +128,10 @@ export class ChangeAvatarComponent implements OnInit, OnDestroy { } } } + + static open(dialogService: DialogService, config: DialogConfig) { + return dialogService.open(ChangeAvatarDialogComponent, config); + } } export class NamedAvatarColor { diff --git a/apps/web/src/app/auth/settings/account/change-avatar.component.html b/apps/web/src/app/auth/settings/account/change-avatar.component.html deleted file mode 100644 index 3a974241d5f..00000000000 --- a/apps/web/src/app/auth/settings/account/change-avatar.component.html +++ /dev/null @@ -1,84 +0,0 @@ - - - diff --git a/apps/web/src/app/auth/settings/account/profile.component.html b/apps/web/src/app/auth/settings/account/profile.component.html index 88f73945f26..4464824c63e 100644 --- a/apps/web/src/app/auth/settings/account/profile.component.html +++ b/apps/web/src/app/auth/settings/account/profile.component.html @@ -45,4 +45,3 @@
- diff --git a/apps/web/src/app/auth/settings/account/profile.component.ts b/apps/web/src/app/auth/settings/account/profile.component.ts index 64c5687c0b8..33463b689c9 100644 --- a/apps/web/src/app/auth/settings/account/profile.component.ts +++ b/apps/web/src/app/auth/settings/account/profile.component.ts @@ -1,28 +1,25 @@ -import { ViewChild, ViewContainerRef, Component, OnDestroy, OnInit } from "@angular/core"; +import { Component, OnInit } from "@angular/core"; import { FormControl, FormGroup } from "@angular/forms"; import { Subject, takeUntil } from "rxjs"; -import { ModalService } from "@bitwarden/angular/services/modal.service"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { UpdateProfileRequest } from "@bitwarden/common/auth/models/request/update-profile.request"; import { ProfileResponse } from "@bitwarden/common/models/response/profile.response"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; +import { DialogService } from "@bitwarden/components"; -import { ChangeAvatarComponent } from "./change-avatar.component"; +import { ChangeAvatarDialogComponent } from "./change-avatar-dialog.component"; @Component({ selector: "app-profile", templateUrl: "profile.component.html", }) -export class ProfileComponent implements OnInit, OnDestroy { +export class ProfileComponent implements OnInit { loading = true; profile: ProfileResponse; fingerprintMaterial: string; - - @ViewChild("avatarModalTemplate", { read: ViewContainerRef, static: true }) - avatarModalRef: ViewContainerRef; private destroy$ = new Subject(); protected formGroup = new FormGroup({ @@ -35,7 +32,7 @@ export class ProfileComponent implements OnInit, OnDestroy { private i18nService: I18nService, private platformUtilsService: PlatformUtilsService, private stateService: StateService, - private modalService: ModalService, + private dialogService: DialogService, ) {} async ngOnInit() { @@ -53,24 +50,17 @@ export class ProfileComponent implements OnInit, OnDestroy { }); } + openChangeAvatar = async () => { + ChangeAvatarDialogComponent.open(this.dialogService, { + data: { profile: this.profile }, + }); + }; + async ngOnDestroy() { this.destroy$.next(); this.destroy$.complete(); } - openChangeAvatar = async () => { - const modalOpened = await this.modalService.openViewRef( - ChangeAvatarComponent, - this.avatarModalRef, - (modal) => { - modal.profile = this.profile; - modal.changeColor.pipe(takeUntil(this.destroy$)).subscribe(() => { - modalOpened[0].close(); - }); - }, - ); - }; - submit = async () => { const request = new UpdateProfileRequest( this.formGroup.get("name").value, diff --git a/apps/web/src/app/shared/loose-components.module.ts b/apps/web/src/app/shared/loose-components.module.ts index 79478d0cae6..b8cfbd34013 100644 --- a/apps/web/src/app/shared/loose-components.module.ts +++ b/apps/web/src/app/shared/loose-components.module.ts @@ -28,7 +28,7 @@ import { RegisterFormModule } from "../auth/register-form/register-form.module"; import { RemovePasswordComponent } from "../auth/remove-password.component"; import { SetPasswordComponent } from "../auth/set-password.component"; import { AccountComponent } from "../auth/settings/account/account.component"; -import { ChangeAvatarComponent } from "../auth/settings/account/change-avatar.component"; +import { ChangeAvatarDialogComponent } from "../auth/settings/account/change-avatar-dialog.component"; import { ChangeEmailComponent } from "../auth/settings/account/change-email.component"; import { DangerZoneComponent } from "../auth/settings/account/danger-zone.component"; import { DeauthorizeSessionsComponent } from "../auth/settings/account/deauthorize-sessions.component"; @@ -156,7 +156,7 @@ import { SharedModule } from "./shared.module"; PreferencesComponent, PremiumBadgeComponent, ProfileComponent, - ChangeAvatarComponent, + ChangeAvatarDialogComponent, ProvidersComponent, PurgeVaultComponent, RecoverDeleteComponent, @@ -230,7 +230,7 @@ import { SharedModule } from "./shared.module"; PreferencesComponent, PremiumBadgeComponent, ProfileComponent, - ChangeAvatarComponent, + ChangeAvatarDialogComponent, ProvidersComponent, PurgeVaultComponent, RecoverDeleteComponent, From 6fadee7cb4391da0d55e7c298f324faf6f719d1d Mon Sep 17 00:00:00 2001 From: KiruthigaManivannan <162679756+KiruthigaManivannan@users.noreply.github.com> Date: Thu, 6 Jun 2024 19:23:29 +0530 Subject: [PATCH 11/14] PM-2055 Update Two Factor Authenticator Dialog (#8972) * PM-2055 Update Two Factor Authenticator Dialog * PM-2055 Added close to disable two factor * PM-2055 Added a event emitter to capture enabled status --- .../two-factor-authenticator.component.html | 189 ++++++++---------- .../two-factor-authenticator.component.ts | 43 +++- .../settings/two-factor-setup.component.html | 1 - .../settings/two-factor-setup.component.ts | 11 +- 4 files changed, 119 insertions(+), 125 deletions(-) diff --git a/apps/web/src/app/auth/settings/two-factor-authenticator.component.html b/apps/web/src/app/auth/settings/two-factor-authenticator.component.html index e17714cca79..a7efaed731e 100644 --- a/apps/web/src/app/auth/settings/two-factor-authenticator.component.html +++ b/apps/web/src/app/auth/settings/two-factor-authenticator.component.html @@ -1,109 +1,80 @@ - +
+ + {{ "twoStepLogin" | i18n }} + {{ "authenticatorAppTitle" | i18n }} + + + + Authenticator app logo +

{{ "twoStepAuthenticatorDesc" | i18n }}

+

+ 1. {{ "twoStepAuthenticatorDownloadApp" | i18n }} +

+
+ + +

{{ "twoStepLoginProviderEnabled" | i18n }}

+ {{ "twoStepAuthenticatorReaddDesc" | i18n }} +
+ Authenticator app logo +

{{ "twoStepAuthenticatorNeedApp" | i18n }}

+
+ +

{{ "twoStepAuthenticatorAppsRecommended" | i18n }}

+

+ 2. {{ "twoStepAuthenticatorScanCode" | i18n }} +

+
+

+
+ {{ key }} +

+ + + 3. {{ "twoStepAuthenticatorEnterCode" | i18n }} + + + +
+ + + + +
+
diff --git a/apps/web/src/app/auth/settings/two-factor-authenticator.component.ts b/apps/web/src/app/auth/settings/two-factor-authenticator.component.ts index 88b695eb728..17cdbb595f7 100644 --- a/apps/web/src/app/auth/settings/two-factor-authenticator.component.ts +++ b/apps/web/src/app/auth/settings/two-factor-authenticator.component.ts @@ -1,4 +1,6 @@ -import { Component, OnDestroy, OnInit } from "@angular/core"; +import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog"; +import { Component, EventEmitter, Inject, OnDestroy, OnInit, Output } from "@angular/core"; +import { FormControl, FormGroup, Validators } from "@angular/forms"; import { firstValueFrom, map } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; @@ -38,15 +40,21 @@ export class TwoFactorAuthenticatorComponent extends TwoFactorBaseComponent implements OnInit, OnDestroy { + @Output() onChangeStatus = new EventEmitter(); type = TwoFactorProviderType.Authenticator; key: string; - token: string; formPromise: Promise; override componentName = "app-two-factor-authenticator"; private qrScript: HTMLScriptElement; + protected formGroup = new FormGroup({ + token: new FormControl(null, [Validators.required]), + }); + constructor( + @Inject(DIALOG_DATA) protected data: AuthResponse, + private dialogRef: DialogRef, apiService: ApiService, i18nService: I18nService, userVerificationService: UserVerificationService, @@ -68,8 +76,9 @@ export class TwoFactorAuthenticatorComponent this.qrScript.async = true; } - ngOnInit() { + async ngOnInit() { window.document.body.appendChild(this.qrScript); + await this.auth(this.data); } ngOnDestroy() { @@ -81,17 +90,24 @@ export class TwoFactorAuthenticatorComponent return this.processResponse(authResponse.response); } - submit() { + submit = async () => { if (this.enabled) { - return super.disable(this.formPromise); + await this.disableAuthentication(this.formPromise); + this.onChangeStatus.emit(this.enabled); + this.close(); } else { - return this.enable(); + await this.enable(); + this.onChangeStatus.emit(this.enabled); } + }; + + private async disableAuthentication(promise: Promise) { + return super.disable(promise); } protected async enable() { const request = await this.buildRequestModel(UpdateTwoFactorAuthenticatorRequest); - request.token = this.token; + request.token = this.formGroup.value.token; request.key = this.key; return super.enable(async () => { @@ -102,7 +118,7 @@ export class TwoFactorAuthenticatorComponent } private async processResponse(response: TwoFactorAuthenticatorResponse) { - this.token = null; + this.formGroup.get("token").setValue(null); this.enabled = response.enabled; this.key = response.key; const email = await firstValueFrom( @@ -121,4 +137,15 @@ export class TwoFactorAuthenticatorComponent }); }, 100); } + + close = () => { + this.dialogRef.close(this.enabled); + }; + + static open( + dialogService: DialogService, + config: DialogConfig>, + ) { + return dialogService.open(TwoFactorAuthenticatorComponent, config); + } } diff --git a/apps/web/src/app/auth/settings/two-factor-setup.component.html b/apps/web/src/app/auth/settings/two-factor-setup.component.html index a20bb345664..28baf72f885 100644 --- a/apps/web/src/app/auth/settings/two-factor-setup.component.html +++ b/apps/web/src/app/auth/settings/two-factor-setup.component.html @@ -80,7 +80,6 @@ - diff --git a/apps/web/src/app/auth/settings/two-factor-setup.component.ts b/apps/web/src/app/auth/settings/two-factor-setup.component.ts index dd4c69aa277..34a7e32089f 100644 --- a/apps/web/src/app/auth/settings/two-factor-setup.component.ts +++ b/apps/web/src/app/auth/settings/two-factor-setup.component.ts @@ -34,8 +34,6 @@ import { TwoFactorYubiKeyComponent } from "./two-factor-yubikey.component"; templateUrl: "two-factor-setup.component.html", }) export class TwoFactorSetupComponent implements OnInit, OnDestroy { - @ViewChild("authenticatorTemplate", { read: ViewContainerRef, static: true }) - authenticatorModalRef: ViewContainerRef; @ViewChild("yubikeyTemplate", { read: ViewContainerRef, static: true }) yubikeyModalRef: ViewContainerRef; @ViewChild("duoTemplate", { read: ViewContainerRef, static: true }) duoModalRef: ViewContainerRef; @@ -137,12 +135,11 @@ export class TwoFactorSetupComponent implements OnInit, OnDestroy { if (!result) { return; } - const authComp = await this.openModal( - this.authenticatorModalRef, - TwoFactorAuthenticatorComponent, + const authComp: DialogRef = TwoFactorAuthenticatorComponent.open( + this.dialogService, + { data: result }, ); - await authComp.auth(result); - authComp.onUpdated.pipe(takeUntil(this.destroy$)).subscribe((enabled: boolean) => { + authComp.componentInstance.onChangeStatus.subscribe((enabled: boolean) => { this.updateStatus(enabled, TwoFactorProviderType.Authenticator); }); break; From c06211829f034c05e1c7077ce8c11777e137cd18 Mon Sep 17 00:00:00 2001 From: vinith-kovan <156108204+vinith-kovan@users.noreply.github.com> Date: Thu, 6 Jun 2024 19:29:54 +0530 Subject: [PATCH 12/14] [PM 5023] migrate payment component (#8345) * payment component migration * payment component migration * payment component migration --- .../app/billing/shared/payment.component.html | 266 +++++++----------- .../app/billing/shared/payment.component.ts | 63 +++-- 2 files changed, 149 insertions(+), 180 deletions(-) diff --git a/apps/web/src/app/billing/shared/payment.component.html b/apps/web/src/app/billing/shared/payment.component.html index db6f4f311b1..d76de65e507 100644 --- a/apps/web/src/app/billing/shared/payment.component.html +++ b/apps/web/src/app/billing/shared/payment.component.html @@ -1,163 +1,113 @@ -
-
- - -
-
- - -
-
- - -
-
- - -
-
- -
-
- -
-
-
- Visa, MasterCard, Discover, AmEx, JCB, Diners Club, UnionPay -
-
- -
-
-
-
- - +
+ + + + + {{ "creditCard" | i18n }} - - + + + + + {{ "bankAccount" | i18n }} + + + PayPal + + + + + {{ "accountCredit" | i18n }} + + +
+ +
+
+ +
+
+
+ Visa, MasterCard, Discover, AmEx, JCB, Diners Club, UnionPay +
+
+ +
+
+
+
+ + + + +
+
-
-
- - - - {{ "verifyBankAccountInitialDesc" | i18n }} {{ "verifyBankAccountFailureWarning" | i18n }} - -
-
- - + + + + {{ "verifyBankAccountInitialDesc" | i18n }} {{ "verifyBankAccountFailureWarning" | i18n }} + +
+ + {{ "routingNumber" | i18n }} + + + + {{ "accountNumber" | i18n }} + + + + {{ "accountHolderName" | i18n }} + + + + + {{ "bankAccountType" | i18n }} + + + + + +
-
- - + + +
+
+ {{ "paypalClickSubmit" | i18n }}
-
- - -
-
- - -
-
-
- -
-
- {{ "paypalClickSubmit" | i18n }} -
-
- - - {{ "makeSureEnoughCredit" | i18n }} - - + + + + {{ "makeSureEnoughCredit" | i18n }} + + +
diff --git a/apps/web/src/app/billing/shared/payment.component.ts b/apps/web/src/app/billing/shared/payment.component.ts index 652bed7801d..95b25a74e4e 100644 --- a/apps/web/src/app/billing/shared/payment.component.ts +++ b/apps/web/src/app/billing/shared/payment.component.ts @@ -1,4 +1,5 @@ import { Component, Input, OnDestroy, OnInit } from "@angular/core"; +import { FormControl, FormGroup, Validators } from "@angular/forms"; import { Subject, takeUntil } from "rxjs"; import { AbstractThemingService } from "@bitwarden/angular/platform/services/theming/theming.service.abstraction"; @@ -17,23 +18,34 @@ import { SharedModule } from "../../shared"; export class PaymentComponent implements OnInit, OnDestroy { @Input() showMethods = true; @Input() showOptions = true; - @Input() method = PaymentMethodType.Card; @Input() hideBank = false; @Input() hidePaypal = false; @Input() hideCredit = false; @Input() trialFlow = false; + @Input() + set method(value: PaymentMethodType) { + this._method = value; + this.paymentForm?.controls.method.setValue(value, { emitEvent: false }); + } + + get method(): PaymentMethodType { + return this._method; + } + private _method: PaymentMethodType = PaymentMethodType.Card; + private destroy$ = new Subject(); - - bank: any = { - routing_number: null, - account_number: null, - account_holder_name: null, - account_holder_type: "", - currency: "USD", - country: "US", - }; - + protected paymentForm = new FormGroup({ + method: new FormControl(this.method), + bank: new FormGroup({ + routing_number: new FormControl(null, [Validators.required]), + account_number: new FormControl(null, [Validators.required]), + account_holder_name: new FormControl(null, [Validators.required]), + account_holder_type: new FormControl("", [Validators.required]), + currency: new FormControl("USD"), + country: new FormControl("US"), + }), + }); paymentMethodType = PaymentMethodType; private btScript: HTMLScriptElement; @@ -85,7 +97,6 @@ export class PaymentComponent implements OnInit, OnDestroy { invalid: "is-invalid", }; } - async ngOnInit() { if (!this.showOptions) { this.hidePaypal = this.method !== PaymentMethodType.PayPal; @@ -97,6 +108,13 @@ export class PaymentComponent implements OnInit, OnDestroy { if (!this.hidePaypal) { window.document.head.appendChild(this.btScript); } + this.paymentForm + .get("method") + .valueChanges.pipe(takeUntil(this.destroy$)) + .subscribe((v) => { + this.method = v; + this.changeMethod(); + }); } ngOnDestroy() { @@ -140,7 +158,6 @@ export class PaymentComponent implements OnInit, OnDestroy { changeMethod() { this.btInstance = null; - if (this.method === PaymentMethodType.PayPal) { window.setTimeout(() => { (window as any).braintree.dropin.create( @@ -209,15 +226,17 @@ export class PaymentComponent implements OnInit, OnDestroy { } }); } else { - this.stripe.createToken("bank_account", this.bank).then((result: any) => { - if (result.error) { - reject(result.error.message); - } else if (result.token && result.token.id != null) { - resolve([result.token.id, this.method]); - } else { - reject(); - } - }); + this.stripe + .createToken("bank_account", this.paymentForm.get("bank").value) + .then((result: any) => { + if (result.error) { + reject(result.error.message); + } else if (result.token && result.token.id != null) { + resolve([result.token.id, this.method]); + } else { + reject(); + } + }); } } }); From 47e66bfeb40dd3197b8cca01fa3410ebaac7ed3e Mon Sep 17 00:00:00 2001 From: KiruthigaManivannan <162679756+KiruthigaManivannan@users.noreply.github.com> Date: Thu, 6 Jun 2024 20:05:21 +0530 Subject: [PATCH 13/14] AC-2723 Defect Range Too large error toast on reporting events is not being displayed (#9478) --- .../common/base.events.component.ts | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) 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 e14bb62a35d..12c051271e1 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 @@ -97,19 +97,15 @@ export abstract class BaseEventsComponent { this.loading = true; let events: EventView[] = []; let promise: Promise; - try { - promise = this.loadAndParseEvents( - dates[0], - dates[1], - clearExisting ? null : this.continuationToken, - ); + promise = this.loadAndParseEvents( + dates[0], + dates[1], + clearExisting ? null : this.continuationToken, + ); - const result = await promise; - this.continuationToken = result.continuationToken; - events = result.events; - } catch (e) { - this.logService.error(`Handled exception: ${e}`); - } + const result = await promise; + this.continuationToken = result.continuationToken; + events = result.events; if (!clearExisting && this.events != null && this.events.length > 0) { this.events = this.events.concat(events); From a7b45a44e340ebd2366a6b4f44c01ec70ef61eb2 Mon Sep 17 00:00:00 2001 From: Nick Krantz <125900171+nick-livefront@users.noreply.github.com> Date: Thu, 6 Jun 2024 09:39:21 -0500 Subject: [PATCH 14/14] [PM-8594] use singular version of the copy for filters (#9509) * use singular for collection and type * use singular verbiage for cipher type * use type translations for singularity --- .../vault-list-filters/vault-list-filters.component.html | 4 ++-- .../popup/services/vault-popup-list-filters.service.ts | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-list-filters/vault-list-filters.component.html b/apps/browser/src/vault/popup/components/vault-v2/vault-list-filters/vault-list-filters.component.html index 6136db59f46..afb7dd40066 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-list-filters/vault-list-filters.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-list-filters/vault-list-filters.component.html @@ -14,7 +14,7 @@ *ngIf="collections.length" formControlName="collection" placeholderIcon="bwi-collection" - [placeholderText]="'collections' | i18n" + [placeholderText]="'collection' | i18n" [options]="collections" > @@ -32,7 +32,7 @@ diff --git a/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.ts b/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.ts index bc42e7cb0a5..69a7039e2ac 100644 --- a/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.ts +++ b/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.ts @@ -139,22 +139,22 @@ export class VaultPopupListFiltersService { readonly cipherTypes: ChipSelectOption[] = [ { value: CipherType.Login, - label: this.i18nService.t("logins"), + label: this.i18nService.t("typeLogin"), icon: "bwi-globe", }, { value: CipherType.Card, - label: this.i18nService.t("cards"), + label: this.i18nService.t("typeCard"), icon: "bwi-credit-card", }, { value: CipherType.Identity, - label: this.i18nService.t("identities"), + label: this.i18nService.t("typeIdentity"), icon: "bwi-id-card", }, { value: CipherType.SecureNote, - label: this.i18nService.t("notes"), + label: this.i18nService.t("note"), icon: "bwi-sticky-note", }, ];