Skip to content

Commit

Permalink
Merge branch 'main' into PM-15593-Standard-Login-UI-is-shown-when-ite…
Browse files Browse the repository at this point in the history
…m-has-no-Auth-key-added
  • Loading branch information
dan-livefront authored Dec 19, 2024
2 parents 11e8be3 + e129e90 commit 9675d73
Show file tree
Hide file tree
Showing 226 changed files with 4,566 additions and 762 deletions.
2 changes: 1 addition & 1 deletion apps/browser/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@bitwarden/browser",
"version": "2024.12.2",
"version": "2024.12.3",
"scripts": {
"build": "npm run build:chrome",
"build:chrome": "cross-env BROWSER=chrome MANIFEST_VERSION=3 webpack",
Expand Down
36 changes: 36 additions & 0 deletions apps/browser/src/_locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -4910,6 +4910,42 @@
"beta": {
"message": "Beta"
},
"importantNotice": {
"message": "Important notice"
},
"setupTwoStepLogin": {
"message": "Set up two-step login"
},
"newDeviceVerificationNoticeContentPage1": {
"message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025."
},
"newDeviceVerificationNoticeContentPage2": {
"message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access."
},
"remindMeLater": {
"message": "Remind me later"
},
"newDeviceVerificationNoticePageOneFormContent": {
"message": "Do you have reliable access to your email, $EMAIL$?",
"placeholders": {
"email": {
"content": "$1",
"example": "[email protected]"
}
}
},
"newDeviceVerificationNoticePageOneEmailAccessNo": {
"message": "No, I do not"
},
"newDeviceVerificationNoticePageOneEmailAccessYes": {
"message": "Yes, I can reliably access my email"
},
"turnOnTwoStepLogin": {
"message": "Turn on two-step login"
},
"changeAcctEmail": {
"message": "Change account email"
},
"extensionWidth": {
"message": "Extension width"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { BrowserScriptInjectorService } from "../../../platform/services/browser
import { AbortManager } from "../../../vault/background/abort-manager";
import { Fido2ContentScript, Fido2ContentScriptId } from "../enums/fido2-content-script.enum";
import { Fido2PortName } from "../enums/fido2-port-name.enum";
import { BrowserFido2ParentWindowReference } from "../services/browser-fido2-user-interface.service";

import { Fido2ExtensionMessage } from "./abstractions/fido2.background";
import { Fido2Background } from "./fido2.background";
Expand Down Expand Up @@ -56,7 +57,7 @@ describe("Fido2Background", () => {
let senderMock!: MockProxy<chrome.runtime.MessageSender>;
let logService!: MockProxy<LogService>;
let fido2ActiveRequestManager: MockProxy<Fido2ActiveRequestManager>;
let fido2ClientService!: MockProxy<Fido2ClientService>;
let fido2ClientService!: MockProxy<Fido2ClientService<BrowserFido2ParentWindowReference>>;
let vaultSettingsService!: MockProxy<VaultSettingsService>;
let scriptInjectorServiceMock!: MockProxy<BrowserScriptInjectorService>;
let configServiceMock!: MockProxy<ConfigService>;
Expand All @@ -73,7 +74,7 @@ describe("Fido2Background", () => {
});
senderMock = mock<chrome.runtime.MessageSender>({ id: "1", tab: tabMock });
logService = mock<LogService>();
fido2ClientService = mock<Fido2ClientService>();
fido2ClientService = mock<Fido2ClientService<BrowserFido2ParentWindowReference>>();
vaultSettingsService = mock<VaultSettingsService>();
abortManagerMock = mock<AbortManager>();
abortController = mock<AbortController>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@ import { ScriptInjectorService } from "../../../platform/services/abstractions/s
import { AbortManager } from "../../../vault/background/abort-manager";
import { Fido2ContentScript, Fido2ContentScriptId } from "../enums/fido2-content-script.enum";
import { Fido2PortName } from "../enums/fido2-port-name.enum";
import { BrowserFido2ParentWindowReference } from "../services/browser-fido2-user-interface.service";

import {
Fido2Background as Fido2BackgroundInterface,
Fido2BackgroundExtensionMessageHandlers,
Fido2Background as Fido2BackgroundInterface,
Fido2ExtensionMessage,
SharedFido2ScriptInjectionDetails,
SharedFido2ScriptRegistrationOptions,
Expand Down Expand Up @@ -56,7 +57,7 @@ export class Fido2Background implements Fido2BackgroundInterface {
constructor(
private logService: LogService,
private fido2ActiveRequestManager: Fido2ActiveRequestManager,
private fido2ClientService: Fido2ClientService,
private fido2ClientService: Fido2ClientService<BrowserFido2ParentWindowReference>,
private vaultSettingsService: VaultSettingsService,
private scriptInjectorService: ScriptInjectorService,
private configService: ConfigService,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,15 @@ export type BrowserFido2Message = { sessionId: string } & (
}
);

export type BrowserFido2ParentWindowReference = chrome.tabs.Tab;

/**
* Browser implementation of the {@link Fido2UserInterfaceService}.
* The user interface is implemented as a popout and the service uses the browser's messaging API to communicate with it.
*/
export class BrowserFido2UserInterfaceService implements Fido2UserInterfaceServiceAbstraction {
export class BrowserFido2UserInterfaceService
implements Fido2UserInterfaceServiceAbstraction<BrowserFido2ParentWindowReference>
{
constructor(private authService: AuthService) {}

async newSession(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ <h2 bitTypography="h6">{{ "vaultSaveOptionsTitle" | i18n }}</h2>
<bit-section>
<bit-item>
<a bit-item-content routerLink="/excluded-domains">{{ "excludedDomains" | i18n }}</a>
<i slot="end" class="bwi bwi-angle-right bwi-lg row-sub-icon" aria-hidden="true"></i>
<i slot="end" class="bwi bwi-angle-right row-sub-icon" aria-hidden="true"></i>
</bit-item>
</bit-section>
</div>
Expand Down
17 changes: 10 additions & 7 deletions apps/browser/src/background/main.background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,11 +201,11 @@ import {
ImportServiceAbstraction,
} from "@bitwarden/importer/core";
import {
DefaultKdfConfigService,
KdfConfigService,
BiometricStateService,
BiometricsService,
DefaultBiometricStateService,
DefaultKdfConfigService,
KdfConfigService,
KeyService as KeyServiceAbstraction,
} from "@bitwarden/key-management";
import {
Expand All @@ -232,7 +232,10 @@ import { MainContextMenuHandler } from "../autofill/browser/main-context-menu-ha
import LegacyOverlayBackground from "../autofill/deprecated/background/overlay.background.deprecated";
import { Fido2Background as Fido2BackgroundAbstraction } from "../autofill/fido2/background/abstractions/fido2.background";
import { Fido2Background } from "../autofill/fido2/background/fido2.background";
import { BrowserFido2UserInterfaceService } from "../autofill/fido2/services/browser-fido2-user-interface.service";
import {
BrowserFido2ParentWindowReference,
BrowserFido2UserInterfaceService,
} from "../autofill/fido2/services/browser-fido2-user-interface.service";
import { AutofillService as AutofillServiceAbstraction } from "../autofill/services/abstractions/autofill.service";
import AutofillService from "../autofill/services/autofill.service";
import { InlineMenuFieldQualificationService } from "../autofill/services/inline-menu-field-qualification.service";
Expand Down Expand Up @@ -337,10 +340,10 @@ export default class MainBackground {
policyApiService: PolicyApiServiceAbstraction;
sendApiService: SendApiServiceAbstraction;
userVerificationApiService: UserVerificationApiServiceAbstraction;
fido2UserInterfaceService: Fido2UserInterfaceServiceAbstraction;
fido2AuthenticatorService: Fido2AuthenticatorServiceAbstraction;
fido2UserInterfaceService: Fido2UserInterfaceServiceAbstraction<BrowserFido2ParentWindowReference>;
fido2AuthenticatorService: Fido2AuthenticatorServiceAbstraction<BrowserFido2ParentWindowReference>;
fido2ActiveRequestManager: Fido2ActiveRequestManagerAbstraction;
fido2ClientService: Fido2ClientServiceAbstraction;
fido2ClientService: Fido2ClientServiceAbstraction<BrowserFido2ParentWindowReference>;
avatarService: AvatarServiceAbstraction;
mainContextMenuHandler: MainContextMenuHandler;
cipherContextMenuHandler: CipherContextMenuHandler;
Expand Down Expand Up @@ -1330,7 +1333,7 @@ export default class MainBackground {
return new Promise<void>((resolve) => {
setTimeout(async () => {
await this.refreshBadge();
await this.fullSync(true);
await this.fullSync(false);
this.taskSchedulerService.setInterval(
ScheduledTaskNames.scheduleNextSyncInterval,
5 * 60 * 1000, // check every 5 minutes
Expand Down
2 changes: 1 addition & 1 deletion apps/browser/src/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"manifest_version": 2,
"name": "__MSG_extName__",
"short_name": "__MSG_appName__",
"version": "2024.12.2",
"version": "2024.12.3",
"description": "__MSG_extDesc__",
"default_locale": "en",
"author": "Bitwarden Inc.",
Expand Down
2 changes: 1 addition & 1 deletion apps/browser/src/manifest.v3.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"minimum_chrome_version": "102.0",
"name": "__MSG_extName__",
"short_name": "__MSG_appName__",
"version": "2024.12.2",
"version": "2024.12.3",
"description": "__MSG_extDesc__",
"default_locale": "en",
"author": "Bitwarden Inc.",
Expand Down
39 changes: 38 additions & 1 deletion apps/browser/src/popup/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
import { canAccessFeature } from "@bitwarden/angular/platform/guard/feature-flag.guard";
import { extensionRefreshRedirect } from "@bitwarden/angular/utils/extension-refresh-redirect";
import { extensionRefreshSwap } from "@bitwarden/angular/utils/extension-refresh-swap";
import { NewDeviceVerificationNoticeGuard } from "@bitwarden/angular/vault/guards";
import {
AnonLayoutWrapperComponent,
AnonLayoutWrapperData,
Expand All @@ -43,6 +44,11 @@ import {
TwoFactorTimeoutIcon,
} from "@bitwarden/auth/angular";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import {
NewDeviceVerificationNoticePageOneComponent,
NewDeviceVerificationNoticePageTwoComponent,
VaultIcons,
} from "@bitwarden/vault";

import { twofactorRefactorSwap } from "../../../../libs/angular/src/utils/two-factor-component-refactor-route-swap";
import { fido2AuthGuard } from "../auth/guards/fido2-auth.guard";
Expand Down Expand Up @@ -661,6 +667,10 @@ const routes: Routes = [
* This ensures that in a passkey flow the `/fido2?<queryParams>` URL does not get
* overwritten in the `BrowserRouterService` by the `/lockV2` route. This way, after
* unlocking, the user can be redirected back to the `/fido2?<queryParams>` URL.
*
* Also, this prevents a routing loop when using biometrics to unlock the vault in MV2 (Firefox),
* locking up the browser (https://bitwarden.atlassian.net/browse/PM-16116). This involves the
* `popup-router-cache.service` pushing the `lockV2` route to the history.
*/
doNotSaveUrl: true,
} satisfies ExtensionAnonLayoutWrapperData & RouteDataProperties,
Expand Down Expand Up @@ -711,6 +721,33 @@ const routes: Routes = [
canActivate: [authGuard],
data: { elevation: 2 } satisfies RouteDataProperties,
},
{
path: "new-device-notice",
component: ExtensionAnonLayoutWrapperComponent,
canActivate: [],
children: [
{
path: "",
component: NewDeviceVerificationNoticePageOneComponent,
data: {
pageIcon: VaultIcons.ExclamationTriangle,
pageTitle: {
key: "importantNotice",
},
},
},
{
path: "setup",
component: NewDeviceVerificationNoticePageTwoComponent,
data: {
pageIcon: VaultIcons.UserLock,
pageTitle: {
key: "setupTwoStepLogin",
},
},
},
],
},
...extensionRefreshSwap(TabsComponent, TabsV2Component, {
path: "tabs",
data: { elevation: 0 } satisfies RouteDataProperties,
Expand All @@ -730,7 +767,7 @@ const routes: Routes = [
},
...extensionRefreshSwap(VaultFilterComponent, VaultV2Component, {
path: "vault",
canActivate: [authGuard],
canActivate: [authGuard, NewDeviceVerificationNoticeGuard],
canDeactivate: [clearVaultStateGuard],
data: { elevation: 0 } satisfies RouteDataProperties,
}),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<popup-page>
<popup-page [loading]="!cipher">
<popup-header slot="header" pageTitle="{{ 'passwordHistory' | i18n }}" showBackButton>
<ng-container slot="end">
<app-pop-out></app-pop-out>
</ng-container>
</popup-header>
<vault-password-history-view *ngIf="cipherId" [cipherId]="cipherId" />
<vault-password-history-view *ngIf="cipher" [cipher]="cipher" />
</popup-page>
Original file line number Diff line number Diff line change
@@ -1,56 +1,76 @@
import { ComponentFixture, TestBed } from "@angular/core/testing";
import { ComponentFixture, fakeAsync, TestBed, tick } from "@angular/core/testing";
import { ActivatedRoute } from "@angular/router";
import { mock } from "jest-mock-extended";
import { Subject } from "rxjs";
import { BehaviorSubject, Subject } from "rxjs";

import { WINDOW } from "@bitwarden/angular/services/injection-tokens";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";

import { PopupRouterCacheService } from "../../../../../platform/popup/view-cache/popup-router-cache.service";

import { PasswordHistoryV2Component } from "./vault-password-history-v2.component";

describe("PasswordHistoryV2Component", () => {
let component: PasswordHistoryV2Component;
let fixture: ComponentFixture<PasswordHistoryV2Component>;
const params$ = new Subject();

const mockCipherView = {
id: "111-222-333",
name: "cipher one",
} as CipherView;

const mockCipher = {
decrypt: jest.fn().mockResolvedValue(mockCipherView),
} as unknown as Cipher;

const back = jest.fn().mockResolvedValue(undefined);
const getCipher = jest.fn().mockResolvedValue(mockCipher);

beforeEach(async () => {
back.mockClear();
getCipher.mockClear();

await TestBed.configureTestingModule({
imports: [PasswordHistoryV2Component],
providers: [
{ provide: WINDOW, useValue: window },
{ provide: PlatformUtilsService, useValue: mock<PlatformUtilsService>() },
{ provide: ConfigService, useValue: mock<ConfigService>() },
{ provide: CipherService, useValue: mock<CipherService>() },
{ provide: AccountService, useValue: mock<AccountService>() },
{ provide: CipherService, useValue: mock<CipherService>({ get: getCipher }) },
{
provide: AccountService,
useValue: mock<AccountService>({
activeAccount$: new BehaviorSubject({ id: "acct-1" } as Account),
}),
},
{ provide: PopupRouterCacheService, useValue: { back } },
{ provide: ActivatedRoute, useValue: { queryParams: params$ } },
{ provide: I18nService, useValue: { t: (key: string) => key } },
],
}).compileComponents();

fixture = TestBed.createComponent(PasswordHistoryV2Component);
component = fixture.componentInstance;
fixture.detectChanges();
});

it("sets the cipherId from the params", () => {
params$.next({ cipherId: "444-33-33-1111" });
it("loads the cipher from params the cipherId from the params", fakeAsync(() => {
params$.next({ cipherId: mockCipherView.id });

expect(component["cipherId"]).toBe("444-33-33-1111");
});
tick(100);

expect(getCipher).toHaveBeenCalledWith(mockCipherView.id);
}));

it("navigates back when a cipherId is not in the params", () => {
params$.next({});

expect(back).toHaveBeenCalledTimes(1);
expect(getCipher).not.toHaveBeenCalled();
});
});
Loading

0 comments on commit 9675d73

Please sign in to comment.