Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโ€™ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PM-15063] Show banner in web app when user has at least one pending authrequest #13147

Open
wants to merge 36 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
6729969
Add banner to vault
alec-livefront Jan 20, 2025
1f0f58e
Merge branch 'main' into auth/pm-15063/pending-auth-request-banner
alec-livefront Jan 20, 2025
cfbbb35
Add real-time-update to device table
alec-livefront Jan 21, 2025
3d2a6a8
test cleanup
alec-livefront Jan 21, 2025
c898516
add comments
alec-livefront Jan 21, 2025
089731a
remove commented code
alec-livefront Jan 21, 2025
5988e4c
Merge branch 'main' into auth/pm-15063/pending-auth-request-banner
alec-livefront Jan 24, 2025
7f779e8
WIP add logging
alec-livefront Jan 27, 2025
c142058
Merge branch 'main' into auth/pm-15063/pending-auth-request-banner
alec-livefront Jan 27, 2025
105dcff
Subscribe to openLoginApproval to add new device to table
alec-livefront Jan 28, 2025
d458c6c
Use allMessages$ observable
alec-livefront Jan 28, 2025
8a206fa
Add messageListener to vault-banners.component
alec-livefront Jan 29, 2025
971ab3a
Update tests
alec-livefront Jan 29, 2025
cf54cb9
Clean up tests and adhere to project patterns
alec-livefront Jan 29, 2025
62e2e57
Cleanup
alec-livefront Jan 29, 2025
4ea8513
Remove ts-strict-ignore
alec-livefront Jan 29, 2025
d6d4d5b
Merge branch 'main' into auth/pm-15063/pending-auth-request-banner
alec-livefront Jan 29, 2025
ff42ca5
strict type fixes
alec-livefront Jan 29, 2025
548bfed
remove private for testability
alec-livefront Jan 29, 2025
ab7df65
strict type fixes
alec-livefront Jan 29, 2025
a54da01
Fix strict type error and improve comments
alec-livefront Jan 29, 2025
191e83b
Merge branch 'main' into auth/pm-15063/pending-auth-request-banner
alec-livefront Jan 30, 2025
9853cde
Merge branch 'main' into auth/pm-15063/pending-auth-request-banner
alec-livefront Feb 5, 2025
271376d
Add back protected delimiter
alec-livefront Feb 5, 2025
3cccbbc
Merge branch 'main' into auth/pm-15063/pending-auth-request-banner
alec-livefront Feb 12, 2025
a2f9fa9
Remove unused variable
alec-livefront Feb 12, 2025
6fe6af1
Await loadDevices to avoid race condition
alec-livefront Feb 12, 2025
899ab33
Add comment
alec-livefront Feb 12, 2025
58644f3
Rename handleAuthRequest method for clarity
alec-livefront Feb 12, 2025
8046442
Fix issue with takeUntilDestroyed
alec-livefront Feb 12, 2025
8729d89
Add device identifier
alec-livefront Feb 12, 2025
c5fff20
Merge branch 'main' into auth/pm-15063/pending-auth-request-banner
alec-livefront Feb 12, 2025
f818e34
Add actual device ID and firstLogin if it exists
alec-livefront Feb 13, 2025
da324a1
Merge branch 'main' into auth/pm-15063/pending-auth-request-banner
alec-livefront Feb 13, 2025
b50a12d
Merge branch 'main' into auth/pm-15063/pending-auth-request-banner
alec-livefront Feb 13, 2025
e0ff15d
Update recovery code text
alec-livefront Feb 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/web/src/app/auth/recover-two-factor.component.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<form [formGroup]="formGroup" [bitSubmit]="submit">
<p bitTypography="body1">
{{ "recoverAccountTwoStepDesc" | i18n }}
{{ "logInBelowUsingYourSingleUseRecoveryCode" | i18n }}
<a
bitLink
href="https://bitwarden.com/help/lost-two-step-device/"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
import { ComponentFixture, TestBed } from "@angular/core/testing";
import { RouterTestingModule } from "@angular/router/testing";
import { Subject } from "rxjs";

