mirror of
https://github.com/bitwarden/browser
synced 2025-12-12 22:33:35 +00:00
[PM-23251] Remove low-kdf banner (#16511)
* Remove low-kdf banner * update tests
This commit is contained in:
@@ -1,14 +1,8 @@
|
|||||||
import { TestBed } from "@angular/core/testing";
|
import { TestBed } from "@angular/core/testing";
|
||||||
import { BehaviorSubject, firstValueFrom, take, timeout } from "rxjs";
|
import { BehaviorSubject, firstValueFrom, take, timeout } from "rxjs";
|
||||||
|
|
||||||
import {
|
import { AuthRequestServiceAbstraction } from "@bitwarden/auth/common";
|
||||||
AuthRequestServiceAbstraction,
|
|
||||||
UserDecryptionOptions,
|
|
||||||
UserDecryptionOptionsServiceAbstraction,
|
|
||||||
} from "@bitwarden/auth/common";
|
|
||||||
import { AccountInfo, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountInfo, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { DevicesServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices/devices.service.abstraction";
|
|
||||||
import { DeviceView } from "@bitwarden/common/auth/abstractions/devices/views/device.view";
|
|
||||||
import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response";
|
import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response";
|
||||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||||
import { DeviceType } from "@bitwarden/common/enums";
|
import { DeviceType } from "@bitwarden/common/enums";
|
||||||
@@ -18,7 +12,6 @@ import { StateProvider } from "@bitwarden/common/platform/state";
|
|||||||
import { FakeStateProvider, mockAccountServiceWith } from "@bitwarden/common/spec";
|
import { FakeStateProvider, mockAccountServiceWith } from "@bitwarden/common/spec";
|
||||||
import { UserId } from "@bitwarden/common/types/guid";
|
import { UserId } from "@bitwarden/common/types/guid";
|
||||||
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
||||||
import { KdfConfigService, KdfType } from "@bitwarden/key-management";
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
PREMIUM_BANNER_REPROMPT_KEY,
|
PREMIUM_BANNER_REPROMPT_KEY,
|
||||||
@@ -34,14 +27,9 @@ describe("VaultBannersService", () => {
|
|||||||
const fakeStateProvider = new FakeStateProvider(mockAccountServiceWith(userId));
|
const fakeStateProvider = new FakeStateProvider(mockAccountServiceWith(userId));
|
||||||
const getEmailVerified = jest.fn().mockResolvedValue(true);
|
const getEmailVerified = jest.fn().mockResolvedValue(true);
|
||||||
const lastSync$ = new BehaviorSubject<Date | null>(null);
|
const lastSync$ = new BehaviorSubject<Date | null>(null);
|
||||||
const userDecryptionOptions$ = new BehaviorSubject<UserDecryptionOptions>({
|
|
||||||
hasMasterPassword: true,
|
|
||||||
});
|
|
||||||
const kdfConfig$ = new BehaviorSubject({ kdfType: KdfType.PBKDF2_SHA256, iterations: 600000 });
|
|
||||||
const accounts$ = new BehaviorSubject<Record<UserId, AccountInfo>>({
|
const accounts$ = new BehaviorSubject<Record<UserId, AccountInfo>>({
|
||||||
[userId]: { email: "test@bitwarden.com", emailVerified: true, name: "name" } as AccountInfo,
|
[userId]: { email: "test@bitwarden.com", emailVerified: true, name: "name" } as AccountInfo,
|
||||||
});
|
});
|
||||||
const devices$ = new BehaviorSubject<DeviceView[]>([]);
|
|
||||||
const pendingAuthRequests$ = new BehaviorSubject<Array<AuthRequestResponse>>([]);
|
const pendingAuthRequests$ = new BehaviorSubject<Array<AuthRequestResponse>>([]);
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -64,32 +52,14 @@ describe("VaultBannersService", () => {
|
|||||||
provide: StateProvider,
|
provide: StateProvider,
|
||||||
useValue: fakeStateProvider,
|
useValue: fakeStateProvider,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
provide: PlatformUtilsService,
|
|
||||||
useValue: { isSelfHost },
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
provide: AccountService,
|
provide: AccountService,
|
||||||
useValue: { accounts$ },
|
useValue: { accounts$ },
|
||||||
},
|
},
|
||||||
{
|
|
||||||
provide: KdfConfigService,
|
|
||||||
useValue: { getKdfConfig$: () => kdfConfig$ },
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
provide: SyncService,
|
provide: SyncService,
|
||||||
useValue: { lastSync$: () => lastSync$ },
|
useValue: { lastSync$: () => lastSync$ },
|
||||||
},
|
},
|
||||||
{
|
|
||||||
provide: UserDecryptionOptionsServiceAbstraction,
|
|
||||||
useValue: {
|
|
||||||
userDecryptionOptionsById$: () => userDecryptionOptions$,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
provide: DevicesServiceAbstraction,
|
|
||||||
useValue: { getDevices$: () => devices$ },
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
provide: AuthRequestServiceAbstraction,
|
provide: AuthRequestServiceAbstraction,
|
||||||
useValue: { getPendingAuthRequests$: () => pendingAuthRequests$ },
|
useValue: { getPendingAuthRequests$: () => pendingAuthRequests$ },
|
||||||
@@ -197,45 +167,6 @@ describe("VaultBannersService", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("KDFSettings", () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
userDecryptionOptions$.next({ hasMasterPassword: true });
|
|
||||||
kdfConfig$.next({ kdfType: KdfType.PBKDF2_SHA256, iterations: 599999 });
|
|
||||||
});
|
|
||||||
|
|
||||||
it("shows low KDF iteration banner", async () => {
|
|
||||||
service = TestBed.inject(VaultBannersService);
|
|
||||||
|
|
||||||
expect(await service.shouldShowLowKDFBanner(userId)).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("does not show low KDF iteration banner if KDF type is not PBKDF2_SHA256", async () => {
|
|
||||||
kdfConfig$.next({ kdfType: KdfType.Argon2id, iterations: 600001 });
|
|
||||||
|
|
||||||
service = TestBed.inject(VaultBannersService);
|
|
||||||
|
|
||||||
expect(await service.shouldShowLowKDFBanner(userId)).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("does not show low KDF for iterations about 600,000", async () => {
|
|
||||||
kdfConfig$.next({ kdfType: KdfType.PBKDF2_SHA256, iterations: 600001 });
|
|
||||||
|
|
||||||
service = TestBed.inject(VaultBannersService);
|
|
||||||
|
|
||||||
expect(await service.shouldShowLowKDFBanner(userId)).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("dismisses low KDF iteration banner", async () => {
|
|
||||||
service = TestBed.inject(VaultBannersService);
|
|
||||||
|
|
||||||
expect(await service.shouldShowLowKDFBanner(userId)).toBe(true);
|
|
||||||
|
|
||||||
await service.dismissBanner(userId, VisibleVaultBanner.KDFSettings);
|
|
||||||
|
|
||||||
expect(await service.shouldShowLowKDFBanner(userId)).toBe(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("OutdatedBrowser", () => {
|
describe("OutdatedBrowser", () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
// Hardcode `MSIE` in userAgent string
|
// Hardcode `MSIE` in userAgent string
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
import { Injectable } from "@angular/core";
|
import { Injectable } from "@angular/core";
|
||||||
import { Observable, combineLatest, firstValueFrom, map, filter, mergeMap, take } from "rxjs";
|
import { Observable, combineLatest, firstValueFrom, map, filter, mergeMap, take } from "rxjs";
|
||||||
|
|
||||||
import {
|
import { AuthRequestServiceAbstraction } from "@bitwarden/auth/common";
|
||||||
AuthRequestServiceAbstraction,
|
|
||||||
UserDecryptionOptionsServiceAbstraction,
|
|
||||||
} from "@bitwarden/auth/common";
|
|
||||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
@@ -18,10 +15,8 @@ import {
|
|||||||
import { UserId } from "@bitwarden/common/types/guid";
|
import { UserId } from "@bitwarden/common/types/guid";
|
||||||
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
||||||
import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values";
|
import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values";
|
||||||
import { PBKDF2KdfConfig, KdfConfigService, KdfType } from "@bitwarden/key-management";
|
|
||||||
|
|
||||||
export const VisibleVaultBanner = {
|
export const VisibleVaultBanner = {
|
||||||
KDFSettings: "kdf-settings",
|
|
||||||
OutdatedBrowser: "outdated-browser",
|
OutdatedBrowser: "outdated-browser",
|
||||||
Premium: "premium",
|
Premium: "premium",
|
||||||
VerifyEmail: "verify-email",
|
VerifyEmail: "verify-email",
|
||||||
@@ -64,9 +59,7 @@ export class VaultBannersService {
|
|||||||
private stateProvider: StateProvider,
|
private stateProvider: StateProvider,
|
||||||
private billingAccountProfileStateService: BillingAccountProfileStateService,
|
private billingAccountProfileStateService: BillingAccountProfileStateService,
|
||||||
private platformUtilsService: PlatformUtilsService,
|
private platformUtilsService: PlatformUtilsService,
|
||||||
private kdfConfigService: KdfConfigService,
|
|
||||||
private syncService: SyncService,
|
private syncService: SyncService,
|
||||||
private userDecryptionOptionsService: UserDecryptionOptionsServiceAbstraction,
|
|
||||||
private authRequestService: AuthRequestServiceAbstraction,
|
private authRequestService: AuthRequestServiceAbstraction,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@@ -133,21 +126,6 @@ export class VaultBannersService {
|
|||||||
return needsVerification && !alreadyDismissed;
|
return needsVerification && !alreadyDismissed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns true when the low KDF iteration banner should be shown */
|
|
||||||
async shouldShowLowKDFBanner(userId: UserId): Promise<boolean> {
|
|
||||||
const hasLowKDF = (
|
|
||||||
await firstValueFrom(this.userDecryptionOptionsService.userDecryptionOptionsById$(userId))
|
|
||||||
)?.hasMasterPassword
|
|
||||||
? await this.isLowKdfIteration(userId)
|
|
||||||
: false;
|
|
||||||
|
|
||||||
const alreadyDismissed = (await this.getBannerDismissedState(userId)).includes(
|
|
||||||
VisibleVaultBanner.KDFSettings,
|
|
||||||
);
|
|
||||||
|
|
||||||
return hasLowKDF && !alreadyDismissed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Dismiss the given banner and perform any respective side effects */
|
/** Dismiss the given banner and perform any respective side effects */
|
||||||
async dismissBanner(userId: UserId, banner: SessionBanners): Promise<void> {
|
async dismissBanner(userId: UserId, banner: SessionBanners): Promise<void> {
|
||||||
if (banner === VisibleVaultBanner.Premium) {
|
if (banner === VisibleVaultBanner.Premium) {
|
||||||
@@ -221,13 +199,4 @@ export class VaultBannersService {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async isLowKdfIteration(userId: UserId) {
|
|
||||||
const kdfConfig = await firstValueFrom(this.kdfConfigService.getKdfConfig$(userId));
|
|
||||||
return (
|
|
||||||
kdfConfig != null &&
|
|
||||||
kdfConfig.kdfType === KdfType.PBKDF2_SHA256 &&
|
|
||||||
kdfConfig.iterations < PBKDF2KdfConfig.ITERATIONS.defaultValue
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,18 +25,6 @@
|
|||||||
</a>
|
</a>
|
||||||
</bit-banner>
|
</bit-banner>
|
||||||
|
|
||||||
<bit-banner
|
|
||||||
id="kdf-settings-banner"
|
|
||||||
bannerType="warning"
|
|
||||||
*ngIf="visibleBanners.includes(VisibleVaultBanner.KDFSettings)"
|
|
||||||
(onClose)="dismissBanner(VisibleVaultBanner.KDFSettings)"
|
|
||||||
>
|
|
||||||
{{ "lowKDFIterationsBanner" | i18n }}
|
|
||||||
<a bitLink linkType="secondary" routerLink="/settings/security/security-keys">
|
|
||||||
{{ "changeKDFSettings" | i18n }}
|
|
||||||
</a>
|
|
||||||
</bit-banner>
|
|
||||||
|
|
||||||
<bit-banner
|
<bit-banner
|
||||||
id="pending-auth-request-banner"
|
id="pending-auth-request-banner"
|
||||||
bannerType="info"
|
bannerType="info"
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ describe("VaultBannersComponent", () => {
|
|||||||
shouldShowPremiumBanner$: jest.fn((userId: UserId) => premiumBanner$),
|
shouldShowPremiumBanner$: jest.fn((userId: UserId) => premiumBanner$),
|
||||||
shouldShowUpdateBrowserBanner: jest.fn(),
|
shouldShowUpdateBrowserBanner: jest.fn(),
|
||||||
shouldShowVerifyEmailBanner: jest.fn(),
|
shouldShowVerifyEmailBanner: jest.fn(),
|
||||||
shouldShowLowKDFBanner: jest.fn(),
|
|
||||||
shouldShowPendingAuthRequestBanner: jest.fn((userId: UserId) =>
|
shouldShowPendingAuthRequestBanner: jest.fn((userId: UserId) =>
|
||||||
Promise.resolve(pendingAuthRequest$.value),
|
Promise.resolve(pendingAuthRequest$.value),
|
||||||
),
|
),
|
||||||
@@ -48,7 +47,6 @@ describe("VaultBannersComponent", () => {
|
|||||||
messageSubject = new Subject<{ command: string }>();
|
messageSubject = new Subject<{ command: string }>();
|
||||||
bannerService.shouldShowUpdateBrowserBanner.mockResolvedValue(false);
|
bannerService.shouldShowUpdateBrowserBanner.mockResolvedValue(false);
|
||||||
bannerService.shouldShowVerifyEmailBanner.mockResolvedValue(false);
|
bannerService.shouldShowVerifyEmailBanner.mockResolvedValue(false);
|
||||||
bannerService.shouldShowLowKDFBanner.mockResolvedValue(false);
|
|
||||||
pendingAuthRequest$.next(false);
|
pendingAuthRequest$.next(false);
|
||||||
premiumBanner$.next(false);
|
premiumBanner$.next(false);
|
||||||
|
|
||||||
@@ -137,11 +135,6 @@ describe("VaultBannersComponent", () => {
|
|||||||
method: bannerService.shouldShowVerifyEmailBanner,
|
method: bannerService.shouldShowVerifyEmailBanner,
|
||||||
banner: VisibleVaultBanner.VerifyEmail,
|
banner: VisibleVaultBanner.VerifyEmail,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "LowKDF",
|
|
||||||
method: bannerService.shouldShowLowKDFBanner,
|
|
||||||
banner: VisibleVaultBanner.KDFSettings,
|
|
||||||
},
|
|
||||||
].forEach(({ name, method, banner }) => {
|
].forEach(({ name, method, banner }) => {
|
||||||
describe(name, () => {
|
describe(name, () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
|||||||
@@ -100,14 +100,12 @@ export class VaultBannersComponent implements OnInit {
|
|||||||
const showBrowserOutdated =
|
const showBrowserOutdated =
|
||||||
await this.vaultBannerService.shouldShowUpdateBrowserBanner(activeUserId);
|
await this.vaultBannerService.shouldShowUpdateBrowserBanner(activeUserId);
|
||||||
const showVerifyEmail = await this.vaultBannerService.shouldShowVerifyEmailBanner(activeUserId);
|
const showVerifyEmail = await this.vaultBannerService.shouldShowVerifyEmailBanner(activeUserId);
|
||||||
const showLowKdf = await this.vaultBannerService.shouldShowLowKDFBanner(activeUserId);
|
|
||||||
const showPendingAuthRequest =
|
const showPendingAuthRequest =
|
||||||
await this.vaultBannerService.shouldShowPendingAuthRequestBanner(activeUserId);
|
await this.vaultBannerService.shouldShowPendingAuthRequestBanner(activeUserId);
|
||||||
|
|
||||||
this.visibleBanners = [
|
this.visibleBanners = [
|
||||||
showBrowserOutdated ? VisibleVaultBanner.OutdatedBrowser : null,
|
showBrowserOutdated ? VisibleVaultBanner.OutdatedBrowser : null,
|
||||||
showVerifyEmail ? VisibleVaultBanner.VerifyEmail : null,
|
showVerifyEmail ? VisibleVaultBanner.VerifyEmail : null,
|
||||||
showLowKdf ? VisibleVaultBanner.KDFSettings : null,
|
|
||||||
showPendingAuthRequest ? VisibleVaultBanner.PendingAuthRequest : null,
|
showPendingAuthRequest ? VisibleVaultBanner.PendingAuthRequest : null,
|
||||||
].filter((banner) => banner !== null);
|
].filter((banner) => banner !== null);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8408,12 +8408,6 @@
|
|||||||
"groupSlashUser": {
|
"groupSlashUser": {
|
||||||
"message": "Group/User"
|
"message": "Group/User"
|
||||||
},
|
},
|
||||||
"lowKdfIterations": {
|
|
||||||
"message": "Low KDF Iterations"
|
|
||||||
},
|
|
||||||
"updateLowKdfIterationsDesc": {
|
|
||||||
"message": "Update your encryption settings to meet new security recommendations and improve account protection."
|
|
||||||
},
|
|
||||||
"kdfSettingsChangeLogoutWarning": {
|
"kdfSettingsChangeLogoutWarning": {
|
||||||
"message": "Proceeding will log you out of all active sessions. You will need to log back in and complete two-step login, if any. We recommend exporting your vault before changing your encryption settings to prevent data loss."
|
"message": "Proceeding will log you out of all active sessions. You will need to log back in and complete two-step login, if any. We recommend exporting your vault before changing your encryption settings to prevent data loss."
|
||||||
},
|
},
|
||||||
@@ -10031,12 +10025,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lowKDFIterationsBanner": {
|
|
||||||
"message": "Low KDF iterations. Increase your iterations to improve the security of your account."
|
|
||||||
},
|
|
||||||
"changeKDFSettings": {
|
|
||||||
"message": "Change KDF settings"
|
|
||||||
},
|
|
||||||
"secureYourInfrastructure": {
|
"secureYourInfrastructure": {
|
||||||
"message": "Secure your infrastructure"
|
"message": "Secure your infrastructure"
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user