diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index cc70d3f5b4a..436e2d4ad0f 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -134,7 +134,7 @@ libs/key-management @bitwarden/team-key-management-dev libs/key-management-ui @bitwarden/team-key-management-dev libs/common/src/key-management @bitwarden/team-key-management-dev -apps/desktop/destkop_native/core/src/biometric/ @bitwarden/team-key-management-dev +apps/desktop/desktop_native/core/src/biometric/ @bitwarden/team-key-management-dev apps/desktop/src/services/native-messaging.service.ts @bitwarden/team-key-management-dev apps/browser/src/background/nativeMessaging.background.ts @bitwarden/team-key-management-dev apps/desktop/src/services/biometric-message-handler.service.ts @bitwarden/team-key-management-dev diff --git a/.github/codecov.yml b/.github/codecov.yml index b79cdd9f413..0a6b3ceacff 100644 --- a/.github/codecov.yml +++ b/.github/codecov.yml @@ -56,11 +56,11 @@ component_management: - apps/browser/src/key-management/** - apps/browser/src/background/nativeMessaging.background.ts - apps/cli/src/key-management/** - - apps/desktop/destkop_native/core/src/biometric/** + - apps/desktop/desktop_native/core/src/biometric/** - apps/desktop/src/key-management/** - apps/desktop/src/services/biometric-message-handler.service.ts - apps/desktop/src/services/native-messaging.service.ts - - apps/web/src/app/key-managemen/** + - apps/web/src/app/key-management/** - libs/common/src/key-management/** - libs/key-management/** - libs/key-management-ui/** diff --git a/apps/browser/package.json b/apps/browser/package.json index 8fc1d733921..202ec1c4fe1 100644 --- a/apps/browser/package.json +++ b/apps/browser/package.json @@ -1,6 +1,6 @@ { "name": "@bitwarden/browser", - "version": "2025.1.4", + "version": "2025.1.3", "scripts": { "build": "npm run build:chrome", "build:chrome": "cross-env BROWSER=chrome MANIFEST_VERSION=3 NODE_OPTIONS=\"--max-old-space-size=8192\" webpack", diff --git a/apps/browser/src/autofill/background/notification.background.ts b/apps/browser/src/autofill/background/notification.background.ts index ad3bee97d8a..a091256b28d 100644 --- a/apps/browser/src/autofill/background/notification.background.ts +++ b/apps/browser/src/autofill/background/notification.background.ts @@ -25,6 +25,7 @@ import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-stat import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { CipherType } from "@bitwarden/common/vault/enums"; +import { buildCipherIcon } from "@bitwarden/common/vault/icon/build-cipher-icon"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { LoginUriView } from "@bitwarden/common/vault/models/view/login-uri.view"; import { LoginView } from "@bitwarden/common/vault/models/view/login.view"; @@ -32,6 +33,7 @@ import { LoginView } from "@bitwarden/common/vault/models/view/login.view"; import { openUnlockPopout } from "../../auth/popup/utils/auth-popout-window"; import { BrowserApi } from "../../platform/browser/browser-api"; import { openAddEditVaultItemPopout } from "../../vault/popup/utils/vault-popout-window"; +import { NotificationCipherData } from "../content/components/cipher/types"; import { NotificationQueueMessageType } from "../enums/notification-queue-message-type.enum"; import { AutofillService } from "../services/abstractions/autofill.service"; @@ -82,6 +84,7 @@ export default class NotificationBackground { bgGetActiveUserServerConfig: () => this.getActiveUserServerConfig(), getWebVaultUrlForNotification: () => this.getWebVaultUrl(), notificationRefreshFlagValue: () => this.getNotificationFlag(), + bgGetDecryptedCiphers: () => this.getNotificationCipherData(), }; private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id)); @@ -132,6 +135,40 @@ export default class NotificationBackground { return await firstValueFrom(this.domainSettingsService.neverDomains$); } + /** + * + * Gets the current active tab and retrieves all decrypted ciphers + * for the tab's URL. It constructs and returns an array of `NotificationCipherData` objects. + * If no active tab or URL is found, it returns an empty array. + * + * @returns {Promise} + */ + + async getNotificationCipherData(): Promise { + const [currentTab, showFavicons, env] = await Promise.all([ + BrowserApi.getTabFromCurrentWindow(), + firstValueFrom(this.domainSettingsService.showFavicons$), + firstValueFrom(this.environmentService.environment$), + ]); + const iconsServerUrl = env.getIconsUrl(); + const decryptedCiphers = await this.cipherService.getAllDecryptedForUrl(currentTab.url); + + return decryptedCiphers.map((view) => { + const { id, name, reprompt, favorite, login } = view; + return { + id, + name, + type: CipherType.Login, + reprompt, + favorite, + icon: buildCipherIcon(iconsServerUrl, view, showFavicons), + login: login && { + username: login.username, + }, + }; + }); + } + /** * Gets the active user server config from the config service. */ diff --git a/apps/browser/src/autofill/content/components/cipher/cipher-info.ts b/apps/browser/src/autofill/content/components/cipher/cipher-info.ts index de374b44a97..6ff32353938 100644 --- a/apps/browser/src/autofill/content/components/cipher/cipher-info.ts +++ b/apps/browser/src/autofill/content/components/cipher/cipher-info.ts @@ -6,10 +6,10 @@ import { Theme } from "@bitwarden/common/platform/enums"; import { themes, typography } from "../../../content/components/constants/styles"; import { CipherInfoIndicatorIcons } from "./cipher-indicator-icons"; -import { CipherData } from "./types"; +import { NotificationCipherData } from "./types"; // @TODO support other cipher types (card, identity, notes, etc) -export function CipherInfo({ cipher, theme }: { cipher: CipherData; theme: Theme }) { +export function CipherInfo({ cipher, theme }: { cipher: NotificationCipherData; theme: Theme }) { const { name, login } = cipher; return html` diff --git a/apps/browser/src/autofill/content/components/cipher/cipher-item.ts b/apps/browser/src/autofill/content/components/cipher/cipher-item.ts index 651c20cac3a..96b44d2c0cc 100644 --- a/apps/browser/src/autofill/content/components/cipher/cipher-item.ts +++ b/apps/browser/src/autofill/content/components/cipher/cipher-item.ts @@ -12,7 +12,7 @@ import { import { CipherAction } from "./cipher-action"; import { CipherIcon } from "./cipher-icon"; import { CipherInfo } from "./cipher-info"; -import { CipherData } from "./types"; +import { NotificationCipherData } from "./types"; const cipherIconWidth = "24px"; @@ -22,7 +22,7 @@ export function CipherItem({ notificationType, theme = ThemeTypes.Light, }: { - cipher: CipherData; + cipher: NotificationCipherData; handleAction?: (e: Event) => void; notificationType?: NotificationType; theme: Theme; diff --git a/apps/browser/src/autofill/content/components/cipher/types.ts b/apps/browser/src/autofill/content/components/cipher/types.ts index acdee756570..ff29f9b559f 100644 --- a/apps/browser/src/autofill/content/components/cipher/types.ts +++ b/apps/browser/src/autofill/content/components/cipher/types.ts @@ -1,6 +1,4 @@ -// FIXME: Remove when updating file. Eslint update -// eslint-disable-next-line @typescript-eslint/no-unused-vars -const CipherTypes = { +export const CipherTypes = { Login: 1, SecureNote: 2, Card: 3, @@ -9,9 +7,7 @@ const CipherTypes = { type CipherType = (typeof CipherTypes)[keyof typeof CipherTypes]; -// FIXME: Remove when updating file. Eslint update -// eslint-disable-next-line @typescript-eslint/no-unused-vars -const CipherRepromptTypes = { +export const CipherRepromptTypes = { None: 0, Password: 1, } as const; @@ -25,13 +21,16 @@ export type WebsiteIconData = { icon: string; }; -export type CipherData = { +type BaseCipherData = { id: string; name: string; - type: CipherType; + type: CipherTypeValue; reprompt: CipherRepromptType; favorite: boolean; icon: WebsiteIconData; +}; + +export type CipherData = BaseCipherData & { accountCreationFieldType?: string; login?: { username: string; @@ -46,3 +45,9 @@ export type CipherData = { username?: string; }; }; + +export type NotificationCipherData = BaseCipherData & { + login?: { + username: string; + }; +}; diff --git a/apps/browser/src/autofill/content/components/lit-stories/notification/body.lit-stories.ts b/apps/browser/src/autofill/content/components/lit-stories/notification/body.lit-stories.ts index 90616647b0e..c4b32e8b0f1 100644 --- a/apps/browser/src/autofill/content/components/lit-stories/notification/body.lit-stories.ts +++ b/apps/browser/src/autofill/content/components/lit-stories/notification/body.lit-stories.ts @@ -5,11 +5,11 @@ import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type"; import { NotificationType } from "../../../../notification/abstractions/notification-bar"; -import { CipherData } from "../../cipher/types"; +import { NotificationCipherData } from "../../cipher/types"; import { NotificationBody } from "../../notification/body"; type Args = { - ciphers: CipherData[]; + ciphers: NotificationCipherData[]; notificationType: NotificationType; theme: Theme; }; @@ -38,7 +38,7 @@ export default { fallbackImage: "https://example.com/fallback.png", icon: "icon-class", }, - login: { username: "user@example.com", passkey: null }, + login: { username: "user@example.com" }, }, ], theme: ThemeTypes.Light, diff --git a/apps/browser/src/autofill/content/components/notification/body.ts b/apps/browser/src/autofill/content/components/notification/body.ts index 6a3ed2e5d1e..6dc957ab8b8 100644 --- a/apps/browser/src/autofill/content/components/notification/body.ts +++ b/apps/browser/src/autofill/content/components/notification/body.ts @@ -5,7 +5,7 @@ import { Theme, ThemeTypes } from "@bitwarden/common/platform/enums"; import { NotificationType } from "../../../notification/abstractions/notification-bar"; import { CipherItem } from "../cipher"; -import { CipherData } from "../cipher/types"; +import { NotificationCipherData } from "../cipher/types"; import { scrollbarStyles, spacing, themes, typography } from "../constants/styles"; import { ItemRow } from "../rows/item-row"; @@ -20,7 +20,7 @@ export function NotificationBody({ notificationType, theme = ThemeTypes.Light, }: { - ciphers: CipherData[]; + ciphers: NotificationCipherData[]; customClasses?: string[]; notificationType?: NotificationType; theme: Theme; diff --git a/apps/browser/src/autofill/content/components/notification/container.ts b/apps/browser/src/autofill/content/components/notification/container.ts index 8bd07ab8296..8d1b57c80cd 100644 --- a/apps/browser/src/autofill/content/components/notification/container.ts +++ b/apps/browser/src/autofill/content/components/notification/container.ts @@ -8,8 +8,7 @@ import { NotificationTypes, NotificationType, } from "../../../notification/abstractions/notification-bar"; -import { createAutofillOverlayCipherDataMock } from "../../../spec/autofill-mocks"; -import { CipherData } from "../cipher/types"; +import { NotificationCipherData } from "../cipher/types"; import { themes, spacing } from "../constants/styles"; import { NotificationBody, componentClassPrefix as notificationBodyClassPrefix } from "./body"; @@ -24,23 +23,15 @@ export function NotificationContainer({ i18n, theme = ThemeTypes.Light, type, + ciphers, }: NotificationBarIframeInitData & { handleCloseNotification: (e: Event) => void } & { i18n: { [key: string]: string }; type: NotificationType; // @TODO typing override for generic `NotificationBarIframeInitData.type` + ciphers: NotificationCipherData[]; }) { const headerMessage = getHeaderMessage(i18n, type); const showBody = true; - // @TODO remove mock ciphers for development - const ciphers = [ - createAutofillOverlayCipherDataMock(1), - { ...createAutofillOverlayCipherDataMock(2), icon: { imageEnabled: false } }, - { - ...createAutofillOverlayCipherDataMock(3), - icon: { imageEnabled: true, image: "https://localhost:8443/icons/webtests.dev/icon.png" }, - }, - ] as CipherData[]; - return html`
${NotificationHeader({ diff --git a/apps/browser/src/autofill/notification/bar.ts b/apps/browser/src/autofill/notification/bar.ts index 202b144258d..2316df19857 100644 --- a/apps/browser/src/autofill/notification/bar.ts +++ b/apps/browser/src/autofill/notification/bar.ts @@ -84,23 +84,25 @@ function initNotificationBar(message: NotificationBarWindowMessage) { document.body.innerHTML = ""; // Current implementations utilize a require for scss files which creates the need to remove the node. document.head.querySelectorAll('link[rel="stylesheet"]').forEach((node) => node.remove()); - const themeType = getTheme(globalThis, theme); // There are other possible passed theme values, but for now, resolve to dark or light const resolvedTheme: Theme = themeType === ThemeTypes.Dark ? ThemeTypes.Dark : ThemeTypes.Light; - // @TODO use context to avoid prop drilling - return render( - NotificationContainer({ - ...notificationBarIframeInitData, - type: notificationBarIframeInitData.type as NotificationType, - theme: resolvedTheme, - handleCloseNotification, - i18n, - }), - document.body, - ); + sendPlatformMessage({ command: "bgGetDecryptedCiphers" }, (cipherData) => { + // @TODO use context to avoid prop drilling + return render( + NotificationContainer({ + ...notificationBarIframeInitData, + type: notificationBarIframeInitData.type as NotificationType, + theme: resolvedTheme, + handleCloseNotification, + i18n, + ciphers: cipherData, + }), + document.body, + ); + }); } setNotificationBarTheme(); diff --git a/apps/browser/src/manifest.json b/apps/browser/src/manifest.json index 90ae271fd14..6a2e017d06c 100644 --- a/apps/browser/src/manifest.json +++ b/apps/browser/src/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "__MSG_extName__", "short_name": "__MSG_appName__", - "version": "2025.1.4", + "version": "2025.1.3", "description": "__MSG_extDesc__", "default_locale": "en", "author": "Bitwarden Inc.", diff --git a/apps/browser/src/manifest.v3.json b/apps/browser/src/manifest.v3.json index c88796fd201..dee6ebef31c 100644 --- a/apps/browser/src/manifest.v3.json +++ b/apps/browser/src/manifest.v3.json @@ -3,7 +3,7 @@ "minimum_chrome_version": "102.0", "name": "__MSG_extName__", "short_name": "__MSG_appName__", - "version": "2025.1.4", + "version": "2025.1.3", "description": "__MSG_extDesc__", "default_locale": "en", "author": "Bitwarden Inc.", diff --git a/apps/browser/src/tools/popup/settings/import/import-browser-v2.component.ts b/apps/browser/src/tools/popup/settings/import/import-browser-v2.component.ts index 66cb5c62f48..1c5558bd90e 100644 --- a/apps/browser/src/tools/popup/settings/import/import-browser-v2.component.ts +++ b/apps/browser/src/tools/popup/settings/import/import-browser-v2.component.ts @@ -4,7 +4,7 @@ import { Router } from "@angular/router"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { AsyncActionsModule, ButtonModule, DialogModule } from "@bitwarden/components"; -import { ImportComponent } from "@bitwarden/importer/ui"; +import { ImportComponent } from "@bitwarden/importer-ui"; import { PopOutComponent } from "../../../../platform/popup/components/pop-out.component"; import { PopupFooterComponent } from "../../../../platform/popup/layout/popup-footer.component"; diff --git a/apps/browser/tsconfig.json b/apps/browser/tsconfig.json index 81b9bc870c4..8055260db57 100644 --- a/apps/browser/tsconfig.json +++ b/apps/browser/tsconfig.json @@ -25,7 +25,7 @@ "@bitwarden/generator-legacy": ["../../libs/tools/generator/extensions/legacy/src"], "@bitwarden/generator-navigation": ["../../libs/tools/generator/extensions/navigation/src"], "@bitwarden/importer-core": ["../../libs/importer/src"], - "@bitwarden/importer/ui": ["../../libs/importer/src/components"], + "@bitwarden/importer-ui": ["../../libs/importer/src/components"], "@bitwarden/key-management": ["../../libs/key-management/src"], "@bitwarden/key-management-ui": ["../../libs/key-management-ui/src"], "@bitwarden/platform": ["../../libs/platform/src"], diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 249145eb3ea..87d3ecbb4ca 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -1,7 +1,7 @@ { "name": "@bitwarden/desktop", "description": "A secure and free password manager for all of your devices.", - "version": "2025.1.4", + "version": "2025.2.0", "keywords": [ "bitwarden", "password", diff --git a/apps/desktop/src/app/tools/import/import-desktop.component.ts b/apps/desktop/src/app/tools/import/import-desktop.component.ts index 62fc007731d..72fe02b14fd 100644 --- a/apps/desktop/src/app/tools/import/import-desktop.component.ts +++ b/apps/desktop/src/app/tools/import/import-desktop.component.ts @@ -4,7 +4,7 @@ import { Component } from "@angular/core"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { AsyncActionsModule, ButtonModule, DialogModule } from "@bitwarden/components"; -import { ImportComponent } from "@bitwarden/importer/ui"; +import { ImportComponent } from "@bitwarden/importer-ui"; @Component({ templateUrl: "import-desktop.component.html", diff --git a/apps/desktop/src/package-lock.json b/apps/desktop/src/package-lock.json index f3dc98b8d9b..4d4b1572b5c 100644 --- a/apps/desktop/src/package-lock.json +++ b/apps/desktop/src/package-lock.json @@ -1,12 +1,12 @@ { "name": "@bitwarden/desktop", - "version": "2025.1.4", + "version": "2025.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@bitwarden/desktop", - "version": "2025.1.4", + "version": "2025.2.0", "license": "GPL-3.0", "dependencies": { "@bitwarden/desktop-napi": "file:../desktop_native/napi" diff --git a/apps/desktop/src/package.json b/apps/desktop/src/package.json index 7497d31d621..91dd860e9f9 100644 --- a/apps/desktop/src/package.json +++ b/apps/desktop/src/package.json @@ -2,7 +2,7 @@ "name": "@bitwarden/desktop", "productName": "Bitwarden", "description": "A secure and free password manager for all of your devices.", - "version": "2025.1.4", + "version": "2025.2.0", "author": "Bitwarden Inc. (https://bitwarden.com)", "homepage": "https://bitwarden.com", "license": "GPL-3.0", diff --git a/apps/desktop/src/vault/app/vault/add-edit.component.ts b/apps/desktop/src/vault/app/vault/add-edit.component.ts index f2ca05c9336..ae332c9723b 100644 --- a/apps/desktop/src/vault/app/vault/add-edit.component.ts +++ b/apps/desktop/src/vault/app/vault/add-edit.component.ts @@ -25,7 +25,7 @@ import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folde import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cipher-authorization.service"; import { DialogService, ToastService } from "@bitwarden/components"; -import { SshKeyPasswordPromptComponent } from "@bitwarden/importer/ui"; +import { SshKeyPasswordPromptComponent } from "@bitwarden/importer-ui"; import { PasswordRepromptService } from "@bitwarden/vault"; const BroadcasterSubscriptionId = "AddEditComponent"; diff --git a/apps/desktop/tsconfig.json b/apps/desktop/tsconfig.json index 1bd97b0130f..0bef5a5564d 100644 --- a/apps/desktop/tsconfig.json +++ b/apps/desktop/tsconfig.json @@ -23,7 +23,7 @@ "@bitwarden/generator-legacy": ["../../libs/tools/generator/extensions/legacy/src"], "@bitwarden/generator-navigation": ["../../libs/tools/generator/extensions/navigation/src"], "@bitwarden/importer-core": ["../../libs/importer/src"], - "@bitwarden/importer/ui": ["../../libs/importer/src/components"], + "@bitwarden/importer-ui": ["../../libs/importer/src/components"], "@bitwarden/key-management": ["../../libs/key-management/src"], "@bitwarden/key-management-ui": ["../../libs/key-management-ui/src"], "@bitwarden/node/*": ["../../libs/node/src/*"], diff --git a/apps/web/src/app/admin-console/organizations/settings/org-import.component.ts b/apps/web/src/app/admin-console/organizations/settings/org-import.component.ts index 78784bdd3d2..e7a0051253f 100644 --- a/apps/web/src/app/admin-console/organizations/settings/org-import.component.ts +++ b/apps/web/src/app/admin-console/organizations/settings/org-import.component.ts @@ -11,8 +11,8 @@ import { } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; -import { ImportComponent } from "@bitwarden/importer/ui"; import { ImportCollectionServiceAbstraction } from "@bitwarden/importer-core"; +import { ImportComponent } from "@bitwarden/importer-ui"; import { LooseComponentsModule, SharedModule } from "../../../shared"; import { ImportCollectionAdminService } from "../../../tools/import/import-collection-admin.service"; diff --git a/apps/web/src/app/auth/settings/account/account.component.html b/apps/web/src/app/auth/settings/account/account.component.html index 9f405c65083..c5edc021614 100644 --- a/apps/web/src/app/auth/settings/account/account.component.html +++ b/apps/web/src/app/auth/settings/account/account.component.html @@ -9,7 +9,7 @@
- + - - -
- -
- -
- -
-
- - - - -
-
- - -
-
- -
- - -
-
-
- - -
-
- - -
-
-
-
- - -
-
-
-
- - -
-
- - -
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
- - -
-
-
-
-
- - -
-
-
-
- - -
-
- - -
-
-
- - -
-
- - -
-
-
-
-
- - -
-
- - diff --git a/apps/web/src/app/tools/generator.component.ts b/apps/web/src/app/tools/generator.component.ts deleted file mode 100644 index a11c0c4a97b..00000000000 --- a/apps/web/src/app/tools/generator.component.ts +++ /dev/null @@ -1,73 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { Component, NgZone } from "@angular/core"; -import { ActivatedRoute } from "@angular/router"; - -import { GeneratorComponent as BaseGeneratorComponent } from "@bitwarden/angular/tools/generator/components/generator.component"; -import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -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, ToastService } from "@bitwarden/components"; -import { - PasswordGenerationServiceAbstraction, - UsernameGenerationServiceAbstraction, -} from "@bitwarden/generator-legacy"; - -import { PasswordGeneratorHistoryComponent } from "./password-generator-history.component"; - -@Component({ - selector: "app-generator", - templateUrl: "generator.component.html", -}) -export class GeneratorComponent extends BaseGeneratorComponent { - constructor( - passwordGenerationService: PasswordGenerationServiceAbstraction, - usernameGenerationService: UsernameGenerationServiceAbstraction, - accountService: AccountService, - platformUtilsService: PlatformUtilsService, - i18nService: I18nService, - logService: LogService, - route: ActivatedRoute, - ngZone: NgZone, - private dialogService: DialogService, - toastService: ToastService, - ) { - super( - passwordGenerationService, - usernameGenerationService, - platformUtilsService, - accountService, - i18nService, - logService, - route, - ngZone, - window, - toastService, - ); - if (platformUtilsService.isSelfHost()) { - // Allow only valid email forwarders for self host - this.forwardOptions = this.forwardOptions.filter((forwarder) => forwarder.validForSelfHosted); - } - } - - get isSelfHosted(): boolean { - return this.platformUtilsService.isSelfHost(); - } - - async history() { - this.dialogService.open(PasswordGeneratorHistoryComponent); - } - - lengthChanged() { - document.getElementById("length").focus(); - } - - minNumberChanged() { - document.getElementById("min-number").focus(); - } - - minSpecialChanged() { - document.getElementById("min-special").focus(); - } -} diff --git a/apps/web/src/app/tools/import/import-web.component.ts b/apps/web/src/app/tools/import/import-web.component.ts index 3f1d5155039..a527b9e71f4 100644 --- a/apps/web/src/app/tools/import/import-web.component.ts +++ b/apps/web/src/app/tools/import/import-web.component.ts @@ -1,7 +1,7 @@ import { Component } from "@angular/core"; import { Router } from "@angular/router"; -import { ImportComponent } from "@bitwarden/importer/ui"; +import { ImportComponent } from "@bitwarden/importer-ui"; import { HeaderModule } from "../../layouts/header/header.module"; import { SharedModule } from "../../shared"; diff --git a/apps/web/src/app/tools/password-generator-history.component.html b/apps/web/src/app/tools/password-generator-history.component.html deleted file mode 100644 index eabb45ece2d..00000000000 --- a/apps/web/src/app/tools/password-generator-history.component.html +++ /dev/null @@ -1,47 +0,0 @@ - - - {{ "passwordHistory" | i18n }} - - - - - - - - - {{ h.date | date: "medium" }} - - - - - - - - -
- {{ "noPasswordsInList" | i18n }} -
-
- - - - -
diff --git a/apps/web/src/app/tools/password-generator-history.component.ts b/apps/web/src/app/tools/password-generator-history.component.ts deleted file mode 100644 index 0c7c9c4e221..00000000000 --- a/apps/web/src/app/tools/password-generator-history.component.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Component } from "@angular/core"; - -import { PasswordGeneratorHistoryComponent as BasePasswordGeneratorHistoryComponent } from "@bitwarden/angular/tools/generator/components/password-generator-history.component"; -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 { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy"; - -@Component({ - selector: "app-password-generator-history", - templateUrl: "password-generator-history.component.html", -}) -export class PasswordGeneratorHistoryComponent extends BasePasswordGeneratorHistoryComponent { - constructor( - passwordGenerationService: PasswordGenerationServiceAbstraction, - platformUtilsService: PlatformUtilsService, - i18nService: I18nService, - toastService: ToastService, - ) { - super(passwordGenerationService, platformUtilsService, i18nService, window, toastService); - } -} diff --git a/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts b/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts index 0af0d720b0e..eb2289d7229 100644 --- a/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts +++ b/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts @@ -366,6 +366,9 @@ export class VaultItemDialogComponent implements OnInit, OnDestroy { const cipherData = new CipherData(cipherResponse); cipher = new Cipher(cipherData); + + // Update organizationUseTotp from server response + this.cipher.organizationUseTotp = cipher.organizationUseTotp; } // Store the updated cipher so any following edits use the most up to date cipher diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index f663a4c6397..0f48595f09b 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -1718,9 +1718,6 @@ "message": "Avoid ambiguous characters", "description": "Label for the avoid ambiguous characters checkbox." }, - "regeneratePassword": { - "message": "Regenerate password" - }, "length": { "message": "Length" }, @@ -4773,9 +4770,6 @@ "passwordGeneratorPolicyDesc": { "message": "Set requirements for password generator." }, - "passwordGeneratorPolicyInEffect": { - "message": "One or more organization policies are affecting your generator settings." - }, "masterPasswordPolicyInEffect": { "message": "One or more organization policies require your master password to meet the following requirements:" }, @@ -6744,15 +6738,6 @@ "message": "Generator", "description": "Short for 'credential generator'." }, - "whatWouldYouLikeToGenerate": { - "message": "What would you like to generate?" - }, - "passwordType": { - "message": "Password type" - }, - "regenerateUsername": { - "message": "Regenerate username" - }, "generateUsername": { "message": "Generate username" }, @@ -6793,9 +6778,6 @@ } } }, - "usernameType": { - "message": "Username type" - }, "plusAddressedEmail": { "message": "Plus addressed email", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -7015,9 +6997,6 @@ "message": "Hostname", "description": "Part of a URL." }, - "apiAccessToken": { - "message": "API access token" - }, "deviceVerification": { "message": "Device verification" }, @@ -8728,9 +8707,6 @@ "message": "Self-host server URL", "description": "Label for field requesting a self-hosted integration service URL" }, - "aliasDomain": { - "message": "Alias domain" - }, "alreadyHaveAccount": { "message": "Already have an account?" }, diff --git a/apps/web/src/scss/pages.scss b/apps/web/src/scss/pages.scss index 684d45a1a66..1e5c233e326 100644 --- a/apps/web/src/scss/pages.scss +++ b/apps/web/src/scss/pages.scss @@ -1,37 +1,3 @@ -app-generator { - #lengthRange { - width: 100%; - } - - .card-generated { - .card-body { - @include themify($themes) { - background: themed("foregroundColor"); - } - align-items: center; - display: flex; - flex-wrap: wrap; - font-family: $font-family-monospace; - font-size: $font-size-lg; - justify-content: center; - text-align: center; - } - } -} - -app-password-generator-history { - .list-group-item { - line-height: 1; - @include themify($themes) { - background: themed("backgroundColor"); - } - - .password { - font-family: $font-family-monospace; - } - } -} - tools-import { textarea { height: 150px; diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json index c05f24b9a8d..68ac8c80085 100644 --- a/apps/web/tsconfig.json +++ b/apps/web/tsconfig.json @@ -19,7 +19,7 @@ "@bitwarden/generator-legacy": ["../../libs/tools/generator/extensions/legacy/src"], "@bitwarden/generator-navigation": ["../../libs/tools/generator/extensions/navigation/src"], "@bitwarden/importer-core": ["../../libs/importer/src"], - "@bitwarden/importer/ui": ["../../libs/importer/src/components"], + "@bitwarden/importer-ui": ["../../libs/importer/src/components"], "@bitwarden/key-management": ["../../libs/key-management/src"], "@bitwarden/key-management-ui": ["../../libs/key-management-ui/src"], "@bitwarden/platform": ["../../libs/platform/src"], diff --git a/bitwarden_license/bit-web/src/app/vault/services/default-admin-task.service.spec.ts b/bitwarden_license/bit-web/src/app/vault/services/default-admin-task.service.spec.ts index d6a686a071a..49a4c16e159 100644 --- a/bitwarden_license/bit-web/src/app/vault/services/default-admin-task.service.spec.ts +++ b/bitwarden_license/bit-web/src/app/vault/services/default-admin-task.service.spec.ts @@ -56,7 +56,7 @@ describe("DefaultAdminTaskService", () => { expect(apiService.send).toHaveBeenCalledWith( "POST", `/tasks/${organizationId}/bulk-create`, - tasks, + { tasks }, true, true, ); diff --git a/bitwarden_license/bit-web/src/app/vault/services/default-admin-task.service.ts b/bitwarden_license/bit-web/src/app/vault/services/default-admin-task.service.ts index 442fde9dbf6..520fb744486 100644 --- a/bitwarden_license/bit-web/src/app/vault/services/default-admin-task.service.ts +++ b/bitwarden_license/bit-web/src/app/vault/services/default-admin-task.service.ts @@ -43,6 +43,12 @@ export class DefaultAdminTaskService implements AdminTaskService { organizationId: OrganizationId, tasks: CreateTasksRequest[], ): Promise { - await this.apiService.send("POST", `/tasks/${organizationId}/bulk-create`, tasks, true, true); + await this.apiService.send( + "POST", + `/tasks/${organizationId}/bulk-create`, + { tasks }, + true, + true, + ); } } diff --git a/bitwarden_license/bit-web/tsconfig.json b/bitwarden_license/bit-web/tsconfig.json index 1c9a530d273..82e0b7f57fa 100644 --- a/bitwarden_license/bit-web/tsconfig.json +++ b/bitwarden_license/bit-web/tsconfig.json @@ -22,7 +22,7 @@ ], "@bitwarden/vault-export-ui": ["../../libs/tools/export/vault-export/vault-export-ui/src"], "@bitwarden/importer-core": ["../../libs/importer/src"], - "@bitwarden/importer/ui": ["../../libs/importer/src/components"], + "@bitwarden/importer-ui": ["../../libs/importer/src/components"], "@bitwarden/key-management": ["../../libs/key-management/src"], "@bitwarden/key-management-ui": ["../../libs/key-management-ui/src"], "@bitwarden/platform": ["../../libs/platform/src"], diff --git a/libs/angular/src/auth/components/sso.component.ts b/libs/angular/src/auth/components/sso.component.ts index d0fc2140f06..5f5e53d8efe 100644 --- a/libs/angular/src/auth/components/sso.component.ts +++ b/libs/angular/src/auth/components/sso.component.ts @@ -1,7 +1,6 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { Directive, OnInit } from "@angular/core"; -import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { ActivatedRoute, NavigationExtras, Router } from "@angular/router"; import { firstValueFrom } from "rxjs"; import { first } from "rxjs/operators"; @@ -28,7 +27,6 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; -import { UserId } from "@bitwarden/common/types/guid"; import { ToastService } from "@bitwarden/components"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy"; @@ -57,7 +55,6 @@ export class SsoComponent implements OnInit { protected redirectUri: string; protected state: string; protected codeChallenge: string; - protected activeUserId: UserId; constructor( protected ssoLoginService: SsoLoginServiceAbstraction, @@ -77,11 +74,7 @@ export class SsoComponent implements OnInit { protected masterPasswordService: InternalMasterPasswordServiceAbstraction, protected accountService: AccountService, protected toastService: ToastService, - ) { - this.accountService.activeAccount$.pipe(takeUntilDestroyed()).subscribe((account) => { - this.activeUserId = account?.id; - }); - } + ) {} async ngOnInit() { // eslint-disable-next-line rxjs/no-async-subscribe @@ -233,10 +226,8 @@ export class SsoComponent implements OnInit { // - TDE login decryption options component // - Browser SSO on extension open // Note: you cannot set this in state before 2FA b/c there won't be an account in state. - await this.ssoLoginService.setActiveUserOrganizationSsoIdentifier( - orgSsoIdentifier, - this.activeUserId, - ); + const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id; + await this.ssoLoginService.setActiveUserOrganizationSsoIdentifier(orgSsoIdentifier, userId); // Users enrolled in admin acct recovery can be forced to set a new password after // having the admin set a temp password for them (affects TDE & standard users) diff --git a/libs/angular/src/auth/components/two-factor-auth/two-factor-auth.component.ts b/libs/angular/src/auth/components/two-factor-auth/two-factor-auth.component.ts index 6afee461c42..3e59e4a29b9 100644 --- a/libs/angular/src/auth/components/two-factor-auth/two-factor-auth.component.ts +++ b/libs/angular/src/auth/components/two-factor-auth/two-factor-auth.component.ts @@ -2,7 +2,6 @@ // @ts-strict-ignore import { CommonModule } from "@angular/common"; import { Component, Inject, OnInit, ViewChild } from "@angular/core"; -import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { FormBuilder, ReactiveFormsModule, Validators } from "@angular/forms"; import { ActivatedRoute, NavigationExtras, Router, RouterLink } from "@angular/router"; import { Subject, takeUntil, lastValueFrom, first, firstValueFrom } from "rxjs"; @@ -32,7 +31,6 @@ import { EnvironmentService } from "@bitwarden/common/platform/abstractions/envi 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 { UserId } from "@bitwarden/common/types/guid"; import { AsyncActionsModule, ButtonModule, @@ -128,7 +126,6 @@ export class TwoFactorAuthComponent extends CaptchaProtectedComponent implements protected changePasswordRoute = "set-password"; protected forcePasswordResetRoute = "update-temp-password"; protected successRoute = "vault"; - protected activeUserId: UserId; constructor( protected loginStrategyService: LoginStrategyServiceAbstraction, @@ -151,10 +148,6 @@ export class TwoFactorAuthComponent extends CaptchaProtectedComponent implements protected toastService: ToastService, ) { super(environmentService, i18nService, platformUtilsService, toastService); - - this.accountService.activeAccount$.pipe(takeUntilDestroyed()).subscribe((account) => { - this.activeUserId = account?.id; - }); } async ngOnInit() { @@ -269,10 +262,8 @@ export class TwoFactorAuthComponent extends CaptchaProtectedComponent implements // Save off the OrgSsoIdentifier for use in the TDE flows // - TDE login decryption options component // - Browser SSO on extension open - await this.ssoLoginService.setActiveUserOrganizationSsoIdentifier( - this.orgIdentifier, - this.activeUserId, - ); + const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id; + await this.ssoLoginService.setActiveUserOrganizationSsoIdentifier(this.orgIdentifier, userId); this.loginEmailService.clearValues(); // note: this flow affects both TDE & standard users diff --git a/libs/angular/src/auth/components/two-factor.component.ts b/libs/angular/src/auth/components/two-factor.component.ts index 49af9d057f7..e43797332ec 100644 --- a/libs/angular/src/auth/components/two-factor.component.ts +++ b/libs/angular/src/auth/components/two-factor.component.ts @@ -35,7 +35,6 @@ 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 { StateService } from "@bitwarden/common/platform/abstractions/state.service"; -import { UserId } from "@bitwarden/common/types/guid"; import { ToastService } from "@bitwarden/components"; import { CaptchaProtectedComponent } from "./captcha-protected.component"; @@ -74,8 +73,6 @@ export class TwoFactorComponent extends CaptchaProtectedComponent implements OnI protected successRoute = "vault"; protected twoFactorTimeoutRoute = "authentication-timeout"; - protected activeUserId: UserId; - get isDuoProvider(): boolean { return ( this.selectedProviderType === TwoFactorProviderType.Duo || @@ -108,10 +105,6 @@ export class TwoFactorComponent extends CaptchaProtectedComponent implements OnI this.webAuthnSupported = this.platformUtilsService.supportsWebAuthn(win); - this.accountService.activeAccount$.pipe(takeUntilDestroyed()).subscribe((account) => { - this.activeUserId = account?.id; - }); - // Add subscription to authenticationSessionTimeout$ and navigate to twoFactorTimeoutRoute if expired this.loginStrategyService.authenticationSessionTimeout$ .pipe(takeUntilDestroyed()) @@ -295,10 +288,8 @@ export class TwoFactorComponent extends CaptchaProtectedComponent implements OnI // Save off the OrgSsoIdentifier for use in the TDE flows // - TDE login decryption options component // - Browser SSO on extension open - await this.ssoLoginService.setActiveUserOrganizationSsoIdentifier( - this.orgIdentifier, - this.activeUserId, - ); + const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id; + await this.ssoLoginService.setActiveUserOrganizationSsoIdentifier(this.orgIdentifier, userId); this.loginEmailService.clearValues(); // note: this flow affects both TDE & standard users diff --git a/libs/angular/src/tools/generator/components/generator.component.ts b/libs/angular/src/tools/generator/components/generator.component.ts deleted file mode 100644 index 1f3c635e499..00000000000 --- a/libs/angular/src/tools/generator/components/generator.component.ts +++ /dev/null @@ -1,389 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { Directive, EventEmitter, Input, NgZone, OnDestroy, OnInit, Output } from "@angular/core"; -import { ActivatedRoute } from "@angular/router"; -import { BehaviorSubject, combineLatest, firstValueFrom, Subject } from "rxjs"; -import { debounceTime, first, map, skipWhile, takeUntil } from "rxjs/operators"; - -import { PasswordGeneratorPolicyOptions } from "@bitwarden/common/admin-console/models/domain/password-generator-policy-options"; -import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -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 { - GeneratorType, - DefaultPasswordBoundaries as DefaultBoundaries, -} from "@bitwarden/generator-core"; -import { - PasswordGenerationServiceAbstraction, - UsernameGenerationServiceAbstraction, - UsernameGeneratorOptions, - PasswordGeneratorOptions, -} from "@bitwarden/generator-legacy"; - -export class EmailForwarderOptions { - name: string; - value: string; - validForSelfHosted: boolean; -} - -@Directive() -export class GeneratorComponent implements OnInit, OnDestroy { - @Input() comingFromAddEdit = false; - @Input() type: GeneratorType | ""; - @Output() onSelected = new EventEmitter(); - - usernameGeneratingPromise: Promise; - typeOptions: any[]; - usernameTypeOptions: any[]; - subaddressOptions: any[]; - catchallOptions: any[]; - forwardOptions: EmailForwarderOptions[]; - usernameOptions: UsernameGeneratorOptions = { website: null }; - passwordOptions: PasswordGeneratorOptions = {}; - username = "-"; - password = "-"; - showOptions = false; - avoidAmbiguous = false; - enforcedPasswordPolicyOptions: PasswordGeneratorPolicyOptions; - usernameWebsite: string = null; - - get passTypeOptions() { - return this._passTypeOptions.filter((o) => !o.disabled); - } - private _passTypeOptions: { name: string; value: GeneratorType; disabled: boolean }[]; - - private destroy$ = new Subject(); - private isInitialized$ = new BehaviorSubject(false); - - // update screen reader minimum password length with 500ms debounce - // so that the user isn't flooded with status updates - private _passwordOptionsMinLengthForReader = new BehaviorSubject( - DefaultBoundaries.length.min, - ); - protected passwordOptionsMinLengthForReader$ = this._passwordOptionsMinLengthForReader.pipe( - map((val) => val || DefaultBoundaries.length.min), - debounceTime(500), - ); - - private _password = new BehaviorSubject("-"); - - constructor( - protected passwordGenerationService: PasswordGenerationServiceAbstraction, - protected usernameGenerationService: UsernameGenerationServiceAbstraction, - protected platformUtilsService: PlatformUtilsService, - protected accountService: AccountService, - protected i18nService: I18nService, - protected logService: LogService, - protected route: ActivatedRoute, - protected ngZone: NgZone, - private win: Window, - protected toastService: ToastService, - ) { - this.typeOptions = [ - { name: i18nService.t("password"), value: "password" }, - { name: i18nService.t("username"), value: "username" }, - ]; - this._passTypeOptions = [ - { name: i18nService.t("password"), value: "password", disabled: false }, - { name: i18nService.t("passphrase"), value: "passphrase", disabled: false }, - ]; - this.usernameTypeOptions = [ - { - name: i18nService.t("plusAddressedEmail"), - value: "subaddress", - desc: i18nService.t("plusAddressedEmailDesc"), - }, - { - name: i18nService.t("catchallEmail"), - value: "catchall", - desc: i18nService.t("catchallEmailDesc"), - }, - { - name: i18nService.t("forwardedEmail"), - value: "forwarded", - desc: i18nService.t("forwardedEmailDesc"), - }, - { name: i18nService.t("randomWord"), value: "word" }, - ]; - this.subaddressOptions = [{ name: i18nService.t("random"), value: "random" }]; - this.catchallOptions = [{ name: i18nService.t("random"), value: "random" }]; - - this.forwardOptions = [ - { name: "", value: "", validForSelfHosted: false }, - { name: "addy.io", value: "anonaddy", validForSelfHosted: true }, - { name: "DuckDuckGo", value: "duckduckgo", validForSelfHosted: false }, - { name: "Fastmail", value: "fastmail", validForSelfHosted: true }, - { name: "Firefox Relay", value: "firefoxrelay", validForSelfHosted: false }, - { name: "SimpleLogin", value: "simplelogin", validForSelfHosted: true }, - { name: "Forward Email", value: "forwardemail", validForSelfHosted: true }, - ].sort((a, b) => a.name.localeCompare(b.name)); - - this._password.pipe(debounceTime(250)).subscribe((password) => { - ngZone.run(() => { - this.password = password; - }); - this.passwordGenerationService.addHistory(this.password).catch((e) => { - this.logService.error(e); - }); - }); - } - - cascadeOptions(navigationType: GeneratorType = undefined, accountEmail: string) { - this.avoidAmbiguous = !this.passwordOptions.ambiguous; - - if (!this.type) { - if (navigationType) { - this.type = navigationType; - } else { - this.type = this.passwordOptions.type === "username" ? "username" : "password"; - } - } - - this.passwordOptions.type = - this.passwordOptions.type === "passphrase" ? "passphrase" : "password"; - - const overrideType = this.enforcedPasswordPolicyOptions.overridePasswordType ?? ""; - const isDisabled = overrideType.length - ? (value: string, policyValue: string) => value !== policyValue - : (_value: string, _policyValue: string) => false; - for (const option of this._passTypeOptions) { - option.disabled = isDisabled(option.value, overrideType); - } - - if (this.usernameOptions.type == null) { - this.usernameOptions.type = "word"; - } - if ( - this.usernameOptions.subaddressEmail == null || - this.usernameOptions.subaddressEmail === "" - ) { - this.usernameOptions.subaddressEmail = accountEmail; - } - if (this.usernameWebsite == null) { - this.usernameOptions.subaddressType = this.usernameOptions.catchallType = "random"; - } else { - this.usernameOptions.website = this.usernameWebsite; - } - } - - async ngOnInit() { - combineLatest([ - this.route.queryParams.pipe(first()), - this.accountService.activeAccount$.pipe(first()), - this.passwordGenerationService.getOptions$(), - this.usernameGenerationService.getOptions$(), - ]) - .pipe( - map(([qParams, account, [passwordOptions, passwordPolicy], usernameOptions]) => ({ - navigationType: qParams.type as GeneratorType, - accountEmail: account.email, - passwordOptions, - passwordPolicy, - usernameOptions, - })), - takeUntil(this.destroy$), - ) - .subscribe((options) => { - this.passwordOptions = options.passwordOptions; - this.enforcedPasswordPolicyOptions = options.passwordPolicy; - this.usernameOptions = options.usernameOptions; - - this.cascadeOptions(options.navigationType, options.accountEmail); - this._passwordOptionsMinLengthForReader.next(this.passwordOptions.minLength); - - if (this.regenerateWithoutButtonPress()) { - this.regenerate().catch((e) => { - this.logService.error(e); - }); - } - - this.isInitialized$.next(true); - }); - - // once initialization is complete, `ngOnInit` should return. - // - // FIXME(#6944): if a sync is in progress, wait to complete until after - // the sync completes. - await firstValueFrom( - this.isInitialized$.pipe( - skipWhile((initialized) => !initialized), - takeUntil(this.destroy$), - ), - ); - - if (this.usernameWebsite !== null) { - const websiteOption = { name: this.i18nService.t("websiteName"), value: "website-name" }; - this.subaddressOptions.push(websiteOption); - this.catchallOptions.push(websiteOption); - } - } - - ngOnDestroy() { - this.destroy$.next(); - this.destroy$.complete(); - this.isInitialized$.complete(); - this._passwordOptionsMinLengthForReader.complete(); - } - - async typeChanged() { - await this.savePasswordOptions(); - } - - async regenerate() { - if (this.type === "password") { - await this.regeneratePassword(); - } else if (this.type === "username") { - await this.regenerateUsername(); - } - } - - async sliderChanged() { - // 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.savePasswordOptions(); - await this.passwordGenerationService.addHistory(this.password); - } - - async onPasswordOptionsMinNumberInput($event: Event) { - // `savePasswordOptions()` replaces the null - this.passwordOptions.number = null; - - await this.savePasswordOptions(); - - // fixes UI desync that occurs when minNumber has a fixed value - // that is reset through normalization - ($event.target as HTMLInputElement).value = `${this.passwordOptions.minNumber}`; - } - - async setPasswordOptionsNumber($event: boolean) { - this.passwordOptions.number = $event; - // `savePasswordOptions()` replaces the null - this.passwordOptions.minNumber = null; - - await this.savePasswordOptions(); - } - - async onPasswordOptionsMinSpecialInput($event: Event) { - // `savePasswordOptions()` replaces the null - this.passwordOptions.special = null; - - await this.savePasswordOptions(); - - // fixes UI desync that occurs when minSpecial has a fixed value - // that is reset through normalization - ($event.target as HTMLInputElement).value = `${this.passwordOptions.minSpecial}`; - } - - async setPasswordOptionsSpecial($event: boolean) { - this.passwordOptions.special = $event; - // `savePasswordOptions()` replaces the null - this.passwordOptions.minSpecial = null; - - await this.savePasswordOptions(); - } - - async sliderInput() { - await this.normalizePasswordOptions(); - } - - async savePasswordOptions() { - // map navigation state into generator type - const restoreType = this.passwordOptions.type; - if (this.type === "username") { - this.passwordOptions.type = this.type; - } - - // save options - await this.normalizePasswordOptions(); - await this.passwordGenerationService.saveOptions(this.passwordOptions); - - // restore the original format - this.passwordOptions.type = restoreType; - } - - async saveUsernameOptions() { - await this.usernameGenerationService.saveOptions(this.usernameOptions); - if (this.usernameOptions.type === "forwarded") { - this.username = "-"; - } - } - - async regeneratePassword() { - this._password.next( - await this.passwordGenerationService.generatePassword(this.passwordOptions), - ); - } - - regenerateUsername() { - return this.generateUsername(); - } - - async generateUsername() { - try { - this.usernameGeneratingPromise = this.usernameGenerationService.generateUsername( - this.usernameOptions, - ); - this.username = await this.usernameGeneratingPromise; - if (this.username === "" || this.username === null) { - this.username = "-"; - } - } catch (e) { - this.logService.error(e); - } - } - - copy() { - const password = this.type === "password"; - const copyOptions = this.win != null ? { window: this.win } : null; - this.platformUtilsService.copyToClipboard( - password ? this.password : this.username, - copyOptions, - ); - this.toastService.showToast({ - variant: "info", - title: null, - message: this.i18nService.t( - "valueCopied", - this.i18nService.t(password ? "password" : "username"), - ), - }); - } - - select() { - this.onSelected.emit(this.type === "password" ? this.password : this.username); - } - - toggleOptions() { - this.showOptions = !this.showOptions; - } - - regenerateWithoutButtonPress() { - return this.type !== "username" || this.usernameOptions.type !== "forwarded"; - } - - private async normalizePasswordOptions() { - // Application level normalize options dependent on class variables - this.passwordOptions.ambiguous = !this.avoidAmbiguous; - - if ( - !this.passwordOptions.uppercase && - !this.passwordOptions.lowercase && - !this.passwordOptions.number && - !this.passwordOptions.special - ) { - this.passwordOptions.lowercase = true; - if (this.win != null) { - const lowercase = this.win.document.querySelector("#lowercase") as HTMLInputElement; - if (lowercase) { - lowercase.checked = true; - } - } - } - - await this.passwordGenerationService.enforcePasswordGeneratorPoliciesOnOptions( - this.passwordOptions, - ); - } -} diff --git a/libs/angular/src/tools/generator/components/password-generator-history.component.ts b/libs/angular/src/tools/generator/components/password-generator-history.component.ts deleted file mode 100644 index 2933163fce2..00000000000 --- a/libs/angular/src/tools/generator/components/password-generator-history.component.ts +++ /dev/null @@ -1,40 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { Directive, OnInit } from "@angular/core"; - -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 { GeneratedPasswordHistory } from "@bitwarden/generator-history"; -import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy"; - -@Directive() -export class PasswordGeneratorHistoryComponent implements OnInit { - history: GeneratedPasswordHistory[] = []; - - constructor( - protected passwordGenerationService: PasswordGenerationServiceAbstraction, - protected platformUtilsService: PlatformUtilsService, - protected i18nService: I18nService, - private win: Window, - protected toastService: ToastService, - ) {} - - async ngOnInit() { - this.history = await this.passwordGenerationService.getHistory(); - } - - clear = async () => { - this.history = await this.passwordGenerationService.clear(); - }; - - copy(password: string) { - const copyOptions = this.win != null ? { window: this.win } : null; - this.platformUtilsService.copyToClipboard(password, copyOptions); - this.toastService.showToast({ - variant: "info", - title: null, - message: this.i18nService.t("valueCopied", this.i18nService.t("password")), - }); - } -} diff --git a/libs/angular/src/tools/generator/generator-swap.ts b/libs/angular/src/tools/generator/generator-swap.ts deleted file mode 100644 index 16fafc67116..00000000000 --- a/libs/angular/src/tools/generator/generator-swap.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Type, inject } from "@angular/core"; -import { Route, Routes } from "@angular/router"; - -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; - -import { componentRouteSwap } from "../../utils/component-route-swap"; - -/** - * Helper function to swap between two components based on the GeneratorToolsModernization feature flag. - * @param defaultComponent - The current non-refreshed component to render. - * @param refreshedComponent - The new refreshed component to render. - * @param options - The shared route options to apply to the default component, and to the alt component if altOptions is not provided. - * @param altOptions - The alt route options to apply to the alt component. - */ -export function generatorSwap( - defaultComponent: Type, - refreshedComponent: Type, - options: Route, - altOptions?: Route, -): Routes { - return componentRouteSwap( - defaultComponent, - refreshedComponent, - async () => { - const configService = inject(ConfigService); - return configService.getFeatureFlag(FeatureFlag.GeneratorToolsModernization); - }, - options, - altOptions, - ); -} diff --git a/libs/angular/src/vault/components/view.component.ts b/libs/angular/src/vault/components/view.component.ts index 227bc14f1b1..bec4ae52206 100644 --- a/libs/angular/src/vault/components/view.component.ts +++ b/libs/angular/src/vault/components/view.component.ts @@ -11,7 +11,7 @@ import { OnInit, Output, } from "@angular/core"; -import { filter, firstValueFrom, map, Observable } from "rxjs"; +import { filter, firstValueFrom, map, Observable, Subject, takeUntil } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AuditService } from "@bitwarden/common/abstractions/audit.service"; @@ -81,6 +81,7 @@ export class ViewComponent implements OnDestroy, OnInit { private passwordReprompted = false; private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id)); + private destroyed$ = new Subject(); get fido2CredentialCreationDateValue(): string { const dateCreated = this.i18nService.t("dateCreated"); @@ -146,12 +147,15 @@ export class ViewComponent implements OnDestroy, OnInit { const activeUserId = await firstValueFrom(this.activeUserId$); // Grab individual cipher from `cipherViews$` for the most up-to-date information - this.cipher = await firstValueFrom( - this.cipherService.cipherViews$.pipe( - map((ciphers) => ciphers.find((c) => c.id === this.cipherId)), + this.cipherService.cipherViews$ + .pipe( + map((ciphers) => ciphers?.find((c) => c.id === this.cipherId)), filter((cipher) => !!cipher), - ), - ); + takeUntil(this.destroyed$), + ) + .subscribe((cipher) => { + this.cipher = cipher; + }); this.canAccessPremium = await firstValueFrom( this.billingAccountProfileStateService.hasPremiumFromAnySource$(activeUserId), @@ -524,6 +528,7 @@ export class ViewComponent implements OnDestroy, OnInit { this.showCardNumber = false; this.showCardCode = false; this.passwordReprompted = false; + this.destroyed$.next(); if (this.totpInterval) { clearInterval(this.totpInterval); } diff --git a/libs/auth/src/angular/sso/sso.component.ts b/libs/auth/src/angular/sso/sso.component.ts index b4373bfe96e..41cbc77e792 100644 --- a/libs/auth/src/angular/sso/sso.component.ts +++ b/libs/auth/src/angular/sso/sso.component.ts @@ -36,7 +36,6 @@ 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 { UserId } from "@bitwarden/common/types/guid"; import { AsyncActionsModule, ButtonModule, @@ -90,7 +89,6 @@ export class SsoComponent implements OnInit { protected state: string | undefined; protected codeChallenge: string | undefined; protected clientId: SsoClientType | undefined; - protected activeUserId: UserId | undefined; formPromise: Promise | undefined; initiateSsoFormPromise: Promise | undefined; @@ -132,8 +130,6 @@ export class SsoComponent implements OnInit { } async ngOnInit() { - this.activeUserId = (await firstValueFrom(this.accountService.activeAccount$))?.id; - const qParams: QueryParams = await firstValueFrom(this.route.queryParams); // This if statement will pass on the second portion of the SSO flow @@ -388,10 +384,10 @@ export class SsoComponent implements OnInit { // - TDE login decryption options component // - Browser SSO on extension open // Note: you cannot set this in state before 2FA b/c there won't be an account in state. - await this.ssoLoginService.setActiveUserOrganizationSsoIdentifier( - orgSsoIdentifier, - this.activeUserId, - ); + + // Grabbing the active user id right before making the state set to ensure it exists. + const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id; + await this.ssoLoginService.setActiveUserOrganizationSsoIdentifier(orgSsoIdentifier, userId); // Users enrolled in admin acct recovery can be forced to set a new password after // having the admin set a temp password for them (affects TDE & standard users) diff --git a/libs/common/src/auth/abstractions/sso-login.service.abstraction.ts b/libs/common/src/auth/abstractions/sso-login.service.abstraction.ts index bf64dcafd69..3dbaf429edb 100644 --- a/libs/common/src/auth/abstractions/sso-login.service.abstraction.ts +++ b/libs/common/src/auth/abstractions/sso-login.service.abstraction.ts @@ -1,5 +1,3 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { UserId } from "@bitwarden/common/types/guid"; export abstract class SsoLoginServiceAbstraction { @@ -13,7 +11,7 @@ export abstract class SsoLoginServiceAbstraction { * @see https://datatracker.ietf.org/doc/html/rfc7636 * @returns The code verifier used for SSO. */ - getCodeVerifier: () => Promise; + abstract getCodeVerifier: () => Promise; /** * Sets the code verifier used for SSO. * @@ -23,7 +21,7 @@ export abstract class SsoLoginServiceAbstraction { * and verify it matches the one sent in the request for the `authorization_code`. * @see https://datatracker.ietf.org/doc/html/rfc7636 */ - setCodeVerifier: (codeVerifier: string) => Promise; + abstract setCodeVerifier: (codeVerifier: string) => Promise; /** * Gets the value of the SSO state. * @@ -33,7 +31,7 @@ export abstract class SsoLoginServiceAbstraction { * @see https://datatracker.ietf.org/doc/html/rfc6749#section-4.1 * @returns The SSO state. */ - getSsoState: () => Promise; + abstract getSsoState: () => Promise; /** * Sets the value of the SSO state. * @@ -42,7 +40,7 @@ export abstract class SsoLoginServiceAbstraction { * returns the `state` in the callback and the client verifies that the value returned matches the value sent. * @see https://datatracker.ietf.org/doc/html/rfc6749#section-4.1 */ - setSsoState: (ssoState: string) => Promise; + abstract setSsoState: (ssoState: string) => Promise; /** * Gets the value of the user's organization sso identifier. * @@ -50,20 +48,20 @@ export abstract class SsoLoginServiceAbstraction { * Do not use this value outside of the SSO login flow. * @returns The user's organization identifier. */ - getOrganizationSsoIdentifier: () => Promise; + abstract getOrganizationSsoIdentifier: () => Promise; /** * Sets the value of the user's organization sso identifier. * * This should only be used during the SSO flow to identify the organization that the user is attempting to log in to. * Do not use this value outside of the SSO login flow. */ - setOrganizationSsoIdentifier: (organizationIdentifier: string) => Promise; + abstract setOrganizationSsoIdentifier: (organizationIdentifier: string) => Promise; /** * Gets the user's email. * Note: This should only be used during the SSO flow to identify the user that is attempting to log in. * @returns The user's email. */ - getSsoEmail: () => Promise; + abstract getSsoEmail: () => Promise; /** * Sets the user's email. * Note: This should only be used during the SSO flow to identify the user that is attempting to log in. @@ -71,20 +69,20 @@ export abstract class SsoLoginServiceAbstraction { * @returns A promise that resolves when the email has been set. * */ - setSsoEmail: (email: string) => Promise; + abstract setSsoEmail: (email: string) => Promise; /** * Gets the value of the active user's organization sso identifier. * * This should only be used post successful SSO login once the user is initialized. * @param userId The user id for retrieving the org identifier state. */ - getActiveUserOrganizationSsoIdentifier: (userId: UserId) => Promise; + abstract getActiveUserOrganizationSsoIdentifier: (userId: UserId) => Promise; /** * Sets the value of the active user's organization sso identifier. * * This should only be used post successful SSO login once the user is initialized. */ - setActiveUserOrganizationSsoIdentifier: ( + abstract setActiveUserOrganizationSsoIdentifier: ( organizationIdentifier: string, userId: UserId | undefined, ) => Promise; diff --git a/libs/common/src/auth/services/sso-login.service.spec.ts b/libs/common/src/auth/services/sso-login.service.spec.ts index 9cf49a07834..6764755e6ca 100644 --- a/libs/common/src/auth/services/sso-login.service.spec.ts +++ b/libs/common/src/auth/services/sso-login.service.spec.ts @@ -87,7 +87,7 @@ describe("SSOLoginService ", () => { const orgIdentifier = "test-active-org-identifier"; await sut.setActiveUserOrganizationSsoIdentifier(orgIdentifier, undefined); - expect(mockLogService.warning).toHaveBeenCalledWith( + expect(mockLogService.error).toHaveBeenCalledWith( "Tried to set a user organization sso identifier with an undefined user id.", ); }); diff --git a/libs/common/src/auth/services/sso-login.service.ts b/libs/common/src/auth/services/sso-login.service.ts index c73be3630be..b77e31dc79a 100644 --- a/libs/common/src/auth/services/sso-login.service.ts +++ b/libs/common/src/auth/services/sso-login.service.ts @@ -1,5 +1,3 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { firstValueFrom } from "rxjs"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; @@ -107,7 +105,7 @@ export class SsoLoginService implements SsoLoginServiceAbstraction { await this.ssoEmailState.update((_) => email); } - getActiveUserOrganizationSsoIdentifier(userId: UserId): Promise { + getActiveUserOrganizationSsoIdentifier(userId: UserId): Promise { return firstValueFrom(this.userOrgSsoIdentifierState(userId).state$); } @@ -116,7 +114,7 @@ export class SsoLoginService implements SsoLoginServiceAbstraction { userId: UserId | undefined, ): Promise { if (userId === undefined) { - this.logService.warning( + this.logService.error( "Tried to set a user organization sso identifier with an undefined user id.", ); return; diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index 550a8c07ff7..a8e036c82d6 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -26,7 +26,6 @@ export enum FeatureFlag { /* Tools */ ItemShare = "item-share", - GeneratorToolsModernization = "generator-tools-modernization", CriticalApps = "pm-14466-risk-insights-critical-application", EnableRiskInsightsNotifications = "enable-risk-insights-notifications", @@ -88,7 +87,6 @@ export const DefaultFeatureFlagValue = { /* Tools */ [FeatureFlag.ItemShare]: FALSE, - [FeatureFlag.GeneratorToolsModernization]: FALSE, [FeatureFlag.CriticalApps]: FALSE, [FeatureFlag.EnableRiskInsightsNotifications]: FALSE, diff --git a/libs/common/src/state-migrations/migrate.ts b/libs/common/src/state-migrations/migrate.ts index 169de447f10..b409f52d936 100644 --- a/libs/common/src/state-migrations/migrate.ts +++ b/libs/common/src/state-migrations/migrate.ts @@ -68,12 +68,13 @@ import { RemoveUnassignedItemsBannerDismissed } from "./migrations/67-remove-una import { MoveLastSyncDate } from "./migrations/68-move-last-sync-date"; import { MigrateIncorrectFolderKey } from "./migrations/69-migrate-incorrect-folder-key"; import { MoveBiometricAutoPromptToAccount } from "./migrations/7-move-biometric-auto-prompt-to-account"; +import { RemoveAcBannersDismissed } from "./migrations/70-remove-ac-banner-dismissed"; import { MoveStateVersionMigrator } from "./migrations/8-move-state-version"; import { MoveBrowserSettingsToGlobal } from "./migrations/9-move-browser-settings-to-global"; import { MinVersionMigrator } from "./migrations/min-version"; export const MIN_VERSION = 3; -export const CURRENT_VERSION = 69; +export const CURRENT_VERSION = 70; export type MinVersion = typeof MIN_VERSION; export function createMigrationBuilder() { @@ -144,7 +145,8 @@ export function createMigrationBuilder() { .with(MoveFinalDesktopSettingsMigrator, 65, 66) .with(RemoveUnassignedItemsBannerDismissed, 66, 67) .with(MoveLastSyncDate, 67, 68) - .with(MigrateIncorrectFolderKey, 68, CURRENT_VERSION); + .with(MigrateIncorrectFolderKey, 68, 69) + .with(RemoveAcBannersDismissed, 69, CURRENT_VERSION); } export async function currentVersion( diff --git a/libs/components/src/dialog/dialog/dialog.component.ts b/libs/components/src/dialog/dialog/dialog.component.ts index e9e3e898257..504dbd3a1ea 100644 --- a/libs/components/src/dialog/dialog/dialog.component.ts +++ b/libs/components/src/dialog/dialog/dialog.component.ts @@ -63,7 +63,8 @@ export class DialogComponent { @Input() loading = false; @HostBinding("class") get classes() { - return ["tw-flex", "tw-flex-col", "tw-max-h-screen", "tw-w-screen", "tw-p-4"].concat( + // `tw-max-h-[90vh]` is needed to prevent dialogs from overlapping the desktop header + return ["tw-flex", "tw-flex-col", "tw-w-screen", "tw-p-4", "tw-max-h-[90vh]"].concat( this.width, ); } diff --git a/libs/importer/src/components/import.component.html b/libs/importer/src/components/import.component.html index c53a1d3f522..072271f8205 100644 --- a/libs/importer/src/components/import.component.html +++ b/libs/importer/src/components/import.component.html @@ -94,6 +94,8 @@

{{ "seeDetailedInstructions" | i18n }} See detailed instructions on our help site at - + https://bitwarden.com/help/import-from-chrome/ See detailed instructions on our help site at See detailed instructions on our help site at - + https://bitwarden.com/help/import-from-safari/. @@ -199,6 +215,8 @@ > See detailed instructions on our help site at Make sure you have python-keyring and python-gnomekeyring installed. Save the - GNOME Keyring Import/Export python script to your desktop as pw_helper.py. Open terminal and run diff --git a/libs/shared/tsconfig.spec.json b/libs/shared/tsconfig.spec.json index c0f082793e9..9c98378659e 100644 --- a/libs/shared/tsconfig.spec.json +++ b/libs/shared/tsconfig.spec.json @@ -16,7 +16,7 @@ "@bitwarden/generator-legacy": ["../tools/generator/extensions/legacy/src"], "@bitwarden/generator-navigation": ["../tools/generator/extensions/navigation/src"], "@bitwarden/importer-core": ["../importer/src"], - "@bitwarden/importer/ui": ["../importer/src/components"], + "@bitwarden/importer-ui": ["../importer/src/components"], "@bitwarden/key-management": ["../key-management/src"], "@bitwarden/key-management-ui": ["../key-management-ui/src/index.ts"], "@bitwarden/node/*": ["../node/src/*"], diff --git a/libs/tools/generator/components/src/credential-generator-history-dialog.component.html b/libs/tools/generator/components/src/credential-generator-history-dialog.component.html index ad629601c34..b07eb62ae98 100644 --- a/libs/tools/generator/components/src/credential-generator-history-dialog.component.html +++ b/libs/tools/generator/components/src/credential-generator-history-dialog.component.html @@ -14,11 +14,5 @@ > {{ "clearHistory" | i18n }} - - diff --git a/libs/tools/generator/components/src/credential-generator-history-dialog.component.ts b/libs/tools/generator/components/src/credential-generator-history-dialog.component.ts index 7bcffd92399..58da1157f7c 100644 --- a/libs/tools/generator/components/src/credential-generator-history-dialog.component.ts +++ b/libs/tools/generator/components/src/credential-generator-history-dialog.component.ts @@ -1,6 +1,5 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { DialogRef } from "@angular/cdk/dialog"; import { CommonModule } from "@angular/common"; import { Component } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; @@ -35,7 +34,6 @@ export class CredentialGeneratorHistoryDialogComponent { private accountService: AccountService, private history: GeneratorHistoryService, private dialogService: DialogService, - private dialogRef: DialogRef, ) { this.accountService.activeAccount$ .pipe( @@ -54,11 +52,6 @@ export class CredentialGeneratorHistoryDialogComponent { .subscribe(this.hasHistory$); } - /** closes the dialog */ - protected close() { - this.dialogRef.close(); - } - /** Launches clear history flow */ protected async clear() { const confirmed = await this.dialogService.openSimpleDialog({ diff --git a/libs/vault/src/cipher-form/components/custom-fields/custom-fields.component.ts b/libs/vault/src/cipher-form/components/custom-fields/custom-fields.component.ts index 1b3b9009946..bdb96f4327d 100644 --- a/libs/vault/src/cipher-form/components/custom-fields/custom-fields.component.ts +++ b/libs/vault/src/cipher-form/components/custom-fields/custom-fields.component.ts @@ -127,8 +127,9 @@ export class CustomFieldsComponent implements OnInit, AfterViewInit { this.destroyed$ = inject(DestroyRef); this.cipherFormContainer.registerChildForm("customFields", this.customFieldsForm); - this.customFieldsForm.valueChanges.pipe(takeUntilDestroyed()).subscribe((values) => { - this.updateCipher(values.fields); + this.customFieldsForm.valueChanges.pipe(takeUntilDestroyed()).subscribe(() => { + // getRawValue ensures disabled fields are included + this.updateCipher(this.fields.getRawValue()); }); } @@ -151,7 +152,7 @@ export class CustomFieldsComponent implements OnInit, AfterViewInit { const prefillCipher = this.cipherFormContainer.getInitialCipherView(); // When available, populate the form with the existing fields - prefillCipher.fields?.forEach((field) => { + prefillCipher?.fields?.forEach((field) => { let value: string | boolean = field.value; if (field.type === FieldType.Boolean) { diff --git a/libs/vault/src/cipher-form/components/item-details/item-details-section.component.ts b/libs/vault/src/cipher-form/components/item-details/item-details-section.component.ts index ca778efac4b..85cd85bbf03 100644 --- a/libs/vault/src/cipher-form/components/item-details/item-details-section.component.ts +++ b/libs/vault/src/cipher-form/components/item-details/item-details-section.component.ts @@ -158,7 +158,7 @@ export class ItemDetailsSectionComponent implements OnInit { get allowOwnershipChange() { // Do not allow ownership change in edit mode and the cipher is owned by an organization - if (this.config.mode === "edit" && this.originalCipherView.organizationId != null) { + if (this.config.mode === "edit" && this.originalCipherView?.organizationId != null) { return false; } diff --git a/package-lock.json b/package-lock.json index 0ea31c0a8ad..17c396db223 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,12 +36,12 @@ "argon2-browser": "1.18.0", "big-integer": "1.6.52", "bootstrap": "4.6.0", - "braintree-web-drop-in": "1.43.0", + "braintree-web-drop-in": "1.44.0", "buffer": "6.0.3", "bufferutil": "4.0.9", "chalk": "4.1.2", "commander": "11.1.0", - "core-js": "3.39.0", + "core-js": "3.40.0", "form-data": "4.0.1", "https-proxy-agent": "7.0.5", "inquirer": "8.2.6", @@ -156,7 +156,7 @@ "lint-staged": "15.4.1", "mini-css-extract-plugin": "2.9.2", "node-ipc": "9.2.1", - "postcss": "8.4.49", + "postcss": "8.5.1", "postcss-loader": "8.1.1", "prettier": "3.4.2", "prettier-plugin-tailwindcss": "0.6.10", @@ -169,7 +169,7 @@ "style-loader": "4.0.0", "tailwindcss": "3.4.17", "ts-jest": "29.2.2", - "ts-loader": "9.5.1", + "ts-loader": "9.5.2", "tsconfig-paths-webpack-plugin": "4.2.0", "type-fest": "2.19.0", "typescript": "5.4.2", @@ -190,7 +190,7 @@ }, "apps/browser": { "name": "@bitwarden/browser", - "version": "2025.1.4" + "version": "2025.1.3" }, "apps/cli": { "name": "@bitwarden/cli", @@ -230,7 +230,7 @@ }, "apps/desktop": { "name": "@bitwarden/desktop", - "version": "2025.1.4", + "version": "2025.2.0", "hasInstallScript": true, "license": "GPL-3.0" }, @@ -7921,6 +7921,36 @@ "license": "MIT", "optional": true }, + "node_modules/@paypal/accelerated-checkout-loader": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@paypal/accelerated-checkout-loader/-/accelerated-checkout-loader-1.1.0.tgz", + "integrity": "sha512-S2KkIpq15VnxYyI0tycvfYiNsqdsg2a92El2huYUVLsWnBbubl8toYK8khaP5nnxZ0MGl9mEB9Y9axmfOw2Yvg==", + "license": "MIT", + "dependencies": { + "@braintree/asset-loader": "2.0.0", + "envify": "^4.1.0", + "typescript": "^4.6.4" + } + }, + "node_modules/@paypal/accelerated-checkout-loader/node_modules/@braintree/asset-loader": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@braintree/asset-loader/-/asset-loader-2.0.0.tgz", + "integrity": "sha512-7Zs3/g3lPTfkdtWr7cKh3tk1pDruXR++TXwGKkx7BPuTjjLNFul2JSfI+ScHzNU4u/gZNPNQagsSTlYxIhBgMA==", + "license": "MIT" + }, + "node_modules/@paypal/accelerated-checkout-loader/node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, "node_modules/@phc/format": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@phc/format/-/format-1.0.0.tgz", @@ -12870,9 +12900,9 @@ } }, "node_modules/braintree-web": { - "version": "3.103.0", - "resolved": "https://registry.npmjs.org/braintree-web/-/braintree-web-3.103.0.tgz", - "integrity": "sha512-gwmC5LSUP5VUC2HmUyaFnEyLjRRAo1iKKHS5eD9KIAZHB7cAQ2il1V1q2f5zdz7+7EE11eSHXznj6n/Qm6jp6w==", + "version": "3.113.0", + "resolved": "https://registry.npmjs.org/braintree-web/-/braintree-web-3.113.0.tgz", + "integrity": "sha512-qykYxZyld4X1tRNgXZQ3ZGzmhDGTBTRQ6Q24KaG9PuYqo+P2TVDEDOVC6tRbkx2RUIdXLv2M6WpkG7oLqEia9Q==", "license": "MIT", "dependencies": { "@braintree/asset-loader": "2.0.1", @@ -12883,6 +12913,7 @@ "@braintree/sanitize-url": "7.0.4", "@braintree/uuid": "1.0.0", "@braintree/wrap-promise": "2.1.0", + "@paypal/accelerated-checkout-loader": "1.1.0", "card-validator": "10.0.0", "credit-card-type": "10.0.1", "framebus": "6.0.0", @@ -12892,9 +12923,9 @@ } }, "node_modules/braintree-web-drop-in": { - "version": "1.43.0", - "resolved": "https://registry.npmjs.org/braintree-web-drop-in/-/braintree-web-drop-in-1.43.0.tgz", - "integrity": "sha512-lkUpQfYXR0CGtR7mPRR17AnZoYkHjhycxVnMGIPcWT6JPagEZcG/7tYyy34iWjYZeGa2wsquLBDV2Xeita962Q==", + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/braintree-web-drop-in/-/braintree-web-drop-in-1.44.0.tgz", + "integrity": "sha512-maOq9SwiXztIzixJhOras7K44x4UIqqnkyQMYAJqxQ8WkADv9AkflCu2j3IeVYCus/Th9gWWFHcBugn3C4sZGw==", "license": "MIT", "dependencies": { "@braintree/asset-loader": "2.0.1", @@ -12902,7 +12933,7 @@ "@braintree/event-emitter": "0.4.1", "@braintree/uuid": "1.0.0", "@braintree/wrap-promise": "2.1.0", - "braintree-web": "3.103.0" + "braintree-web": "3.113.0" } }, "node_modules/browser-assert": { @@ -14451,9 +14482,9 @@ } }, "node_modules/core-js": { - "version": "3.39.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.39.0.tgz", - "integrity": "sha512-raM0ew0/jJUqkJ0E6e8UDtl+y/7ktFivgWvqw8dNSQeNWoSDLvQ1H/RN3aPXB9tBd4/FhyR4RDPGhsNIMsAn7g==", + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.40.0.tgz", + "integrity": "sha512-7vsMc/Lty6AGnn7uFpYT56QesI5D2Y/UkgKounk87OP9Z2H9Z8kj6jzcSGAxFmUtDOS0ntK6lbQz+Nsa0Jj6mQ==", "hasInstallScript": true, "license": "MIT", "funding": { @@ -15996,6 +16027,19 @@ "node": ">=6" } }, + "node_modules/envify": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/envify/-/envify-4.1.0.tgz", + "integrity": "sha512-IKRVVoAYr4pIx4yIWNsz9mOsboxlNXiu7TNBnem/K/uTHdkyzXWDzHCK7UTolqBbgaBz0tQHsD3YNls0uIIjiw==", + "license": "MIT", + "dependencies": { + "esprima": "^4.0.0", + "through": "~2.3.4" + }, + "bin": { + "envify": "bin/envify" + } + }, "node_modules/envinfo": { "version": "7.14.0", "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.14.0.tgz", @@ -16853,7 +16897,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, "license": "BSD-2-Clause", "bin": { "esparse": "bin/esparse.js", @@ -24966,9 +25009,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", "dev": true, "funding": [ { @@ -27088,9 +27131,9 @@ } }, "node_modules/postcss": { - "version": "8.4.49", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", - "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz", + "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==", "dev": true, "funding": [ { @@ -27108,7 +27151,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.7", + "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -31181,9 +31224,9 @@ } }, "node_modules/ts-loader": { - "version": "9.5.1", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.1.tgz", - "integrity": "sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==", + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.2.tgz", + "integrity": "sha512-Qo4piXvOTWcMGIgRiuFa6nHNm+54HbYaZCKqc9eeZCLRy3XqafQgwX2F7mofrbJG3g7EEb+lkiR+z2Lic2s3Zw==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index a9a281db728..5145b058f6f 100644 --- a/package.json +++ b/package.json @@ -117,7 +117,7 @@ "lint-staged": "15.4.1", "mini-css-extract-plugin": "2.9.2", "node-ipc": "9.2.1", - "postcss": "8.4.49", + "postcss": "8.5.1", "postcss-loader": "8.1.1", "prettier": "3.4.2", "prettier-plugin-tailwindcss": "0.6.10", @@ -130,7 +130,7 @@ "style-loader": "4.0.0", "tailwindcss": "3.4.17", "ts-jest": "29.2.2", - "ts-loader": "9.5.1", + "ts-loader": "9.5.2", "tsconfig-paths-webpack-plugin": "4.2.0", "type-fest": "2.19.0", "typescript": "5.4.2", @@ -166,12 +166,12 @@ "argon2-browser": "1.18.0", "big-integer": "1.6.52", "bootstrap": "4.6.0", - "braintree-web-drop-in": "1.43.0", + "braintree-web-drop-in": "1.44.0", "buffer": "6.0.3", "bufferutil": "4.0.9", "chalk": "4.1.2", "commander": "11.1.0", - "core-js": "3.39.0", + "core-js": "3.40.0", "form-data": "4.0.1", "https-proxy-agent": "7.0.5", "inquirer": "8.2.6", diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json index 91586ce1dd2..611b30a3bdb 100644 --- a/tsconfig.eslint.json +++ b/tsconfig.eslint.json @@ -29,7 +29,7 @@ "@bitwarden/generator-legacy": ["./libs/tools/generator/extensions/legacy/src"], "@bitwarden/generator-navigation": ["./libs/tools/generator/extensions/navigation/src"], "@bitwarden/importer-core": ["./libs/importer/src"], - "@bitwarden/importer/ui": ["./libs/importer/src/components"], + "@bitwarden/importer-ui": ["./libs/importer/src/components"], "@bitwarden/key-management": ["./libs/key-management/src"], "@bitwarden/key-management-ui": ["./libs/key-management-ui/src/index,ts"], "@bitwarden/node/*": ["./libs/node/src/*"], diff --git a/tsconfig.json b/tsconfig.json index 95f0dd154b8..e6e4c47096b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -30,7 +30,7 @@ "@bitwarden/generator-legacy": ["./libs/tools/generator/extensions/legacy/src"], "@bitwarden/generator-navigation": ["./libs/tools/generator/extensions/navigation/src"], "@bitwarden/importer-core": ["./libs/importer/src"], - "@bitwarden/importer/ui": ["./libs/importer/src/components"], + "@bitwarden/importer-ui": ["./libs/importer/src/components"], "@bitwarden/key-management": ["./libs/key-management/src"], "@bitwarden/key-management-ui": ["./libs/key-management-ui/src"], "@bitwarden/node/*": ["./libs/node/src/*"],