From bcddf575a1935e2c29189f0707587afaf82f5f84 Mon Sep 17 00:00:00 2001
From: Nick Krantz <125900171+nick-livefront@users.noreply.github.com>
Date: Mon, 23 Sep 2024 11:56:09 -0500
Subject: [PATCH 01/23] [PM-11306] Disable Animation Setting (#11157)
* add appearance option to disable animations
* add check to only show the form after it's populated to avoid flash of inaccurate values
* switch to form loading while waiting for form values
---
apps/browser/src/_locales/en/messages.json | 3 +++
.../settings/appearance-v2.component.html | 9 ++++++--
.../settings/appearance-v2.component.spec.ts | 17 +++++++++++++-
.../popup/settings/appearance-v2.component.ts | 22 +++++++++++++++++++
4 files changed, 48 insertions(+), 3 deletions(-)
diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json
index 8eae45e6b38..900c3b37caa 100644
--- a/apps/browser/src/_locales/en/messages.json
+++ b/apps/browser/src/_locales/en/messages.json
@@ -4201,6 +4201,9 @@
"enableAnimations": {
"message": "Enable animations"
},
+ "showAnimations": {
+ "message": "Show animations"
+ },
"addAccount": {
"message": "Add account"
},
diff --git a/apps/browser/src/vault/popup/settings/appearance-v2.component.html b/apps/browser/src/vault/popup/settings/appearance-v2.component.html
index 565699a6f5b..b267e1c5cb2 100644
--- a/apps/browser/src/vault/popup/settings/appearance-v2.component.html
+++ b/apps/browser/src/vault/popup/settings/appearance-v2.component.html
@@ -1,4 +1,4 @@
-
+
@@ -23,10 +23,15 @@
{{ "showNumberOfAutofillSuggestions" | i18n }}
-
+
{{ "enableFavicon" | i18n }}
+
+
+
+ {{ "showAnimations" | i18n }}
+
diff --git a/apps/browser/src/vault/popup/settings/appearance-v2.component.spec.ts b/apps/browser/src/vault/popup/settings/appearance-v2.component.spec.ts
index 69186359e2b..bbd210b65a3 100644
--- a/apps/browser/src/vault/popup/settings/appearance-v2.component.spec.ts
+++ b/apps/browser/src/vault/popup/settings/appearance-v2.component.spec.ts
@@ -5,6 +5,7 @@ import { BehaviorSubject } from "rxjs";
import { BadgeSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/badge-settings.service";
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
+import { AnimationControlService } from "@bitwarden/common/platform/abstractions/animation-control.service";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
@@ -41,14 +42,17 @@ describe("AppearanceV2Component", () => {
const showFavicons$ = new BehaviorSubject(true);
const enableBadgeCounter$ = new BehaviorSubject(true);
const selectedTheme$ = new BehaviorSubject(ThemeType.Nord);
+ const enableRoutingAnimation$ = new BehaviorSubject(true);
const setSelectedTheme = jest.fn().mockResolvedValue(undefined);
const setShowFavicons = jest.fn().mockResolvedValue(undefined);
const setEnableBadgeCounter = jest.fn().mockResolvedValue(undefined);
+ const setEnableRoutingAnimation = jest.fn().mockResolvedValue(undefined);
beforeEach(async () => {
setSelectedTheme.mockClear();
setShowFavicons.mockClear();
setEnableBadgeCounter.mockClear();
+ setEnableRoutingAnimation.mockClear();
await TestBed.configureTestingModule({
imports: [AppearanceV2Component],
@@ -58,11 +62,15 @@ describe("AppearanceV2Component", () => {
{ provide: MessagingService, useValue: mock() },
{ provide: I18nService, useValue: { t: (key: string) => key } },
{ provide: DomainSettingsService, useValue: { showFavicons$, setShowFavicons } },
+ { provide: ThemeStateService, useValue: { selectedTheme$, setSelectedTheme } },
+ {
+ provide: AnimationControlService,
+ useValue: { enableRoutingAnimation$, setEnableRoutingAnimation },
+ },
{
provide: BadgeSettingsServiceAbstraction,
useValue: { enableBadgeCounter$, setEnableBadgeCounter },
},
- { provide: ThemeStateService, useValue: { selectedTheme$, setSelectedTheme } },
],
})
.overrideComponent(AppearanceV2Component, {
@@ -82,6 +90,7 @@ describe("AppearanceV2Component", () => {
it("populates the form with the user's current settings", () => {
expect(component.appearanceForm.value).toEqual({
+ enableAnimations: true,
enableFavicon: true,
enableBadgeCounter: true,
theme: ThemeType.Nord,
@@ -106,5 +115,11 @@ describe("AppearanceV2Component", () => {
expect(setEnableBadgeCounter).toHaveBeenCalledWith(false);
});
+
+ it("updates the animation setting", () => {
+ component.appearanceForm.controls.enableAnimations.setValue(false);
+
+ expect(setEnableRoutingAnimation).toHaveBeenCalledWith(false);
+ });
});
});
diff --git a/apps/browser/src/vault/popup/settings/appearance-v2.component.ts b/apps/browser/src/vault/popup/settings/appearance-v2.component.ts
index 12f5c540409..9d600ec83e8 100644
--- a/apps/browser/src/vault/popup/settings/appearance-v2.component.ts
+++ b/apps/browser/src/vault/popup/settings/appearance-v2.component.ts
@@ -7,6 +7,7 @@ import { firstValueFrom } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { BadgeSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/badge-settings.service";
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
+import { AnimationControlService } from "@bitwarden/common/platform/abstractions/animation-control.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { ThemeType } from "@bitwarden/common/platform/enums";
@@ -41,8 +42,12 @@ export class AppearanceV2Component implements OnInit {
enableFavicon: false,
enableBadgeCounter: true,
theme: ThemeType.System,
+ enableAnimations: true,
});
+ /** To avoid flashes of inaccurate values, only show the form after the entire form is populated. */
+ formLoading = true;
+
/** Available theme options */
themeOptions: { name: string; value: ThemeType }[];
@@ -53,6 +58,7 @@ export class AppearanceV2Component implements OnInit {
private themeStateService: ThemeStateService,
private formBuilder: FormBuilder,
private destroyRef: DestroyRef,
+ private animationControlService: AnimationControlService,
i18nService: I18nService,
) {
this.themeOptions = [
@@ -66,14 +72,20 @@ export class AppearanceV2Component implements OnInit {
const enableFavicon = await firstValueFrom(this.domainSettingsService.showFavicons$);
const enableBadgeCounter = await firstValueFrom(this.badgeSettingsService.enableBadgeCounter$);
const theme = await firstValueFrom(this.themeStateService.selectedTheme$);
+ const enableAnimations = await firstValueFrom(
+ this.animationControlService.enableRoutingAnimation$,
+ );
// Set initial values for the form
this.appearanceForm.setValue({
enableFavicon,
enableBadgeCounter,
theme,
+ enableAnimations,
});
+ this.formLoading = false;
+
this.appearanceForm.controls.theme.valueChanges
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((newTheme) => {
@@ -91,6 +103,12 @@ export class AppearanceV2Component implements OnInit {
.subscribe((enableBadgeCounter) => {
void this.updateBadgeCounter(enableBadgeCounter);
});
+
+ this.appearanceForm.controls.enableAnimations.valueChanges
+ .pipe(takeUntilDestroyed(this.destroyRef))
+ .subscribe((enableBadgeCounter) => {
+ void this.updateAnimations(enableBadgeCounter);
+ });
}
async updateFavicon(enableFavicon: boolean) {
@@ -105,4 +123,8 @@ export class AppearanceV2Component implements OnInit {
async saveTheme(newTheme: ThemeType) {
await this.themeStateService.setSelectedTheme(newTheme);
}
+
+ async updateAnimations(enableAnimations: boolean) {
+ await this.animationControlService.setEnableRoutingAnimation(enableAnimations);
+ }
}
From 08f0dadc2f370737db54e4bb69e1148b5b56ea7b Mon Sep 17 00:00:00 2001
From: cyprain-okeke <108260115+cyprain-okeke@users.noreply.github.com>
Date: Mon, 23 Sep 2024 19:23:53 +0100
Subject: [PATCH 02/23] Resolve the bug on change payment method (#11202)
---
.../change-plan-dialog.component.html | 22 ++++++++-----------
1 file changed, 9 insertions(+), 13 deletions(-)
diff --git a/apps/web/src/app/billing/organizations/change-plan-dialog.component.html b/apps/web/src/app/billing/organizations/change-plan-dialog.component.html
index bd8ae20c92e..27d51af92b3 100644
--- a/apps/web/src/app/billing/organizations/change-plan-dialog.component.html
+++ b/apps/web/src/app/billing/organizations/change-plan-dialog.component.html
@@ -332,20 +332,16 @@
{{ "paymentMethod" | i18n }}
-
+
- {{ billing?.paymentSource?.description }}
- {{
- "changePaymentMethod" | i18n
- }}
-
-
-
-
- {{ paymentSource?.description }}
- {{
- "changePaymentMethod" | i18n
- }}
+ {{
+ deprecateStripeSourcesAPI
+ ? paymentSource?.description
+ : billing?.paymentSource?.description
+ }}
+
+ {{ "changePaymentMethod" | i18n }}
+
Date: Mon, 23 Sep 2024 16:39:57 -0400
Subject: [PATCH 03/23] Add Retries to `get` (#11176)
---
.../browser/src/background/main.background.ts | 2 +-
.../services/browser-local-storage.service.ts | 61 ++++++++++++++++++-
.../src/popup/services/services.module.ts | 2 +-
3 files changed, 61 insertions(+), 4 deletions(-)
diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts
index 18883a5fe5e..7111b34875b 100644
--- a/apps/browser/src/background/main.background.ts
+++ b/apps/browser/src/background/main.background.ts
@@ -419,7 +419,7 @@ export default class MainBackground {
this.logService = new ConsoleLogService(isDev);
this.cryptoFunctionService = new WebCryptoFunctionService(self);
this.keyGenerationService = new KeyGenerationService(this.cryptoFunctionService);
- this.storageService = new BrowserLocalStorageService();
+ this.storageService = new BrowserLocalStorageService(this.logService);
this.intraprocessMessagingSubject = new Subject>>();
diff --git a/apps/browser/src/platform/services/browser-local-storage.service.ts b/apps/browser/src/platform/services/browser-local-storage.service.ts
index 61a2653f137..9c315b7f6f4 100644
--- a/apps/browser/src/platform/services/browser-local-storage.service.ts
+++ b/apps/browser/src/platform/services/browser-local-storage.service.ts
@@ -1,10 +1,67 @@
-import AbstractChromeStorageService from "./abstractions/abstract-chrome-storage-api.service";
+import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
+
+import AbstractChromeStorageService, {
+ SerializedValue,
+} from "./abstractions/abstract-chrome-storage-api.service";
export default class BrowserLocalStorageService extends AbstractChromeStorageService {
- constructor() {
+ constructor(private readonly logService: LogService) {
super(chrome.storage.local);
}
+ override async get(key: string): Promise {
+ return await this.getWithRetries(key, 0);
+ }
+
+ private async getWithRetries(key: string, retryNum: number): Promise {
+ // See: https://github.com/EFForg/privacybadger/pull/2980
+ const MAX_RETRIES = 5;
+ const WAIT_TIME = 200;
+
+ const store = await this.getStore(key);
+
+ if (store == null) {
+ if (retryNum >= MAX_RETRIES) {
+ throw new Error(`Failed to get a value for key '${key}', see logs for more details.`);
+ }
+
+ retryNum++;
+ this.logService.warning(`Retrying attempt to get value for key '${key}' in ${WAIT_TIME}ms`);
+ await new Promise((resolve) => setTimeout(resolve, WAIT_TIME));
+ return await this.getWithRetries(key, retryNum);
+ }
+
+ // We have a store
+ return this.processGetObject(store[key] as T | SerializedValue);
+ }
+
+ private async getStore(key: string) {
+ if (this.chromeStorageApi == null) {
+ this.logService.warning(
+ `chrome.storage.local was not initialized while retrieving key '${key}'.`,
+ );
+ return null;
+ }
+
+ return new Promise<{ [key: string]: unknown }>((resolve) => {
+ this.chromeStorageApi.get(key, (store) => {
+ if (chrome.runtime.lastError) {
+ this.logService.warning(`Failed to get value for key '${key}'`, chrome.runtime.lastError);
+ resolve(null);
+ return;
+ }
+
+ if (store == null) {
+ this.logService.warning(`Store was empty while retrieving value for key '${key}'`);
+ resolve(null);
+ return;
+ }
+
+ resolve(store);
+ });
+ });
+ }
+
async fillBuffer() {
// Write 4MB of data in chrome.storage.local, log files will hold 4MB of data (by default)
// before forcing a compaction. To force a compaction and have it remove previously saved data,
diff --git a/apps/browser/src/popup/services/services.module.ts b/apps/browser/src/popup/services/services.module.ts
index efbe9ce6bf5..129744fd3bc 100644
--- a/apps/browser/src/popup/services/services.module.ts
+++ b/apps/browser/src/popup/services/services.module.ts
@@ -304,7 +304,7 @@ const safeProviders: SafeProvider[] = [
safeProvider({
provide: AbstractStorageService,
useClass: BrowserLocalStorageService,
- deps: [],
+ deps: [LogService],
}),
safeProvider({
provide: AutofillServiceAbstraction,
From 0db179e97443db9996b8a65459516584ef7ba6c7 Mon Sep 17 00:00:00 2001
From: SmithThe4th
Date: Mon, 23 Sep 2024 16:45:53 -0400
Subject: [PATCH 04/23] [PM-739] Using a space at the beginning of otpauth://
generate a wrong OTP (#11204)
* Trimmed tariling whitespace from totp field
* Trimmed tariling whitespace from totp field
* Fix failing test
---
libs/angular/src/vault/components/add-edit.component.ts | 5 +++++
.../login-details-section/login-details-section.component.ts | 2 +-
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/libs/angular/src/vault/components/add-edit.component.ts b/libs/angular/src/vault/components/add-edit.component.ts
index 45475440d0a..255d553a3ec 100644
--- a/libs/angular/src/vault/components/add-edit.component.ts
+++ b/libs/angular/src/vault/components/add-edit.component.ts
@@ -329,6 +329,11 @@ export class AddEditComponent implements OnInit, OnDestroy {
this.cipher.card.expYear = normalizeExpiryYearFormat(this.cipher.card.expYear);
}
+ // trim whitespace from the TOTP field
+ if (this.cipher.type === this.cipherType.Login && this.cipher.login.totp) {
+ this.cipher.login.totp = this.cipher.login.totp.trim();
+ }
+
if (this.cipher.name == null || this.cipher.name === "") {
this.platformUtilsService.showToast(
"error",
diff --git a/libs/vault/src/cipher-form/components/login-details-section/login-details-section.component.ts b/libs/vault/src/cipher-form/components/login-details-section/login-details-section.component.ts
index eb34f820df8..691b05be2b4 100644
--- a/libs/vault/src/cipher-form/components/login-details-section/login-details-section.component.ts
+++ b/libs/vault/src/cipher-form/components/login-details-section/login-details-section.component.ts
@@ -125,7 +125,7 @@ export class LoginDetailsSectionComponent implements OnInit {
Object.assign(cipher.login, {
username: value.username,
password: value.password,
- totp: value.totp,
+ totp: value.totp?.trim(),
} as LoginView);
return cipher;
From 4b9935b28c48e5824a19166170604ec3a6911593 Mon Sep 17 00:00:00 2001
From: Shane Melton
Date: Mon, 23 Sep 2024 15:01:48 -0700
Subject: [PATCH 05/23] [PM-12528] AC Fix Collection Refresh (#11207)
* [PM-12528] Ensure collections refresh when the refresh$ subject emits
* [PM-12528] Cleanup all collections observable
---
.../app/vault/org-vault/vault.component.ts | 27 +++----------------
1 file changed, 4 insertions(+), 23 deletions(-)
diff --git a/apps/web/src/app/vault/org-vault/vault.component.ts b/apps/web/src/app/vault/org-vault/vault.component.ts
index fce33a972bf..0a8e8769715 100644
--- a/apps/web/src/app/vault/org-vault/vault.component.ts
+++ b/apps/web/src/app/vault/org-vault/vault.component.ts
@@ -11,7 +11,6 @@ import { ActivatedRoute, Params, Router } from "@angular/router";
import {
BehaviorSubject,
combineLatest,
- defer,
firstValueFrom,
lastValueFrom,
Observable,
@@ -283,27 +282,10 @@ export class VaultComponent implements OnInit, OnDestroy {
this.currentSearchText$ = this.route.queryParams.pipe(map((queryParams) => queryParams.search));
- this.allCollectionsWithoutUnassigned$ = combineLatest([
- organizationId$.pipe(switchMap((orgId) => this.collectionAdminService.getAll(orgId))),
- defer(() => this.collectionService.getAllDecrypted()),
- ]).pipe(
- map(([adminCollections, syncCollections]) => {
- const syncCollectionDict = Object.fromEntries(syncCollections.map((c) => [c.id, c]));
-
- return adminCollections.map((collection) => {
- const currentId: any = collection.id;
-
- const match = syncCollectionDict[currentId];
-
- if (match) {
- collection.manage = match.manage;
- collection.readOnly = match.readOnly;
- collection.hidePasswords = match.hidePasswords;
- }
- return collection;
- });
- }),
- shareReplay({ refCount: true, bufferSize: 1 }),
+ this.allCollectionsWithoutUnassigned$ = this.refresh$.pipe(
+ switchMap(() => organizationId$),
+ switchMap((orgId) => this.collectionAdminService.getAll(orgId)),
+ shareReplay({ refCount: false, bufferSize: 1 }),
);
this.editableCollections$ = this.allCollectionsWithoutUnassigned$.pipe(
@@ -367,7 +349,6 @@ export class VaultComponent implements OnInit, OnDestroy {
map((ciphers) => {
return Object.fromEntries(ciphers.map((c) => [c.id, c]));
}),
- shareReplay({ refCount: true, bufferSize: 1 }),
);
const nestedCollections$ = allCollections$.pipe(
From e691e2dadba1c959c92e6c8ca5a9b2afe0fd6858 Mon Sep 17 00:00:00 2001
From: "bw-ghapp[bot]" <178206702+bw-ghapp[bot]@users.noreply.github.com>
Date: Mon, 23 Sep 2024 22:48:23 +0000
Subject: [PATCH 06/23] Bumped client version(s) (#11211)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
---
apps/browser/package.json | 2 +-
apps/browser/src/manifest.json | 2 +-
apps/browser/src/manifest.v3.json | 2 +-
apps/cli/package.json | 2 +-
apps/desktop/package.json | 2 +-
apps/desktop/src/package-lock.json | 4 ++--
apps/desktop/src/package.json | 2 +-
apps/web/package.json | 2 +-
package-lock.json | 8 ++++----
9 files changed, 13 insertions(+), 13 deletions(-)
diff --git a/apps/browser/package.json b/apps/browser/package.json
index 4d008b684cb..fff378c72b9 100644
--- a/apps/browser/package.json
+++ b/apps/browser/package.json
@@ -1,6 +1,6 @@
{
"name": "@bitwarden/browser",
- "version": "2024.9.1",
+ "version": "2024.9.2",
"scripts": {
"build": "cross-env MANIFEST_VERSION=3 webpack",
"build:mv2": "webpack",
diff --git a/apps/browser/src/manifest.json b/apps/browser/src/manifest.json
index 2d7f46fa59a..35692dd5674 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": "2024.9.1",
+ "version": "2024.9.2",
"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 5e132774e6e..7bd40691768 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": "2024.9.1",
+ "version": "2024.9.2",
"description": "__MSG_extDesc__",
"default_locale": "en",
"author": "Bitwarden Inc.",
diff --git a/apps/cli/package.json b/apps/cli/package.json
index b5317794c35..8ab2afc4129 100644
--- a/apps/cli/package.json
+++ b/apps/cli/package.json
@@ -1,7 +1,7 @@
{
"name": "@bitwarden/cli",
"description": "A secure and free password manager for all of your devices.",
- "version": "2024.9.0",
+ "version": "2024.9.1",
"keywords": [
"bitwarden",
"password",
diff --git a/apps/desktop/package.json b/apps/desktop/package.json
index 40ce2fec5d9..c50e7ccbac4 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": "2024.9.1",
+ "version": "2024.9.2",
"keywords": [
"bitwarden",
"password",
diff --git a/apps/desktop/src/package-lock.json b/apps/desktop/src/package-lock.json
index ce08dfde2cd..3bd771c66af 100644
--- a/apps/desktop/src/package-lock.json
+++ b/apps/desktop/src/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "@bitwarden/desktop",
- "version": "2024.9.1",
+ "version": "2024.9.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@bitwarden/desktop",
- "version": "2024.9.1",
+ "version": "2024.9.2",
"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 e34641f1e09..29b3f8ed7d1 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": "2024.9.1",
+ "version": "2024.9.2",
"author": "Bitwarden Inc. (https://bitwarden.com)",
"homepage": "https://bitwarden.com",
"license": "GPL-3.0",
diff --git a/apps/web/package.json b/apps/web/package.json
index 37deab411b9..520c096a50c 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -1,6 +1,6 @@
{
"name": "@bitwarden/web-vault",
- "version": "2024.9.1",
+ "version": "2024.9.2",
"scripts": {
"build:oss": "webpack",
"build:bit": "webpack -c ../../bitwarden_license/bit-web/webpack.config.js",
diff --git a/package-lock.json b/package-lock.json
index bfc943b55b5..b2058de0dc8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -192,11 +192,11 @@
},
"apps/browser": {
"name": "@bitwarden/browser",
- "version": "2024.9.1"
+ "version": "2024.9.2"
},
"apps/cli": {
"name": "@bitwarden/cli",
- "version": "2024.9.0",
+ "version": "2024.9.1",
"license": "SEE LICENSE IN LICENSE.txt",
"dependencies": {
"@koa/multer": "3.0.2",
@@ -232,7 +232,7 @@
},
"apps/desktop": {
"name": "@bitwarden/desktop",
- "version": "2024.9.1",
+ "version": "2024.9.2",
"hasInstallScript": true,
"license": "GPL-3.0"
},
@@ -246,7 +246,7 @@
},
"apps/web": {
"name": "@bitwarden/web-vault",
- "version": "2024.9.1"
+ "version": "2024.9.2"
},
"libs/admin-console": {
"name": "@bitwarden/admin-console",
From aa91a8d5ca0444b09bff6ff00cb4a13aff1cb789 Mon Sep 17 00:00:00 2001
From: Jordan Aasen <166539328+jaasen-livefront@users.noreply.github.com>
Date: Tue, 24 Sep 2024 00:12:27 -0700
Subject: [PATCH 07/23] fix send form defects (#11212)
---
.../options/send-options.component.html | 13 +++++++----
.../send-details/send-details.component.html | 6 ++---
.../send-file-details.component.html | 22 ++++++++++---------
.../send-file-details.component.ts | 8 ++++++-
4 files changed, 31 insertions(+), 18 deletions(-)
diff --git a/libs/tools/send/send-ui/src/send-form/components/options/send-options.component.html b/libs/tools/send/send-ui/src/send-form/components/options/send-options.component.html
index 9c5183aabd2..53483065b73 100644
--- a/libs/tools/send/send-ui/src/send-form/components/options/send-options.component.html
+++ b/libs/tools/send/send-ui/src/send-form/components/options/send-options.component.html
@@ -1,6 +1,6 @@
- {{ "additionalOptions" | i18n }}
+ {{ "additionalOptions" | i18n }}
@@ -16,16 +16,21 @@
{{ "newPassword" | i18n }}
-
+
{{ "sendPasswordDescV2" | i18n }}
{{ "hideYourEmail" | i18n }}
-
+
{{ "privateNote" | i18n }}
-
+
diff --git a/libs/tools/send/send-ui/src/send-form/components/send-details/send-details.component.html b/libs/tools/send/send-ui/src/send-form/components/send-details/send-details.component.html
index e5b99828fe4..98f399760be 100644
--- a/libs/tools/send/send-ui/src/send-form/components/send-details/send-details.component.html
+++ b/libs/tools/send/send-ui/src/send-form/components/send-details/send-details.component.html
@@ -1,6 +1,6 @@
-
- {{ "sendDetails" | i18n }}
+
+ {{ "sendDetails" | i18n }}
@@ -34,7 +34,7 @@
>
-
+
{{ "deletionDate" | i18n }}
-
{{ "file" | i18n }}
+
{{ "file" | i18n }}
{{ originalSendView.file.fileName }}
{{ originalSendView.file.sizeName }}
{{ "fileToShare" | i18n }}
-
-
- {{ fileName || ("noFileChosen" | i18n) }}
+
+
+
+ {{ fileName || ("noFileChosen" | i18n) }}
+
Date: Tue, 24 Sep 2024 00:26:25 -0700
Subject: [PATCH 08/23] [PM-12505] - add delete send button to footer (#11187)
* add delete send button to footer
* add basic error handling
* update copy. user bitAction
* use arrow function. remove border class
---
apps/browser/src/_locales/en/messages.json | 4 ++
.../add-edit/send-add-edit.component.html | 9 ++++
.../add-edit/send-add-edit.component.ts | 45 ++++++++++++++++++-
.../send-list-items-container.component.ts | 2 +-
4 files changed, 58 insertions(+), 2 deletions(-)
diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json
index 900c3b37caa..8c08caec353 100644
--- a/apps/browser/src/_locales/en/messages.json
+++ b/apps/browser/src/_locales/en/messages.json
@@ -2374,6 +2374,10 @@
"message": "Are you sure you want to delete this Send?",
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
},
+ "deleteSendPermanentConfirmation": {
+ "message": "Are you sure you want to permanently delete this Send?",
+ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
+ },
"editSend": {
"message": "Edit Send",
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
diff --git a/apps/browser/src/tools/popup/send-v2/add-edit/send-add-edit.component.html b/apps/browser/src/tools/popup/send-v2/add-edit/send-add-edit.component.html
index 3e9a8d7c50d..7f723cc7364 100644
--- a/apps/browser/src/tools/popup/send-v2/add-edit/send-add-edit.component.html
+++ b/apps/browser/src/tools/popup/send-v2/add-edit/send-add-edit.component.html
@@ -13,5 +13,14 @@
+
diff --git a/apps/browser/src/tools/popup/send-v2/add-edit/send-add-edit.component.ts b/apps/browser/src/tools/popup/send-v2/add-edit/send-add-edit.component.ts
index 49526bb032b..91311ab7e7a 100644
--- a/apps/browser/src/tools/popup/send-v2/add-edit/send-add-edit.component.ts
+++ b/apps/browser/src/tools/popup/send-v2/add-edit/send-add-edit.component.ts
@@ -8,8 +8,16 @@ import { map, switchMap } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { SendType } from "@bitwarden/common/tools/send/enums/send-type";
+import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction";
import { SendId } from "@bitwarden/common/types/guid";
-import { AsyncActionsModule, ButtonModule, SearchModule } from "@bitwarden/components";
+import {
+ AsyncActionsModule,
+ ButtonModule,
+ DialogService,
+ IconButtonModule,
+ SearchModule,
+ ToastService,
+} from "@bitwarden/components";
import {
DefaultSendFormConfigService,
SendFormConfig,
@@ -58,6 +66,7 @@ export type AddEditQueryParams = Partial>;
JslibModule,
FormsModule,
ButtonModule,
+ IconButtonModule,
PopupPageComponent,
PopupHeaderComponent,
PopupFooterComponent,
@@ -81,6 +90,9 @@ export class SendAddEditComponent {
private location: Location,
private i18nService: I18nService,
private addEditFormConfigService: SendFormConfigService,
+ private sendApiService: SendApiService,
+ private toastService: ToastService,
+ private dialogService: DialogService,
) {
this.subscribeToParams();
}
@@ -92,6 +104,37 @@ export class SendAddEditComponent {
this.location.back();
}
+ deleteSend = async () => {
+ const confirmed = await this.dialogService.openSimpleDialog({
+ title: { key: "deleteSend" },
+ content: { key: "deleteSendPermanentConfirmation" },
+ type: "warning",
+ });
+
+ if (!confirmed) {
+ return;
+ }
+
+ try {
+ await this.sendApiService.delete(this.config.originalSend?.id);
+ } catch (e) {
+ this.toastService.showToast({
+ variant: "error",
+ title: null,
+ message: e.message,
+ });
+ return;
+ }
+
+ this.location.back();
+
+ this.toastService.showToast({
+ variant: "success",
+ title: null,
+ message: this.i18nService.t("deletedSend"),
+ });
+ };
+
/**
* Subscribes to the route query parameters and builds the configuration based on the parameters.
*/
diff --git a/libs/tools/send/send-ui/src/send-list-items-container/send-list-items-container.component.ts b/libs/tools/send/send-ui/src/send-list-items-container/send-list-items-container.component.ts
index 2dd8078fd7a..a0f10c2cea9 100644
--- a/libs/tools/send/send-ui/src/send-list-items-container/send-list-items-container.component.ts
+++ b/libs/tools/send/send-ui/src/send-list-items-container/send-list-items-container.component.ts
@@ -64,7 +64,7 @@ export class SendListItemsContainerComponent {
async deleteSend(s: SendView): Promise {
const confirmed = await this.dialogService.openSimpleDialog({
title: { key: "deleteSend" },
- content: { key: "deleteSendConfirmation" },
+ content: { key: "deleteSendPermanentConfirmation" },
type: "warning",
});
From d92b2cbea27feb62fd23d0bceed96fa588b6b519 Mon Sep 17 00:00:00 2001
From: Bernd Schoolmann
Date: Tue, 24 Sep 2024 11:28:33 +0200
Subject: [PATCH 09/23] [PM-11477] Remove deprecated cryptoservice functions
(#10854)
* Remove deprecated cryptoservice functions
* Use getUserkeyWithLegacySupport to get userkey
* Fix tests
* Fix tests
* Fix tests
* Remove unused cryptoservice instances
* Fix build
* Remove unused apiService in constructor
* Fix encryption
* Ensure passed in key is used if present
* Fix sends and folders
* Fix tests
* Remove logged key
* Fix import for account restricted keys
---
.../browser/src/background/main.background.ts | 8 +++
.../background/nativeMessaging.background.ts | 6 ++-
.../add-edit-folder-dialog.component.spec.ts | 35 ++++++++++--
.../add-edit-folder-dialog.component.ts | 9 +++-
.../components/vault/attachments.component.ts | 3 ++
.../popup/components/vault/view.component.ts | 3 ++
.../settings/folder-add-edit.component.ts | 6 +++
apps/cli/src/commands/download.command.ts | 6 +--
apps/cli/src/commands/edit.command.ts | 9 +++-
apps/cli/src/commands/get.command.ts | 8 +--
apps/cli/src/oss-serve-configurator.ts | 5 +-
.../service-container/service-container.ts | 7 +++
.../src/tools/send/commands/get.command.ts | 6 +--
.../tools/send/commands/receive.command.ts | 8 +--
apps/cli/src/tools/send/send.program.ts | 7 +--
apps/cli/src/vault.program.ts | 3 ++
apps/cli/src/vault/create.command.ts | 8 ++-
.../src/app/services/services.module.ts | 2 +-
.../native-message-handler.service.ts | 8 +--
.../src/services/native-messaging.service.ts | 6 ++-
.../vault/app/vault/attachments.component.ts | 3 ++
.../app/vault/folder-add-edit.component.ts | 6 +++
.../src/vault/app/vault/view.component.ts | 3 ++
.../emergency-access-attachments.component.ts | 3 ++
.../organization-plans.component.ts | 6 ++-
.../tools/send/send-access-file.component.ts | 6 +--
.../vault/core/collection-admin.service.ts | 6 ++-
.../individual-vault/attachments.component.ts | 3 ++
.../folder-add-edit.component.ts | 11 +++-
.../vault/org-vault/attachments.component.ts | 3 ++
.../bit-cli/src/service-container.ts | 1 +
.../organization-auth-request.service.spec.ts | 4 ++
.../organization-auth-request.service.ts | 4 +-
.../device-approvals.component.ts | 8 ++-
.../services/web-provider.service.ts | 2 +-
.../src/services/jslib-services.module.ts | 10 +++-
.../vault/components/attachments.component.ts | 6 ++-
.../components/folder-add-edit.component.ts | 9 +++-
.../src/vault/components/view.component.ts | 4 +-
.../auth-request-login.strategy.spec.ts | 3 ++
.../login-strategies/login.strategy.spec.ts | 5 ++
.../common/login-strategies/login.strategy.ts | 2 +
.../password-login.strategy.spec.ts | 4 ++
.../sso-login.strategy.spec.ts | 4 ++
.../user-api-login.strategy.spec.ts | 3 ++
.../webauthn-login.strategy.spec.ts | 14 +++--
.../webauthn-login.strategy.ts | 2 +-
.../login-strategy.service.ts | 1 +
.../platform/abstractions/crypto.service.ts | 32 -----------
.../src/platform/services/crypto.service.ts | 53 -------------------
.../src/tools/send/models/domain/send.spec.ts | 12 +++--
.../src/tools/send/models/domain/send.ts | 4 +-
.../folder/folder.service.abstraction.ts | 2 +-
.../src/vault/services/cipher.service.spec.ts | 16 +++++-
.../src/vault/services/cipher.service.ts | 43 +++++++++------
.../src/vault/services/collection.service.ts | 5 +-
.../services/folder/folder.service.spec.ts | 14 +++--
.../vault/services/folder/folder.service.ts | 7 ++-
...warden-password-protected-importer.spec.ts | 6 ++-
.../src/components/import.component.ts | 2 +
.../bitwarden/bitwarden-json-importer.ts | 14 +++--
.../bitwarden-password-protected-importer.ts | 8 +--
.../src/services/import.service.spec.ts | 4 ++
libs/importer/src/services/import.service.ts | 6 ++-
.../src/services/base-vault-export.service.ts | 8 +--
.../individual-vault-export.service.spec.ts | 27 ++++++++--
.../individual-vault-export.service.ts | 15 ++++--
.../src/services/org-vault-export.service.ts | 8 +--
.../src/services/vault-export.service.spec.ts | 26 +++++++--
69 files changed, 404 insertions(+), 197 deletions(-)
diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts
index 7111b34875b..43a6d5968e9 100644
--- a/apps/browser/src/background/main.background.ts
+++ b/apps/browser/src/background/main.background.ts
@@ -693,6 +693,7 @@ export default class MainBackground {
this.collectionService = new CollectionService(
this.cryptoService,
+ this.encryptService,
this.i18nService,
this.stateProvider,
);
@@ -803,9 +804,11 @@ export default class MainBackground {
this.cipherFileUploadService,
this.configService,
this.stateProvider,
+ this.accountService,
);
this.folderService = new FolderService(
this.cryptoService,
+ this.encryptService,
this.i18nService,
this.cipherService,
this.stateProvider,
@@ -977,6 +980,7 @@ export default class MainBackground {
this.i18nService,
this.collectionService,
this.cryptoService,
+ this.encryptService,
this.pinService,
this.accountService,
);
@@ -986,8 +990,10 @@ export default class MainBackground {
this.cipherService,
this.pinService,
this.cryptoService,
+ this.encryptService,
this.cryptoFunctionService,
this.kdfConfigService,
+ this.accountService,
);
this.organizationVaultExportService = new OrganizationVaultExportService(
@@ -995,6 +1001,7 @@ export default class MainBackground {
this.apiService,
this.pinService,
this.cryptoService,
+ this.encryptService,
this.cryptoFunctionService,
this.collectionService,
this.kdfConfigService,
@@ -1098,6 +1105,7 @@ export default class MainBackground {
);
this.nativeMessagingBackground = new NativeMessagingBackground(
this.cryptoService,
+ this.encryptService,
this.cryptoFunctionService,
this.runtimeBackground,
this.messagingService,
diff --git a/apps/browser/src/background/nativeMessaging.background.ts b/apps/browser/src/background/nativeMessaging.background.ts
index 8f2cac7915c..68a43fbdfe3 100644
--- a/apps/browser/src/background/nativeMessaging.background.ts
+++ b/apps/browser/src/background/nativeMessaging.background.ts
@@ -6,6 +6,7 @@ import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authenticatio
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
+import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
@@ -73,6 +74,7 @@ export class NativeMessagingBackground {
constructor(
private cryptoService: CryptoService,
+ private encryptService: EncryptService,
private cryptoFunctionService: CryptoFunctionService,
private runtimeBackground: RuntimeBackground,
private messagingService: MessagingService,
@@ -227,7 +229,7 @@ export class NativeMessagingBackground {
await this.secureCommunication();
}
- return await this.cryptoService.encrypt(JSON.stringify(message), this.sharedSecret);
+ return await this.encryptService.encrypt(JSON.stringify(message), this.sharedSecret);
}
getResponse(): Promise {
@@ -273,7 +275,7 @@ export class NativeMessagingBackground {
let message = rawMessage as ReceiveMessage;
if (!this.platformUtilsService.isSafari()) {
message = JSON.parse(
- await this.cryptoService.decryptToUtf8(rawMessage as EncString, this.sharedSecret),
+ await this.encryptService.decryptToUtf8(rawMessage as EncString, this.sharedSecret),
);
}
diff --git a/apps/browser/src/vault/popup/components/vault-v2/add-edit-folder-dialog/add-edit-folder-dialog.component.spec.ts b/apps/browser/src/vault/popup/components/vault-v2/add-edit-folder-dialog/add-edit-folder-dialog.component.spec.ts
index 8453b4cc63e..156cc9d8195 100644
--- a/apps/browser/src/vault/popup/components/vault-v2/add-edit-folder-dialog/add-edit-folder-dialog.component.spec.ts
+++ b/apps/browser/src/vault/popup/components/vault-v2/add-edit-folder-dialog/add-edit-folder-dialog.component.spec.ts
@@ -1,9 +1,13 @@
import { DIALOG_DATA, DialogRef } from "@angular/cdk/dialog";
import { ComponentFixture, TestBed } from "@angular/core/testing";
import { NoopAnimationsModule } from "@angular/platform-browser/animations";
+import { BehaviorSubject } from "rxjs";
+import { AccountInfo, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
+import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
+import { UserId } from "@bitwarden/common/types/guid";
import { FolderApiServiceAbstraction } from "@bitwarden/common/vault/abstractions/folder/folder-api.service.abstraction";
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
import { Folder } from "@bitwarden/common/vault/models/domain/folder";
@@ -25,6 +29,7 @@ describe("AddEditFolderDialogComponent", () => {
const save = jest.fn().mockResolvedValue(null);
const deleteFolder = jest.fn().mockResolvedValue(null);
const openSimpleDialog = jest.fn().mockResolvedValue(true);
+ const getUserKeyWithLegacySupport = jest.fn().mockResolvedValue("");
const error = jest.fn();
const close = jest.fn();
const showToast = jest.fn();
@@ -41,12 +46,29 @@ describe("AddEditFolderDialogComponent", () => {
close.mockClear();
showToast.mockClear();
+ const userId = "" as UserId;
+ const accountInfo: AccountInfo = {
+ email: "",
+ emailVerified: true,
+ name: undefined,
+ };
+
await TestBed.configureTestingModule({
imports: [AddEditFolderDialogComponent, NoopAnimationsModule],
providers: [
{ provide: I18nService, useValue: { t: (key: string) => key } },
{ provide: FolderService, useValue: { encrypt } },
{ provide: FolderApiServiceAbstraction, useValue: { save, delete: deleteFolder } },
+ {
+ provide: AccountService,
+ useValue: { activeAccount$: new BehaviorSubject({ id: userId, ...accountInfo }) },
+ },
+ {
+ provide: CryptoService,
+ useValue: {
+ getUserKeyWithLegacySupport,
+ },
+ },
{ provide: LogService, useValue: { error } },
{ provide: ToastService, useValue: { showToast } },
{ provide: DIALOG_DATA, useValue: dialogData },
@@ -82,7 +104,7 @@ describe("AddEditFolderDialogComponent", () => {
const newFolder = new FolderView();
newFolder.name = "New Folder";
- expect(encrypt).toHaveBeenCalledWith(newFolder);
+ expect(encrypt).toHaveBeenCalledWith(newFolder, "");
expect(save).toHaveBeenCalled();
});
@@ -137,10 +159,13 @@ describe("AddEditFolderDialogComponent", () => {
component.folderForm.controls.name.setValue("Edited Folder");
await component.submit();
- expect(encrypt).toHaveBeenCalledWith({
- ...dialogData.editFolderConfig.folder,
- name: "Edited Folder",
- });
+ expect(encrypt).toHaveBeenCalledWith(
+ {
+ ...dialogData.editFolderConfig.folder,
+ name: "Edited Folder",
+ },
+ "",
+ );
});
it("deletes the folder", async () => {
diff --git a/apps/browser/src/vault/popup/components/vault-v2/add-edit-folder-dialog/add-edit-folder-dialog.component.ts b/apps/browser/src/vault/popup/components/vault-v2/add-edit-folder-dialog/add-edit-folder-dialog.component.ts
index 33263533990..4f793abb6a0 100644
--- a/apps/browser/src/vault/popup/components/vault-v2/add-edit-folder-dialog/add-edit-folder-dialog.component.ts
+++ b/apps/browser/src/vault/popup/components/vault-v2/add-edit-folder-dialog/add-edit-folder-dialog.component.ts
@@ -11,8 +11,11 @@ import {
} from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { FormBuilder, ReactiveFormsModule, Validators } from "@angular/forms";
+import { firstValueFrom } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
+import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
+import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { FolderApiServiceAbstraction } from "@bitwarden/common/vault/abstractions/folder/folder-api.service.abstraction";
@@ -68,6 +71,8 @@ export class AddEditFolderDialogComponent implements AfterViewInit, OnInit {
private formBuilder: FormBuilder,
private folderService: FolderService,
private folderApiService: FolderApiServiceAbstraction,
+ private accountService: AccountService,
+ private cryptoService: CryptoService,
private toastService: ToastService,
private i18nService: I18nService,
private logService: LogService,
@@ -107,7 +112,9 @@ export class AddEditFolderDialogComponent implements AfterViewInit, OnInit {
this.folder.name = this.folderForm.controls.name.value;
try {
- const folder = await this.folderService.encrypt(this.folder);
+ const activeUserId = await firstValueFrom(this.accountService.activeAccount$);
+ const userKey = await this.cryptoService.getUserKeyWithLegacySupport(activeUserId.id);
+ const folder = await this.folderService.encrypt(this.folder, userKey);
await this.folderApiService.save(folder);
this.toastService.showToast({
diff --git a/apps/browser/src/vault/popup/components/vault/attachments.component.ts b/apps/browser/src/vault/popup/components/vault/attachments.component.ts
index 75819689b44..1ab1ff428b2 100644
--- a/apps/browser/src/vault/popup/components/vault/attachments.component.ts
+++ b/apps/browser/src/vault/popup/components/vault/attachments.component.ts
@@ -8,6 +8,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
+import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@@ -28,6 +29,7 @@ export class AttachmentsComponent extends BaseAttachmentsComponent implements On
cipherService: CipherService,
i18nService: I18nService,
cryptoService: CryptoService,
+ encryptService: EncryptService,
platformUtilsService: PlatformUtilsService,
apiService: ApiService,
private location: Location,
@@ -44,6 +46,7 @@ export class AttachmentsComponent extends BaseAttachmentsComponent implements On
cipherService,
i18nService,
cryptoService,
+ encryptService,
platformUtilsService,
apiService,
window,
diff --git a/apps/browser/src/vault/popup/components/vault/view.component.ts b/apps/browser/src/vault/popup/components/vault/view.component.ts
index f8e7de21dc8..a77a27046af 100644
--- a/apps/browser/src/vault/popup/components/vault/view.component.ts
+++ b/apps/browser/src/vault/popup/components/vault/view.component.ts
@@ -13,6 +13,7 @@ import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
+import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@@ -80,6 +81,7 @@ export class ViewComponent extends BaseViewComponent implements OnInit, OnDestro
tokenService: TokenService,
i18nService: I18nService,
cryptoService: CryptoService,
+ encryptService: EncryptService,
platformUtilsService: PlatformUtilsService,
auditService: AuditService,
private route: ActivatedRoute,
@@ -108,6 +110,7 @@ export class ViewComponent extends BaseViewComponent implements OnInit, OnDestro
tokenService,
i18nService,
cryptoService,
+ encryptService,
platformUtilsService,
auditService,
window,
diff --git a/apps/browser/src/vault/popup/settings/folder-add-edit.component.ts b/apps/browser/src/vault/popup/settings/folder-add-edit.component.ts
index b873735b460..3ef5fc73aa9 100644
--- a/apps/browser/src/vault/popup/settings/folder-add-edit.component.ts
+++ b/apps/browser/src/vault/popup/settings/folder-add-edit.component.ts
@@ -4,6 +4,8 @@ import { ActivatedRoute, Router } from "@angular/router";
import { first } from "rxjs/operators";
import { FolderAddEditComponent as BaseFolderAddEditComponent } from "@bitwarden/angular/vault/components/folder-add-edit.component";
+import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
+import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.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";
@@ -20,6 +22,8 @@ export class FolderAddEditComponent extends BaseFolderAddEditComponent implement
constructor(
folderService: FolderService,
folderApiService: FolderApiServiceAbstraction,
+ accountService: AccountService,
+ cryptoService: CryptoService,
i18nService: I18nService,
platformUtilsService: PlatformUtilsService,
private router: Router,
@@ -31,6 +35,8 @@ export class FolderAddEditComponent extends BaseFolderAddEditComponent implement
super(
folderService,
folderApiService,
+ accountService,
+ cryptoService,
i18nService,
platformUtilsService,
logService,
diff --git a/apps/cli/src/commands/download.command.ts b/apps/cli/src/commands/download.command.ts
index e417b7c3b88..f819875063d 100644
--- a/apps/cli/src/commands/download.command.ts
+++ b/apps/cli/src/commands/download.command.ts
@@ -1,6 +1,6 @@
import * as fet from "node-fetch";
-import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
+import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { EncArrayBuffer } from "@bitwarden/common/platform/models/domain/enc-array-buffer";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
@@ -9,7 +9,7 @@ import { FileResponse } from "../models/response/file.response";
import { CliUtils } from "../utils";
export abstract class DownloadCommand {
- constructor(protected cryptoService: CryptoService) {}
+ constructor(protected encryptService: EncryptService) {}
protected async saveAttachmentToFile(
url: string,
@@ -26,7 +26,7 @@ export abstract class DownloadCommand {
try {
const encBuf = await EncArrayBuffer.fromResponse(response);
- const decBuf = await this.cryptoService.decryptFromBytes(encBuf, key);
+ const decBuf = await this.encryptService.decryptToBytes(encBuf, key);
if (process.env.BW_SERVE === "true") {
const res = new FileResponse(Buffer.from(decBuf), fileName);
return Response.success(res);
diff --git a/apps/cli/src/commands/edit.command.ts b/apps/cli/src/commands/edit.command.ts
index bac1cce7c75..84ed7190a54 100644
--- a/apps/cli/src/commands/edit.command.ts
+++ b/apps/cli/src/commands/edit.command.ts
@@ -7,6 +7,7 @@ import { CipherExport } from "@bitwarden/common/models/export/cipher.export";
import { CollectionExport } from "@bitwarden/common/models/export/collection.export";
import { FolderExport } from "@bitwarden/common/models/export/folder.export";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
+import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { FolderApiServiceAbstraction } from "@bitwarden/common/vault/abstractions/folder/folder-api.service.abstraction";
@@ -25,6 +26,7 @@ export class EditCommand {
private cipherService: CipherService,
private folderService: FolderService,
private cryptoService: CryptoService,
+ private encryptService: EncryptService,
private apiService: ApiService,
private folderApiService: FolderApiServiceAbstraction,
private accountService: AccountService,
@@ -139,7 +141,10 @@ export class EditCommand {
let folderView = await folder.decrypt();
folderView = FolderExport.toView(req, folderView);
- const encFolder = await this.folderService.encrypt(folderView);
+
+ const activeUserId = await firstValueFrom(this.accountService.activeAccount$);
+ const userKey = await this.cryptoService.getUserKeyWithLegacySupport(activeUserId.id);
+ const encFolder = await this.folderService.encrypt(folderView, userKey);
try {
await this.folderApiService.save(encFolder);
const updatedFolder = await this.folderService.get(folder.id);
@@ -187,7 +192,7 @@ export class EditCommand {
(u) => new SelectionReadOnlyRequest(u.id, u.readOnly, u.hidePasswords, u.manage),
);
const request = new CollectionRequest();
- request.name = (await this.cryptoService.encrypt(req.name, orgKey)).encryptedString;
+ request.name = (await this.encryptService.encrypt(req.name, orgKey)).encryptedString;
request.externalId = req.externalId;
request.groups = groups;
request.users = users;
diff --git a/apps/cli/src/commands/get.command.ts b/apps/cli/src/commands/get.command.ts
index 923187bfcd0..3b2b18c66e3 100644
--- a/apps/cli/src/commands/get.command.ts
+++ b/apps/cli/src/commands/get.command.ts
@@ -20,6 +20,7 @@ import { LoginExport } from "@bitwarden/common/models/export/login.export";
import { SecureNoteExport } from "@bitwarden/common/models/export/secure-note.export";
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
+import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
@@ -56,7 +57,8 @@ export class GetCommand extends DownloadCommand {
private collectionService: CollectionService,
private totpService: TotpService,
private auditService: AuditService,
- cryptoService: CryptoService,
+ private cryptoService: CryptoService,
+ encryptService: EncryptService,
private stateService: StateService,
private searchService: SearchService,
private apiService: ApiService,
@@ -65,7 +67,7 @@ export class GetCommand extends DownloadCommand {
private accountProfileService: BillingAccountProfileStateService,
private accountService: AccountService,
) {
- super(cryptoService);
+ super(encryptService);
}
async run(object: string, id: string, cmdOptions: Record): Promise {
@@ -451,7 +453,7 @@ export class GetCommand extends DownloadCommand {
const response = await this.apiService.getCollectionAccessDetails(options.organizationId, id);
const decCollection = new CollectionView(response);
- decCollection.name = await this.cryptoService.decryptToUtf8(
+ decCollection.name = await this.encryptService.decryptToUtf8(
new EncString(response.name),
orgKey,
);
diff --git a/apps/cli/src/oss-serve-configurator.ts b/apps/cli/src/oss-serve-configurator.ts
index d7ef9ac871d..fd2a10975f3 100644
--- a/apps/cli/src/oss-serve-configurator.ts
+++ b/apps/cli/src/oss-serve-configurator.ts
@@ -57,6 +57,7 @@ export class OssServeConfigurator {
this.serviceContainer.totpService,
this.serviceContainer.auditService,
this.serviceContainer.cryptoService,
+ this.serviceContainer.encryptService,
this.serviceContainer.stateService,
this.serviceContainer.searchService,
this.serviceContainer.apiService,
@@ -79,6 +80,7 @@ export class OssServeConfigurator {
this.serviceContainer.cipherService,
this.serviceContainer.folderService,
this.serviceContainer.cryptoService,
+ this.serviceContainer.encryptService,
this.serviceContainer.apiService,
this.serviceContainer.folderApiService,
this.serviceContainer.billingAccountProfileStateService,
@@ -89,6 +91,7 @@ export class OssServeConfigurator {
this.serviceContainer.cipherService,
this.serviceContainer.folderService,
this.serviceContainer.cryptoService,
+ this.serviceContainer.encryptService,
this.serviceContainer.apiService,
this.serviceContainer.folderApiService,
this.serviceContainer.accountService,
@@ -150,7 +153,7 @@ export class OssServeConfigurator {
this.serviceContainer.sendService,
this.serviceContainer.environmentService,
this.serviceContainer.searchService,
- this.serviceContainer.cryptoService,
+ this.serviceContainer.encryptService,
);
this.sendEditCommand = new SendEditCommand(
this.serviceContainer.sendService,
diff --git a/apps/cli/src/service-container/service-container.ts b/apps/cli/src/service-container/service-container.ts
index b9225fec43d..98c5fc86b52 100644
--- a/apps/cli/src/service-container/service-container.ts
+++ b/apps/cli/src/service-container/service-container.ts
@@ -494,6 +494,7 @@ export class ServiceContainer {
this.collectionService = new CollectionService(
this.cryptoService,
+ this.encryptService,
this.i18nService,
this.stateProvider,
);
@@ -631,10 +632,12 @@ export class ServiceContainer {
this.cipherFileUploadService,
this.configService,
this.stateProvider,
+ this.accountService,
);
this.folderService = new FolderService(
this.cryptoService,
+ this.encryptService,
this.i18nService,
this.cipherService,
this.stateProvider,
@@ -721,6 +724,7 @@ export class ServiceContainer {
this.i18nService,
this.collectionService,
this.cryptoService,
+ this.encryptService,
this.pinService,
this.accountService,
);
@@ -730,8 +734,10 @@ export class ServiceContainer {
this.cipherService,
this.pinService,
this.cryptoService,
+ this.encryptService,
this.cryptoFunctionService,
this.kdfConfigService,
+ this.accountService,
);
this.organizationExportService = new OrganizationVaultExportService(
@@ -739,6 +745,7 @@ export class ServiceContainer {
this.apiService,
this.pinService,
this.cryptoService,
+ this.encryptService,
this.cryptoFunctionService,
this.collectionService,
this.kdfConfigService,
diff --git a/apps/cli/src/tools/send/commands/get.command.ts b/apps/cli/src/tools/send/commands/get.command.ts
index 2ffe2423174..057a1c27f33 100644
--- a/apps/cli/src/tools/send/commands/get.command.ts
+++ b/apps/cli/src/tools/send/commands/get.command.ts
@@ -2,7 +2,7 @@ import { OptionValues } from "commander";
import { firstValueFrom } from "rxjs";
import { SearchService } from "@bitwarden/common/abstractions/search.service";
-import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
+import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { SendView } from "@bitwarden/common/tools/send/models/view/send.view";
@@ -17,9 +17,9 @@ export class SendGetCommand extends DownloadCommand {
private sendService: SendService,
private environmentService: EnvironmentService,
private searchService: SearchService,
- cryptoService: CryptoService,
+ encryptService: EncryptService,
) {
- super(cryptoService);
+ super(encryptService);
}
async run(id: string, options: OptionValues) {
diff --git a/apps/cli/src/tools/send/commands/receive.command.ts b/apps/cli/src/tools/send/commands/receive.command.ts
index dc662f02727..a8740992f72 100644
--- a/apps/cli/src/tools/send/commands/receive.command.ts
+++ b/apps/cli/src/tools/send/commands/receive.command.ts
@@ -2,10 +2,10 @@ import { OptionValues } from "commander";
import * as inquirer from "inquirer";
import { firstValueFrom } from "rxjs";
-import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
+import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
@@ -27,14 +27,14 @@ export class SendReceiveCommand extends DownloadCommand {
private sendAccessRequest: SendAccessRequest;
constructor(
- private apiService: ApiService,
- cryptoService: CryptoService,
+ private cryptoService: CryptoService,
+ encryptService: EncryptService,
private cryptoFunctionService: CryptoFunctionService,
private platformUtilsService: PlatformUtilsService,
private environmentService: EnvironmentService,
private sendApiService: SendApiService,
) {
- super(cryptoService);
+ super(encryptService);
}
async run(url: string, options: OptionValues): Promise {
diff --git a/apps/cli/src/tools/send/send.program.ts b/apps/cli/src/tools/send/send.program.ts
index 05e7e7d22d7..878eaa52b24 100644
--- a/apps/cli/src/tools/send/send.program.ts
+++ b/apps/cli/src/tools/send/send.program.ts
@@ -100,8 +100,8 @@ export class SendProgram extends BaseProgram {
})
.action(async (url: string, options: OptionValues) => {
const cmd = new SendReceiveCommand(
- this.serviceContainer.apiService,
this.serviceContainer.cryptoService,
+ this.serviceContainer.encryptService,
this.serviceContainer.cryptoFunctionService,
this.serviceContainer.platformUtilsService,
this.serviceContainer.environmentService,
@@ -143,6 +143,7 @@ export class SendProgram extends BaseProgram {
this.serviceContainer.totpService,
this.serviceContainer.auditService,
this.serviceContainer.cryptoService,
+ this.serviceContainer.encryptService,
this.serviceContainer.stateService,
this.serviceContainer.searchService,
this.serviceContainer.apiService,
@@ -187,7 +188,7 @@ export class SendProgram extends BaseProgram {
this.serviceContainer.sendService,
this.serviceContainer.environmentService,
this.serviceContainer.searchService,
- this.serviceContainer.cryptoService,
+ this.serviceContainer.encryptService,
);
const response = await cmd.run(id, options);
this.processResponse(response);
@@ -246,7 +247,7 @@ export class SendProgram extends BaseProgram {
this.serviceContainer.sendService,
this.serviceContainer.environmentService,
this.serviceContainer.searchService,
- this.serviceContainer.cryptoService,
+ this.serviceContainer.encryptService,
);
const cmd = new SendEditCommand(
this.serviceContainer.sendService,
diff --git a/apps/cli/src/vault.program.ts b/apps/cli/src/vault.program.ts
index 2dad9a7c68a..11876ef6ece 100644
--- a/apps/cli/src/vault.program.ts
+++ b/apps/cli/src/vault.program.ts
@@ -178,6 +178,7 @@ export class VaultProgram extends BaseProgram {
this.serviceContainer.totpService,
this.serviceContainer.auditService,
this.serviceContainer.cryptoService,
+ this.serviceContainer.encryptService,
this.serviceContainer.stateService,
this.serviceContainer.searchService,
this.serviceContainer.apiService,
@@ -224,6 +225,7 @@ export class VaultProgram extends BaseProgram {
this.serviceContainer.cipherService,
this.serviceContainer.folderService,
this.serviceContainer.cryptoService,
+ this.serviceContainer.encryptService,
this.serviceContainer.apiService,
this.serviceContainer.folderApiService,
this.serviceContainer.billingAccountProfileStateService,
@@ -272,6 +274,7 @@ export class VaultProgram extends BaseProgram {
this.serviceContainer.cipherService,
this.serviceContainer.folderService,
this.serviceContainer.cryptoService,
+ this.serviceContainer.encryptService,
this.serviceContainer.apiService,
this.serviceContainer.folderApiService,
this.serviceContainer.accountService,
diff --git a/apps/cli/src/vault/create.command.ts b/apps/cli/src/vault/create.command.ts
index 0284ccc37bd..fc52720f77d 100644
--- a/apps/cli/src/vault/create.command.ts
+++ b/apps/cli/src/vault/create.command.ts
@@ -12,6 +12,7 @@ import { CipherExport } from "@bitwarden/common/models/export/cipher.export";
import { CollectionExport } from "@bitwarden/common/models/export/collection.export";
import { FolderExport } from "@bitwarden/common/models/export/folder.export";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
+import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { FolderApiServiceAbstraction } from "@bitwarden/common/vault/abstractions/folder/folder-api.service.abstraction";
@@ -31,6 +32,7 @@ export class CreateCommand {
private cipherService: CipherService,
private folderService: FolderService,
private cryptoService: CryptoService,
+ private encryptService: EncryptService,
private apiService: ApiService,
private folderApiService: FolderApiServiceAbstraction,
private accountProfileService: BillingAccountProfileStateService,
@@ -167,7 +169,9 @@ export class CreateCommand {
}
private async createFolder(req: FolderExport) {
- const folder = await this.folderService.encrypt(FolderExport.toView(req));
+ const activeAccountId = await firstValueFrom(this.accountService.activeAccount$);
+ const userKey = await this.cryptoService.getUserKeyWithLegacySupport(activeAccountId.id);
+ const folder = await this.folderService.encrypt(FolderExport.toView(req), userKey);
try {
await this.folderApiService.save(folder);
const newFolder = await this.folderService.get(folder.id);
@@ -210,7 +214,7 @@ export class CreateCommand {
(u) => new SelectionReadOnlyRequest(u.id, u.readOnly, u.hidePasswords, u.manage),
);
const request = new CollectionRequest();
- request.name = (await this.cryptoService.encrypt(req.name, orgKey)).encryptedString;
+ request.name = (await this.encryptService.encrypt(req.name, orgKey)).encryptedString;
request.externalId = req.externalId;
request.groups = groups;
request.users = users;
diff --git a/apps/desktop/src/app/services/services.module.ts b/apps/desktop/src/app/services/services.module.ts
index d5672f54c0f..a6db7fe5db4 100644
--- a/apps/desktop/src/app/services/services.module.ts
+++ b/apps/desktop/src/app/services/services.module.ts
@@ -234,7 +234,7 @@ const safeProviders: SafeProvider[] = [
provide: NativeMessageHandlerService,
deps: [
StateServiceAbstraction,
- CryptoServiceAbstraction,
+ EncryptService,
CryptoFunctionServiceAbstraction,
MessagingServiceAbstraction,
EncryptedMessageHandlerService,
diff --git a/apps/desktop/src/services/native-message-handler.service.ts b/apps/desktop/src/services/native-message-handler.service.ts
index 065726559b1..106dc11e41b 100644
--- a/apps/desktop/src/services/native-message-handler.service.ts
+++ b/apps/desktop/src/services/native-message-handler.service.ts
@@ -3,7 +3,7 @@ import { firstValueFrom } from "rxjs";
import { NativeMessagingVersion } from "@bitwarden/common/enums";
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
-import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
+import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
@@ -31,7 +31,7 @@ export class NativeMessageHandlerService {
constructor(
private stateService: StateService,
- private cryptoService: CryptoService,
+ private encryptService: EncryptService,
private cryptoFunctionService: CryptoFunctionService,
private messagingService: MessagingService,
private encryptedMessageHandlerService: EncryptedMessageHandlerService,
@@ -162,7 +162,7 @@ export class NativeMessageHandlerService {
payload: DecryptedCommandData,
key: SymmetricCryptoKey,
): Promise {
- return await this.cryptoService.encrypt(JSON.stringify(payload), key);
+ return await this.encryptService.encrypt(JSON.stringify(payload), key);
}
private async decryptPayload(message: EncryptedMessage): Promise {
@@ -182,7 +182,7 @@ export class NativeMessageHandlerService {
}
try {
- let decryptedResult = await this.cryptoService.decryptToUtf8(
+ let decryptedResult = await this.encryptService.decryptToUtf8(
message.encryptedCommand as EncString,
this.ddgSharedSecret,
);
diff --git a/apps/desktop/src/services/native-messaging.service.ts b/apps/desktop/src/services/native-messaging.service.ts
index f106d137b76..2b218001947 100644
--- a/apps/desktop/src/services/native-messaging.service.ts
+++ b/apps/desktop/src/services/native-messaging.service.ts
@@ -6,6 +6,7 @@ import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
+import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { BiometricStateService } from "@bitwarden/common/platform/biometrics/biometric-state.service";
@@ -33,6 +34,7 @@ export class NativeMessagingService {
constructor(
private cryptoFunctionService: CryptoFunctionService,
private cryptoService: CryptoService,
+ private encryptService: EncryptService,
private logService: LogService,
private messagingService: MessagingService,
private desktopSettingService: DesktopSettingsService,
@@ -111,7 +113,7 @@ export class NativeMessagingService {
}
const message: LegacyMessage = JSON.parse(
- await this.cryptoService.decryptToUtf8(
+ await this.encryptService.decryptToUtf8(
rawMessage as EncString,
SymmetricCryptoKey.fromString(await ipc.platform.ephemeralStore.getEphemeralValue(appId)),
),
@@ -224,7 +226,7 @@ export class NativeMessagingService {
private async send(message: any, appId: string) {
message.timestamp = Date.now();
- const encrypted = await this.cryptoService.encrypt(
+ const encrypted = await this.encryptService.encrypt(
JSON.stringify(message),
SymmetricCryptoKey.fromString(await ipc.platform.ephemeralStore.getEphemeralValue(appId)),
);
diff --git a/apps/desktop/src/vault/app/vault/attachments.component.ts b/apps/desktop/src/vault/app/vault/attachments.component.ts
index 2e25d390872..47e08a13cde 100644
--- a/apps/desktop/src/vault/app/vault/attachments.component.ts
+++ b/apps/desktop/src/vault/app/vault/attachments.component.ts
@@ -5,6 +5,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
+import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@@ -22,6 +23,7 @@ export class AttachmentsComponent extends BaseAttachmentsComponent {
cipherService: CipherService,
i18nService: I18nService,
cryptoService: CryptoService,
+ encryptService: EncryptService,
platformUtilsService: PlatformUtilsService,
apiService: ApiService,
logService: LogService,
@@ -36,6 +38,7 @@ export class AttachmentsComponent extends BaseAttachmentsComponent {
cipherService,
i18nService,
cryptoService,
+ encryptService,
platformUtilsService,
apiService,
window,
diff --git a/apps/desktop/src/vault/app/vault/folder-add-edit.component.ts b/apps/desktop/src/vault/app/vault/folder-add-edit.component.ts
index 3865ec4837c..0cc55d65f3e 100644
--- a/apps/desktop/src/vault/app/vault/folder-add-edit.component.ts
+++ b/apps/desktop/src/vault/app/vault/folder-add-edit.component.ts
@@ -2,6 +2,8 @@ import { Component } from "@angular/core";
import { FormBuilder } from "@angular/forms";
import { FolderAddEditComponent as BaseFolderAddEditComponent } from "@bitwarden/angular/vault/components/folder-add-edit.component";
+import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
+import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.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";
@@ -17,6 +19,8 @@ export class FolderAddEditComponent extends BaseFolderAddEditComponent {
constructor(
folderService: FolderService,
folderApiService: FolderApiServiceAbstraction,
+ accountService: AccountService,
+ cryptoService: CryptoService,
i18nService: I18nService,
platformUtilsService: PlatformUtilsService,
logService: LogService,
@@ -26,6 +30,8 @@ export class FolderAddEditComponent extends BaseFolderAddEditComponent {
super(
folderService,
folderApiService,
+ accountService,
+ cryptoService,
i18nService,
platformUtilsService,
logService,
diff --git a/apps/desktop/src/vault/app/vault/view.component.ts b/apps/desktop/src/vault/app/vault/view.component.ts
index 140e1e9ced6..7f2fb9ceae5 100644
--- a/apps/desktop/src/vault/app/vault/view.component.ts
+++ b/apps/desktop/src/vault/app/vault/view.component.ts
@@ -19,6 +19,7 @@ import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
+import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@@ -48,6 +49,7 @@ export class ViewComponent extends BaseViewComponent implements OnInit, OnDestro
tokenService: TokenService,
i18nService: I18nService,
cryptoService: CryptoService,
+ encryptService: EncryptService,
platformUtilsService: PlatformUtilsService,
auditService: AuditService,
broadcasterService: BroadcasterService,
@@ -72,6 +74,7 @@ export class ViewComponent extends BaseViewComponent implements OnInit, OnDestro
tokenService,
i18nService,
cryptoService,
+ encryptService,
platformUtilsService,
auditService,
window,
diff --git a/apps/web/src/app/auth/settings/emergency-access/attachments/emergency-access-attachments.component.ts b/apps/web/src/app/auth/settings/emergency-access/attachments/emergency-access-attachments.component.ts
index e0a6f6c53d5..4912a866fd1 100644
--- a/apps/web/src/app/auth/settings/emergency-access/attachments/emergency-access-attachments.component.ts
+++ b/apps/web/src/app/auth/settings/emergency-access/attachments/emergency-access-attachments.component.ts
@@ -5,6 +5,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
+import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@@ -26,6 +27,7 @@ export class EmergencyAccessAttachmentsComponent extends BaseAttachmentsComponen
cipherService: CipherService,
i18nService: I18nService,
cryptoService: CryptoService,
+ encryptService: EncryptService,
stateService: StateService,
platformUtilsService: PlatformUtilsService,
apiService: ApiService,
@@ -40,6 +42,7 @@ export class EmergencyAccessAttachmentsComponent extends BaseAttachmentsComponen
cipherService,
i18nService,
cryptoService,
+ encryptService,
platformUtilsService,
apiService,
window,
diff --git a/apps/web/src/app/billing/organizations/organization-plans.component.ts b/apps/web/src/app/billing/organizations/organization-plans.component.ts
index 3d02fa027ef..924b128a50a 100644
--- a/apps/web/src/app/billing/organizations/organization-plans.component.ts
+++ b/apps/web/src/app/billing/organizations/organization-plans.component.ts
@@ -31,6 +31,7 @@ import { PlanResponse } from "@bitwarden/common/billing/models/response/plan.res
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
+import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
@@ -147,6 +148,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
private i18nService: I18nService,
private platformUtilsService: PlatformUtilsService,
private cryptoService: CryptoService,
+ private encryptService: EncryptService,
private router: Router,
private syncService: SyncService,
private policyService: PolicyService,
@@ -590,7 +592,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
if (this.createOrganization) {
const orgKey = await this.cryptoService.makeOrgKey();
const key = orgKey[0].encryptedString;
- const collection = await this.cryptoService.encrypt(
+ const collection = await this.encryptService.encrypt(
this.i18nService.t("defaultCollection"),
orgKey[1],
);
@@ -744,7 +746,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
);
const providerKey = await this.cryptoService.getProviderKey(this.providerId);
providerRequest.organizationCreateRequest.key = (
- await this.cryptoService.encrypt(orgKey.key, providerKey)
+ await this.encryptService.encrypt(orgKey.key, providerKey)
).encryptedString;
const orgId = (
await this.apiService.postProviderCreateOrganization(this.providerId, providerRequest)
diff --git a/apps/web/src/app/tools/send/send-access-file.component.ts b/apps/web/src/app/tools/send/send-access-file.component.ts
index 8bb3558a69d..1efabb5fec7 100644
--- a/apps/web/src/app/tools/send/send-access-file.component.ts
+++ b/apps/web/src/app/tools/send/send-access-file.component.ts
@@ -1,6 +1,6 @@
import { Component, Input } from "@angular/core";
-import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
+import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
@@ -26,7 +26,7 @@ export class SendAccessFileComponent {
constructor(
private i18nService: I18nService,
private toastService: ToastService,
- private cryptoService: CryptoService,
+ private encryptService: EncryptService,
private fileDownloadService: FileDownloadService,
private sendApiService: SendApiService,
) {}
@@ -62,7 +62,7 @@ export class SendAccessFileComponent {
try {
const encBuf = await EncArrayBuffer.fromResponse(response);
- const decBuf = await this.cryptoService.decryptFromBytes(encBuf, this.decKey);
+ const decBuf = await this.encryptService.decryptToBytes(encBuf, this.decKey);
this.fileDownloadService.download({
fileName: this.send.file.fileName,
blobData: decBuf,
diff --git a/apps/web/src/app/vault/core/collection-admin.service.ts b/apps/web/src/app/vault/core/collection-admin.service.ts
index b6ddd452a1d..e0c15e34047 100644
--- a/apps/web/src/app/vault/core/collection-admin.service.ts
+++ b/apps/web/src/app/vault/core/collection-admin.service.ts
@@ -3,6 +3,7 @@ import { Injectable } from "@angular/core";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { SelectionReadOnlyRequest } from "@bitwarden/common/admin-console/models/request/selection-read-only.request";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
+import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service";
import { CollectionData } from "@bitwarden/common/vault/models/data/collection.data";
@@ -23,6 +24,7 @@ export class CollectionAdminService {
constructor(
private apiService: ApiService,
private cryptoService: CryptoService,
+ private encryptService: EncryptService,
private collectionService: CollectionService,
) {}
@@ -116,7 +118,7 @@ export class CollectionAdminService {
const promises = collections.map(async (c) => {
const view = new CollectionAdminView();
view.id = c.id;
- view.name = await this.cryptoService.decryptToUtf8(new EncString(c.name), orgKey);
+ view.name = await this.encryptService.decryptToUtf8(new EncString(c.name), orgKey);
view.externalId = c.externalId;
view.organizationId = c.organizationId;
@@ -146,7 +148,7 @@ export class CollectionAdminService {
}
const collection = new CollectionRequest();
collection.externalId = model.externalId;
- collection.name = (await this.cryptoService.encrypt(model.name, key)).encryptedString;
+ collection.name = (await this.encryptService.encrypt(model.name, key)).encryptedString;
collection.groups = model.groups.map(
(group) =>
new SelectionReadOnlyRequest(group.id, group.readOnly, group.hidePasswords, group.manage),
diff --git a/apps/web/src/app/vault/individual-vault/attachments.component.ts b/apps/web/src/app/vault/individual-vault/attachments.component.ts
index b578efcae67..bb070ef6887 100644
--- a/apps/web/src/app/vault/individual-vault/attachments.component.ts
+++ b/apps/web/src/app/vault/individual-vault/attachments.component.ts
@@ -5,6 +5,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
+import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@@ -25,6 +26,7 @@ export class AttachmentsComponent extends BaseAttachmentsComponent {
cipherService: CipherService,
i18nService: I18nService,
cryptoService: CryptoService,
+ encryptService: EncryptService,
stateService: StateService,
platformUtilsService: PlatformUtilsService,
apiService: ApiService,
@@ -39,6 +41,7 @@ export class AttachmentsComponent extends BaseAttachmentsComponent {
cipherService,
i18nService,
cryptoService,
+ encryptService,
platformUtilsService,
apiService,
window,
diff --git a/apps/web/src/app/vault/individual-vault/folder-add-edit.component.ts b/apps/web/src/app/vault/individual-vault/folder-add-edit.component.ts
index fe61d9b9a11..bc31cdc8cde 100644
--- a/apps/web/src/app/vault/individual-vault/folder-add-edit.component.ts
+++ b/apps/web/src/app/vault/individual-vault/folder-add-edit.component.ts
@@ -1,8 +1,11 @@
import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog";
import { Component, Inject } from "@angular/core";
import { FormBuilder } from "@angular/forms";
+import { firstValueFrom } from "rxjs";
import { FolderAddEditComponent as BaseFolderAddEditComponent } from "@bitwarden/angular/vault/components/folder-add-edit.component";
+import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
+import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.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";
@@ -19,6 +22,8 @@ export class FolderAddEditComponent extends BaseFolderAddEditComponent {
constructor(
folderService: FolderService,
folderApiService: FolderApiServiceAbstraction,
+ protected accountSerivce: AccountService,
+ protected cryptoService: CryptoService,
i18nService: I18nService,
platformUtilsService: PlatformUtilsService,
logService: LogService,
@@ -31,6 +36,8 @@ export class FolderAddEditComponent extends BaseFolderAddEditComponent {
super(
folderService,
folderApiService,
+ accountSerivce,
+ cryptoService,
i18nService,
platformUtilsService,
logService,
@@ -73,7 +80,9 @@ export class FolderAddEditComponent extends BaseFolderAddEditComponent {
}
try {
- const folder = await this.folderService.encrypt(this.folder);
+ const activeAccountId = (await firstValueFrom(this.accountSerivce.activeAccount$)).id;
+ const userKey = await this.cryptoService.getUserKeyWithLegacySupport(activeAccountId);
+ const folder = await this.folderService.encrypt(this.folder, userKey);
this.formPromise = this.folderApiService.save(folder);
await this.formPromise;
this.platformUtilsService.showToast(
diff --git a/apps/web/src/app/vault/org-vault/attachments.component.ts b/apps/web/src/app/vault/org-vault/attachments.component.ts
index 2bba4d389c0..62e65ade5de 100644
--- a/apps/web/src/app/vault/org-vault/attachments.component.ts
+++ b/apps/web/src/app/vault/org-vault/attachments.component.ts
@@ -5,6 +5,7 @@ import { Organization } from "@bitwarden/common/admin-console/models/domain/orga
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
+import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@@ -31,6 +32,7 @@ export class AttachmentsComponent extends BaseAttachmentsComponent implements On
cipherService: CipherService,
i18nService: I18nService,
cryptoService: CryptoService,
+ encryptService: EncryptService,
stateService: StateService,
platformUtilsService: PlatformUtilsService,
apiService: ApiService,
@@ -45,6 +47,7 @@ export class AttachmentsComponent extends BaseAttachmentsComponent implements On
cipherService,
i18nService,
cryptoService,
+ encryptService,
stateService,
platformUtilsService,
apiService,
diff --git a/bitwarden_license/bit-cli/src/service-container.ts b/bitwarden_license/bit-cli/src/service-container.ts
index 716c045fd16..c624afd9460 100644
--- a/bitwarden_license/bit-cli/src/service-container.ts
+++ b/bitwarden_license/bit-cli/src/service-container.ts
@@ -18,6 +18,7 @@ export class ServiceContainer extends OssServiceContainer {
this.organizationAuthRequestService = new OrganizationAuthRequestService(
this.organizationAuthRequestApiService,
this.cryptoService,
+ this.encryptService,
this.organizationUserApiService,
);
}
diff --git a/bitwarden_license/bit-common/src/admin-console/auth-requests/organization-auth-request.service.spec.ts b/bitwarden_license/bit-common/src/admin-console/auth-requests/organization-auth-request.service.spec.ts
index a8e6445d331..3ee89cbda51 100644
--- a/bitwarden_license/bit-common/src/admin-console/auth-requests/organization-auth-request.service.spec.ts
+++ b/bitwarden_license/bit-common/src/admin-console/auth-requests/organization-auth-request.service.spec.ts
@@ -6,6 +6,7 @@ import {
} from "@bitwarden/admin-console/common";
import { ListResponse } from "@bitwarden/common/models/response/list.response";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
+import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
import { OrganizationAuthRequestApiService } from "./organization-auth-request-api.service";
@@ -16,16 +17,19 @@ import { PendingAuthRequestView } from "./pending-auth-request.view";
describe("OrganizationAuthRequestService", () => {
let organizationAuthRequestApiService: MockProxy;
let cryptoService: MockProxy;
+ let encryptService: MockProxy;
let organizationUserApiService: MockProxy;
let organizationAuthRequestService: OrganizationAuthRequestService;
beforeEach(() => {
organizationAuthRequestApiService = mock();
cryptoService = mock();
+ encryptService = mock();
organizationUserApiService = mock();
organizationAuthRequestService = new OrganizationAuthRequestService(
organizationAuthRequestApiService,
cryptoService,
+ encryptService,
organizationUserApiService,
);
});
diff --git a/bitwarden_license/bit-common/src/admin-console/auth-requests/organization-auth-request.service.ts b/bitwarden_license/bit-common/src/admin-console/auth-requests/organization-auth-request.service.ts
index edba399b8b2..ad6e29c5834 100644
--- a/bitwarden_license/bit-common/src/admin-console/auth-requests/organization-auth-request.service.ts
+++ b/bitwarden_license/bit-common/src/admin-console/auth-requests/organization-auth-request.service.ts
@@ -3,6 +3,7 @@ import {
OrganizationUserResetPasswordDetailsResponse,
} from "@bitwarden/admin-console/common";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
+import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
@@ -15,6 +16,7 @@ export class OrganizationAuthRequestService {
constructor(
private organizationAuthRequestApiService: OrganizationAuthRequestApiService,
private cryptoService: CryptoService,
+ private encryptService: EncryptService,
private organizationUserApiService: OrganizationUserApiService,
) {}
@@ -109,7 +111,7 @@ export class OrganizationAuthRequestService {
// Decrypt Organization's encrypted Private Key with org key
const orgSymKey = await this.cryptoService.getOrgKey(organizationId);
- const decOrgPrivateKey = await this.cryptoService.decryptToBytes(
+ const decOrgPrivateKey = await this.encryptService.decryptToBytes(
new EncString(encryptedOrgPrivateKey),
orgSymKey,
);
diff --git a/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/device-approvals/device-approvals.component.ts b/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/device-approvals/device-approvals.component.ts
index 34c7bba7d0c..e074e0dd315 100644
--- a/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/device-approvals/device-approvals.component.ts
+++ b/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/device-approvals/device-approvals.component.ts
@@ -10,6 +10,7 @@ import { PendingAuthRequestView } from "@bitwarden/bit-common/admin-console/auth
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
+import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.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";
@@ -30,7 +31,12 @@ import { SharedModule } from "@bitwarden/web-vault/app/shared/shared.module";
}),
safeProvider({
provide: OrganizationAuthRequestService,
- deps: [OrganizationAuthRequestApiService, CryptoService, OrganizationUserApiService],
+ deps: [
+ OrganizationAuthRequestApiService,
+ CryptoService,
+ EncryptService,
+ OrganizationUserApiService,
+ ],
}),
] satisfies SafeProvider[],
imports: [SharedModule, NoItemsModule, LooseComponentsModule],
diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/services/web-provider.service.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/services/web-provider.service.ts
index 76094646808..3a2c7b8b644 100644
--- a/bitwarden_license/bit-web/src/app/admin-console/providers/services/web-provider.service.ts
+++ b/bitwarden_license/bit-web/src/app/admin-console/providers/services/web-provider.service.ts
@@ -27,7 +27,7 @@ export class WebProviderService {
const orgKey = await this.cryptoService.getOrgKey(organizationId);
const providerKey = await this.cryptoService.getProviderKey(providerId);
- const encryptedOrgKey = await this.cryptoService.encrypt(orgKey.key, providerKey);
+ const encryptedOrgKey = await this.encryptService.encrypt(orgKey.key, providerKey);
const request = new ProviderAddOrganizationRequest();
request.organizationId = organizationId;
diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts
index 4cdf5be8651..734ae03d59b 100644
--- a/libs/angular/src/services/jslib-services.module.ts
+++ b/libs/angular/src/services/jslib-services.module.ts
@@ -449,6 +449,7 @@ const safeProviders: SafeProvider[] = [
fileUploadService: CipherFileUploadServiceAbstraction,
configService: ConfigService,
stateProvider: StateProvider,
+ accountService: AccountServiceAbstraction,
) =>
new CipherService(
cryptoService,
@@ -463,6 +464,7 @@ const safeProviders: SafeProvider[] = [
fileUploadService,
configService,
stateProvider,
+ accountService,
),
deps: [
CryptoServiceAbstraction,
@@ -477,6 +479,7 @@ const safeProviders: SafeProvider[] = [
CipherFileUploadServiceAbstraction,
ConfigService,
StateProvider,
+ AccountServiceAbstraction,
],
}),
safeProvider({
@@ -484,6 +487,7 @@ const safeProviders: SafeProvider[] = [
useClass: FolderService,
deps: [
CryptoServiceAbstraction,
+ EncryptService,
I18nServiceAbstraction,
CipherServiceAbstraction,
StateProvider,
@@ -527,7 +531,7 @@ const safeProviders: SafeProvider[] = [
safeProvider({
provide: CollectionServiceAbstraction,
useClass: CollectionService,
- deps: [CryptoServiceAbstraction, I18nServiceAbstraction, StateProvider],
+ deps: [CryptoServiceAbstraction, EncryptService, I18nServiceAbstraction, StateProvider],
}),
safeProvider({
provide: EnvironmentService,
@@ -785,6 +789,7 @@ const safeProviders: SafeProvider[] = [
I18nServiceAbstraction,
CollectionServiceAbstraction,
CryptoServiceAbstraction,
+ EncryptService,
PinServiceAbstraction,
AccountServiceAbstraction,
],
@@ -797,8 +802,10 @@ const safeProviders: SafeProvider[] = [
CipherServiceAbstraction,
PinServiceAbstraction,
CryptoServiceAbstraction,
+ EncryptService,
CryptoFunctionServiceAbstraction,
KdfConfigServiceAbstraction,
+ AccountServiceAbstraction,
],
}),
safeProvider({
@@ -809,6 +816,7 @@ const safeProviders: SafeProvider[] = [
ApiServiceAbstraction,
PinServiceAbstraction,
CryptoServiceAbstraction,
+ EncryptService,
CryptoFunctionServiceAbstraction,
CollectionServiceAbstraction,
KdfConfigServiceAbstraction,
diff --git a/libs/angular/src/vault/components/attachments.component.ts b/libs/angular/src/vault/components/attachments.component.ts
index 4ae68c9ca9d..43c5a0d6d41 100644
--- a/libs/angular/src/vault/components/attachments.component.ts
+++ b/libs/angular/src/vault/components/attachments.component.ts
@@ -6,6 +6,7 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
+import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@@ -40,6 +41,7 @@ export class AttachmentsComponent implements OnInit {
protected cipherService: CipherService,
protected i18nService: I18nService,
protected cryptoService: CryptoService,
+ protected encryptService: EncryptService,
protected platformUtilsService: PlatformUtilsService,
protected apiService: ApiService,
protected win: Window,
@@ -178,7 +180,7 @@ export class AttachmentsComponent implements OnInit {
attachment.key != null
? attachment.key
: await this.cryptoService.getOrgKey(this.cipher.organizationId);
- const decBuf = await this.cryptoService.decryptFromBytes(encBuf, key);
+ const decBuf = await this.encryptService.decryptToBytes(encBuf, key);
this.fileDownloadService.download({
fileName: attachment.fileName,
blobData: decBuf,
@@ -249,7 +251,7 @@ export class AttachmentsComponent implements OnInit {
attachment.key != null
? attachment.key
: await this.cryptoService.getOrgKey(this.cipher.organizationId);
- const decBuf = await this.cryptoService.decryptFromBytes(encBuf, key);
+ const decBuf = await this.encryptService.decryptToBytes(encBuf, key);
const activeUserId = await firstValueFrom(
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
);
diff --git a/libs/angular/src/vault/components/folder-add-edit.component.ts b/libs/angular/src/vault/components/folder-add-edit.component.ts
index 0f179ae012c..199feb599ff 100644
--- a/libs/angular/src/vault/components/folder-add-edit.component.ts
+++ b/libs/angular/src/vault/components/folder-add-edit.component.ts
@@ -1,6 +1,9 @@
import { Directive, EventEmitter, Input, OnInit, Output } from "@angular/core";
import { Validators, FormBuilder } from "@angular/forms";
+import { firstValueFrom } from "rxjs";
+import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
+import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.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";
@@ -29,6 +32,8 @@ export class FolderAddEditComponent implements OnInit {
constructor(
protected folderService: FolderService,
protected folderApiService: FolderApiServiceAbstraction,
+ protected accountService: AccountService,
+ protected cryptoService: CryptoService,
protected i18nService: I18nService,
protected platformUtilsService: PlatformUtilsService,
protected logService: LogService,
@@ -52,7 +57,9 @@ export class FolderAddEditComponent implements OnInit {
}
try {
- const folder = await this.folderService.encrypt(this.folder);
+ const activeAccountId = await firstValueFrom(this.accountService.activeAccount$);
+ const userKey = await this.cryptoService.getUserKeyWithLegacySupport(activeAccountId.id);
+ const folder = await this.folderService.encrypt(this.folder, userKey);
this.formPromise = this.folderApiService.save(folder);
await this.formPromise;
this.platformUtilsService.showToast(
diff --git a/libs/angular/src/vault/components/view.component.ts b/libs/angular/src/vault/components/view.component.ts
index a6e96bc542a..ac644acf9e4 100644
--- a/libs/angular/src/vault/components/view.component.ts
+++ b/libs/angular/src/vault/components/view.component.ts
@@ -21,6 +21,7 @@ import { EventType } from "@bitwarden/common/enums";
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
+import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@@ -87,6 +88,7 @@ export class ViewComponent implements OnDestroy, OnInit {
protected tokenService: TokenService,
protected i18nService: I18nService,
protected cryptoService: CryptoService,
+ protected encryptService: EncryptService,
protected platformUtilsService: PlatformUtilsService,
protected auditService: AuditService,
protected win: Window,
@@ -442,7 +444,7 @@ export class ViewComponent implements OnDestroy, OnInit {
attachment.key != null
? attachment.key
: await this.cryptoService.getOrgKey(this.cipher.organizationId);
- const decBuf = await this.cryptoService.decryptFromBytes(encBuf, key);
+ const decBuf = await this.encryptService.decryptToBytes(encBuf, key);
this.fileDownloadService.download({
fileName: attachment.fileName,
blobData: decBuf,
diff --git a/libs/auth/src/common/login-strategies/auth-request-login.strategy.spec.ts b/libs/auth/src/common/login-strategies/auth-request-login.strategy.spec.ts
index b112e5aa2ab..efc6da51d9f 100644
--- a/libs/auth/src/common/login-strategies/auth-request-login.strategy.spec.ts
+++ b/libs/auth/src/common/login-strategies/auth-request-login.strategy.spec.ts
@@ -12,6 +12,7 @@ import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abs
import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum";
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
+import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
@@ -37,6 +38,7 @@ describe("AuthRequestLoginStrategy", () => {
let cache: AuthRequestLoginStrategyData;
let cryptoService: MockProxy;
+ let encryptService: MockProxy;
let apiService: MockProxy;
let tokenService: MockProxy;
let appIdService: MockProxy;
@@ -101,6 +103,7 @@ describe("AuthRequestLoginStrategy", () => {
accountService,
masterPasswordService,
cryptoService,
+ encryptService,
apiService,
tokenService,
appIdService,
diff --git a/libs/auth/src/common/login-strategies/login.strategy.spec.ts b/libs/auth/src/common/login-strategies/login.strategy.spec.ts
index 665857c1f47..35d62ca76b3 100644
--- a/libs/auth/src/common/login-strategies/login.strategy.spec.ts
+++ b/libs/auth/src/common/login-strategies/login.strategy.spec.ts
@@ -22,6 +22,7 @@ import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abs
import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum";
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
+import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
@@ -104,6 +105,7 @@ describe("LoginStrategy", () => {
let loginStrategyService: MockProxy;
let cryptoService: MockProxy;
+ let encryptService: MockProxy;
let apiService: MockProxy;
let tokenService: MockProxy;
let appIdService: MockProxy;
@@ -128,6 +130,7 @@ describe("LoginStrategy", () => {
loginStrategyService = mock();
cryptoService = mock();
+ encryptService = mock();
apiService = mock();
tokenService = mock();
appIdService = mock();
@@ -156,6 +159,7 @@ describe("LoginStrategy", () => {
accountService,
masterPasswordService,
cryptoService,
+ encryptService,
apiService,
tokenService,
appIdService,
@@ -467,6 +471,7 @@ describe("LoginStrategy", () => {
accountService,
masterPasswordService,
cryptoService,
+ encryptService,
apiService,
tokenService,
appIdService,
diff --git a/libs/auth/src/common/login-strategies/login.strategy.ts b/libs/auth/src/common/login-strategies/login.strategy.ts
index ff6bf07af7e..2e881f978dc 100644
--- a/libs/auth/src/common/login-strategies/login.strategy.ts
+++ b/libs/auth/src/common/login-strategies/login.strategy.ts
@@ -26,6 +26,7 @@ import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action
import { KeysRequest } from "@bitwarden/common/models/request/keys.request";
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
+import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
@@ -66,6 +67,7 @@ export abstract class LoginStrategy {
protected accountService: AccountService,
protected masterPasswordService: InternalMasterPasswordServiceAbstraction,
protected cryptoService: CryptoService,
+ protected encryptService: EncryptService,
protected apiService: ApiService,
protected tokenService: TokenService,
protected appIdService: AppIdService,
diff --git a/libs/auth/src/common/login-strategies/password-login.strategy.spec.ts b/libs/auth/src/common/login-strategies/password-login.strategy.spec.ts
index 7ba58e1443a..07cbf2424ab 100644
--- a/libs/auth/src/common/login-strategies/password-login.strategy.spec.ts
+++ b/libs/auth/src/common/login-strategies/password-login.strategy.spec.ts
@@ -16,6 +16,7 @@ import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abs
import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum";
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
+import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
@@ -63,6 +64,7 @@ describe("PasswordLoginStrategy", () => {
let loginStrategyService: MockProxy;
let cryptoService: MockProxy;
+ let encryptService: MockProxy;
let apiService: MockProxy;
let tokenService: MockProxy;
let appIdService: MockProxy;
@@ -88,6 +90,7 @@ describe("PasswordLoginStrategy", () => {
loginStrategyService = mock();
cryptoService = mock();
+ encryptService = mock();
apiService = mock();
tokenService = mock();
appIdService = mock();
@@ -127,6 +130,7 @@ describe("PasswordLoginStrategy", () => {
accountService,
masterPasswordService,
cryptoService,
+ encryptService,
apiService,
tokenService,
appIdService,
diff --git a/libs/auth/src/common/login-strategies/sso-login.strategy.spec.ts b/libs/auth/src/common/login-strategies/sso-login.strategy.spec.ts
index 8e28a2c0222..f5de10766c0 100644
--- a/libs/auth/src/common/login-strategies/sso-login.strategy.spec.ts
+++ b/libs/auth/src/common/login-strategies/sso-login.strategy.spec.ts
@@ -17,6 +17,7 @@ import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
+import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
@@ -44,6 +45,7 @@ describe("SsoLoginStrategy", () => {
let masterPasswordService: FakeMasterPasswordService;
let cryptoService: MockProxy;
+ let encryptService: MockProxy;
let apiService: MockProxy;
let tokenService: MockProxy;
let appIdService: MockProxy;
@@ -78,6 +80,7 @@ describe("SsoLoginStrategy", () => {
masterPasswordService = new FakeMasterPasswordService();
cryptoService = mock();
+ encryptService = mock();
apiService = mock();
tokenService = mock();
appIdService = mock();
@@ -125,6 +128,7 @@ describe("SsoLoginStrategy", () => {
accountService,
masterPasswordService,
cryptoService,
+ encryptService,
apiService,
tokenService,
appIdService,
diff --git a/libs/auth/src/common/login-strategies/user-api-login.strategy.spec.ts b/libs/auth/src/common/login-strategies/user-api-login.strategy.spec.ts
index 16614497964..d299a8e0ced 100644
--- a/libs/auth/src/common/login-strategies/user-api-login.strategy.spec.ts
+++ b/libs/auth/src/common/login-strategies/user-api-login.strategy.spec.ts
@@ -11,6 +11,7 @@ import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abs
import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum";
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
+import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import {
Environment,
EnvironmentService,
@@ -39,6 +40,7 @@ describe("UserApiLoginStrategy", () => {
let masterPasswordService: FakeMasterPasswordService;
let cryptoService: MockProxy;
+ let encryptService: MockProxy;
let apiService: MockProxy;
let tokenService: MockProxy;
let appIdService: MockProxy;
@@ -99,6 +101,7 @@ describe("UserApiLoginStrategy", () => {
accountService,
masterPasswordService,
cryptoService,
+ encryptService,
apiService,
tokenService,
appIdService,
diff --git a/libs/auth/src/common/login-strategies/webauthn-login.strategy.spec.ts b/libs/auth/src/common/login-strategies/webauthn-login.strategy.spec.ts
index 0db41c1e64f..b25022d25df 100644
--- a/libs/auth/src/common/login-strategies/webauthn-login.strategy.spec.ts
+++ b/libs/auth/src/common/login-strategies/webauthn-login.strategy.spec.ts
@@ -14,6 +14,7 @@ import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abs
import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum";
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
+import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
@@ -37,6 +38,7 @@ describe("WebAuthnLoginStrategy", () => {
let masterPasswordService: FakeMasterPasswordService;
let cryptoService!: MockProxy;
+ let encryptService!: MockProxy;
let apiService!: MockProxy;
let tokenService!: MockProxy;
let appIdService!: MockProxy;
@@ -79,6 +81,7 @@ describe("WebAuthnLoginStrategy", () => {
masterPasswordService = new FakeMasterPasswordService();
cryptoService = mock();
+ encryptService = mock();
apiService = mock();
tokenService = mock();
appIdService = mock();
@@ -103,6 +106,7 @@ describe("WebAuthnLoginStrategy", () => {
accountService,
masterPasswordService,
cryptoService,
+ encryptService,
apiService,
tokenService,
appIdService,
@@ -221,7 +225,7 @@ describe("WebAuthnLoginStrategy", () => {
const mockUserKeyArray: Uint8Array = randomBytes(32);
const mockUserKey = new SymmetricCryptoKey(mockUserKeyArray) as UserKey;
- cryptoService.decryptToBytes.mockResolvedValue(mockPrfPrivateKey);
+ encryptService.decryptToBytes.mockResolvedValue(mockPrfPrivateKey);
cryptoService.rsaDecrypt.mockResolvedValue(mockUserKeyArray);
// Act
@@ -235,8 +239,8 @@ describe("WebAuthnLoginStrategy", () => {
userId,
);
- expect(cryptoService.decryptToBytes).toHaveBeenCalledTimes(1);
- expect(cryptoService.decryptToBytes).toHaveBeenCalledWith(
+ expect(encryptService.decryptToBytes).toHaveBeenCalledTimes(1);
+ expect(encryptService.decryptToBytes).toHaveBeenCalledWith(
idTokenResponse.userDecryptionOptions.webAuthnPrfOption.encryptedPrivateKey,
webAuthnCredentials.prfKey,
);
@@ -268,7 +272,7 @@ describe("WebAuthnLoginStrategy", () => {
await webAuthnLoginStrategy.logIn(webAuthnCredentials);
// Assert
- expect(cryptoService.decryptToBytes).not.toHaveBeenCalled();
+ expect(encryptService.decryptToBytes).not.toHaveBeenCalled();
expect(cryptoService.rsaDecrypt).not.toHaveBeenCalled();
expect(cryptoService.setUserKey).not.toHaveBeenCalled();
});
@@ -303,7 +307,7 @@ describe("WebAuthnLoginStrategy", () => {
apiService.postIdentityToken.mockResolvedValue(idTokenResponse);
- cryptoService.decryptToBytes.mockResolvedValue(null);
+ encryptService.decryptToBytes.mockResolvedValue(null);
// Act
await webAuthnLoginStrategy.logIn(webAuthnCredentials);
diff --git a/libs/auth/src/common/login-strategies/webauthn-login.strategy.ts b/libs/auth/src/common/login-strategies/webauthn-login.strategy.ts
index d283d163da1..96f8bc7d633 100644
--- a/libs/auth/src/common/login-strategies/webauthn-login.strategy.ts
+++ b/libs/auth/src/common/login-strategies/webauthn-login.strategy.ts
@@ -80,7 +80,7 @@ export class WebAuthnLoginStrategy extends LoginStrategy {
}
// decrypt prf encrypted private key
- const privateKey = await this.cryptoService.decryptToBytes(
+ const privateKey = await this.encryptService.decryptToBytes(
webAuthnPrfOption.encryptedPrivateKey,
credentials.prfKey,
);
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 67bcdc3658e..89c2bc01d94 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
@@ -317,6 +317,7 @@ export class LoginStrategyService implements LoginStrategyServiceAbstraction {
this.accountService,
this.masterPasswordService,
this.cryptoService,
+ this.encryptService,
this.apiService,
this.tokenService,
this.appIdService,
diff --git a/libs/common/src/platform/abstractions/crypto.service.ts b/libs/common/src/platform/abstractions/crypto.service.ts
index 1fe97e023f2..2a8e1ad6476 100644
--- a/libs/common/src/platform/abstractions/crypto.service.ts
+++ b/libs/common/src/platform/abstractions/crypto.service.ts
@@ -15,7 +15,6 @@ import {
UserPublicKey,
} from "../../types/key";
import { KeySuffixOptions, HashPurpose } from "../enums";
-import { EncArrayBuffer } from "../models/domain/enc-array-buffer";
import { EncString } from "../models/domain/enc-string";
import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key";
@@ -373,37 +372,6 @@ export abstract class CryptoService {
* @param userId The desired user
*/
abstract clearDeprecatedKeys(keySuffix: KeySuffixOptions, userId?: string): Promise;
- /**
- * @deprecated July 25 2022: Get the key you need from CryptoService (getKeyForUserEncryption or getOrgKey)
- * and then call encryptService.encrypt
- */
- abstract encrypt(plainValue: string | Uint8Array, key?: SymmetricCryptoKey): Promise;
- /**
- * @deprecated July 25 2022: Get the key you need from CryptoService (getKeyForUserEncryption or getOrgKey)
- * and then call encryptService.encryptToBytes
- */
- abstract encryptToBytes(
- plainValue: Uint8Array,
- key?: SymmetricCryptoKey,
- ): Promise;
- /**
- * @deprecated July 25 2022: Get the key you need from CryptoService (getKeyForUserEncryption or getOrgKey)
- * and then call encryptService.decryptToBytes
- */
- abstract decryptToBytes(encString: EncString, key?: SymmetricCryptoKey): Promise;
- /**
- * @deprecated July 25 2022: Get the key you need from CryptoService (getKeyForUserEncryption or getOrgKey)
- * and then call encryptService.decryptToUtf8
- */
- abstract decryptToUtf8(encString: EncString, key?: SymmetricCryptoKey): Promise;
- /**
- * @deprecated July 25 2022: Get the key you need from CryptoService (getKeyForUserEncryption or getOrgKey)
- * and then call encryptService.decryptToBytes
- */
- abstract decryptFromBytes(
- encBuffer: EncArrayBuffer,
- key: SymmetricCryptoKey,
- ): Promise;
/**
* Retrieves all the keys needed for decrypting Ciphers
diff --git a/libs/common/src/platform/services/crypto.service.ts b/libs/common/src/platform/services/crypto.service.ts
index 8ce2b5e1a0c..6a93ac7f3ff 100644
--- a/libs/common/src/platform/services/crypto.service.ts
+++ b/libs/common/src/platform/services/crypto.service.ts
@@ -48,7 +48,6 @@ import { StateService } from "../abstractions/state.service";
import { KeySuffixOptions, HashPurpose, EncryptionType } from "../enums";
import { convertValues } from "../misc/convert-values";
import { EFFLongWordList } from "../misc/wordlist";
-import { EncArrayBuffer } from "../models/domain/enc-array-buffer";
import { EncString, EncryptedString } from "../models/domain/enc-string";
import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key";
import { ActiveUserState, StateProvider } from "../state";
@@ -859,58 +858,6 @@ export class CryptoService implements CryptoServiceAbstraction {
}
}
- // --DEPRECATED METHODS--
-
- /**
- * @deprecated July 25 2022: Get the key you need from CryptoService (getKeyForUserEncryption or getOrgKey)
- * and then call encryptService.encrypt
- */
- async encrypt(plainValue: string | Uint8Array, key?: SymmetricCryptoKey): Promise {
- key ||= await this.getUserKeyWithLegacySupport();
- return await this.encryptService.encrypt(plainValue, key);
- }
-
- /**
- * @deprecated July 25 2022: Get the key you need from CryptoService (getKeyForUserEncryption or getOrgKey)
- * and then call encryptService.encryptToBytes
- */
- async encryptToBytes(plainValue: Uint8Array, key?: SymmetricCryptoKey): Promise {
- key ||= await this.getUserKeyWithLegacySupport();
- return this.encryptService.encryptToBytes(plainValue, key);
- }
-
- /**
- * @deprecated July 25 2022: Get the key you need from CryptoService (getKeyForUserEncryption or getOrgKey)
- * and then call encryptService.decryptToBytes
- */
- async decryptToBytes(encString: EncString, key?: SymmetricCryptoKey): Promise {
- key ||= await this.getUserKeyWithLegacySupport();
- return this.encryptService.decryptToBytes(encString, key);
- }
-
- /**
- * @deprecated July 25 2022: Get the key you need from CryptoService (getKeyForUserEncryption or getOrgKey)
- * and then call encryptService.decryptToUtf8
- */
- async decryptToUtf8(encString: EncString, key?: SymmetricCryptoKey): Promise {
- key ||= await this.getUserKeyWithLegacySupport();
- return await this.encryptService.decryptToUtf8(encString, key);
- }
-
- /**
- * @deprecated July 25 2022: Get the key you need from CryptoService (getKeyForUserEncryption or getOrgKey)
- * and then call encryptService.decryptToBytes
- */
- async decryptFromBytes(encBuffer: EncArrayBuffer, key: SymmetricCryptoKey): Promise {
- if (encBuffer == null) {
- throw new Error("No buffer provided for decryption.");
- }
-
- key ||= await this.getUserKeyWithLegacySupport();
-
- return this.encryptService.decryptToBytes(encBuffer, key);
- }
-
userKey$(userId: UserId): Observable {
return this.stateProvider.getUser(userId, USER_KEY).state$;
}
diff --git a/libs/common/src/tools/send/models/domain/send.spec.ts b/libs/common/src/tools/send/models/domain/send.spec.ts
index bd42ab09cf9..5b1d7e73dae 100644
--- a/libs/common/src/tools/send/models/domain/send.spec.ts
+++ b/libs/common/src/tools/send/models/domain/send.spec.ts
@@ -1,5 +1,8 @@
import { mock } from "jest-mock-extended";
+import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
+import { UserKey } from "@bitwarden/common/types/key";
+
import { makeStaticByteArray, mockEnc } from "../../../../../spec";
import { CryptoService } from "../../../../platform/abstractions/crypto.service";
import { EncryptService } from "../../../../platform/abstractions/encrypt.service";
@@ -89,6 +92,7 @@ describe("Send", () => {
it("Decrypt", async () => {
const text = mock();
text.decrypt.mockResolvedValue("textView" as any);
+ const userKey = new SymmetricCryptoKey(new Uint8Array(32)) as UserKey;
const send = new Send();
send.id = "id";
@@ -106,13 +110,13 @@ describe("Send", () => {
send.disabled = false;
send.hideEmail = true;
+ const encryptService = mock();
const cryptoService = mock();
- cryptoService.decryptToBytes
- .calledWith(send.key, null)
+ encryptService.decryptToBytes
+ .calledWith(send.key, userKey)
.mockResolvedValue(makeStaticByteArray(32));
cryptoService.makeSendKey.mockResolvedValue("cryptoKey" as any);
-
- const encryptService = mock();
+ cryptoService.getUserKey.mockResolvedValue(userKey);
(window as any).bitwardenContainerService = new ContainerService(cryptoService, encryptService);
diff --git a/libs/common/src/tools/send/models/domain/send.ts b/libs/common/src/tools/send/models/domain/send.ts
index 610980062b6..41d1fecc10b 100644
--- a/libs/common/src/tools/send/models/domain/send.ts
+++ b/libs/common/src/tools/send/models/domain/send.ts
@@ -73,9 +73,11 @@ export class Send extends Domain {
const model = new SendView(this);
const cryptoService = Utils.getContainerService().getCryptoService();
+ const encryptService = Utils.getContainerService().getEncryptService();
try {
- model.key = await cryptoService.decryptToBytes(this.key, null);
+ const sendKeyEncryptionKey = await cryptoService.getUserKey();
+ model.key = await encryptService.decryptToBytes(this.key, sendKeyEncryptionKey);
model.cryptoKey = await cryptoService.makeSendKey(model.key);
} catch (e) {
// TODO: error?
diff --git a/libs/common/src/vault/abstractions/folder/folder.service.abstraction.ts b/libs/common/src/vault/abstractions/folder/folder.service.abstraction.ts
index 3480a8aca03..857915ddb80 100644
--- a/libs/common/src/vault/abstractions/folder/folder.service.abstraction.ts
+++ b/libs/common/src/vault/abstractions/folder/folder.service.abstraction.ts
@@ -15,7 +15,7 @@ export abstract class FolderService implements UserKeyRotationDataProvider;
clearCache: () => Promise;
- encrypt: (model: FolderView, key?: SymmetricCryptoKey) => Promise;
+ encrypt: (model: FolderView, key: SymmetricCryptoKey) => Promise;
get: (id: string) => Promise;
getDecrypted$: (id: string) => Observable;
getAllFromState: () => Promise;
diff --git a/libs/common/src/vault/services/cipher.service.spec.ts b/libs/common/src/vault/services/cipher.service.spec.ts
index b2712dee559..0873fa9d928 100644
--- a/libs/common/src/vault/services/cipher.service.spec.ts
+++ b/libs/common/src/vault/services/cipher.service.spec.ts
@@ -145,6 +145,7 @@ describe("Cipher Service", () => {
cipherFileUploadService,
configService,
stateProvider,
+ accountService,
);
cipherObj = new Cipher(cipherData);
@@ -273,7 +274,7 @@ describe("Cipher Service", () => {
cryptoService.makeCipherKey.mockReturnValue(
Promise.resolve(new SymmetricCryptoKey(makeStaticByteArray(64)) as CipherKey),
);
- cryptoService.encrypt.mockImplementation(encryptText);
+ encryptService.encrypt.mockImplementation(encryptText);
jest.spyOn(cipherService as any, "getAutofillOnPageLoadDefault").mockResolvedValue(true);
});
@@ -285,6 +286,10 @@ describe("Cipher Service", () => {
{ uri: "uri", match: UriMatchStrategy.RegularExpression } as LoginUriView,
];
+ cryptoService.getOrgKey.mockReturnValue(
+ Promise.resolve(new SymmetricCryptoKey(new Uint8Array(32)) as OrgKey),
+ );
+
const domain = await cipherService.encrypt(cipherView, userId);
expect(domain.login.uris).toEqual([
@@ -301,6 +306,9 @@ describe("Cipher Service", () => {
it("is null when feature flag is false", async () => {
configService.getFeatureFlag.mockResolvedValue(false);
+ cryptoService.getOrgKey.mockReturnValue(
+ Promise.resolve(new SymmetricCryptoKey(new Uint8Array(32)) as OrgKey),
+ );
const cipher = await cipherService.encrypt(cipherView, userId);
expect(cipher.key).toBeNull();
@@ -322,6 +330,9 @@ describe("Cipher Service", () => {
it("is not called when feature flag is false", async () => {
configService.getFeatureFlag.mockResolvedValue(false);
+ cryptoService.getOrgKey.mockReturnValue(
+ Promise.resolve(new SymmetricCryptoKey(new Uint8Array(32)) as OrgKey),
+ );
await cipherService.encrypt(cipherView, userId);
@@ -330,6 +341,9 @@ describe("Cipher Service", () => {
it("is called when feature flag is true", async () => {
configService.getFeatureFlag.mockResolvedValue(true);
+ cryptoService.getOrgKey.mockReturnValue(
+ Promise.resolve(new SymmetricCryptoKey(new Uint8Array(32)) as OrgKey),
+ );
await cipherService.encrypt(cipherView, userId);
diff --git a/libs/common/src/vault/services/cipher.service.ts b/libs/common/src/vault/services/cipher.service.ts
index 70b7c77fc15..9761387284f 100644
--- a/libs/common/src/vault/services/cipher.service.ts
+++ b/libs/common/src/vault/services/cipher.service.ts
@@ -1,6 +1,7 @@
import { firstValueFrom, map, Observable, skipWhile, switchMap } from "rxjs";
import { SemVer } from "semver";
+import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { BulkEncryptService } from "@bitwarden/common/platform/abstractions/bulk-encrypt.service";
@@ -108,6 +109,7 @@ export class CipherService implements CipherServiceAbstraction {
private cipherFileUploadService: CipherFileUploadService,
private configService: ConfigService,
private stateProvider: StateProvider,
+ private accountService: AccountService,
) {
this.localDataState = this.stateProvider.getActive(LOCAL_DATA_KEY);
this.encryptedCiphersState = this.stateProvider.getActive(ENCRYPTED_CIPHERS);
@@ -165,7 +167,7 @@ export class CipherService implements CipherServiceAbstraction {
async encrypt(
model: CipherView,
userId: UserId,
- keyForEncryption?: SymmetricCryptoKey,
+ keyForCipherEncryption?: SymmetricCryptoKey,
keyForCipherKeyDecryption?: SymmetricCryptoKey,
originalCipher: Cipher = null,
): Promise {
@@ -195,26 +197,21 @@ export class CipherService implements CipherServiceAbstraction {
const userOrOrgKey = await this.getKeyForCipherKeyDecryption(cipher, userId);
// The keyForEncryption is only used for encrypting the cipher key, not the cipher itself, since cipher key encryption is enabled.
// If the caller has provided a key for cipher key encryption, use it. Otherwise, use the user or org key.
- keyForEncryption ||= userOrOrgKey;
+ keyForCipherEncryption ||= userOrOrgKey;
// If the caller has provided a key for cipher key decryption, use it. Otherwise, use the user or org key.
keyForCipherKeyDecryption ||= userOrOrgKey;
return this.encryptCipherWithCipherKey(
model,
cipher,
- keyForEncryption,
+ keyForCipherEncryption,
keyForCipherKeyDecryption,
);
} else {
- if (keyForEncryption == null && cipher.organizationId != null) {
- keyForEncryption = await this.cryptoService.getOrgKey(cipher.organizationId);
- if (keyForEncryption == null) {
- throw new Error("Cannot encrypt cipher for organization. No key.");
- }
- }
+ keyForCipherEncryption ||= await this.getKeyForCipherKeyDecryption(cipher, userId);
// We want to ensure that the cipher key is null if cipher key encryption is disabled
// so that decryption uses the proper key.
cipher.key = null;
- return this.encryptCipher(model, cipher, keyForEncryption);
+ return this.encryptCipher(model, cipher, keyForCipherEncryption);
}
}
@@ -243,7 +240,7 @@ export class CipherService implements CipherServiceAbstraction {
key,
).then(async () => {
if (model.key != null) {
- attachment.key = await this.cryptoService.encrypt(model.key.key, key);
+ attachment.key = await this.encryptService.encrypt(model.key.key, key);
}
encAttachments.push(attachment);
});
@@ -1348,7 +1345,9 @@ export class CipherService implements CipherServiceAbstraction {
}
const encBuf = await EncArrayBuffer.fromResponse(attachmentResponse);
- const decBuf = await this.cryptoService.decryptFromBytes(encBuf, null);
+ const activeUserId = await firstValueFrom(this.accountService.activeAccount$);
+ const userKey = await this.cryptoService.getUserKeyWithLegacySupport(activeUserId.id);
+ const decBuf = await this.encryptService.decryptToBytes(encBuf, userKey);
let encKey: UserKey | OrgKey;
encKey = await this.cryptoService.getOrgKey(organizationId);
@@ -1412,7 +1411,7 @@ export class CipherService implements CipherServiceAbstraction {
.then(() => {
const modelProp = (model as any)[map[theProp] || theProp];
if (modelProp && modelProp !== "") {
- return self.cryptoService.encrypt(modelProp, key);
+ return self.encryptService.encrypt(modelProp, key);
}
return null;
})
@@ -1458,7 +1457,7 @@ export class CipherService implements CipherServiceAbstraction {
key,
);
const uriHash = await this.encryptService.hash(model.login.uris[i].uri, "sha256");
- loginUri.uriChecksum = await this.cryptoService.encrypt(uriHash, key);
+ loginUri.uriChecksum = await this.encryptService.encrypt(uriHash, key);
cipher.login.uris.push(loginUri);
}
}
@@ -1485,8 +1484,8 @@ export class CipherService implements CipherServiceAbstraction {
},
key,
);
- domainKey.counter = await this.cryptoService.encrypt(String(viewKey.counter), key);
- domainKey.discoverable = await this.cryptoService.encrypt(
+ domainKey.counter = await this.encryptService.encrypt(String(viewKey.counter), key);
+ domainKey.discoverable = await this.encryptService.encrypt(
String(viewKey.discoverable),
key,
);
@@ -1605,11 +1604,23 @@ export class CipherService implements CipherServiceAbstraction {
this.sortedCiphersCache.clear();
}
+ /**
+ * Encrypts a cipher object.
+ * @param model The cipher view model.
+ * @param cipher The cipher object.
+ * @param key The encryption key to encrypt with. This can be the org key, user key or cipher key, but must never be null
+ */
private async encryptCipher(
model: CipherView,
cipher: Cipher,
key: SymmetricCryptoKey,
): Promise {
+ if (key == null) {
+ throw new Error(
+ "Key to encrypt cipher must not be null. Use the org key, user key or cipher key.",
+ );
+ }
+
await Promise.all([
this.encryptObjProperty(
model,
diff --git a/libs/common/src/vault/services/collection.service.ts b/libs/common/src/vault/services/collection.service.ts
index e9ad09a4831..09d21390aea 100644
--- a/libs/common/src/vault/services/collection.service.ts
+++ b/libs/common/src/vault/services/collection.service.ts
@@ -1,6 +1,8 @@
import { firstValueFrom, map, Observable } from "rxjs";
import { Jsonify } from "type-fest";
+import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
+
import { CryptoService } from "../../platform/abstractions/crypto.service";
import { I18nService } from "../../platform/abstractions/i18n.service";
import { Utils } from "../../platform/misc/utils";
@@ -61,6 +63,7 @@ export class CollectionService implements CollectionServiceAbstraction {
constructor(
private cryptoService: CryptoService,
+ private encryptService: EncryptService,
private i18nService: I18nService,
protected stateProvider: StateProvider,
) {
@@ -101,7 +104,7 @@ export class CollectionService implements CollectionServiceAbstraction {
collection.organizationId = model.organizationId;
collection.readOnly = model.readOnly;
collection.externalId = model.externalId;
- collection.name = await this.cryptoService.encrypt(model.name, key);
+ collection.name = await this.encryptService.encrypt(model.name, key);
return collection;
}
diff --git a/libs/common/src/vault/services/folder/folder.service.spec.ts b/libs/common/src/vault/services/folder/folder.service.spec.ts
index c27ea7646b0..05e1cdebc93 100644
--- a/libs/common/src/vault/services/folder/folder.service.spec.ts
+++ b/libs/common/src/vault/services/folder/folder.service.spec.ts
@@ -49,7 +49,13 @@ describe("Folder Service", () => {
);
encryptService.decryptToUtf8.mockResolvedValue("DEC");
- folderService = new FolderService(cryptoService, i18nService, cipherService, stateProvider);
+ folderService = new FolderService(
+ cryptoService,
+ encryptService,
+ i18nService,
+ cipherService,
+ stateProvider,
+ );
folderState = stateProvider.activeUser.getFake(FOLDER_ENCRYPTED_FOLDERS);
@@ -62,9 +68,9 @@ describe("Folder Service", () => {
model.id = "2";
model.name = "Test Folder";
- cryptoService.encrypt.mockResolvedValue(new EncString("ENC"));
+ encryptService.encrypt.mockResolvedValue(new EncString("ENC"));
- const result = await folderService.encrypt(model);
+ const result = await folderService.encrypt(model, null);
expect(result).toEqual({
id: "2",
@@ -185,7 +191,7 @@ describe("Folder Service", () => {
beforeEach(() => {
encryptedKey = new EncString("Re-encrypted Folder");
- cryptoService.encrypt.mockResolvedValue(encryptedKey);
+ encryptService.encrypt.mockResolvedValue(encryptedKey);
});
it("returns re-encrypted user folders", async () => {
diff --git a/libs/common/src/vault/services/folder/folder.service.ts b/libs/common/src/vault/services/folder/folder.service.ts
index 0c17d7178b2..2adbc8c6d0e 100644
--- a/libs/common/src/vault/services/folder/folder.service.ts
+++ b/libs/common/src/vault/services/folder/folder.service.ts
@@ -1,5 +1,7 @@
import { Observable, firstValueFrom, map, shareReplay } from "rxjs";
+import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
+
import { CryptoService } from "../../../platform/abstractions/crypto.service";
import { I18nService } from "../../../platform/abstractions/i18n.service";
import { Utils } from "../../../platform/misc/utils";
@@ -25,6 +27,7 @@ export class FolderService implements InternalFolderServiceAbstraction {
constructor(
private cryptoService: CryptoService,
+ private encryptService: EncryptService,
private i18nService: I18nService,
private cipherService: CipherService,
private stateProvider: StateProvider,
@@ -48,10 +51,10 @@ export class FolderService implements InternalFolderServiceAbstraction {
}
// TODO: This should be moved to EncryptService or something
- async encrypt(model: FolderView, key?: SymmetricCryptoKey): Promise {
+ async encrypt(model: FolderView, key: SymmetricCryptoKey): Promise {
const folder = new Folder();
folder.id = model.id;
- folder.name = await this.cryptoService.encrypt(model.name, key);
+ folder.name = await this.encryptService.encrypt(model.name, key);
return folder;
}
diff --git a/libs/importer/spec/bitwarden-password-protected-importer.spec.ts b/libs/importer/spec/bitwarden-password-protected-importer.spec.ts
index d36ce8b9a64..e5100e49900 100644
--- a/libs/importer/spec/bitwarden-password-protected-importer.spec.ts
+++ b/libs/importer/spec/bitwarden-password-protected-importer.spec.ts
@@ -3,6 +3,7 @@ import { mock, MockProxy } from "jest-mock-extended";
import { PinServiceAbstraction } from "@bitwarden/auth/common";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
+import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { KdfType } from "@bitwarden/common/platform/enums";
import { Utils } from "@bitwarden/common/platform/misc/utils";
@@ -19,6 +20,7 @@ import { emptyUnencryptedExport } from "./test-data/bitwarden-json/unencrypted.j
describe("BitwardenPasswordProtectedImporter", () => {
let importer: BitwardenPasswordProtectedImporter;
let cryptoService: MockProxy;
+ let encryptService: MockProxy;
let i18nService: MockProxy;
let cipherService: MockProxy;
let pinService: MockProxy;
@@ -30,6 +32,7 @@ describe("BitwardenPasswordProtectedImporter", () => {
beforeEach(() => {
cryptoService = mock();
+ encryptService = mock();
i18nService = mock();
cipherService = mock();
pinService = mock();
@@ -37,6 +40,7 @@ describe("BitwardenPasswordProtectedImporter", () => {
importer = new BitwardenPasswordProtectedImporter(
cryptoService,
+ encryptService,
i18nService,
cipherService,
pinService,
@@ -91,7 +95,7 @@ describe("BitwardenPasswordProtectedImporter", () => {
});
it("succeeds with default jdoc", async () => {
- cryptoService.decryptToUtf8.mockReturnValue(Promise.resolve(emptyUnencryptedExport));
+ encryptService.decryptToUtf8.mockReturnValue(Promise.resolve(emptyUnencryptedExport));
expect((await importer.parse(JSON.stringify(jDoc))).success).toEqual(true);
});
diff --git a/libs/importer/src/components/import.component.ts b/libs/importer/src/components/import.component.ts
index 8ee882734b3..10a3f5a89ab 100644
--- a/libs/importer/src/components/import.component.ts
+++ b/libs/importer/src/components/import.component.ts
@@ -30,6 +30,7 @@ import { Organization } from "@bitwarden/common/admin-console/models/domain/orga
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { ClientType } from "@bitwarden/common/enums";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
+import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.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";
@@ -89,6 +90,7 @@ const safeProviders: SafeProvider[] = [
I18nService,
CollectionService,
CryptoService,
+ EncryptService,
PinServiceAbstraction,
AccountService,
],
diff --git a/libs/importer/src/importers/bitwarden/bitwarden-json-importer.ts b/libs/importer/src/importers/bitwarden/bitwarden-json-importer.ts
index 2248606814b..9adc8a97819 100644
--- a/libs/importer/src/importers/bitwarden/bitwarden-json-importer.ts
+++ b/libs/importer/src/importers/bitwarden/bitwarden-json-importer.ts
@@ -8,8 +8,10 @@ import {
FolderWithIdExport,
} from "@bitwarden/common/models/export";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
+import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
+import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { OrganizationId } from "@bitwarden/common/types/guid";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view";
@@ -31,6 +33,7 @@ export class BitwardenJsonImporter extends BaseImporter implements Importer {
protected constructor(
protected cryptoService: CryptoService,
+ protected encryptService: EncryptService,
protected i18nService: I18nService,
protected cipherService: CipherService,
protected pinService: PinServiceAbstraction,
@@ -60,11 +63,16 @@ export class BitwardenJsonImporter extends BaseImporter implements Importer {
results: BitwardenEncryptedIndividualJsonExport | BitwardenEncryptedOrgJsonExport,
) {
if (results.encKeyValidation_DO_NOT_EDIT != null) {
- const orgKey = await this.cryptoService.getOrgKey(this.organizationId);
+ let keyForDecryption: SymmetricCryptoKey = await this.cryptoService.getOrgKey(
+ this.organizationId,
+ );
+ if (keyForDecryption == null) {
+ keyForDecryption = await this.cryptoService.getUserKeyWithLegacySupport();
+ }
const encKeyValidation = new EncString(results.encKeyValidation_DO_NOT_EDIT);
- const encKeyValidationDecrypt = await this.cryptoService.decryptToUtf8(
+ const encKeyValidationDecrypt = await this.encryptService.decryptToUtf8(
encKeyValidation,
- orgKey,
+ keyForDecryption,
);
if (encKeyValidationDecrypt === null) {
this.result.success = false;
diff --git a/libs/importer/src/importers/bitwarden/bitwarden-password-protected-importer.ts b/libs/importer/src/importers/bitwarden/bitwarden-password-protected-importer.ts
index a854346bccb..35a0ec0f22c 100644
--- a/libs/importer/src/importers/bitwarden/bitwarden-password-protected-importer.ts
+++ b/libs/importer/src/importers/bitwarden/bitwarden-password-protected-importer.ts
@@ -6,6 +6,7 @@ import {
PBKDF2KdfConfig,
} from "@bitwarden/common/auth/models/domain/kdf-config";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
+import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { KdfType } from "@bitwarden/common/platform/enums";
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
@@ -23,13 +24,14 @@ export class BitwardenPasswordProtectedImporter extends BitwardenJsonImporter im
constructor(
cryptoService: CryptoService,
+ encryptService: EncryptService,
i18nService: I18nService,
cipherService: CipherService,
pinService: PinServiceAbstraction,
accountService: AccountService,
private promptForPassword_callback: () => Promise,
) {
- super(cryptoService, i18nService, cipherService, pinService, accountService);
+ super(cryptoService, encryptService, i18nService, cipherService, pinService, accountService);
}
async parse(data: string): Promise {
@@ -65,7 +67,7 @@ export class BitwardenPasswordProtectedImporter extends BitwardenJsonImporter im
}
const encData = new EncString(parsedData.data);
- const clearTextData = await this.cryptoService.decryptToUtf8(encData, this.key);
+ const clearTextData = await this.encryptService.decryptToUtf8(encData, this.key);
return await super.parse(clearTextData);
}
@@ -86,7 +88,7 @@ export class BitwardenPasswordProtectedImporter extends BitwardenJsonImporter im
const encKeyValidation = new EncString(jdoc.encKeyValidation_DO_NOT_EDIT);
- const encKeyValidationDecrypt = await this.cryptoService.decryptToUtf8(
+ const encKeyValidationDecrypt = await this.encryptService.decryptToUtf8(
encKeyValidation,
this.key,
);
diff --git a/libs/importer/src/services/import.service.spec.ts b/libs/importer/src/services/import.service.spec.ts
index e44c8f6aa98..ef605746e6e 100644
--- a/libs/importer/src/services/import.service.spec.ts
+++ b/libs/importer/src/services/import.service.spec.ts
@@ -3,6 +3,7 @@ import { mock, MockProxy } from "jest-mock-extended";
import { PinServiceAbstraction } from "@bitwarden/auth/common";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
+import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
@@ -27,6 +28,7 @@ describe("ImportService", () => {
let i18nService: MockProxy;
let collectionService: MockProxy;
let cryptoService: MockProxy;
+ let encryptService: MockProxy;
let pinService: MockProxy;
let accountService: MockProxy;
@@ -37,6 +39,7 @@ describe("ImportService", () => {
i18nService = mock();
collectionService = mock();
cryptoService = mock();
+ encryptService = mock();
pinService = mock();
importService = new ImportService(
@@ -46,6 +49,7 @@ describe("ImportService", () => {
i18nService,
collectionService,
cryptoService,
+ encryptService,
pinService,
accountService,
);
diff --git a/libs/importer/src/services/import.service.ts b/libs/importer/src/services/import.service.ts
index 13b77fb5b4e..2295f4f7041 100644
--- a/libs/importer/src/services/import.service.ts
+++ b/libs/importer/src/services/import.service.ts
@@ -7,6 +7,7 @@ import { ImportOrganizationCiphersRequest } from "@bitwarden/common/models/reque
import { KvpRequest } from "@bitwarden/common/models/request/kvp.request";
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
+import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
@@ -104,6 +105,7 @@ export class ImportService implements ImportServiceAbstraction {
private i18nService: I18nService,
private collectionService: CollectionService,
private cryptoService: CryptoService,
+ private encryptService: EncryptService,
private pinService: PinServiceAbstraction,
private accountService: AccountService,
) {}
@@ -207,6 +209,7 @@ export class ImportService implements ImportServiceAbstraction {
case "bitwardenpasswordprotected":
return new BitwardenPasswordProtectedImporter(
this.cryptoService,
+ this.encryptService,
this.i18nService,
this.cipherService,
this.pinService,
@@ -344,9 +347,10 @@ export class ImportService implements ImportServiceAbstraction {
const c = await this.cipherService.encrypt(importResult.ciphers[i], activeUserId);
request.ciphers.push(new CipherRequest(c));
}
+ const userKey = await this.cryptoService.getUserKeyWithLegacySupport(activeUserId);
if (importResult.folders != null) {
for (let i = 0; i < importResult.folders.length; i++) {
- const f = await this.folderService.encrypt(importResult.folders[i]);
+ const f = await this.folderService.encrypt(importResult.folders[i], userKey);
request.folders.push(new FolderWithIdRequest(f));
}
}
diff --git a/libs/tools/export/vault-export/vault-export-core/src/services/base-vault-export.service.ts b/libs/tools/export/vault-export/vault-export-core/src/services/base-vault-export.service.ts
index a494885698e..76b008be620 100644
--- a/libs/tools/export/vault-export/vault-export-core/src/services/base-vault-export.service.ts
+++ b/libs/tools/export/vault-export/vault-export-core/src/services/base-vault-export.service.ts
@@ -2,7 +2,7 @@ import { PinServiceAbstraction } from "@bitwarden/auth/common";
import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
import { KdfConfig } from "@bitwarden/common/auth/models/domain/kdf-config";
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
-import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
+import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { KdfType } from "@bitwarden/common/platform/enums";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { CipherType } from "@bitwarden/common/vault/enums";
@@ -12,7 +12,7 @@ import { BitwardenCsvExportType, BitwardenPasswordProtectedFileFormat } from "..
export class BaseVaultExportService {
constructor(
protected pinService: PinServiceAbstraction,
- protected cryptoService: CryptoService,
+ protected encryptService: EncryptService,
private cryptoFunctionService: CryptoFunctionService,
private kdfConfigService: KdfConfigService,
) {}
@@ -23,8 +23,8 @@ export class BaseVaultExportService {
const salt = Utils.fromBufferToB64(await this.cryptoFunctionService.randomBytes(16));
const key = await this.pinService.makePinKey(password, salt, kdfConfig);
- const encKeyValidation = await this.cryptoService.encrypt(Utils.newGuid(), key);
- const encText = await this.cryptoService.encrypt(clearText, key);
+ const encKeyValidation = await this.encryptService.encrypt(Utils.newGuid(), key);
+ const encText = await this.encryptService.encrypt(clearText, key);
const jsonDoc: BitwardenPasswordProtectedFileFormat = {
encrypted: true,
diff --git a/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.spec.ts b/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.spec.ts
index 44df18116de..1a66fe92256 100644
--- a/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.spec.ts
+++ b/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.spec.ts
@@ -1,6 +1,8 @@
import { mock, MockProxy } from "jest-mock-extended";
+import { BehaviorSubject } from "rxjs";
import { PinServiceAbstraction } from "@bitwarden/auth/common";
+import { AccountInfo, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
import {
DEFAULT_KDF_CONFIG,
@@ -9,9 +11,11 @@ import {
import { CipherWithIdExport } from "@bitwarden/common/models/export/cipher-with-ids.export";
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
+import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { KdfType } from "@bitwarden/common/platform/enums";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { EncryptedString, EncString } from "@bitwarden/common/platform/models/domain/enc-string";
+import { UserId } from "@bitwarden/common/types/guid";
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";
@@ -149,7 +153,9 @@ describe("VaultExportService", () => {
let pinService: MockProxy;
let folderService: MockProxy;
let cryptoService: MockProxy;
+ let encryptService: MockProxy;
let kdfConfigService: MockProxy;
+ let accountService: MockProxy;
beforeEach(() => {
cryptoFunctionService = mock();
@@ -157,20 +163,35 @@ describe("VaultExportService", () => {
pinService = mock();
folderService = mock();
cryptoService = mock();
+ encryptService = mock();
kdfConfigService = mock();
+ accountService = mock();
+
+ cryptoService.userKey$.mockReturnValue(new BehaviorSubject("mockOriginalUserKey" as any));
+
+ const userId = "" as UserId;
+ const accountInfo: AccountInfo = {
+ email: "",
+ emailVerified: true,
+ name: undefined,
+ };
+ const activeAccount = { id: userId, ...accountInfo };
+ accountService.activeAccount$ = new BehaviorSubject(activeAccount);
folderService.getAllDecryptedFromState.mockResolvedValue(UserFolderViews);
folderService.getAllFromState.mockResolvedValue(UserFolders);
kdfConfigService.getKdfConfig.mockResolvedValue(DEFAULT_KDF_CONFIG);
- cryptoService.encrypt.mockResolvedValue(new EncString("encrypted"));
+ encryptService.encrypt.mockResolvedValue(new EncString("encrypted"));
exportService = new IndividualVaultExportService(
folderService,
cipherService,
pinService,
cryptoService,
+ encryptService,
cryptoFunctionService,
kdfConfigService,
+ accountService,
);
});
@@ -250,7 +271,7 @@ describe("VaultExportService", () => {
});
it("has a mac property", async () => {
- cryptoService.encrypt.mockResolvedValue(mac);
+ encryptService.encrypt.mockResolvedValue(mac);
exportString = await exportService.getPasswordProtectedExport(password);
exportObject = JSON.parse(exportString);
@@ -258,7 +279,7 @@ describe("VaultExportService", () => {
});
it("has data property", async () => {
- cryptoService.encrypt.mockResolvedValue(data);
+ encryptService.encrypt.mockResolvedValue(data);
exportString = await exportService.getPasswordProtectedExport(password);
exportObject = JSON.parse(exportString);
diff --git a/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.ts b/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.ts
index 3da92ef16b5..d6d37b28ac7 100644
--- a/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.ts
+++ b/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.ts
@@ -1,10 +1,13 @@
import * as papa from "papaparse";
+import { firstValueFrom, map } from "rxjs";
import { PinServiceAbstraction } from "@bitwarden/auth/common";
+import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
import { CipherWithIdExport, FolderWithIdExport } from "@bitwarden/common/models/export";
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
+import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
@@ -32,11 +35,13 @@ export class IndividualVaultExportService
private folderService: FolderService,
private cipherService: CipherService,
pinService: PinServiceAbstraction,
- cryptoService: CryptoService,
+ private cryptoService: CryptoService,
+ encryptService: EncryptService,
cryptoFunctionService: CryptoFunctionService,
kdfConfigService: KdfConfigService,
+ private accountService: AccountService,
) {
- super(pinService, cryptoService, cryptoFunctionService, kdfConfigService);
+ super(pinService, encryptService, cryptoFunctionService, kdfConfigService);
}
async getExport(format: ExportFormat = "csv"): Promise {
@@ -96,7 +101,11 @@ export class IndividualVaultExportService
await Promise.all(promises);
- const encKeyValidation = await this.cryptoService.encrypt(Utils.newGuid());
+ const activeUserId = await firstValueFrom(
+ this.accountService.activeAccount$.pipe(map((a) => a?.id)),
+ );
+ const userKey = await this.cryptoService.getUserKeyWithLegacySupport(activeUserId);
+ const encKeyValidation = await this.encryptService.encrypt(Utils.newGuid(), userKey);
const jsonDoc: BitwardenEncryptedIndividualJsonExport = {
encrypted: true,
diff --git a/libs/tools/export/vault-export/vault-export-core/src/services/org-vault-export.service.ts b/libs/tools/export/vault-export/vault-export-core/src/services/org-vault-export.service.ts
index 0c3e94178f6..9fc1f20b832 100644
--- a/libs/tools/export/vault-export/vault-export-core/src/services/org-vault-export.service.ts
+++ b/libs/tools/export/vault-export/vault-export-core/src/services/org-vault-export.service.ts
@@ -8,6 +8,7 @@ import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config
import { CipherWithIdExport, CollectionWithIdExport } from "@bitwarden/common/models/export";
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
+import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { OrganizationId } from "@bitwarden/common/types/guid";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
@@ -39,13 +40,14 @@ export class OrganizationVaultExportService
private cipherService: CipherService,
private apiService: ApiService,
pinService: PinServiceAbstraction,
- cryptoService: CryptoService,
+ private cryptoService: CryptoService,
+ encryptService: EncryptService,
cryptoFunctionService: CryptoFunctionService,
private collectionService: CollectionService,
kdfConfigService: KdfConfigService,
private accountService: AccountService,
) {
- super(pinService, cryptoService, cryptoFunctionService, kdfConfigService);
+ super(pinService, encryptService, cryptoFunctionService, kdfConfigService);
}
async getPasswordProtectedExport(
@@ -242,7 +244,7 @@ export class OrganizationVaultExportService
ciphers: Cipher[],
): Promise {
const orgKey = await this.cryptoService.getOrgKey(organizationId);
- const encKeyValidation = await this.cryptoService.encrypt(Utils.newGuid(), orgKey);
+ const encKeyValidation = await this.encryptService.encrypt(Utils.newGuid(), orgKey);
const jsonDoc: BitwardenEncryptedOrgJsonExport = {
encrypted: true,
diff --git a/libs/tools/export/vault-export/vault-export-core/src/services/vault-export.service.spec.ts b/libs/tools/export/vault-export/vault-export-core/src/services/vault-export.service.spec.ts
index 44df18116de..7e93c78fc51 100644
--- a/libs/tools/export/vault-export/vault-export-core/src/services/vault-export.service.spec.ts
+++ b/libs/tools/export/vault-export/vault-export-core/src/services/vault-export.service.spec.ts
@@ -1,6 +1,8 @@
import { mock, MockProxy } from "jest-mock-extended";
+import { BehaviorSubject } from "rxjs";
import { PinServiceAbstraction } from "@bitwarden/auth/common";
+import { AccountInfo, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
import {
DEFAULT_KDF_CONFIG,
@@ -9,9 +11,11 @@ import {
import { CipherWithIdExport } from "@bitwarden/common/models/export/cipher-with-ids.export";
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
+import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { KdfType } from "@bitwarden/common/platform/enums";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { EncryptedString, EncString } from "@bitwarden/common/platform/models/domain/enc-string";
+import { UserId } from "@bitwarden/common/types/guid";
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";
@@ -149,6 +153,8 @@ describe("VaultExportService", () => {
let pinService: MockProxy;
let folderService: MockProxy;
let cryptoService: MockProxy;
+ let encryptService: MockProxy;
+ let accountService: MockProxy;
let kdfConfigService: MockProxy;
beforeEach(() => {
@@ -157,20 +163,34 @@ describe("VaultExportService", () => {
pinService = mock();
folderService = mock();
cryptoService = mock();
+ encryptService = mock();
+ accountService = mock();
+
kdfConfigService = mock();
folderService.getAllDecryptedFromState.mockResolvedValue(UserFolderViews);
folderService.getAllFromState.mockResolvedValue(UserFolders);
kdfConfigService.getKdfConfig.mockResolvedValue(DEFAULT_KDF_CONFIG);
- cryptoService.encrypt.mockResolvedValue(new EncString("encrypted"));
+ encryptService.encrypt.mockResolvedValue(new EncString("encrypted"));
+ cryptoService.userKey$.mockReturnValue(new BehaviorSubject("mockOriginalUserKey" as any));
+ const userId = "" as UserId;
+ const accountInfo: AccountInfo = {
+ email: "",
+ emailVerified: true,
+ name: undefined,
+ };
+ const activeAccount = { id: userId, ...accountInfo };
+ accountService.activeAccount$ = new BehaviorSubject(activeAccount);
exportService = new IndividualVaultExportService(
folderService,
cipherService,
pinService,
cryptoService,
+ encryptService,
cryptoFunctionService,
kdfConfigService,
+ accountService,
);
});
@@ -250,7 +270,7 @@ describe("VaultExportService", () => {
});
it("has a mac property", async () => {
- cryptoService.encrypt.mockResolvedValue(mac);
+ encryptService.encrypt.mockResolvedValue(mac);
exportString = await exportService.getPasswordProtectedExport(password);
exportObject = JSON.parse(exportString);
@@ -258,7 +278,7 @@ describe("VaultExportService", () => {
});
it("has data property", async () => {
- cryptoService.encrypt.mockResolvedValue(data);
+ encryptService.encrypt.mockResolvedValue(data);
exportString = await exportService.getPasswordProtectedExport(password);
exportObject = JSON.parse(exportString);
From 8507097fe7e50a80288a0a55b4e2039e23adce63 Mon Sep 17 00:00:00 2001
From: Jordan Aasen <166539328+jaasen-livefront@users.noreply.github.com>
Date: Tue, 24 Sep 2024 02:45:08 -0700
Subject: [PATCH 10/23] fix send password input (#11208)
---
.../send-form/components/options/send-options.component.ts | 2 +-
.../send-ui/src/send-form/components/send-form.component.ts | 5 +++++
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/libs/tools/send/send-ui/src/send-form/components/options/send-options.component.ts b/libs/tools/send/send-ui/src/send-form/components/options/send-options.component.ts
index ab4ffaa9dd0..89ab9d19ba2 100644
--- a/libs/tools/send/send-ui/src/send-form/components/options/send-options.component.ts
+++ b/libs/tools/send/send-ui/src/send-form/components/options/send-options.component.ts
@@ -102,7 +102,7 @@ export class SendOptionsComponent implements OnInit {
this.sendOptionsForm.patchValue({
maxAccessCount: this.sendFormContainer.originalSendView.maxAccessCount,
accessCount: this.sendFormContainer.originalSendView.accessCount,
- password: this.sendFormContainer.originalSendView.password,
+ password: null,
hideEmail: this.sendFormContainer.originalSendView.hideEmail,
notes: this.sendFormContainer.originalSendView.notes,
});
diff --git a/libs/tools/send/send-ui/src/send-form/components/send-form.component.ts b/libs/tools/send/send-ui/src/send-form/components/send-form.component.ts
index b265b644df4..1d93804e11f 100644
--- a/libs/tools/send/send-ui/src/send-form/components/send-form.component.ts
+++ b/libs/tools/send/send-ui/src/send-form/components/send-form.component.ts
@@ -16,6 +16,7 @@ import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { FormBuilder, ReactiveFormsModule } from "@angular/forms";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
+import { Utils } from "@bitwarden/common/platform/misc/utils";
import { SendType } from "@bitwarden/common/tools/send/enums/send-type";
import { SendView } from "@bitwarden/common/tools/send/models/view/send.view";
import {
@@ -199,6 +200,10 @@ export class SendFormComponent implements AfterViewInit, OnInit, OnChanges, Send
return;
}
+ if (Utils.isNullOrWhitespace(this.updatedSendView.password)) {
+ this.updatedSendView.password = null;
+ }
+
await this.addEditFormService.saveSend(this.updatedSendView, this.file, this.config);
this.toastService.showToast({
From c8084cc4e3d5bb2a97b9f8424a00d130e36a07dd Mon Sep 17 00:00:00 2001
From: Alex Morask <144709477+amorask-bitwarden@users.noreply.github.com>
Date: Tue, 24 Sep 2024 09:26:39 -0400
Subject: [PATCH 11/23] Fixed free organization upgrade after stripe sources
deprecation (#11205)
---
.../organization-plans.component.ts | 37 +++++++++++--------
1 file changed, 22 insertions(+), 15 deletions(-)
diff --git a/apps/web/src/app/billing/organizations/organization-plans.component.ts b/apps/web/src/app/billing/organizations/organization-plans.component.ts
index 924b128a50a..9ee56b0bcea 100644
--- a/apps/web/src/app/billing/organizations/organization-plans.component.ts
+++ b/apps/web/src/app/billing/organizations/organization-plans.component.ts
@@ -23,8 +23,11 @@ import { OrganizationKeysRequest } from "@bitwarden/common/admin-console/models/
import { OrganizationUpgradeRequest } from "@bitwarden/common/admin-console/models/request/organization-upgrade.request";
import { ProviderOrganizationCreateRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-organization-create.request";
import { ProviderResponse } from "@bitwarden/common/admin-console/models/response/provider/provider.response";
+import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions";
import { PaymentMethodType, PlanType, ProductTierType } from "@bitwarden/common/billing/enums";
+import { ExpandedTaxInfoUpdateRequest } from "@bitwarden/common/billing/models/request/expanded-tax-info-update.request";
import { PaymentRequest } from "@bitwarden/common/billing/models/request/payment.request";
+import { UpdatePaymentMethodRequest } from "@bitwarden/common/billing/models/request/update-payment-method.request";
import { BillingResponse } from "@bitwarden/common/billing/models/response/billing.response";
import { OrganizationSubscriptionResponse } from "@bitwarden/common/billing/models/response/organization-subscription.response";
import { PlanResponse } from "@bitwarden/common/billing/models/response/plan.response";
@@ -33,7 +36,6 @@ import { ConfigService } from "@bitwarden/common/platform/abstractions/config/co
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
-import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
@@ -153,15 +155,15 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
private syncService: SyncService,
private policyService: PolicyService,
private organizationService: OrganizationService,
- private logService: LogService,
private messagingService: MessagingService,
private formBuilder: FormBuilder,
private organizationApiService: OrganizationApiServiceAbstraction,
private providerApiService: ProviderApiServiceAbstraction,
private toastService: ToastService,
private configService: ConfigService,
+ private billingApiService: BillingApiServiceAbstraction,
) {
- this.selfHosted = platformUtilsService.isSelfHost();
+ this.selfHosted = this.platformUtilsService.isSelfHost();
}
async ngOnInit() {
@@ -660,21 +662,26 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
this.buildSecretsManagerRequest(request);
if (this.upgradeRequiresPaymentMethod) {
- let type: PaymentMethodType;
- let token: string;
-
if (this.deprecateStripeSourcesAPI) {
- ({ type, token } = await this.paymentV2Component.tokenize());
+ const updatePaymentMethodRequest = new UpdatePaymentMethodRequest();
+ updatePaymentMethodRequest.paymentSource = await this.paymentV2Component.tokenize();
+ const expandedTaxInfoUpdateRequest = new ExpandedTaxInfoUpdateRequest();
+ expandedTaxInfoUpdateRequest.country = this.taxComponent.country;
+ expandedTaxInfoUpdateRequest.postalCode = this.taxComponent.postalCode;
+ updatePaymentMethodRequest.taxInformation = expandedTaxInfoUpdateRequest;
+ await this.billingApiService.updateOrganizationPaymentMethod(
+ this.organizationId,
+ updatePaymentMethodRequest,
+ );
} else {
- [token, type] = await this.paymentComponent.createPaymentToken();
+ const [paymentToken, paymentMethodType] = await this.paymentComponent.createPaymentToken();
+ const paymentRequest = new PaymentRequest();
+ paymentRequest.paymentToken = paymentToken;
+ paymentRequest.paymentMethodType = paymentMethodType;
+ paymentRequest.country = this.taxComponent.taxFormGroup?.value.country;
+ paymentRequest.postalCode = this.taxComponent.taxFormGroup?.value.postalCode;
+ await this.organizationApiService.updatePayment(this.organizationId, paymentRequest);
}
-
- const paymentRequest = new PaymentRequest();
- paymentRequest.paymentToken = token;
- paymentRequest.paymentMethodType = type;
- paymentRequest.country = this.taxComponent.taxFormGroup?.value.country;
- paymentRequest.postalCode = this.taxComponent.taxFormGroup?.value.postalCode;
- await this.organizationApiService.updatePayment(this.organizationId, paymentRequest);
}
// Backfill pub/priv key if necessary
From e88e231d48ef3f0040833206308c8a739dbe9f4d Mon Sep 17 00:00:00 2001
From: Jonathan Prusik
Date: Tue, 24 Sep 2024 10:36:44 -0400
Subject: [PATCH 12/23] [PM-11588] Bugfix - parse user input value for combined
expiry date when creating/adding a card cipher (#11103)
* simplify logic and fix some pattern-matching bugs
* add first pass at parsing combined expiry year and month from user input
* clean up code
* fix broken three-digit parsing case
* fix case where splitCombinedDateValues returns empty strings when the input is only a delimiter
* fix incorrect expectation of falsy negative integers
* clean up code
* split out logic from parseYearMonthExpiry
* move utils from vault to autofill
---
.../autofill/background/overlay.background.ts | 15 +-
.../autofill/services/autofill-constants.ts | 2 -
.../src/autofill/services/autofill.service.ts | 15 +-
.../components/vault/add-edit.component.ts | 2 +-
.../individual-vault/add-edit.component.ts | 2 +-
.../vault/components/add-edit.component.ts | 2 +-
libs/common/src/autofill/constants/index.ts | 2 +
.../src/autofill/constants/match-patterns.ts | 26 ++
libs/common/src/autofill/utils.spec.ts | 284 ++++++++++++++++
libs/common/src/autofill/utils.ts | 307 ++++++++++++++++++
.../common/src/vault/models/view/card.view.ts | 2 +-
libs/common/src/vault/utils.spec.ts | 122 -------
libs/common/src/vault/utils.ts | 83 -----
libs/importer/src/importers/base-importer.ts | 2 +-
.../card-details-section.component.ts | 2 +-
.../src/cipher-view/cipher-view.component.ts | 2 +-
16 files changed, 648 insertions(+), 222 deletions(-)
create mode 100644 libs/common/src/autofill/constants/match-patterns.ts
create mode 100644 libs/common/src/autofill/utils.spec.ts
create mode 100644 libs/common/src/autofill/utils.ts
delete mode 100644 libs/common/src/vault/utils.spec.ts
delete mode 100644 libs/common/src/vault/utils.ts
diff --git a/apps/browser/src/autofill/background/overlay.background.ts b/apps/browser/src/autofill/background/overlay.background.ts
index 0047d1de28e..c8d250df509 100644
--- a/apps/browser/src/autofill/background/overlay.background.ts
+++ b/apps/browser/src/autofill/background/overlay.background.ts
@@ -20,6 +20,7 @@ import {
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
import { InlineMenuVisibilitySetting } from "@bitwarden/common/autofill/types";
+import { parseYearMonthExpiry } from "@bitwarden/common/autofill/utils";
import { NeverDomains } from "@bitwarden/common/models/domain/domain-service";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import {
@@ -1898,11 +1899,21 @@ export class OverlayBackground implements OverlayBackgroundInterface {
const cardView = new CardView();
cardView.cardholderName = card.cardholderName || "";
cardView.number = card.number || "";
- cardView.expMonth = card.expirationMonth || "";
- cardView.expYear = card.expirationYear || "";
cardView.code = card.cvv || "";
cardView.brand = card.number ? CardView.getCardBrandByPatterns(card.number) : "";
+ // If there's a combined expiration date value and no individual month or year values,
+ // try to parse them from the combined value
+ if (card.expirationDate && !card.expirationMonth && !card.expirationYear) {
+ const [parsedYear, parsedMonth] = parseYearMonthExpiry(card.expirationDate);
+
+ cardView.expMonth = parsedMonth || "";
+ cardView.expYear = parsedYear || "";
+ } else {
+ cardView.expMonth = card.expirationMonth || "";
+ cardView.expYear = card.expirationYear || "";
+ }
+
const cipherView = new CipherView();
cipherView.name = "";
cipherView.folderId = null;
diff --git a/apps/browser/src/autofill/services/autofill-constants.ts b/apps/browser/src/autofill/services/autofill-constants.ts
index 9cf2b6848c6..c379daaf2d8 100644
--- a/apps/browser/src/autofill/services/autofill-constants.ts
+++ b/apps/browser/src/autofill/services/autofill-constants.ts
@@ -300,8 +300,6 @@ export class CreditCardAutoFillConstants {
"cb-type",
];
- static readonly CardExpiryDateDelimiters: string[] = ["/", "-", ".", " "];
-
// Note, these are expressions of user-guidance for the expected expiry date format to be used
static readonly CardExpiryDateFormats: CardExpiryDateFormat[] = [
// English
diff --git a/apps/browser/src/autofill/services/autofill.service.ts b/apps/browser/src/autofill/services/autofill.service.ts
index 49d00624f34..5d9bfa9f9d4 100644
--- a/apps/browser/src/autofill/services/autofill.service.ts
+++ b/apps/browser/src/autofill/services/autofill.service.ts
@@ -6,11 +6,15 @@ import { AccountInfo, AccountService } from "@bitwarden/common/auth/abstractions
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
-import { AutofillOverlayVisibility } from "@bitwarden/common/autofill/constants";
+import {
+ AutofillOverlayVisibility,
+ CardExpiryDateDelimiters,
+} from "@bitwarden/common/autofill/constants";
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
import { UserNotificationSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/user-notification-settings.service";
import { InlineMenuVisibilitySetting } from "@bitwarden/common/autofill/types";
+import { normalizeExpiryYearFormat } from "@bitwarden/common/autofill/utils";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { EventType } from "@bitwarden/common/enums";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
@@ -30,7 +34,6 @@ import { CardView } from "@bitwarden/common/vault/models/view/card.view";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { FieldView } from "@bitwarden/common/vault/models/view/field.view";
import { IdentityView } from "@bitwarden/common/vault/models/view/identity.view";
-import { normalizeExpiryYearFormat } from "@bitwarden/common/vault/utils";
import { BrowserApi } from "../../platform/browser/browser-api";
import { ScriptInjectorService } from "../../platform/services/abstractions/script-injector.service";
@@ -1397,8 +1400,7 @@ export default class AutofillService implements AutofillServiceInterface {
if (expectedExpiryDateFormat) {
const { Month, MonthShort, Year } = expiryDateFormatPatterns;
- const expiryDateDelimitersPattern =
- "\\" + CreditCardAutoFillConstants.CardExpiryDateDelimiters.join("\\");
+ const expiryDateDelimitersPattern = "\\" + CardExpiryDateDelimiters.join("\\");
// assign the delimiter from the expected format string
delimiter =
@@ -1450,8 +1452,7 @@ export default class AutofillService implements AutofillServiceInterface {
let expectedDateFormat = null;
let dateFormatPatterns = null;
- const expiryDateDelimitersPattern =
- "\\" + CreditCardAutoFillConstants.CardExpiryDateDelimiters.join("\\");
+ const expiryDateDelimitersPattern = "\\" + CardExpiryDateDelimiters.join("\\");
CreditCardAutoFillConstants.CardExpiryDateFormats.find((dateFormat) => {
dateFormatPatterns = dateFormat;
@@ -1489,6 +1490,8 @@ export default class AutofillService implements AutofillServiceInterface {
return false;
});
});
+ // @TODO if expectedDateFormat is still null, and there is a `pattern` attribute, cycle
+ // through generated formatted values, checking against the provided regex pattern
return [expectedDateFormat, dateFormatPatterns];
}
diff --git a/apps/browser/src/vault/popup/components/vault/add-edit.component.ts b/apps/browser/src/vault/popup/components/vault/add-edit.component.ts
index 02654f37efe..de8e5615e2f 100644
--- a/apps/browser/src/vault/popup/components/vault/add-edit.component.ts
+++ b/apps/browser/src/vault/popup/components/vault/add-edit.component.ts
@@ -12,6 +12,7 @@ import { OrganizationService } from "@bitwarden/common/admin-console/abstraction
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
+import { normalizeExpiryYearFormat } from "@bitwarden/common/autofill/utils";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@@ -23,7 +24,6 @@ import { CollectionService } from "@bitwarden/common/vault/abstractions/collecti
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
import { CipherType } from "@bitwarden/common/vault/enums";
import { LoginUriView } from "@bitwarden/common/vault/models/view/login-uri.view";
-import { normalizeExpiryYearFormat } from "@bitwarden/common/vault/utils";
import { DialogService } from "@bitwarden/components";
import { PasswordRepromptService } from "@bitwarden/vault";
diff --git a/apps/web/src/app/vault/individual-vault/add-edit.component.ts b/apps/web/src/app/vault/individual-vault/add-edit.component.ts
index d1b51b611f5..9826d9f2f5a 100644
--- a/apps/web/src/app/vault/individual-vault/add-edit.component.ts
+++ b/apps/web/src/app/vault/individual-vault/add-edit.component.ts
@@ -8,6 +8,7 @@ import { EventCollectionService } from "@bitwarden/common/abstractions/event/eve
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
+import { isCardExpired } from "@bitwarden/common/autofill/utils";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { ProductTierType } from "@bitwarden/common/billing/enums";
import { EventType } from "@bitwarden/common/enums";
@@ -24,7 +25,6 @@ import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folde
import { TotpService } from "@bitwarden/common/vault/abstractions/totp.service";
import { CipherType } from "@bitwarden/common/vault/enums";
import { Launchable } from "@bitwarden/common/vault/interfaces/launchable";
-import { isCardExpired } from "@bitwarden/common/vault/utils";
import { DialogService } from "@bitwarden/components";
import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
import { PasswordRepromptService } from "@bitwarden/vault";
diff --git a/libs/angular/src/vault/components/add-edit.component.ts b/libs/angular/src/vault/components/add-edit.component.ts
index 255d553a3ec..21a7b35ac51 100644
--- a/libs/angular/src/vault/components/add-edit.component.ts
+++ b/libs/angular/src/vault/components/add-edit.component.ts
@@ -12,6 +12,7 @@ import { PolicyService } from "@bitwarden/common/admin-console/abstractions/poli
import { OrganizationUserStatusType, PolicyType } from "@bitwarden/common/admin-console/enums";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
+import { normalizeExpiryYearFormat } from "@bitwarden/common/autofill/utils";
import { EventType } from "@bitwarden/common/enums";
import { UriMatchStrategy } from "@bitwarden/common/models/domain/domain-service";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
@@ -36,7 +37,6 @@ import { IdentityView } from "@bitwarden/common/vault/models/view/identity.view"
import { LoginUriView } from "@bitwarden/common/vault/models/view/login-uri.view";
import { LoginView } from "@bitwarden/common/vault/models/view/login.view";
import { SecureNoteView } from "@bitwarden/common/vault/models/view/secure-note.view";
-import { normalizeExpiryYearFormat } from "@bitwarden/common/vault/utils";
import { DialogService } from "@bitwarden/components";
import { PasswordRepromptService } from "@bitwarden/vault";
diff --git a/libs/common/src/autofill/constants/index.ts b/libs/common/src/autofill/constants/index.ts
index 15005691d29..4ccec81a447 100644
--- a/libs/common/src/autofill/constants/index.ts
+++ b/libs/common/src/autofill/constants/index.ts
@@ -109,3 +109,5 @@ export type ExtensionCommandType = (typeof ExtensionCommand)[keyof typeof Extens
export const CLEAR_NOTIFICATION_LOGIN_DATA_DURATION = 60 * 1000; // 1 minute
export const MAX_DEEP_QUERY_RECURSION_DEPTH = 4;
+
+export * from "./match-patterns";
diff --git a/libs/common/src/autofill/constants/match-patterns.ts b/libs/common/src/autofill/constants/match-patterns.ts
new file mode 100644
index 00000000000..f756537d28d
--- /dev/null
+++ b/libs/common/src/autofill/constants/match-patterns.ts
@@ -0,0 +1,26 @@
+export const CardExpiryDateDelimiters: string[] = ["/", "-", ".", " "];
+
+// `CardExpiryDateDelimiters` is not intended solely for regex consumption,
+// so we need to format it here
+export const ExpiryDateDelimitersPattern =
+ "\\" +
+ CardExpiryDateDelimiters.join("\\")
+ // replace space character with the regex whitespace character class
+ .replace(" ", "s");
+
+export const MonthPattern = "(([1]{1}[0-2]{1})|(0?[1-9]{1}))";
+
+// Because we're dealing with expiry dates, we assume the year will be in current or next century (as of 2024)
+export const ExpiryFullYearPattern = "2[0-1]{1}\\d{2}";
+
+export const DelimiterPatternExpression = new RegExp(`[${ExpiryDateDelimitersPattern}]`, "g");
+
+export const IrrelevantExpiryCharactersPatternExpression = new RegExp(
+ // "nor digits" to ensure numbers are removed from guidance pattern, which aren't covered by ^\w
+ `[^\\d${ExpiryDateDelimitersPattern}]`,
+ "g",
+);
+
+export const MonthPatternExpression = new RegExp(`^${MonthPattern}$`);
+
+export const ExpiryFullYearPatternExpression = new RegExp(`^${ExpiryFullYearPattern}$`);
diff --git a/libs/common/src/autofill/utils.spec.ts b/libs/common/src/autofill/utils.spec.ts
new file mode 100644
index 00000000000..b09dc723b8e
--- /dev/null
+++ b/libs/common/src/autofill/utils.spec.ts
@@ -0,0 +1,284 @@
+import {
+ normalizeExpiryYearFormat,
+ isCardExpired,
+ parseYearMonthExpiry,
+} from "@bitwarden/common/autofill/utils";
+import { CardView } from "@bitwarden/common/vault/models/view/card.view";
+
+function getExpiryYearValueFormats(currentCentury: string) {
+ return [
+ [-12, `${currentCentury}12`],
+ [0, `${currentCentury}00`],
+ [2043, "2043"], // valid year with a length of four should be taken directly
+ [24, `${currentCentury}24`],
+ [3054, "3054"], // valid year with a length of four should be taken directly
+ [31423524543, `${currentCentury}43`],
+ [4, `${currentCentury}04`],
+ [null, null],
+ [undefined, null],
+ ["-12", `${currentCentury}12`],
+ ["", null],
+ ["0", `${currentCentury}00`],
+ ["00", `${currentCentury}00`],
+ ["000", `${currentCentury}00`],
+ ["0000", `${currentCentury}00`],
+ ["00000", `${currentCentury}00`],
+ ["0234234", `${currentCentury}34`],
+ ["04", `${currentCentury}04`],
+ ["2043", "2043"], // valid year with a length of four should be taken directly
+ ["24", `${currentCentury}24`],
+ ["3054", "3054"], // valid year with a length of four should be taken directly
+ ["31423524543", `${currentCentury}43`],
+ ["4", `${currentCentury}04`],
+ ["aaaa", null],
+ ["adgshsfhjsdrtyhsrth", null],
+ ["agdredg42grg35grrr. ea3534@#^145345ag$%^ -_#$rdg ", `${currentCentury}45`],
+ ];
+}
+
+describe("normalizeExpiryYearFormat", () => {
+ const currentCentury = `${new Date().getFullYear()}`.slice(0, 2);
+
+ const expiryYearValueFormats = getExpiryYearValueFormats(currentCentury);
+
+ expiryYearValueFormats.forEach(([inputValue, expectedValue]) => {
+ it(`should return '${expectedValue}' when '${inputValue}' is passed`, () => {
+ const formattedValue = normalizeExpiryYearFormat(inputValue);
+
+ expect(formattedValue).toEqual(expectedValue);
+ });
+ });
+
+ describe("in the year 3107", () => {
+ const theDistantFuture = new Date(Date.UTC(3107, 1, 1));
+ jest.spyOn(Date, "now").mockReturnValue(theDistantFuture.valueOf());
+
+ beforeAll(() => {
+ jest.useFakeTimers({ advanceTimers: true });
+ jest.setSystemTime(theDistantFuture);
+ });
+
+ afterAll(() => {
+ jest.useRealTimers();
+ });
+
+ const currentCentury = `${new Date(Date.now()).getFullYear()}`.slice(0, 2);
+ expect(currentCentury).toBe("31");
+
+ const expiryYearValueFormats = getExpiryYearValueFormats(currentCentury);
+
+ expiryYearValueFormats.forEach(([inputValue, expectedValue]) => {
+ it(`should return '${expectedValue}' when '${inputValue}' is passed`, () => {
+ const formattedValue = normalizeExpiryYearFormat(inputValue);
+
+ expect(formattedValue).toEqual(expectedValue);
+ });
+ });
+ jest.clearAllTimers();
+ });
+});
+
+function getCardExpiryDateValues() {
+ const currentDate = new Date();
+
+ const currentYear = currentDate.getFullYear();
+
+ // `Date` months are zero-indexed, our expiry date month inputs are one-indexed
+ const currentMonth = currentDate.getMonth() + 1;
+
+ return [
+ [null, null, false], // no month, no year
+ [undefined, undefined, false], // no month, no year, invalid values
+ ["", "", false], // no month, no year, invalid values
+ ["12", "agdredg42grg35grrr. ea3534@#^145345ag$%^ -_#$rdg ", false], // invalid values
+ ["0", `${currentYear}`, true], // invalid month
+ ["0", `${currentYear - 1}`, true], // invalid 0 month
+ ["00", `${currentYear + 1}`, false], // invalid 0 month
+ [`${currentMonth}`, "0000", true], // current month, in the year 2000
+ [null, `${currentYear}`.slice(-2), false], // no month, this year
+ [null, `${currentYear - 1}`.slice(-2), true], // no month, last year
+ ["1", null, false], // no year, January
+ ["1", `${currentYear - 1}`, true], // January last year
+ ["13", `${currentYear}`, false], // 12 + 1 is Feb. in the next year (Date is zero-indexed)
+ [`${currentMonth + 36}`, `${currentYear - 1}`, true], // even though the month value would put the date 3 years into the future when calculated with `Date`, an explicit year in the past indicates the card is expired
+ [`${currentMonth}`, `${currentYear}`, false], // this year, this month (not expired until the month is over)
+ [`${currentMonth}`, `${currentYear}`.slice(-2), false], // This month, this year (not expired until the month is over)
+ [`${currentMonth - 1}`, `${currentYear}`, true], // last month
+ [`${currentMonth - 1}`, `${currentYear + 1}`, false], // 11 months from now
+ ];
+}
+
+describe("isCardExpired", () => {
+ const expiryYearValueFormats = getCardExpiryDateValues();
+
+ expiryYearValueFormats.forEach(
+ ([inputMonth, inputYear, expectedValue]: [string | null, string | null, boolean]) => {
+ it(`should return ${expectedValue} when the card expiry month is ${inputMonth} and the card expiry year is ${inputYear}`, () => {
+ const testCardView = new CardView();
+ testCardView.expMonth = inputMonth;
+ testCardView.expYear = inputYear;
+
+ const cardIsExpired = isCardExpired(testCardView);
+
+ expect(cardIsExpired).toBe(expectedValue);
+ });
+ },
+ );
+});
+
+const combinedDateTestValues = [
+ " 2024 / 05 ",
+ "05 2024",
+ "05 2024", // Tab whitespace character
+ "05 2024", // Em Quad
+ "05 2024", // Em Space
+ "05 2024", // En Quad
+ "05 2024", // En Space
+ "05 2024", // Figure Space
+ "05 2024", // Four-Per-Em Space
+ "05 2024", // Hair Space
+ "05 2024", // Ideographic Space
+ "05 2024", // Medium Mathematical Space
+ "05 2024", // No-Break Space
+ "05 2024", // ogham space mark
+ "05 2024", // Punctuation Space
+ "05 2024", // Six-Per-Em Space
+ "05 2024", // Thin Space
+ "05 2024", // Three-Per-Em Space
+ "05 24",
+ "05-2024",
+ "05-24",
+ "05.2024",
+ "05.24",
+ "05/2024",
+ "05/24",
+ "052024",
+ "0524",
+ "2024 05",
+ "2024 5",
+ "2024-05",
+ "2024-5",
+ "2024.05",
+ "2024.5",
+ "2024/05",
+ "2024/5",
+ "202405",
+ "20245",
+ "24 05",
+ "24 5",
+ "24-05",
+ "24-5",
+ "24.05",
+ "24.5",
+ "24/05",
+ "24/5",
+ "2405",
+ "5 2024",
+ "5 24",
+ "5-2024",
+ "5-24",
+ "5.2024",
+ "5.24",
+ "5/2024",
+ "5/24",
+ "52024",
+];
+const expectedParsedValue = ["2024", "5"];
+describe("parseYearMonthExpiry", () => {
+ it('returns "null" expiration year and month values when a value of "" is passed', () => {
+ expect(parseYearMonthExpiry("")).toStrictEqual([null, null]);
+ });
+
+ it('returns "null" expiration year and month values when a value of "/" is passed', () => {
+ expect(parseYearMonthExpiry("/")).toStrictEqual([null, null]);
+ });
+
+ combinedDateTestValues.forEach((combinedDate) => {
+ it(`returns an expiration year value of "${expectedParsedValue[0]}" and month value of "${expectedParsedValue[1]}" when a value of "${combinedDate}" is passed`, () => {
+ expect(parseYearMonthExpiry(combinedDate)).toStrictEqual(expectedParsedValue);
+ });
+ });
+
+ it('returns an expiration year value of "2002" and month value of "2" when a value of "022" is passed', () => {
+ expect(parseYearMonthExpiry("022")).toStrictEqual(["2002", "2"]);
+ });
+
+ it('returns an expiration year value of "2002" and month value of "2" when a value of "202" is passed', () => {
+ expect(parseYearMonthExpiry("202")).toStrictEqual(["2002", "2"]);
+ });
+
+ it('returns an expiration year value of "2002" and month value of "1" when a value of "1/2/3/4" is passed', () => {
+ expect(parseYearMonthExpiry("1/2/3/4")).toStrictEqual(["2002", "1"]);
+ });
+
+ it('returns valid expiration year and month values when a value of "198" is passed', () => {
+ // This static value will cause the test to fail in 2098
+ const testValue = "198";
+ const parsedValue = parseYearMonthExpiry(testValue);
+
+ expect(parsedValue[0]).toHaveLength(4);
+ expect(parsedValue[1]).toMatch(/^[\d]{1,2}$/);
+
+ expect(parsedValue).toStrictEqual(["2098", "1"]);
+ });
+
+ // Ambiguous input cases: we use try/catch for these cases as a workaround to accept either
+ // outcome (both are valid interpretations) in the event of any future code changes.
+ describe("ambiguous input cases", () => {
+ it('returns valid expiration year and month values when a value of "111" is passed', () => {
+ const testValue = "111";
+ const parsedValue = parseYearMonthExpiry(testValue);
+
+ expect(parsedValue[0]).toHaveLength(4);
+ expect(parsedValue[1]).toMatch(/^[\d]{1,2}$/);
+
+ try {
+ expect(parsedValue).toStrictEqual(["2011", "1"]);
+ } catch {
+ expect(parsedValue).toStrictEqual(["2001", "11"]);
+ }
+ });
+
+ it('returns valid expiration year and month values when a value of "212" is passed', () => {
+ const testValue = "212";
+ const parsedValue = parseYearMonthExpiry(testValue);
+
+ expect(parsedValue[0]).toHaveLength(4);
+ expect(parsedValue[1]).toMatch(/^[\d]{1,2}$/);
+
+ try {
+ expect(parsedValue).toStrictEqual(["2012", "2"]);
+ } catch {
+ expect(parsedValue).toStrictEqual(["2021", "2"]);
+ }
+ });
+
+ it('returns valid expiration year and month values when a value of "245" is passed', () => {
+ const testValue = "245";
+ const parsedValue = parseYearMonthExpiry(testValue);
+
+ expect(parsedValue[0]).toHaveLength(4);
+ expect(parsedValue[1]).toMatch(/^[\d]{1,2}$/);
+
+ try {
+ expect(parsedValue).toStrictEqual(["2045", "2"]);
+ } catch {
+ expect(parsedValue).toStrictEqual(["2024", "5"]);
+ }
+ });
+
+ it('returns valid expiration year and month values when a value of "524" is passed', () => {
+ const testValue = "524";
+ const parsedValue = parseYearMonthExpiry(testValue);
+
+ expect(parsedValue[0]).toHaveLength(4);
+ expect(parsedValue[1]).toMatch(/^[\d]{1,2}$/);
+
+ try {
+ expect(parsedValue).toStrictEqual(["2024", "5"]);
+ } catch {
+ expect(parsedValue).toStrictEqual(["2052", "4"]);
+ }
+ });
+ });
+});
diff --git a/libs/common/src/autofill/utils.ts b/libs/common/src/autofill/utils.ts
new file mode 100644
index 00000000000..86411691ea2
--- /dev/null
+++ b/libs/common/src/autofill/utils.ts
@@ -0,0 +1,307 @@
+import {
+ DelimiterPatternExpression,
+ ExpiryFullYearPattern,
+ ExpiryFullYearPatternExpression,
+ IrrelevantExpiryCharactersPatternExpression,
+ MonthPatternExpression,
+} from "@bitwarden/common/autofill/constants";
+import { CardView } from "@bitwarden/common/vault/models/view/card.view";
+
+type NonZeroIntegers = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
+type Year = `${NonZeroIntegers}${NonZeroIntegers}${0 | NonZeroIntegers}${0 | NonZeroIntegers}`;
+
+/**
+ * Takes a string or number value and returns a string value formatted as a valid 4-digit year
+ *
+ * @param {(string | number)} yearInput
+ * @return {*} {(Year | null)}
+ */
+export function normalizeExpiryYearFormat(yearInput: string | number): Year | null {
+ // The input[type="number"] is returning a number, convert it to a string
+ // An empty field returns null, avoid casting `"null"` to a string
+ const yearInputIsEmpty = yearInput == null || yearInput === "";
+ let expirationYear = yearInputIsEmpty ? null : `${yearInput}`;
+
+ // Exit early if year is already formatted correctly or empty
+ if (yearInputIsEmpty || /^[1-9]{1}\d{3}$/.test(expirationYear)) {
+ return expirationYear as Year;
+ }
+
+ expirationYear = expirationYear
+ // For safety, because even input[type="number"] will allow decimals
+ .replace(/[^\d]/g, "")
+ // remove any leading zero padding (leave the last leading zero if it ends the string)
+ .replace(/^[0]+(?=.)/, "");
+
+ if (expirationYear === "") {
+ expirationYear = null;
+ }
+
+ // given the context of payment card expiry, a year character length of 3, or over 4
+ // is more likely to be a mistake than an intentional value for the far past or far future.
+ if (expirationYear && expirationYear.length !== 4) {
+ const paddedYear = ("00" + expirationYear).slice(-2);
+ const currentCentury = `${new Date().getFullYear()}`.slice(0, 2);
+
+ expirationYear = currentCentury + paddedYear;
+ }
+
+ return expirationYear as Year | null;
+}
+
+/**
+ * Takes a cipher card view and returns "true" if the month and year affirmativey indicate
+ * the card is expired.
+ *
+ * @param {CardView} cipherCard
+ * @return {*} {boolean}
+ */
+export function isCardExpired(cipherCard: CardView): boolean {
+ if (cipherCard) {
+ const { expMonth = null, expYear = null } = cipherCard;
+
+ const now = new Date();
+ const normalizedYear = normalizeExpiryYearFormat(expYear);
+
+ // If the card year is before the current year, don't bother checking the month
+ if (normalizedYear && parseInt(normalizedYear, 10) < now.getFullYear()) {
+ return true;
+ }
+
+ if (normalizedYear && expMonth) {
+ const parsedMonthInteger = parseInt(expMonth, 10);
+
+ const parsedMonth = isNaN(parsedMonthInteger)
+ ? 0
+ : // Add a month floor of 0 to protect against an invalid low month value of "0" or negative integers
+ Math.max(
+ // `Date` months are zero-indexed
+ parsedMonthInteger - 1,
+ 0,
+ );
+
+ const parsedYear = parseInt(normalizedYear, 10);
+
+ // First day of the next month minus one, to get last day of the card month
+ const cardExpiry = new Date(parsedYear, parsedMonth + 1, 0);
+
+ return cardExpiry < now;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * Attempt to split a string into date segments on the basis of expected formats and delimiter symbols.
+ *
+ * @param {string} combinedExpiryValue
+ * @return {*} {string[]}
+ */
+function splitCombinedDateValues(combinedExpiryValue: string): string[] {
+ let sanitizedValue = combinedExpiryValue
+ .replace(IrrelevantExpiryCharactersPatternExpression, "")
+ .trim();
+
+ // Do this after initial value replace to avoid identifying leading whitespace as delimiter
+ const parsedDelimiter = sanitizedValue.match(DelimiterPatternExpression)?.[0] || null;
+
+ let dateParts = [sanitizedValue];
+
+ if (parsedDelimiter?.length) {
+ // If the parsed delimiter is a whitespace character, assign 's' (character class) instead
+ const delimiterPattern = /\s/.test(parsedDelimiter) ? "\\s" : "\\" + parsedDelimiter;
+
+ sanitizedValue = sanitizedValue
+ // Remove all other delimiter characters not identified as the delimiter
+ .replace(new RegExp(`[^\\d${delimiterPattern}]`, "g"), "")
+ // Also de-dupe the delimiter character
+ .replace(new RegExp(`[${delimiterPattern}]{2,}`, "g"), parsedDelimiter);
+
+ dateParts = sanitizedValue.split(parsedDelimiter);
+ }
+
+ return (
+ dateParts
+ // remove values that have no length
+ .filter((splitValue) => splitValue?.length)
+ );
+}
+
+/**
+ * Given an array of split card expiry date parts,
+ * returns an array of those values ordered by year then month
+ *
+ * @param {string[]} splitDateInput
+ * @return {*} {([string | null, string | null])}
+ */
+function parseDelimitedYearMonthExpiry([firstPart, secondPart]: string[]): [string, string] {
+ // Conditionals here are structured to avoid unnecessary evaluations and are ordered
+ // from more authoritative checks to checks yielding increasingly inferred conclusions
+
+ // If a 4-digit value is found (when there are multiple parts), it can't be month
+ if (ExpiryFullYearPatternExpression.test(firstPart)) {
+ return [firstPart, secondPart];
+ }
+
+ // If a 4-digit value is found (when there are multiple parts), it can't be month
+ if (ExpiryFullYearPatternExpression.test(secondPart)) {
+ return [secondPart, firstPart];
+ }
+
+ // If it's a two digit value that doesn't match against month pattern, assume it's a year
+ if (/\d{2}/.test(firstPart) && !MonthPatternExpression.test(firstPart)) {
+ return [firstPart, secondPart];
+ }
+
+ // If it's a two digit value that doesn't match against month pattern, assume it's a year
+ if (/\d{2}/.test(secondPart) && !MonthPatternExpression.test(secondPart)) {
+ return [secondPart, firstPart];
+ }
+
+ // Values are too ambiguous (e.g. "12/09"). For the most part,
+ // a month-looking value likely is, at the time of writing (year 2024).
+ let parsedYear = firstPart;
+ let parsedMonth = secondPart;
+
+ if (MonthPatternExpression.test(firstPart)) {
+ parsedYear = secondPart;
+ parsedMonth = firstPart;
+ }
+
+ return [parsedYear, parsedMonth];
+}
+
+/**
+ * Given a single string of integers, attempts to identify card expiry date portions within
+ * and return values ordered by year then month
+ *
+ * @param {string} dateInput
+ * @return {*} {([string | null, string | null])}
+ */
+function parseNonDelimitedYearMonthExpiry(dateInput: string): [string | null, string | null] {
+ if (dateInput.length > 4) {
+ // e.g.
+ // "052024"
+ // "202405"
+ // "20245"
+ // "52024"
+
+ // If the value is over 5-characters long, it likely has a full year format in it
+ const [parsedYear, parsedMonth] = dateInput
+ .split(new RegExp(`(?=${ExpiryFullYearPattern})|(?<=${ExpiryFullYearPattern})`, "g"))
+ .sort((current: string, next: string) => (current.length > next.length ? -1 : 1));
+
+ return [parsedYear, parsedMonth];
+ }
+
+ if (dateInput.length === 4) {
+ // e.g.
+ // "0524"
+ // "2405"
+
+ // If the `sanitizedFirstPart` value is a length of 4, it must be split in half, since
+ // neither a year or month will be represented with three characters
+ const splitFirstPartFirstHalf = dateInput.slice(0, 2);
+ const splitFirstPartSecondHalf = dateInput.slice(-2);
+
+ let parsedYear = splitFirstPartSecondHalf;
+ let parsedMonth = splitFirstPartFirstHalf;
+
+ // If the first part doesn't match a month pattern, assume it's a year
+ if (!MonthPatternExpression.test(splitFirstPartFirstHalf)) {
+ parsedYear = splitFirstPartFirstHalf;
+ parsedMonth = splitFirstPartSecondHalf;
+ }
+
+ return [parsedYear, parsedMonth];
+ }
+
+ // e.g.
+ // "245"
+ // "202"
+ // "212"
+ // "022"
+ // "111"
+
+ // A valid year representation here must be two characters so try to find it first.
+
+ let parsedYear = null;
+ let parsedMonth = null;
+
+ // Split if there is a digit with a leading zero
+ const splitFirstPartOnLeadingZero = dateInput.split(/(?<=0[1-9]{1})|(?=0[1-9]{1})/);
+
+ // Assume a leading zero indicates a month in ambiguous cases (e.g. "202"), since we're
+ // dealing with expiry dates and the next two-digit year with a leading zero will be 2100
+ if (splitFirstPartOnLeadingZero.length > 1) {
+ parsedYear = splitFirstPartOnLeadingZero[0];
+ parsedMonth = splitFirstPartOnLeadingZero[1];
+
+ if (splitFirstPartOnLeadingZero[0].startsWith("0")) {
+ parsedMonth = splitFirstPartOnLeadingZero[0];
+ parsedYear = splitFirstPartOnLeadingZero[1];
+ }
+ } else {
+ // Here, a year has to be two-digits, and a month can't be more than one, so assume the first two digits that are greater than the current year is the year representation.
+ parsedYear = dateInput.slice(0, 2);
+ parsedMonth = dateInput.slice(-1);
+
+ const currentYear = new Date().getFullYear();
+ const normalizedParsedYear = parseInt(normalizeExpiryYearFormat(parsedYear), 10);
+ const normalizedParsedYearAlternative = parseInt(
+ normalizeExpiryYearFormat(dateInput.slice(-2)),
+ 10,
+ );
+
+ if (normalizedParsedYear < currentYear && normalizedParsedYearAlternative >= currentYear) {
+ parsedYear = dateInput.slice(-2);
+ parsedMonth = dateInput.slice(0, 1);
+ }
+ }
+
+ return [parsedYear, parsedMonth];
+}
+
+/**
+ * Attempt to parse year and month parts of a combined expiry date value.
+ *
+ * @param {string} combinedExpiryValue
+ * @return {*} {([string | null, string | null])}
+ */
+export function parseYearMonthExpiry(combinedExpiryValue: string): [Year | null, string | null] {
+ let parsedYear = null;
+ let parsedMonth = null;
+
+ const dateParts = splitCombinedDateValues(combinedExpiryValue);
+
+ if (dateParts.length < 1) {
+ return [null, null];
+ }
+
+ const sanitizedFirstPart =
+ dateParts[0]?.replace(IrrelevantExpiryCharactersPatternExpression, "") || "";
+ const sanitizedSecondPart =
+ dateParts[1]?.replace(IrrelevantExpiryCharactersPatternExpression, "") || "";
+
+ // If there is only one date part, no delimiter was found in the passed value
+ if (dateParts.length === 1) {
+ [parsedYear, parsedMonth] = parseNonDelimitedYearMonthExpiry(sanitizedFirstPart);
+ }
+ // There are multiple date parts
+ else {
+ [parsedYear, parsedMonth] = parseDelimitedYearMonthExpiry([
+ sanitizedFirstPart,
+ sanitizedSecondPart,
+ ]);
+ }
+
+ const normalizedParsedYear = normalizeExpiryYearFormat(parsedYear);
+ const normalizedParsedMonth = parsedMonth?.replace(/^0+/, "").slice(0, 2);
+
+ // Set "empty" values to null
+ parsedYear = normalizedParsedYear?.length ? normalizedParsedYear : null;
+ parsedMonth = normalizedParsedMonth?.length ? normalizedParsedMonth : null;
+
+ return [parsedYear, parsedMonth];
+}
diff --git a/libs/common/src/vault/models/view/card.view.ts b/libs/common/src/vault/models/view/card.view.ts
index f3bf4e1fab2..fad10851e6a 100644
--- a/libs/common/src/vault/models/view/card.view.ts
+++ b/libs/common/src/vault/models/view/card.view.ts
@@ -1,8 +1,8 @@
import { Jsonify } from "type-fest";
+import { normalizeExpiryYearFormat } from "../../../autofill/utils";
import { CardLinkedId as LinkedId } from "../../enums";
import { linkedFieldOption } from "../../linked-field-option.decorator";
-import { normalizeExpiryYearFormat } from "../../utils";
import { ItemView } from "./item.view";
diff --git a/libs/common/src/vault/utils.spec.ts b/libs/common/src/vault/utils.spec.ts
deleted file mode 100644
index 54ec66984e2..00000000000
--- a/libs/common/src/vault/utils.spec.ts
+++ /dev/null
@@ -1,122 +0,0 @@
-import { CardView } from "@bitwarden/common/vault/models/view/card.view";
-import { normalizeExpiryYearFormat, isCardExpired } from "@bitwarden/common/vault/utils";
-
-function getExpiryYearValueFormats(currentCentury: string) {
- return [
- [-12, `${currentCentury}12`],
- [0, `${currentCentury}00`],
- [2043, "2043"], // valid year with a length of four should be taken directly
- [24, `${currentCentury}24`],
- [3054, "3054"], // valid year with a length of four should be taken directly
- [31423524543, `${currentCentury}43`],
- [4, `${currentCentury}04`],
- [null, null],
- [undefined, null],
- ["-12", `${currentCentury}12`],
- ["", null],
- ["0", `${currentCentury}00`],
- ["00", `${currentCentury}00`],
- ["000", `${currentCentury}00`],
- ["0000", `${currentCentury}00`],
- ["00000", `${currentCentury}00`],
- ["0234234", `${currentCentury}34`],
- ["04", `${currentCentury}04`],
- ["2043", "2043"], // valid year with a length of four should be taken directly
- ["24", `${currentCentury}24`],
- ["3054", "3054"], // valid year with a length of four should be taken directly
- ["31423524543", `${currentCentury}43`],
- ["4", `${currentCentury}04`],
- ["aaaa", null],
- ["adgshsfhjsdrtyhsrth", null],
- ["agdredg42grg35grrr. ea3534@#^145345ag$%^ -_#$rdg ", `${currentCentury}45`],
- ];
-}
-
-describe("normalizeExpiryYearFormat", () => {
- const currentCentury = `${new Date().getFullYear()}`.slice(0, 2);
-
- const expiryYearValueFormats = getExpiryYearValueFormats(currentCentury);
-
- expiryYearValueFormats.forEach(([inputValue, expectedValue]) => {
- it(`should return '${expectedValue}' when '${inputValue}' is passed`, () => {
- const formattedValue = normalizeExpiryYearFormat(inputValue);
-
- expect(formattedValue).toEqual(expectedValue);
- });
- });
-
- describe("in the year 3107", () => {
- const theDistantFuture = new Date(Date.UTC(3107, 1, 1));
- jest.spyOn(Date, "now").mockReturnValue(theDistantFuture.valueOf());
-
- beforeAll(() => {
- jest.useFakeTimers({ advanceTimers: true });
- jest.setSystemTime(theDistantFuture);
- });
-
- afterAll(() => {
- jest.useRealTimers();
- });
-
- const currentCentury = `${new Date(Date.now()).getFullYear()}`.slice(0, 2);
- expect(currentCentury).toBe("31");
-
- const expiryYearValueFormats = getExpiryYearValueFormats(currentCentury);
-
- expiryYearValueFormats.forEach(([inputValue, expectedValue]) => {
- it(`should return '${expectedValue}' when '${inputValue}' is passed`, () => {
- const formattedValue = normalizeExpiryYearFormat(inputValue);
-
- expect(formattedValue).toEqual(expectedValue);
- });
- });
- jest.clearAllTimers();
- });
-});
-
-function getCardExpiryDateValues() {
- const currentDate = new Date();
-
- const currentYear = currentDate.getFullYear();
-
- // `Date` months are zero-indexed, our expiry date month inputs are one-indexed
- const currentMonth = currentDate.getMonth() + 1;
-
- return [
- [null, null, false], // no month, no year
- [undefined, undefined, false], // no month, no year, invalid values
- ["", "", false], // no month, no year, invalid values
- ["12", "agdredg42grg35grrr. ea3534@#^145345ag$%^ -_#$rdg ", false], // invalid values
- ["0", `${currentYear - 1}`, true], // invalid 0 month
- ["00", `${currentYear + 1}`, false], // invalid 0 month
- [`${currentMonth}`, "0000", true], // current month, in the year 2000
- [null, `${currentYear}`.slice(-2), false], // no month, this year
- [null, `${currentYear - 1}`.slice(-2), true], // no month, last year
- ["1", null, false], // no year, January
- ["1", `${currentYear - 1}`, true], // January last year
- ["13", `${currentYear}`, false], // 12 + 1 is Feb. in the next year (Date is zero-indexed)
- [`${currentMonth + 36}`, `${currentYear - 1}`, true], // even though the month value would put the date 3 years into the future when calculated with `Date`, an explicit year in the past indicates the card is expired
- [`${currentMonth}`, `${currentYear}`, false], // this year, this month (not expired until the month is over)
- [`${currentMonth}`, `${currentYear}`.slice(-2), false], // This month, this year (not expired until the month is over)
- [`${currentMonth - 1}`, `${currentYear}`, true], // last month
- [`${currentMonth - 1}`, `${currentYear + 1}`, false], // 11 months from now
- ];
-}
-
-describe("isCardExpired", () => {
- const expiryYearValueFormats = getCardExpiryDateValues();
-
- expiryYearValueFormats.forEach(
- ([inputMonth, inputYear, expectedValue]: [string | null, string | null, boolean]) => {
- it(`should return ${expectedValue} when the card expiry month is ${inputMonth} and the card expiry year is ${inputYear}`, () => {
- const testCardView = new CardView();
- testCardView.expMonth = inputMonth;
- testCardView.expYear = inputYear;
-
- const cardIsExpired = isCardExpired(testCardView);
-
- expect(cardIsExpired).toBe(expectedValue);
- });
- },
- );
-});
diff --git a/libs/common/src/vault/utils.ts b/libs/common/src/vault/utils.ts
deleted file mode 100644
index 7d8784eda78..00000000000
--- a/libs/common/src/vault/utils.ts
+++ /dev/null
@@ -1,83 +0,0 @@
-import { CardView } from "@bitwarden/common/vault/models/view/card.view";
-
-type NonZeroIntegers = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
-type Year = `${NonZeroIntegers}${NonZeroIntegers}${0 | NonZeroIntegers}${0 | NonZeroIntegers}`;
-
-/**
- * Takes a string or number value and returns a string value formatted as a valid 4-digit year
- *
- * @export
- * @param {(string | number)} yearInput
- * @return {*} {(Year | null)}
- */
-export function normalizeExpiryYearFormat(yearInput: string | number): Year | null {
- // The input[type="number"] is returning a number, convert it to a string
- // An empty field returns null, avoid casting `"null"` to a string
- const yearInputIsEmpty = yearInput == null || yearInput === "";
- let expirationYear = yearInputIsEmpty ? null : `${yearInput}`;
-
- // Exit early if year is already formatted correctly or empty
- if (yearInputIsEmpty || /^[1-9]{1}\d{3}$/.test(expirationYear)) {
- return expirationYear as Year;
- }
-
- expirationYear = expirationYear
- // For safety, because even input[type="number"] will allow decimals
- .replace(/[^\d]/g, "")
- // remove any leading zero padding (leave the last leading zero if it ends the string)
- .replace(/^[0]+(?=.)/, "");
-
- if (expirationYear === "") {
- expirationYear = null;
- }
-
- // given the context of payment card expiry, a year character length of 3, or over 4
- // is more likely to be a mistake than an intentional value for the far past or far future.
- if (expirationYear && expirationYear.length !== 4) {
- const paddedYear = ("00" + expirationYear).slice(-2);
- const currentCentury = `${new Date().getFullYear()}`.slice(0, 2);
-
- expirationYear = currentCentury + paddedYear;
- }
-
- return expirationYear as Year | null;
-}
-
-/**
- * Takes a cipher card view and returns "true" if the month and year affirmativey indicate
- * the card is expired.
- *
- * @export
- * @param {CardView} cipherCard
- * @return {*} {boolean}
- */
-export function isCardExpired(cipherCard: CardView): boolean {
- if (cipherCard) {
- const { expMonth = null, expYear = null } = cipherCard;
-
- const now = new Date();
- const normalizedYear = normalizeExpiryYearFormat(expYear);
-
- // If the card year is before the current year, don't bother checking the month
- if (normalizedYear && parseInt(normalizedYear) < now.getFullYear()) {
- return true;
- }
-
- if (normalizedYear && expMonth) {
- // `Date` months are zero-indexed
- const parsedMonth =
- parseInt(expMonth) - 1 ||
- // Add a month floor of 0 to protect against an invalid low month value of "0"
- 0;
-
- const parsedYear = parseInt(normalizedYear);
-
- // First day of the next month minus one, to get last day of the card month
- const cardExpiry = new Date(parsedYear, parsedMonth + 1, 0);
-
- return cardExpiry < now;
- }
- }
-
- return false;
-}
diff --git a/libs/importer/src/importers/base-importer.ts b/libs/importer/src/importers/base-importer.ts
index 215210eda14..9cba62c5faf 100644
--- a/libs/importer/src/importers/base-importer.ts
+++ b/libs/importer/src/importers/base-importer.ts
@@ -1,5 +1,6 @@
import * as papa from "papaparse";
+import { normalizeExpiryYearFormat } from "@bitwarden/common/autofill/utils";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service";
@@ -11,7 +12,6 @@ import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
import { LoginUriView } from "@bitwarden/common/vault/models/view/login-uri.view";
import { LoginView } from "@bitwarden/common/vault/models/view/login.view";
import { SecureNoteView } from "@bitwarden/common/vault/models/view/secure-note.view";
-import { normalizeExpiryYearFormat } from "@bitwarden/common/vault/utils";
import { ImportResult } from "../models/import-result";
diff --git a/libs/vault/src/cipher-form/components/card-details-section/card-details-section.component.ts b/libs/vault/src/cipher-form/components/card-details-section/card-details-section.component.ts
index e1ef3dc0f37..bc4ff608805 100644
--- a/libs/vault/src/cipher-form/components/card-details-section/card-details-section.component.ts
+++ b/libs/vault/src/cipher-form/components/card-details-section/card-details-section.component.ts
@@ -5,11 +5,11 @@ import { FormBuilder, ReactiveFormsModule } from "@angular/forms";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
+import { normalizeExpiryYearFormat } from "@bitwarden/common/autofill/utils";
import { EventType } from "@bitwarden/common/enums";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { CardView } from "@bitwarden/common/vault/models/view/card.view";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
-import { normalizeExpiryYearFormat } from "@bitwarden/common/vault/utils";
import {
CardComponent,
FormFieldModule,
diff --git a/libs/vault/src/cipher-view/cipher-view.component.ts b/libs/vault/src/cipher-view/cipher-view.component.ts
index cadf388e76d..e28f7f2a2bb 100644
--- a/libs/vault/src/cipher-view/cipher-view.component.ts
+++ b/libs/vault/src/cipher-view/cipher-view.component.ts
@@ -5,13 +5,13 @@ import { firstValueFrom, Observable, Subject, takeUntil } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
+import { isCardExpired } from "@bitwarden/common/autofill/utils";
import { CollectionId } from "@bitwarden/common/types/guid";
import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service";
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view";
import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
-import { isCardExpired } from "@bitwarden/common/vault/utils";
import { CalloutModule, SearchModule } from "@bitwarden/components";
import { AdditionalOptionsComponent } from "./additional-options/additional-options.component";
From 0089ae0886c3556b4e9f181763badaa15b181cbe Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Tue, 24 Sep 2024 11:35:01 -0400
Subject: [PATCH 13/23] [deps] DevOps: Update gh minor (#11064)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
.github/workflows/build-browser.yml | 4 ++--
.github/workflows/build-cli.yml | 4 ++--
.github/workflows/build-desktop.yml | 14 +++++++-------
.github/workflows/build-web.yml | 2 +-
.github/workflows/chromatic.yml | 4 ++--
.github/workflows/lint.yml | 2 +-
.github/workflows/release-desktop-beta.yml | 10 +++++-----
.github/workflows/scan.yml | 4 ++--
.github/workflows/test.yml | 2 +-
.github/workflows/version-bump.yml | 2 +-
10 files changed, 24 insertions(+), 24 deletions(-)
diff --git a/.github/workflows/build-browser.yml b/.github/workflows/build-browser.yml
index 610769859fe..562567ffe77 100644
--- a/.github/workflows/build-browser.yml
+++ b/.github/workflows/build-browser.yml
@@ -114,7 +114,7 @@ jobs:
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Set up Node
- uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
+ uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
@@ -257,7 +257,7 @@ jobs:
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Set up Node
- uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
+ uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
diff --git a/.github/workflows/build-cli.yml b/.github/workflows/build-cli.yml
index 76d86b45500..bccfc6d57ce 100644
--- a/.github/workflows/build-cli.yml
+++ b/.github/workflows/build-cli.yml
@@ -93,7 +93,7 @@ jobs:
awk '{print tolower($0)}')" >> $GITHUB_ENV
- name: Set up Node
- uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
+ uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
@@ -171,7 +171,7 @@ jobs:
choco install nasm --no-progress
- name: Set up Node
- uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
+ uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
diff --git a/.github/workflows/build-desktop.yml b/.github/workflows/build-desktop.yml
index ddb87320839..05039c3982b 100644
--- a/.github/workflows/build-desktop.yml
+++ b/.github/workflows/build-desktop.yml
@@ -143,7 +143,7 @@ jobs:
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Set up Node
- uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
+ uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
@@ -252,7 +252,7 @@ jobs:
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Set up Node
- uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
+ uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
@@ -458,7 +458,7 @@ jobs:
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Set up Node
- uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
+ uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
@@ -622,7 +622,7 @@ jobs:
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Set up Node
- uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
+ uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
@@ -839,7 +839,7 @@ jobs:
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Set up Node
- uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
+ uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
@@ -1045,7 +1045,7 @@ jobs:
(github.ref == 'refs/heads/main'
|| github.ref == 'refs/heads/rc'
|| github.ref == 'refs/heads/hotfix-rc-desktop')
- uses: slackapi/slack-github-action@6c661ce58804a1a20f6dc5fbee7f0381b469e001 # v1.25.0
+ uses: slackapi/slack-github-action@37ebaef184d7626c5f204ab8d3baff4262dd30f0 # v1.27.0
with:
channel-id: C074F5UESQ0
payload: |
@@ -1084,7 +1084,7 @@ jobs:
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Set up Node
- uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
+ uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
diff --git a/.github/workflows/build-web.yml b/.github/workflows/build-web.yml
index d875078757c..54e993edb33 100644
--- a/.github/workflows/build-web.yml
+++ b/.github/workflows/build-web.yml
@@ -94,7 +94,7 @@ jobs:
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Set up Node
- uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
+ uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
diff --git a/.github/workflows/chromatic.yml b/.github/workflows/chromatic.yml
index c8dd3e77838..272b68139ac 100644
--- a/.github/workflows/chromatic.yml
+++ b/.github/workflows/chromatic.yml
@@ -38,7 +38,7 @@ jobs:
echo "node_version=$NODE_VERSION" >> $GITHUB_OUTPUT
- name: Set up Node
- uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
+ uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
with:
node-version: ${{ steps.retrieve-node-version.outputs.node_version }}
@@ -57,7 +57,7 @@ jobs:
run: npm run build-storybook:ci
- name: Publish to Chromatic
- uses: chromaui/action@b984808b772126a9f44b2b7737b131b68a2ede32 # v11.7.1
+ uses: chromaui/action@c883154c39671e194613be2f09ff4d17bb95f1e6 # v11.10.3
with:
token: ${{ secrets.GITHUB_TOKEN }}
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index bb495a5a26d..3f72d62214b 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -48,7 +48,7 @@ jobs:
echo "node_version=$NODE_VERSION" >> $GITHUB_OUTPUT
- name: Set up Node
- uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
+ uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
diff --git a/.github/workflows/release-desktop-beta.yml b/.github/workflows/release-desktop-beta.yml
index 5a6a3d52361..80b1ae092c6 100644
--- a/.github/workflows/release-desktop-beta.yml
+++ b/.github/workflows/release-desktop-beta.yml
@@ -130,7 +130,7 @@ jobs:
ref: ${{ needs.setup.outputs.branch-name }}
- name: Set up Node
- uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
+ uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
@@ -220,7 +220,7 @@ jobs:
ref: ${{ needs.setup.outputs.branch-name }}
- name: Set up Node
- uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
+ uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
@@ -409,7 +409,7 @@ jobs:
ref: ${{ needs.setup.outputs.branch-name }}
- name: Set up Node
- uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
+ uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
@@ -543,7 +543,7 @@ jobs:
ref: ${{ needs.setup.outputs.branch-name }}
- name: Set up Node
- uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
+ uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
@@ -756,7 +756,7 @@ jobs:
ref: ${{ needs.setup.outputs.branch-name }}
- name: Set up Node
- uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
+ uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
diff --git a/.github/workflows/scan.yml b/.github/workflows/scan.yml
index 076bfb46e80..2320bae72f3 100644
--- a/.github/workflows/scan.yml
+++ b/.github/workflows/scan.yml
@@ -32,7 +32,7 @@ jobs:
ref: ${{ github.event.pull_request.head.sha }}
- name: Scan with Checkmarx
- uses: checkmarx/ast-github-action@1fe318de2993222574e6249750ba9000a4e2a6cd # 2.0.33
+ uses: checkmarx/ast-github-action@9fda5a4a2c297608117a5a56af424502a9192e57 # 2.0.34
env:
INCREMENTAL: "${{ contains(github.event_name, 'pull_request') && '--sast-incremental' || '' }}"
with:
@@ -47,7 +47,7 @@ jobs:
--output-path . ${{ env.INCREMENTAL }}
- name: Upload Checkmarx results to GitHub
- uses: github/codeql-action/upload-sarif@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6
+ uses: github/codeql-action/upload-sarif@294a9d92911152fe08befb9ec03e240add280cb3 # v3.26.8
with:
sarif_file: cx_result.sarif
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 8d4067c1167..fbe0c0a798b 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -51,7 +51,7 @@ jobs:
echo "node_version=$NODE_VERSION" >> $GITHUB_OUTPUT
- name: Set up Node
- uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
+ uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
diff --git a/.github/workflows/version-bump.yml b/.github/workflows/version-bump.yml
index 71e7d3c10aa..fea68161bab 100644
--- a/.github/workflows/version-bump.yml
+++ b/.github/workflows/version-bump.yml
@@ -447,7 +447,7 @@ jobs:
echo "EOF" >> $GITHUB_ENV
- name: Generate GH App token
- uses: actions/create-github-app-token@3378cda945da322a8db4b193e19d46352ebe2de5 # v1.10.4
+ uses: actions/create-github-app-token@5d869da34e18e7287c1daad50e0b8ea0f506ce69 # v1.11.0
id: app-token
with:
app-id: ${{ secrets.BW_GHAPP_ID }}
From e3c75b3c1b4ec36c13b5fe2387ab1941eb6e56fb Mon Sep 17 00:00:00 2001
From: cyprain-okeke <108260115+cyprain-okeke@users.noreply.github.com>
Date: Tue, 24 Sep 2024 18:07:29 +0100
Subject: [PATCH 14/23] Resolve the payment display (#11219)
---
.../organizations/change-plan-dialog.component.ts | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/apps/web/src/app/billing/organizations/change-plan-dialog.component.ts b/apps/web/src/app/billing/organizations/change-plan-dialog.component.ts
index 63ac3afb930..42a987664f0 100644
--- a/apps/web/src/app/billing/organizations/change-plan-dialog.component.ts
+++ b/apps/web/src/app/billing/organizations/change-plan-dialog.component.ts
@@ -403,11 +403,13 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy {
}
get upgradeRequiresPaymentMethod() {
- return (
- this.organization?.productTierType === ProductTierType.Free &&
- !this.showFree &&
- !this.billing?.paymentSource
- );
+ const isFreeTier = this.organization?.productTierType === ProductTierType.Free;
+ const shouldHideFree = !this.showFree;
+ const hasNoPaymentSource = this.deprecateStripeSourcesAPI
+ ? !this.paymentSource
+ : !this.billing?.paymentSource;
+
+ return isFreeTier && shouldHideFree && hasNoPaymentSource;
}
get selectedSecretsManagerPlan() {
From 3646214a0f8ea069fd01a982a0fdd86035c38646 Mon Sep 17 00:00:00 2001
From: SmithThe4th
Date: Tue, 24 Sep 2024 14:13:27 -0400
Subject: [PATCH 15/23] Made allCiphers$ depend on the refresh subject$
(#11225)
---
apps/web/src/app/vault/org-vault/vault.component.ts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/apps/web/src/app/vault/org-vault/vault.component.ts b/apps/web/src/app/vault/org-vault/vault.component.ts
index 0a8e8769715..f378e171e78 100644
--- a/apps/web/src/app/vault/org-vault/vault.component.ts
+++ b/apps/web/src/app/vault/org-vault/vault.component.ts
@@ -318,8 +318,8 @@ export class VaultComponent implements OnInit, OnDestroy {
shareReplay({ refCount: true, bufferSize: 1 }),
);
- const allCiphers$ = organization$.pipe(
- concatMap(async (organization) => {
+ const allCiphers$ = combineLatest([organization$, this.refresh$]).pipe(
+ switchMap(async ([organization]) => {
// If user swaps organization reset the addAccessToggle
if (!this.showAddAccessToggle || organization) {
this.addAccessToggle(0);
From bdcf920e624981612d39945e283beb66c2c1c14d Mon Sep 17 00:00:00 2001
From: Todd Martin <106564991+trmartin4@users.noreply.github.com>
Date: Tue, 24 Sep 2024 16:47:10 -0400
Subject: [PATCH 16/23] Remove references to device-trust-logging feature flag
(#11183)
---
.../src/auth/services/device-trust.service.implementation.ts | 4 ----
libs/common/src/enums/feature-flag.enum.ts | 2 --
2 files changed, 6 deletions(-)
diff --git a/libs/common/src/auth/services/device-trust.service.implementation.ts b/libs/common/src/auth/services/device-trust.service.implementation.ts
index fe0f2cff0ab..c1cf871e257 100644
--- a/libs/common/src/auth/services/device-trust.service.implementation.ts
+++ b/libs/common/src/auth/services/device-trust.service.implementation.ts
@@ -2,7 +2,6 @@ import { firstValueFrom, map, Observable } from "rxjs";
import { UserDecryptionOptionsServiceAbstraction } from "@bitwarden/auth/common";
-import { FeatureFlag } from "../../enums/feature-flag.enum";
import { AppIdService } from "../../platform/abstractions/app-id.service";
import { ConfigService } from "../../platform/abstractions/config/config.service";
import { CryptoFunctionService } from "../../platform/abstractions/crypto-function.service";
@@ -334,9 +333,6 @@ export class DeviceTrustService implements DeviceTrustServiceAbstraction {
}
async recordDeviceTrustLoss(): Promise {
- if (!(await this.configService.getFeatureFlag(FeatureFlag.DeviceTrustLogging))) {
- return;
- }
const deviceIdentifier = await this.appIdService.getAppId();
await this.devicesApiService.postDeviceTrustLoss(deviceIdentifier);
}
diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts
index 3d426aee3b7..aeefa98b5dc 100644
--- a/libs/common/src/enums/feature-flag.enum.ts
+++ b/libs/common/src/enums/feature-flag.enum.ts
@@ -24,7 +24,6 @@ export enum FeatureFlag {
VaultBulkManagementAction = "vault-bulk-management-action",
AC2828_ProviderPortalMembersPage = "AC-2828_provider-portal-members-page",
IdpAutoSubmitLogin = "idp-auto-submit-login",
- DeviceTrustLogging = "pm-8285-device-trust-logging",
AuthenticatorTwoFactorToken = "authenticator-2fa-token",
UnauthenticatedExtensionUIRefresh = "unauth-ui-refresh",
EnableUpgradePasswordManagerSub = "AC-2708-upgrade-password-manager-sub",
@@ -69,7 +68,6 @@ export const DefaultFeatureFlagValue = {
[FeatureFlag.VaultBulkManagementAction]: FALSE,
[FeatureFlag.AC2828_ProviderPortalMembersPage]: FALSE,
[FeatureFlag.IdpAutoSubmitLogin]: FALSE,
- [FeatureFlag.DeviceTrustLogging]: FALSE,
[FeatureFlag.AuthenticatorTwoFactorToken]: FALSE,
[FeatureFlag.UnauthenticatedExtensionUIRefresh]: FALSE,
[FeatureFlag.EnableUpgradePasswordManagerSub]: FALSE,
From 85b97d930481f2c35c4de14feefb1041277fa2a3 Mon Sep 17 00:00:00 2001
From: Todd Martin <106564991+trmartin4@users.noreply.github.com>
Date: Tue, 24 Sep 2024 17:02:39 -0400
Subject: [PATCH 17/23] Remove authenticator-token feature flag (#11182)
---
.../auth/settings/two-factor-authenticator.component.ts | 8 --------
libs/common/src/enums/feature-flag.enum.ts | 2 --
2 files changed, 10 deletions(-)
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 fdd595b7fcd..da5378f4790 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
@@ -11,7 +11,6 @@ import { DisableTwoFactorAuthenticatorRequest } from "@bitwarden/common/auth/mod
import { UpdateTwoFactorAuthenticatorRequest } from "@bitwarden/common/auth/models/request/update-two-factor-authenticator.request";
import { TwoFactorAuthenticatorResponse } from "@bitwarden/common/auth/models/response/two-factor-authenticator.response";
import { AuthResponse } from "@bitwarden/common/auth/types/auth-response";
-import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@@ -127,13 +126,6 @@ export class TwoFactorAuthenticatorComponent
}
protected override async disableMethod() {
- const twoFactorAuthenticatorTokenFeatureFlag = await this.configService.getFeatureFlag(
- FeatureFlag.AuthenticatorTwoFactorToken,
- );
- if (twoFactorAuthenticatorTokenFeatureFlag === false) {
- return super.disableMethod();
- }
-
const confirmed = await this.dialogService.openSimpleDialog({
title: { key: "disable" },
content: { key: "twoStepDisableDesc" },
diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts
index aeefa98b5dc..7ac473ff6ab 100644
--- a/libs/common/src/enums/feature-flag.enum.ts
+++ b/libs/common/src/enums/feature-flag.enum.ts
@@ -24,7 +24,6 @@ export enum FeatureFlag {
VaultBulkManagementAction = "vault-bulk-management-action",
AC2828_ProviderPortalMembersPage = "AC-2828_provider-portal-members-page",
IdpAutoSubmitLogin = "idp-auto-submit-login",
- AuthenticatorTwoFactorToken = "authenticator-2fa-token",
UnauthenticatedExtensionUIRefresh = "unauth-ui-refresh",
EnableUpgradePasswordManagerSub = "AC-2708-upgrade-password-manager-sub",
GenerateIdentityFillScriptRefactor = "generate-identity-fill-script-refactor",
@@ -68,7 +67,6 @@ export const DefaultFeatureFlagValue = {
[FeatureFlag.VaultBulkManagementAction]: FALSE,
[FeatureFlag.AC2828_ProviderPortalMembersPage]: FALSE,
[FeatureFlag.IdpAutoSubmitLogin]: FALSE,
- [FeatureFlag.AuthenticatorTwoFactorToken]: FALSE,
[FeatureFlag.UnauthenticatedExtensionUIRefresh]: FALSE,
[FeatureFlag.EnableUpgradePasswordManagerSub]: FALSE,
[FeatureFlag.GenerateIdentityFillScriptRefactor]: FALSE,
From 4269489548d739dcff7829b21bba1baf119fc6c6 Mon Sep 17 00:00:00 2001
From: Jonathan Prusik
Date: Tue, 24 Sep 2024 17:07:17 -0400
Subject: [PATCH 18/23] pin regedit dependency (#11228)
---
package-lock.json | 2 +-
package.json | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index b2058de0dc8..57b3aa028fb 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -164,7 +164,7 @@
"prettier": "3.3.3",
"prettier-plugin-tailwindcss": "0.6.6",
"process": "0.11.10",
- "regedit": "^3.0.3",
+ "regedit": "3.0.3",
"remark-gfm": "4.0.0",
"rimraf": "6.0.1",
"sass": "1.74.1",
diff --git a/package.json b/package.json
index aafc92bbd3d..b073cd27d87 100644
--- a/package.json
+++ b/package.json
@@ -126,7 +126,7 @@
"prettier": "3.3.3",
"prettier-plugin-tailwindcss": "0.6.6",
"process": "0.11.10",
- "regedit": "^3.0.3",
+ "regedit": "3.0.3",
"remark-gfm": "4.0.0",
"rimraf": "6.0.1",
"sass": "1.74.1",
From e6c3de9f47b3ffe3cb8a5fa9e24df04683e9eee4 Mon Sep 17 00:00:00 2001
From: Shane Melton
Date: Tue, 24 Sep 2024 14:30:06 -0700
Subject: [PATCH 19/23] [PM-12609] Use shareReplay for allCiphers$ observable
(#11229)
---
apps/web/src/app/vault/org-vault/vault.component.ts | 1 +
1 file changed, 1 insertion(+)
diff --git a/apps/web/src/app/vault/org-vault/vault.component.ts b/apps/web/src/app/vault/org-vault/vault.component.ts
index f378e171e78..31f981b4898 100644
--- a/apps/web/src/app/vault/org-vault/vault.component.ts
+++ b/apps/web/src/app/vault/org-vault/vault.component.ts
@@ -343,6 +343,7 @@ export class VaultComponent implements OnInit, OnDestroy {
await this.searchService.indexCiphers(ciphers, organization.id);
return ciphers;
}),
+ shareReplay({ refCount: true, bufferSize: 1 }),
);
const allCipherMap$ = allCiphers$.pipe(
From d587be1831601ec6482a00ad6647579ab90a31ad Mon Sep 17 00:00:00 2001
From: Jordan Aasen <166539328+jaasen-livefront@users.noreply.github.com>
Date: Wed, 25 Sep 2024 04:07:01 -0700
Subject: [PATCH 20/23] [PM-12403] - Implement Remove Send policy on Add/edit
screen (#11178)
* disable edit send if policy requires
* remove unused var
* don't display free bitwarden families button
* Revert "don't display free bitwarden families button"
This reverts commit 832564d705e082efcd6c7938cc7c1d9c6b23def4.
* use config instead of policy service
* Revert "don't display free bitwarden families button"
This reverts commit 832564d705e082efcd6c7938cc7c1d9c6b23def4.
* remove unnecessary code
* Use short when transforming deletionDate instead of fixed format
---------
Co-authored-by: Daniel James Smith
---
.../options/send-options.component.ts | 4 +
.../base-send-details.component.ts | 107 ------------------
.../send-details/send-details.component.ts | 105 ++++++++++++++---
.../send-file-details.component.ts | 4 +
.../send-text-details.component.ts | 4 +
5 files changed, 104 insertions(+), 120 deletions(-)
delete mode 100644 libs/tools/send/send-ui/src/send-form/components/send-details/base-send-details.component.ts
diff --git a/libs/tools/send/send-ui/src/send-form/components/options/send-options.component.ts b/libs/tools/send/send-ui/src/send-form/components/options/send-options.component.ts
index 89ab9d19ba2..a73a3a6ad88 100644
--- a/libs/tools/send/send-ui/src/send-form/components/options/send-options.component.ts
+++ b/libs/tools/send/send-ui/src/send-form/components/options/send-options.component.ts
@@ -97,6 +97,7 @@ export class SendOptionsComponent implements OnInit {
});
});
}
+
ngOnInit() {
if (this.sendFormContainer.originalSendView) {
this.sendOptionsForm.patchValue({
@@ -107,5 +108,8 @@ export class SendOptionsComponent implements OnInit {
notes: this.sendFormContainer.originalSendView.notes,
});
}
+ if (!this.config.areSendsAllowed) {
+ this.sendOptionsForm.disable();
+ }
}
}
diff --git a/libs/tools/send/send-ui/src/send-form/components/send-details/base-send-details.component.ts b/libs/tools/send/send-ui/src/send-form/components/send-details/base-send-details.component.ts
deleted file mode 100644
index b5cf8ee0c76..00000000000
--- a/libs/tools/send/send-ui/src/send-form/components/send-details/base-send-details.component.ts
+++ /dev/null
@@ -1,107 +0,0 @@
-import { DatePipe } from "@angular/common";
-import { Component, Input, OnInit } from "@angular/core";
-import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
-import { FormBuilder, FormControl, Validators } from "@angular/forms";
-
-import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
-import { SendView } from "@bitwarden/common/tools/send/models/view/send.view";
-
-import { SendFormConfig } from "../../abstractions/send-form-config.service";
-import { SendFormContainer } from "../../send-form-container";
-
-// Value = hours
-export enum DatePreset {
- OneHour = 1,
- OneDay = 24,
- TwoDays = 48,
- ThreeDays = 72,
- SevenDays = 168,
- FourteenDays = 336,
- ThirtyDays = 720,
-}
-
-export interface DatePresetSelectOption {
- name: string;
- value: DatePreset | string;
-}
-
-@Component({
- selector: "base-send-details-behavior",
- template: "",
-})
-export class BaseSendDetailsComponent implements OnInit {
- @Input() config: SendFormConfig;
- @Input() originalSendView?: SendView;
-
- customDeletionDateOption: DatePresetSelectOption | null = null;
- datePresetOptions: DatePresetSelectOption[] = [];
-
- sendDetailsForm = this.formBuilder.group({
- name: new FormControl("", Validators.required),
- selectedDeletionDatePreset: new FormControl(DatePreset.SevenDays || "", Validators.required),
- });
-
- constructor(
- protected sendFormContainer: SendFormContainer,
- protected formBuilder: FormBuilder,
- protected i18nService: I18nService,
- protected datePipe: DatePipe,
- ) {
- this.sendDetailsForm.valueChanges.pipe(takeUntilDestroyed()).subscribe((value) => {
- this.sendFormContainer.patchSend((send) => {
- return Object.assign(send, {
- name: value.name,
- deletionDate: new Date(this.formattedDeletionDate),
- expirationDate: new Date(this.formattedDeletionDate),
- } as SendView);
- });
- });
-
- this.sendFormContainer.registerChildForm("sendDetailsForm", this.sendDetailsForm);
- }
-
- async ngOnInit() {
- this.setupDeletionDatePresets();
-
- if (this.originalSendView) {
- this.sendDetailsForm.patchValue({
- name: this.originalSendView.name,
- selectedDeletionDatePreset: this.originalSendView.deletionDate.toString(),
- });
-
- if (this.originalSendView.deletionDate) {
- this.customDeletionDateOption = {
- name: this.datePipe.transform(this.originalSendView.deletionDate, "MM/dd/yyyy, hh:mm a"),
- value: this.originalSendView.deletionDate.toString(),
- };
- this.datePresetOptions.unshift(this.customDeletionDateOption);
- }
- }
- }
-
- setupDeletionDatePresets() {
- const defaultSelections: DatePresetSelectOption[] = [
- { name: this.i18nService.t("oneHour"), value: DatePreset.OneHour },
- { name: this.i18nService.t("oneDay"), value: DatePreset.OneDay },
- { name: this.i18nService.t("days", "2"), value: DatePreset.TwoDays },
- { name: this.i18nService.t("days", "3"), value: DatePreset.ThreeDays },
- { name: this.i18nService.t("days", "7"), value: DatePreset.SevenDays },
- { name: this.i18nService.t("days", "14"), value: DatePreset.FourteenDays },
- { name: this.i18nService.t("days", "30"), value: DatePreset.ThirtyDays },
- ];
-
- this.datePresetOptions = defaultSelections;
- }
-
- get formattedDeletionDate(): string {
- const now = new Date();
- const selectedValue = this.sendDetailsForm.controls.selectedDeletionDatePreset.value;
-
- if (typeof selectedValue === "string") {
- return selectedValue;
- }
-
- const milliseconds = now.setTime(now.getTime() + (selectedValue as number) * 60 * 60 * 1000);
- return new Date(milliseconds).toString();
- }
-}
diff --git a/libs/tools/send/send-ui/src/send-form/components/send-details/send-details.component.ts b/libs/tools/send/send-ui/src/send-form/components/send-details/send-details.component.ts
index 0b287205be4..68a5e40a575 100644
--- a/libs/tools/send/send-ui/src/send-form/components/send-details/send-details.component.ts
+++ b/libs/tools/send/send-ui/src/send-form/components/send-details/send-details.component.ts
@@ -1,12 +1,14 @@
import { CommonModule, DatePipe } from "@angular/common";
-import { Component, OnInit } from "@angular/core";
-import { FormBuilder, ReactiveFormsModule } from "@angular/forms";
+import { Component, OnInit, Input } from "@angular/core";
+import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
+import { FormBuilder, FormControl, ReactiveFormsModule, Validators } from "@angular/forms";
import { firstValueFrom } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { SendType } from "@bitwarden/common/tools/send/enums/send-type";
+import { SendView } from "@bitwarden/common/tools/send/models/view/send.view";
import {
SectionComponent,
SectionHeaderComponent,
@@ -18,13 +20,29 @@ import {
SelectModule,
} from "@bitwarden/components";
+import { SendFormConfig } from "../../abstractions/send-form-config.service";
import { SendFormContainer } from "../../send-form-container";
import { SendOptionsComponent } from "../options/send-options.component";
-import { BaseSendDetailsComponent } from "./base-send-details.component";
import { SendFileDetailsComponent } from "./send-file-details.component";
import { SendTextDetailsComponent } from "./send-text-details.component";
+// Value = hours
+export enum DatePreset {
+ OneHour = 1,
+ OneDay = 24,
+ TwoDays = 48,
+ ThreeDays = 72,
+ SevenDays = 168,
+ FourteenDays = 336,
+ ThirtyDays = 720,
+}
+
+export interface DatePresetSelectOption {
+ name: string;
+ value: DatePreset | string;
+}
+
@Component({
selector: "tools-send-details",
templateUrl: "./send-details.component.html",
@@ -46,10 +64,20 @@ import { SendTextDetailsComponent } from "./send-text-details.component";
SelectModule,
],
})
-export class SendDetailsComponent extends BaseSendDetailsComponent implements OnInit {
+export class SendDetailsComponent implements OnInit {
+ @Input() config: SendFormConfig;
+ @Input() originalSendView?: SendView;
+
FileSendType = SendType.File;
TextSendType = SendType.Text;
sendLink: string | null = null;
+ customDeletionDateOption: DatePresetSelectOption | null = null;
+ datePresetOptions: DatePresetSelectOption[] = [];
+
+ sendDetailsForm = this.formBuilder.group({
+ name: new FormControl("", Validators.required),
+ selectedDeletionDatePreset: new FormControl(DatePreset.SevenDays || "", Validators.required),
+ });
constructor(
protected sendFormContainer: SendFormContainer,
@@ -58,18 +86,69 @@ export class SendDetailsComponent extends BaseSendDetailsComponent implements On
protected datePipe: DatePipe,
protected environmentService: EnvironmentService,
) {
- super(sendFormContainer, formBuilder, i18nService, datePipe);
- }
+ this.sendDetailsForm.valueChanges.pipe(takeUntilDestroyed()).subscribe((value) => {
+ this.sendFormContainer.patchSend((send) => {
+ return Object.assign(send, {
+ name: value.name,
+ deletionDate: new Date(this.formattedDeletionDate),
+ expirationDate: new Date(this.formattedDeletionDate),
+ } as SendView);
+ });
+ });
- async getSendLink() {}
+ this.sendFormContainer.registerChildForm("sendDetailsForm", this.sendDetailsForm);
+ }
async ngOnInit() {
- await super.ngOnInit();
- if (!this.originalSendView) {
- return;
+ this.setupDeletionDatePresets();
+
+ if (this.originalSendView) {
+ this.sendDetailsForm.patchValue({
+ name: this.originalSendView.name,
+ selectedDeletionDatePreset: this.originalSendView.deletionDate.toString(),
+ });
+
+ if (this.originalSendView.deletionDate) {
+ this.customDeletionDateOption = {
+ name: this.datePipe.transform(this.originalSendView.deletionDate, "short"),
+ value: this.originalSendView.deletionDate.toString(),
+ };
+ this.datePresetOptions.unshift(this.customDeletionDateOption);
+ }
+
+ const env = await firstValueFrom(this.environmentService.environment$);
+ this.sendLink =
+ env.getSendUrl() + this.originalSendView.accessId + "/" + this.originalSendView.urlB64Key;
}
- const env = await firstValueFrom(this.environmentService.environment$);
- this.sendLink =
- env.getSendUrl() + this.originalSendView.accessId + "/" + this.originalSendView.urlB64Key;
+
+ if (!this.config.areSendsAllowed) {
+ this.sendDetailsForm.disable();
+ }
+ }
+
+ setupDeletionDatePresets() {
+ const defaultSelections: DatePresetSelectOption[] = [
+ { name: this.i18nService.t("oneHour"), value: DatePreset.OneHour },
+ { name: this.i18nService.t("oneDay"), value: DatePreset.OneDay },
+ { name: this.i18nService.t("days", "2"), value: DatePreset.TwoDays },
+ { name: this.i18nService.t("days", "3"), value: DatePreset.ThreeDays },
+ { name: this.i18nService.t("days", "7"), value: DatePreset.SevenDays },
+ { name: this.i18nService.t("days", "14"), value: DatePreset.FourteenDays },
+ { name: this.i18nService.t("days", "30"), value: DatePreset.ThirtyDays },
+ ];
+
+ this.datePresetOptions = defaultSelections;
+ }
+
+ get formattedDeletionDate(): string {
+ const now = new Date();
+ const selectedValue = this.sendDetailsForm.controls.selectedDeletionDatePreset.value;
+
+ if (typeof selectedValue === "string") {
+ return selectedValue;
+ }
+
+ const milliseconds = now.setTime(now.getTime() + (selectedValue as number) * 60 * 60 * 1000);
+ return new Date(milliseconds).toString();
}
}
diff --git a/libs/tools/send/send-ui/src/send-form/components/send-details/send-file-details.component.ts b/libs/tools/send/send-ui/src/send-form/components/send-details/send-file-details.component.ts
index 7739ca26528..447256cacdf 100644
--- a/libs/tools/send/send-ui/src/send-form/components/send-details/send-file-details.component.ts
+++ b/libs/tools/send/send-ui/src/send-form/components/send-details/send-file-details.component.ts
@@ -73,5 +73,9 @@ export class SendFileDetailsComponent implements OnInit {
file: this.originalSendView.file,
});
}
+
+ if (!this.config.areSendsAllowed) {
+ this.sendFileDetailsForm.disable();
+ }
}
}
diff --git a/libs/tools/send/send-ui/src/send-form/components/send-details/send-text-details.component.ts b/libs/tools/send/send-ui/src/send-form/components/send-details/send-text-details.component.ts
index 85fc324f2f7..873f85c9e38 100644
--- a/libs/tools/send/send-ui/src/send-form/components/send-details/send-text-details.component.ts
+++ b/libs/tools/send/send-ui/src/send-form/components/send-details/send-text-details.component.ts
@@ -57,5 +57,9 @@ export class SendTextDetailsComponent implements OnInit {
hidden: this.originalSendView.text?.hidden || false,
});
}
+
+ if (!this.config.areSendsAllowed) {
+ this.sendTextDetailsForm.disable();
+ }
}
}
From cd9045483bc31768698b197d9452b5b01da68c99 Mon Sep 17 00:00:00 2001
From: Jordan Aasen <166539328+jaasen-livefront@users.noreply.github.com>
Date: Wed, 25 Sep 2024 05:03:42 -0700
Subject: [PATCH 21/23] [PM-12561] - add data attrs for send form (#11209)
* add data attrs for send form
* Add data-testid for toggle view password
* Revert "Add data-testid for toggle view password"
This reverts commit bd6fcc8c1bf6d4d61888ace8480d34b3098d0770.
* move dataid to component
---------
Co-authored-by: Daniel James Smith
---
.../components/options/send-options.component.html | 9 ++++++++-
.../components/send-details/send-details.component.html | 2 +-
.../send-details/send-file-details.component.html | 4 ++--
3 files changed, 11 insertions(+), 4 deletions(-)
diff --git a/libs/tools/send/send-ui/src/send-form/components/options/send-options.component.html b/libs/tools/send/send-ui/src/send-form/components/options/send-options.component.html
index 53483065b73..01b96e3bc5a 100644
--- a/libs/tools/send/send-ui/src/send-form/components/options/send-options.component.html
+++ b/libs/tools/send/send-ui/src/send-form/components/options/send-options.component.html
@@ -15,12 +15,19 @@
{{ "password" | i18n }}
{{ "newPassword" | i18n }}
-
+
{{ "sendPasswordDescV2" | i18n }}
diff --git a/libs/tools/send/send-ui/src/send-form/components/send-details/send-details.component.html b/libs/tools/send/send-ui/src/send-form/components/send-details/send-details.component.html
index 98f399760be..d4c253303bd 100644
--- a/libs/tools/send/send-ui/src/send-form/components/send-details/send-details.component.html
+++ b/libs/tools/send/send-ui/src/send-form/components/send-details/send-details.component.html
@@ -23,7 +23,7 @@
{{ "sendLink" | i18n }}
-
+
{{ "passwordHistory" | i18n }}
diff --git a/libs/vault/src/cipher-view/item-history/item-history-v2.component.ts b/libs/vault/src/cipher-view/item-history/item-history-v2.component.ts
index 55c8b90da15..4a37c9a491b 100644
--- a/libs/vault/src/cipher-view/item-history/item-history-v2.component.ts
+++ b/libs/vault/src/cipher-view/item-history/item-history-v2.component.ts
@@ -3,6 +3,8 @@ import { Component, Input } from "@angular/core";
import { RouterModule } from "@angular/router";
import { JslibModule } from "@bitwarden/angular/jslib.module";
+import { CipherId } from "@bitwarden/common/types/guid";
+import { ViewPasswordHistoryService } from "@bitwarden/common/vault/abstractions/view-password-history.service";
import { CipherType } from "@bitwarden/common/vault/enums";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import {
@@ -31,7 +33,16 @@ import {
export class ItemHistoryV2Component {
@Input() cipher: CipherView;
+ constructor(private viewPasswordHistoryService: ViewPasswordHistoryService) {}
+
get isLogin() {
return this.cipher.type === CipherType.Login;
}
+
+ /**
+ * View the password history for the cipher.
+ */
+ async viewPasswordHistory() {
+ await this.viewPasswordHistoryService.viewPasswordHistory(this.cipher?.id as CipherId);
+ }
}