import { AuthRequestApiService } from "@bitwarden/auth/common";
import { DevicesServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices/devices.service.abstraction";
import { DeviceView } from "@bitwarden/common/auth/abstractions/devices/views/device.view";
import { DeviceType } from "@bitwarden/common/enums";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
import { MessageListener } from "@bitwarden/common/platform/messaging";
import { DialogService, ToastService, TableModule, PopoverModule } from "@bitwarden/components";

import { SharedModule } from "../../../shared";
import { VaultBannersService } from "../../../vault/individual-vault/vault-banners/services/vault-banners.service";

import { DeviceManagementComponent } from "./device-management.component";

class MockResizeObserver {
observe = jest.fn();
unobserve = jest.fn();
disconnect = jest.fn();
}

global.ResizeObserver = MockResizeObserver;

interface Message {
command: string;
notificationId?: string;
}

describe("DeviceManagementComponent", () => {
let fixture: ComponentFixture<DeviceManagementComponent>;
let messageSubject: Subject<Message>;
let mockDevices: DeviceView[];
let vaultBannersService: VaultBannersService;

const mockDeviceResponse = {
id: "test-id",
requestDeviceType: "test-type",
requestDeviceTypeValue: DeviceType.Android,
requestDeviceIdentifier: "test-identifier",
requestIpAddress: "127.0.0.1",
creationDate: new Date().toISOString(),
responseDate: null,
key: "test-key",
masterPasswordHash: null,
publicKey: "test-public-key",
requestApproved: false,
origin: "test-origin",
};

beforeEach(async () => {
messageSubject = new Subject<Message>();
mockDevices = [];

await TestBed.configureTestingModule({
imports: [
RouterTestingModule,
SharedModule,
TableModule,
PopoverModule,
DeviceManagementComponent,
],
providers: [
{
provide: DevicesServiceAbstraction,
useValue: {
getDevices$: jest.fn().mockReturnValue(mockDevices),
updateTrustedDeviceKeys: jest.fn(),
},
},
{
provide: AuthRequestApiService,
useValue: {
getAuthRequest: jest.fn().mockResolvedValue(mockDeviceResponse),
},
},
{
provide: MessageListener,
useValue: {
allMessages$: messageSubject.asObservable(),
},
},
{
provide: DialogService,
useValue: {
openSimpleDialog: jest.fn(),
},
},
{
provide: ToastService,
useValue: {
success: jest.fn(),
error: jest.fn(),
},
},
{
provide: VaultBannersService,
useValue: {
shouldShowPendingAuthRequestBanner: jest.fn(),
},
},
{
provide: I18nService,
useValue: {
t: jest.fn((key: string) => key),
},
},
{
provide: ValidationService,
useValue: {
showError: jest.fn(),
},
},
],
}).compileComponents();

fixture = TestBed.createComponent(DeviceManagementComponent);

vaultBannersService = TestBed.inject(VaultBannersService);
});

describe("message listener", () => {
beforeEach(() => {
jest.spyOn(vaultBannersService, "shouldShowPendingAuthRequestBanner").mockResolvedValue(true);
});

it("adds device to table when auth request message received", async () => {
const message: Message = {
command: "openLoginApproval",
notificationId: "test-id",
};
messageSubject.next(message);
await fixture.whenStable();

const tableData = fixture.componentInstance.dataSource.data;
expect(tableData.length).toBeGreaterThan(0);
expect(tableData[0].id).toBe("test-id");
expect(tableData[0].hasPendingAuthRequest).toBe(true);
});

it("ignores other message types", async () => {
const initialDataLength = fixture.componentInstance.dataSource.data.length;
const message: Message = { command: "other", notificationId: "test-id" };
messageSubject.next(message);
await fixture.whenStable();

expect(fixture.componentInstance.dataSource.data.length).toBe(initialDataLength);
});

it("stops listening when component is destroyed", async () => {
fixture.destroy();
const message: Message = {
command: "openLoginApproval",
notificationId: "test-id",
};
messageSubject.next(message);
expect(fixture.componentInstance.dataSource.data.length).toBe(0);
});
});
});
Loading
Loading