Files
clients/libs/angular/src/auth/guards/auth.guard.spec.ts
Maciej Zieniuk e73f902aee [PM-18576] Fix missing user id on remove password (#13777)
* Passed in userId on RemovePasswordComponent.

* Added userId on other references to KeyConnectorService methods

* remove password component refactor, test coverage, enabled strict

* explicit user id provided to key connector service

* redirect to / instead when user not logged in or not managing organization

* key connector service explicit user id

* key connector service no longer requires account service

* key connector service missing null type

* cli convert to key connector unit tests

* remove unnecessary SyncService

* error toast not showing on ErrorResponse

* bad import due to merge conflict

* bad import due to merge conflict

* missing loading in remove password component for browser extension

* error handling in remove password component

* organization observable race condition in key-connector

* usesKeyConnector always returns boolean

* unit test coverage

* key connector reactive

* reactive key connector service

* introducing convertAccountRequired$

* cli build fix

* moving message sending side effect to sync

* key connector service unit tests

* fix unit tests

* unit tests in wrong place after KM code ownership move

* infinite page reload

* failing unit tests

* failing unit tests

---------

Co-authored-by: Todd Martin <tmartin@bitwarden.com>
2025-05-19 08:51:46 +02:00

180 lines
7.2 KiB
TypeScript

import { TestBed } from "@angular/core/testing";
import { Router } from "@angular/router";
import { RouterTestingModule } from "@angular/router/testing";
import { MockProxy, mock } from "jest-mock-extended";
import { BehaviorSubject, of } from "rxjs";
import { EmptyComponent } from "@bitwarden/angular/platform/guard/feature-flag.guard.spec";
import {
Account,
AccountInfo,
AccountService,
} from "@bitwarden/common/auth/abstractions/account.service";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
import { KeyConnectorService } from "@bitwarden/common/key-management/key-connector/abstractions/key-connector.service";
import { MasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { UserId } from "@bitwarden/common/types/guid";
import { authGuard } from "./auth.guard";
describe("AuthGuard", () => {
const setup = (
authStatus: AuthenticationStatus,
forceSetPasswordReason: ForceSetPasswordReason,
keyConnectorServiceRequiresAccountConversion: boolean = false,
) => {
const authService: MockProxy<AuthService> = mock<AuthService>();
authService.getAuthStatus.mockResolvedValue(authStatus);
const messagingService: MockProxy<MessagingService> = mock<MessagingService>();
const keyConnectorService: MockProxy<KeyConnectorService> = mock<KeyConnectorService>();
keyConnectorService.convertAccountRequired$ = of(keyConnectorServiceRequiresAccountConversion);
const accountService: MockProxy<AccountService> = mock<AccountService>();
const activeAccountSubject = new BehaviorSubject<Account | null>(null);
accountService.activeAccount$ = activeAccountSubject;
activeAccountSubject.next(
Object.assign(
{
name: "Test User 1",
email: "test@email.com",
emailVerified: true,
} as AccountInfo,
{ id: "test-id" as UserId },
),
);
const forceSetPasswordReasonSubject = new BehaviorSubject<ForceSetPasswordReason>(
forceSetPasswordReason,
);
const masterPasswordService: MockProxy<MasterPasswordServiceAbstraction> =
mock<MasterPasswordServiceAbstraction>();
masterPasswordService.forceSetPasswordReason$.mockReturnValue(forceSetPasswordReasonSubject);
const testBed = TestBed.configureTestingModule({
imports: [
RouterTestingModule.withRoutes([
{ path: "", component: EmptyComponent },
{ path: "guarded-route", component: EmptyComponent, canActivate: [authGuard] },
{ path: "lock", component: EmptyComponent },
{ path: "set-password", component: EmptyComponent },
{ path: "update-temp-password", component: EmptyComponent },
{ path: "remove-password", component: EmptyComponent },
]),
],
providers: [
{ provide: AuthService, useValue: authService },
{ provide: MessagingService, useValue: messagingService },
{ provide: KeyConnectorService, useValue: keyConnectorService },
{ provide: AccountService, useValue: accountService },
{ provide: MasterPasswordServiceAbstraction, useValue: masterPasswordService },
],
});
return {
router: testBed.inject(Router),
};
};
it("should be created", () => {
const { router } = setup(AuthenticationStatus.LoggedOut, ForceSetPasswordReason.None);
expect(router).toBeTruthy();
});
it("should return allow access to the guarded route when user is logged in & unlocked", async () => {
const { router } = setup(AuthenticationStatus.Unlocked, ForceSetPasswordReason.None);
await router.navigate(["guarded-route"]);
expect(router.url).toBe("/guarded-route");
});
it("should redirect to /lock when user is locked", async () => {
const { router } = setup(AuthenticationStatus.Locked, ForceSetPasswordReason.None);
await router.navigate(["guarded-route"]);
expect(router.url).toContain("/lock");
});
it("should redirect to / when user is logged out", async () => {
const { router } = setup(AuthenticationStatus.LoggedOut, ForceSetPasswordReason.None);
await router.navigate(["guarded-route"]);
expect(router.url).toBe("/");
});
it("should redirect to /remove-password if keyconnector service requires account conversion", async () => {
const { router } = setup(AuthenticationStatus.Unlocked, ForceSetPasswordReason.None, true);
await router.navigate(["guarded-route"]);
expect(router.url).toBe("/remove-password");
});
it("should redirect to set-password when user is TDE user without password and has password reset permission", async () => {
const { router } = setup(
AuthenticationStatus.Unlocked,
ForceSetPasswordReason.TdeUserWithoutPasswordHasPasswordResetPermission,
);
await router.navigate(["guarded-route"]);
expect(router.url).toContain("/set-password");
});
it("should redirect to update-temp-password when user has force set password reason", async () => {
const { router } = setup(
AuthenticationStatus.Unlocked,
ForceSetPasswordReason.AdminForcePasswordReset,
);
await router.navigate(["guarded-route"]);
expect(router.url).toContain("/update-temp-password");
});
it("should redirect to update-temp-password when user has weak password", async () => {
const { router } = setup(
AuthenticationStatus.Unlocked,
ForceSetPasswordReason.WeakMasterPassword,
);
await router.navigate(["guarded-route"]);
expect(router.url).toContain("/update-temp-password");
});
it("should allow navigation to set-password when the user is unlocked, is a TDE user without password, and has password reset permission", async () => {
const { router } = setup(
AuthenticationStatus.Unlocked,
ForceSetPasswordReason.TdeUserWithoutPasswordHasPasswordResetPermission,
);
await router.navigate(["/set-password"]);
expect(router.url).toContain("/set-password");
});
it("should allow navigation to update-temp-password when the user is unlocked and has admin force password reset permission", async () => {
const { router } = setup(
AuthenticationStatus.Unlocked,
ForceSetPasswordReason.AdminForcePasswordReset,
);
await router.navigate(["/update-temp-password"]);
expect(router.url).toContain("/update-temp-password");
});
it("should allow navigation to update-temp-password when the user is unlocked and has weak password", async () => {
const { router } = setup(
AuthenticationStatus.Unlocked,
ForceSetPasswordReason.WeakMasterPassword,
);
await router.navigate(["/update-temp-password"]);
expect(router.url).toContain("/update-temp-password");
});
it("should allow navigation to remove-password when the user is unlocked and has 'none' password reset permission", async () => {
const { router } = setup(AuthenticationStatus.Unlocked, ForceSetPasswordReason.None);
await router.navigate(["/remove-password"]);
expect(router.url).toContain("/remove-password");
});
});