Add bulk auto-confirm functionality on user login

- Implemented the `initBulkAutoConfirmOnLoginSweep` method in `DefaultAutomaticUserConfirmationService` to trigger bulk auto-confirmation for users transitioning from Locked to Unlocked status.
- Enhanced unit tests to verify the behavior of the sweep service under different authentication states.
- Updated the service to utilize `AuthenticationStatus` for improved state management during user confirmation processes.

These changes streamline the user confirmation workflow, ensuring timely processing of pending confirmations upon user login.
pull/20331/head
JaredScar 3 weeks ago
parent 51fca8ef6e
commit eb4938fcf6
No known key found for this signature in database

@ -19,6 +19,7 @@ import { Organization } from "@bitwarden/common/admin-console/models/domain/orga
import { ProfileOrganizationResponse } from "@bitwarden/common/admin-console/models/response/profile-organization.response";
import { 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 { ListResponse } from "@bitwarden/common/models/response/list.response";
import { UserKeyResponse } from "@bitwarden/common/models/response/user-key.response";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
@ -27,6 +28,8 @@ import { FakeStateProvider, mockAccountServiceWith } from "@bitwarden/common/spe
import { OrganizationId, UserId } from "@bitwarden/common/types/guid";
import { newGuid } from "@bitwarden/guid";
import { AUTO_CONFIRM_STATE, AutoConfirmState } from "../models/auto-confirm-state.model";
import { DefaultAutomaticUserConfirmationService } from "./default-auto-confirm.service";
describe("DefaultAutomaticUserConfirmationService", () => {
@ -636,4 +639,73 @@ describe("DefaultAutomaticUserConfirmationService", () => {
expect(organizationUserApiService.getPendingAutoConfirmUsers).not.toHaveBeenCalled();
});
});
describe("initBulkAutoConfirmOnLoginSweep", () => {
let accountsSubject: BehaviorSubject<Record<string, unknown>>;
let authStatusSubject: BehaviorSubject<AuthenticationStatus>;
const createSweepService = () =>
new DefaultAutomaticUserConfirmationService(
apiService,
organizationUserService,
stateProvider,
organizationService,
organizationUserApiService,
policyService,
authService,
accountService,
configService,
);
beforeEach(() => {
accountsSubject = new BehaviorSubject({} as Record<string, unknown>);
accountService.accounts$ = accountsSubject;
});
it("should trigger sweep when a user account appears already in the Unlocked state (fresh login)", () => {
authStatusSubject = new BehaviorSubject<AuthenticationStatus>(AuthenticationStatus.Unlocked);
authService.authStatusFor$.mockReturnValue(authStatusSubject);
const svc = createSweepService();
const spy = jest
.spyOn(svc as any, "bulkAutoConfirmPendingUsers")
.mockResolvedValue(undefined);
// Account becomes visible in accounts$ for the first time while already Unlocked
accountsSubject.next({ [mockUserId]: {} });
expect(spy).toHaveBeenCalledWith(mockUserId);
});
it("should trigger sweep when a user transitions from Locked to Unlocked", () => {
authStatusSubject = new BehaviorSubject<AuthenticationStatus>(AuthenticationStatus.Locked);
authService.authStatusFor$.mockReturnValue(authStatusSubject);
const svc = createSweepService();
const spy = jest
.spyOn(svc as any, "bulkAutoConfirmPendingUsers")
.mockResolvedValue(undefined);
accountsSubject.next({ [mockUserId]: {} });
expect(spy).not.toHaveBeenCalled();
authStatusSubject.next(AuthenticationStatus.Unlocked);
expect(spy).toHaveBeenCalledWith(mockUserId);
});
it("should not trigger sweep when status stays Locked", () => {
authStatusSubject = new BehaviorSubject<AuthenticationStatus>(AuthenticationStatus.Locked);
authService.authStatusFor$.mockReturnValue(authStatusSubject);
const svc = createSweepService();
const spy = jest
.spyOn(svc as any, "bulkAutoConfirmPendingUsers")
.mockResolvedValue(undefined);
accountsSubject.next({ [mockUserId]: {} });
expect(spy).not.toHaveBeenCalled();
});
});
});

@ -7,6 +7,7 @@ import {
merge,
Observable,
pairwise,
startWith,
switchMap,
} from "rxjs";
@ -55,6 +56,7 @@ export class DefaultAutomaticUserConfirmationService implements AutomaticUserCon
merge(
...Object.keys(accounts).map((userId) =>
this.authService.authStatusFor$(userId as UserId).pipe(
startWith(AuthenticationStatus.LoggedOut),
distinctUntilChanged(),
pairwise(),
filter(

Loading…
Cancel
Save