mirror of
https://github.com/bitwarden/browser
synced 2026-02-18 10:23:52 +00:00
[PM-26901] Add notification handler for auto confirm (#18886)
* add notification handler for auto confirm * add missing state check * fix test * isolate angular specific code from shared lib code * clean up * use autoconfirm method * fix test
This commit is contained in:
@@ -56,6 +56,7 @@ import {
|
||||
UserDecryptionOptionsService,
|
||||
UserDecryptionOptionsServiceAbstraction,
|
||||
} from "@bitwarden/auth/common";
|
||||
import { AutomaticUserConfirmationService } from "@bitwarden/auto-confirm";
|
||||
import { ApiService as ApiServiceAbstraction } from "@bitwarden/common/abstractions/api.service";
|
||||
import { AuditService as AuditServiceAbstraction } from "@bitwarden/common/abstractions/audit.service";
|
||||
import { EventCollectionService as EventCollectionServiceAbstraction } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||
@@ -1079,6 +1080,7 @@ const safeProviders: SafeProvider[] = [
|
||||
AuthRequestAnsweringService,
|
||||
ConfigService,
|
||||
InternalPolicyService,
|
||||
AutomaticUserConfirmationService,
|
||||
],
|
||||
}),
|
||||
safeProvider({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||
import { OrganizationId } from "@bitwarden/common/types/guid";
|
||||
import { UserId } from "@bitwarden/user-core";
|
||||
|
||||
import { AutoConfirmState } from "../models/auto-confirm-state.model";
|
||||
@@ -27,12 +27,12 @@ export abstract class AutomaticUserConfirmationService {
|
||||
/**
|
||||
* Calls the API endpoint to initiate automatic user confirmation.
|
||||
* @param userId The userId of the logged in admin performing auto confirmation. This is neccesary to perform the key exchange and for permissions checks.
|
||||
* @param confirmingUserId The userId of the user being confirmed.
|
||||
* @param organization the organization the user is being auto confirmed to.
|
||||
* @param confirmedUserId The userId of the member being confirmed.
|
||||
* @param organization the organization the member is being auto confirmed to.
|
||||
**/
|
||||
abstract autoConfirmUser(
|
||||
userId: UserId,
|
||||
confirmingUserId: UserId,
|
||||
organization: Organization,
|
||||
confirmedUserId: UserId,
|
||||
organization: OrganizationId,
|
||||
): Promise<void>;
|
||||
}
|
||||
|
||||
@@ -3,14 +3,13 @@ import { Router, UrlTree } from "@angular/router";
|
||||
import { mock, MockProxy } from "jest-mock-extended";
|
||||
import { BehaviorSubject, firstValueFrom, Observable, of } from "rxjs";
|
||||
|
||||
import { AutomaticUserConfirmationService } from "@bitwarden/auto-confirm";
|
||||
import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
import { ToastService } from "@bitwarden/components";
|
||||
import { newGuid } from "@bitwarden/guid";
|
||||
|
||||
import { AutomaticUserConfirmationService } from "../abstractions";
|
||||
|
||||
import { canAccessAutoConfirmSettings } from "./automatic-user-confirmation-settings.guard";
|
||||
|
||||
describe("canAccessAutoConfirmSettings", () => {
|
||||
@@ -2,13 +2,12 @@ import { inject } from "@angular/core";
|
||||
import { CanActivateFn, Router } from "@angular/router";
|
||||
import { map, switchMap } from "rxjs";
|
||||
|
||||
import { AutomaticUserConfirmationService } from "@bitwarden/auto-confirm";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { filterOutNullish } from "@bitwarden/common/vault/utils/observable-utilities";
|
||||
import { ToastService } from "@bitwarden/components";
|
||||
|
||||
import { AutomaticUserConfirmationService } from "../abstractions";
|
||||
|
||||
export const canAccessAutoConfirmSettings: CanActivateFn = () => {
|
||||
const accountService = inject(AccountService);
|
||||
const autoConfirmService = inject(AutomaticUserConfirmationService);
|
||||
8
libs/auto-confirm/src/angular/index.ts
Normal file
8
libs/auto-confirm/src/angular/index.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
// Re-export core auto-confirm functionality for convenience
|
||||
export * from "../abstractions";
|
||||
export * from "../models";
|
||||
export * from "../services";
|
||||
|
||||
// Angular-specific exports
|
||||
export * from "./components";
|
||||
export * from "./guards";
|
||||
@@ -1,5 +1,3 @@
|
||||
export * from "./abstractions";
|
||||
export * from "./components";
|
||||
export * from "./guards";
|
||||
export * from "./models";
|
||||
export * from "./services";
|
||||
|
||||
@@ -377,48 +377,70 @@ describe("DefaultAutomaticUserConfirmationService", () => {
|
||||
defaultUserCollectionName: "encrypted-collection",
|
||||
} as OrganizationUserConfirmRequest;
|
||||
|
||||
beforeEach(() => {
|
||||
beforeEach(async () => {
|
||||
const organizations$ = new BehaviorSubject<Organization[]>([mockOrganization]);
|
||||
organizationService.organizations$.mockReturnValue(organizations$);
|
||||
configService.getFeatureFlag$.mockReturnValue(of(true));
|
||||
policyService.policyAppliesToUser$.mockReturnValue(of(true));
|
||||
|
||||
// Enable auto-confirm configuration for the user
|
||||
const enabledConfig = new AutoConfirmState();
|
||||
enabledConfig.enabled = true;
|
||||
await stateProvider.setUserState(
|
||||
AUTO_CONFIRM_STATE,
|
||||
{ [mockUserId]: enabledConfig },
|
||||
mockUserId,
|
||||
);
|
||||
|
||||
apiService.getUserPublicKey.mockResolvedValue({
|
||||
publicKey: mockPublicKey,
|
||||
} as UserKeyResponse);
|
||||
jest.spyOn(Utils, "fromB64ToArray").mockReturnValue(mockPublicKeyArray);
|
||||
organizationUserService.buildConfirmRequest.mockReturnValue(of(mockConfirmRequest));
|
||||
organizationUserApiService.postOrganizationUserConfirm.mockResolvedValue(undefined);
|
||||
organizationUserApiService.postOrganizationUserAutoConfirm.mockResolvedValue(undefined);
|
||||
});
|
||||
|
||||
it("should successfully auto-confirm a user", async () => {
|
||||
await service.autoConfirmUser(mockUserId, mockConfirmingUserId, mockOrganization);
|
||||
it("should successfully auto-confirm a user with organizationId", async () => {
|
||||
await service.autoConfirmUser(mockUserId, mockConfirmingUserId, mockOrganizationId);
|
||||
|
||||
expect(apiService.getUserPublicKey).toHaveBeenCalledWith(mockUserId);
|
||||
expect(organizationUserService.buildConfirmRequest).toHaveBeenCalledWith(
|
||||
mockOrganization,
|
||||
mockPublicKeyArray,
|
||||
);
|
||||
expect(organizationUserApiService.postOrganizationUserConfirm).toHaveBeenCalledWith(
|
||||
expect(organizationUserApiService.postOrganizationUserAutoConfirm).toHaveBeenCalledWith(
|
||||
mockOrganizationId,
|
||||
mockConfirmingUserId,
|
||||
mockConfirmRequest,
|
||||
);
|
||||
});
|
||||
|
||||
it("should not confirm user when canManageAutoConfirm returns false", async () => {
|
||||
it("should return early when canManageAutoConfirm returns false", async () => {
|
||||
configService.getFeatureFlag$.mockReturnValue(of(false));
|
||||
|
||||
await expect(
|
||||
service.autoConfirmUser(mockUserId, mockConfirmingUserId, mockOrganization),
|
||||
).rejects.toThrow("Cannot automatically confirm user (insufficient permissions)");
|
||||
await service.autoConfirmUser(mockUserId, mockConfirmingUserId, mockOrganizationId);
|
||||
|
||||
expect(apiService.getUserPublicKey).not.toHaveBeenCalled();
|
||||
expect(organizationUserApiService.postOrganizationUserConfirm).not.toHaveBeenCalled();
|
||||
expect(organizationUserApiService.postOrganizationUserAutoConfirm).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should return early when auto-confirm is disabled in configuration", async () => {
|
||||
const disabledConfig = new AutoConfirmState();
|
||||
disabledConfig.enabled = false;
|
||||
await stateProvider.setUserState(
|
||||
AUTO_CONFIRM_STATE,
|
||||
{ [mockUserId]: disabledConfig },
|
||||
mockUserId,
|
||||
);
|
||||
|
||||
await service.autoConfirmUser(mockUserId, mockConfirmingUserId, mockOrganizationId);
|
||||
|
||||
expect(apiService.getUserPublicKey).not.toHaveBeenCalled();
|
||||
expect(organizationUserApiService.postOrganizationUserAutoConfirm).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should build confirm request with organization and public key", async () => {
|
||||
await service.autoConfirmUser(mockUserId, mockConfirmingUserId, mockOrganization);
|
||||
await service.autoConfirmUser(mockUserId, mockConfirmingUserId, mockOrganizationId);
|
||||
|
||||
expect(organizationUserService.buildConfirmRequest).toHaveBeenCalledWith(
|
||||
mockOrganization,
|
||||
@@ -427,10 +449,10 @@ describe("DefaultAutomaticUserConfirmationService", () => {
|
||||
});
|
||||
|
||||
it("should call API with correct parameters", async () => {
|
||||
await service.autoConfirmUser(mockUserId, mockConfirmingUserId, mockOrganization);
|
||||
await service.autoConfirmUser(mockUserId, mockConfirmingUserId, mockOrganizationId);
|
||||
|
||||
expect(organizationUserApiService.postOrganizationUserConfirm).toHaveBeenCalledWith(
|
||||
mockOrganization.id,
|
||||
expect(organizationUserApiService.postOrganizationUserAutoConfirm).toHaveBeenCalledWith(
|
||||
mockOrganizationId,
|
||||
mockConfirmingUserId,
|
||||
mockConfirmRequest,
|
||||
);
|
||||
@@ -441,10 +463,10 @@ describe("DefaultAutomaticUserConfirmationService", () => {
|
||||
apiService.getUserPublicKey.mockRejectedValue(apiError);
|
||||
|
||||
await expect(
|
||||
service.autoConfirmUser(mockUserId, mockConfirmingUserId, mockOrganization),
|
||||
service.autoConfirmUser(mockUserId, mockConfirmingUserId, mockOrganizationId),
|
||||
).rejects.toThrow("API Error");
|
||||
|
||||
expect(organizationUserApiService.postOrganizationUserConfirm).not.toHaveBeenCalled();
|
||||
expect(organizationUserApiService.postOrganizationUserAutoConfirm).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should handle buildConfirmRequest errors gracefully", async () => {
|
||||
@@ -452,10 +474,10 @@ describe("DefaultAutomaticUserConfirmationService", () => {
|
||||
organizationUserService.buildConfirmRequest.mockReturnValue(throwError(() => buildError));
|
||||
|
||||
await expect(
|
||||
service.autoConfirmUser(mockUserId, mockConfirmingUserId, mockOrganization),
|
||||
service.autoConfirmUser(mockUserId, mockConfirmingUserId, mockOrganizationId),
|
||||
).rejects.toThrow("Build Error");
|
||||
|
||||
expect(organizationUserApiService.postOrganizationUserConfirm).not.toHaveBeenCalled();
|
||||
expect(organizationUserApiService.postOrganizationUserAutoConfirm).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -8,10 +8,11 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { InternalOrganizationServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { getById } from "@bitwarden/common/platform/misc";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { OrganizationId } from "@bitwarden/common/types/guid";
|
||||
import { StateProvider } from "@bitwarden/state";
|
||||
import { UserId } from "@bitwarden/user-core";
|
||||
|
||||
@@ -66,26 +67,44 @@ export class DefaultAutomaticUserConfirmationService implements AutomaticUserCon
|
||||
|
||||
async autoConfirmUser(
|
||||
userId: UserId,
|
||||
confirmingUserId: UserId,
|
||||
organization: Organization,
|
||||
confirmedUserId: UserId,
|
||||
organizationId: OrganizationId,
|
||||
): Promise<void> {
|
||||
const canManage = await firstValueFrom(this.canManageAutoConfirm$(userId));
|
||||
|
||||
if (!canManage) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only initiate auto confirmation if the local client setting has been turned on
|
||||
const autoConfirmEnabled = await firstValueFrom(
|
||||
this.configuration$(userId).pipe(map((state) => state.enabled)),
|
||||
);
|
||||
|
||||
if (!autoConfirmEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const organization$ = this.organizationService.organizations$(userId).pipe(
|
||||
getById(organizationId),
|
||||
map((organization) => {
|
||||
if (organization == null) {
|
||||
throw new Error("Organization not found");
|
||||
}
|
||||
return organization;
|
||||
}),
|
||||
);
|
||||
|
||||
const publicKeyResponse = await this.apiService.getUserPublicKey(userId);
|
||||
const publicKey = Utils.fromB64ToArray(publicKeyResponse.publicKey);
|
||||
|
||||
await firstValueFrom(
|
||||
this.canManageAutoConfirm$(userId).pipe(
|
||||
map((canManage) => {
|
||||
if (!canManage) {
|
||||
throw new Error("Cannot automatically confirm user (insufficient permissions)");
|
||||
}
|
||||
return canManage;
|
||||
}),
|
||||
switchMap(() => this.apiService.getUserPublicKey(userId)),
|
||||
map((publicKeyResponse) => Utils.fromB64ToArray(publicKeyResponse.publicKey)),
|
||||
switchMap((publicKey) =>
|
||||
this.organizationUserService.buildConfirmRequest(organization, publicKey),
|
||||
),
|
||||
organization$.pipe(
|
||||
switchMap((org) => this.organizationUserService.buildConfirmRequest(org, publicKey)),
|
||||
switchMap((request) =>
|
||||
this.organizationUserApiService.postOrganizationUserConfirm(
|
||||
organization.id,
|
||||
confirmingUserId,
|
||||
this.organizationUserApiService.postOrganizationUserAutoConfirm(
|
||||
organizationId,
|
||||
confirmedUserId,
|
||||
request,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -35,4 +35,5 @@ export enum NotificationType {
|
||||
ProviderBankAccountVerified = 24,
|
||||
|
||||
SyncPolicy = 25,
|
||||
AutoConfirmMember = 26,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
import { NotificationType } from "../../enums";
|
||||
|
||||
import { AutoConfirmMemberNotification, NotificationResponse } from "./notification.response";
|
||||
|
||||
describe("NotificationResponse", () => {
|
||||
describe("AutoConfirmMemberNotification", () => {
|
||||
it("should parse AutoConfirmMemberNotification payload", () => {
|
||||
const response = {
|
||||
ContextId: "context-123",
|
||||
Type: NotificationType.AutoConfirmMember,
|
||||
Payload: {
|
||||
TargetUserId: "target-user-id",
|
||||
UserId: "user-id",
|
||||
OrganizationId: "org-id",
|
||||
},
|
||||
};
|
||||
|
||||
const notification = new NotificationResponse(response);
|
||||
|
||||
expect(notification.type).toBe(NotificationType.AutoConfirmMember);
|
||||
expect(notification.payload).toBeInstanceOf(AutoConfirmMemberNotification);
|
||||
expect(notification.payload.targetUserId).toBe("target-user-id");
|
||||
expect(notification.payload.userId).toBe("user-id");
|
||||
expect(notification.payload.organizationId).toBe("org-id");
|
||||
});
|
||||
|
||||
it("should handle stringified JSON payload", () => {
|
||||
const response = {
|
||||
ContextId: "context-123",
|
||||
Type: NotificationType.AutoConfirmMember,
|
||||
Payload: JSON.stringify({
|
||||
TargetUserId: "target-user-id-2",
|
||||
UserId: "user-id-2",
|
||||
OrganizationId: "org-id-2",
|
||||
}),
|
||||
};
|
||||
|
||||
const notification = new NotificationResponse(response);
|
||||
|
||||
expect(notification.type).toBe(NotificationType.AutoConfirmMember);
|
||||
expect(notification.payload).toBeInstanceOf(AutoConfirmMemberNotification);
|
||||
expect(notification.payload.targetUserId).toBe("target-user-id-2");
|
||||
expect(notification.payload.userId).toBe("user-id-2");
|
||||
expect(notification.payload.organizationId).toBe("org-id-2");
|
||||
});
|
||||
});
|
||||
|
||||
describe("AutoConfirmMemberNotification constructor", () => {
|
||||
it("should extract all properties from response", () => {
|
||||
const response = {
|
||||
TargetUserId: "target-user-id",
|
||||
UserId: "user-id",
|
||||
OrganizationId: "org-id",
|
||||
};
|
||||
|
||||
const notification = new AutoConfirmMemberNotification(response);
|
||||
|
||||
expect(notification.targetUserId).toBe("target-user-id");
|
||||
expect(notification.userId).toBe("user-id");
|
||||
expect(notification.organizationId).toBe("org-id");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -75,6 +75,9 @@ export class NotificationResponse extends BaseResponse {
|
||||
case NotificationType.SyncPolicy:
|
||||
this.payload = new SyncPolicyNotification(payload);
|
||||
break;
|
||||
case NotificationType.AutoConfirmMember:
|
||||
this.payload = new AutoConfirmMemberNotification(payload);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -210,3 +213,16 @@ export class LogOutNotification extends BaseResponse {
|
||||
this.reason = this.getResponseProperty("Reason");
|
||||
}
|
||||
}
|
||||
|
||||
export class AutoConfirmMemberNotification extends BaseResponse {
|
||||
userId: string;
|
||||
targetUserId: string;
|
||||
organizationId: string;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.targetUserId = this.getResponseProperty("TargetUserId");
|
||||
this.userId = this.getResponseProperty("UserId");
|
||||
this.organizationId = this.getResponseProperty("OrganizationId");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { BehaviorSubject, bufferCount, firstValueFrom, Subject, ObservedValueOf
|
||||
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { LogoutReason } from "@bitwarden/auth/common";
|
||||
import { AutomaticUserConfirmationService } from "@bitwarden/auto-confirm";
|
||||
import { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { AuthRequestAnsweringService } from "@bitwarden/common/auth/abstractions/auth-request-answering/auth-request-answering.service.abstraction";
|
||||
|
||||
@@ -36,6 +37,7 @@ describe("DefaultServerNotificationsService (multi-user)", () => {
|
||||
let authRequestAnsweringService: MockProxy<AuthRequestAnsweringService>;
|
||||
let configService: MockProxy<ConfigService>;
|
||||
let policyService: MockProxy<InternalPolicyService>;
|
||||
let autoConfirmService: MockProxy<AutomaticUserConfirmationService>;
|
||||
|
||||
let activeUserAccount$: BehaviorSubject<ObservedValueOf<AccountService["activeAccount$"]>>;
|
||||
let userAccounts$: BehaviorSubject<ObservedValueOf<AccountService["accounts$"]>>;
|
||||
@@ -131,6 +133,8 @@ describe("DefaultServerNotificationsService (multi-user)", () => {
|
||||
|
||||
policyService = mock<InternalPolicyService>();
|
||||
|
||||
autoConfirmService = mock<AutomaticUserConfirmationService>();
|
||||
|
||||
defaultServerNotificationsService = new DefaultServerNotificationsService(
|
||||
mock<LogService>(),
|
||||
syncService,
|
||||
@@ -145,6 +149,7 @@ describe("DefaultServerNotificationsService (multi-user)", () => {
|
||||
authRequestAnsweringService,
|
||||
configService,
|
||||
policyService,
|
||||
autoConfirmService,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import { BehaviorSubject, bufferCount, firstValueFrom, ObservedValueOf, of, Subj
|
||||
// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop.
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { LogoutReason } from "@bitwarden/auth/common";
|
||||
import { AutomaticUserConfirmationService } from "@bitwarden/auto-confirm";
|
||||
import { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||
import { AuthRequestAnsweringService } from "@bitwarden/common/auth/abstractions/auth-request-answering/auth-request-answering.service.abstraction";
|
||||
@@ -45,6 +46,7 @@ describe("NotificationsService", () => {
|
||||
let authRequestAnsweringService: MockProxy<AuthRequestAnsweringService>;
|
||||
let configService: MockProxy<ConfigService>;
|
||||
let policyService: MockProxy<InternalPolicyService>;
|
||||
let autoConfirmService: MockProxy<AutomaticUserConfirmationService>;
|
||||
|
||||
let activeAccount: BehaviorSubject<ObservedValueOf<AccountService["activeAccount$"]>>;
|
||||
let accounts: BehaviorSubject<ObservedValueOf<AccountService["accounts$"]>>;
|
||||
@@ -75,6 +77,7 @@ describe("NotificationsService", () => {
|
||||
authRequestAnsweringService = mock<AuthRequestAnsweringService>();
|
||||
configService = mock<ConfigService>();
|
||||
policyService = mock<InternalPolicyService>();
|
||||
autoConfirmService = mock<AutomaticUserConfirmationService>();
|
||||
|
||||
// For these tests, use the active-user implementation (feature flag disabled)
|
||||
configService.getFeatureFlag$.mockImplementation(() => of(true));
|
||||
@@ -128,6 +131,7 @@ describe("NotificationsService", () => {
|
||||
authRequestAnsweringService,
|
||||
configService,
|
||||
policyService,
|
||||
autoConfirmService,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -507,5 +511,29 @@ describe("NotificationsService", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("NotificationType.AutoConfirmMember", () => {
|
||||
it("should call autoConfirmService.autoConfirmUser with correct parameters", async () => {
|
||||
autoConfirmService.autoConfirmUser.mockResolvedValue();
|
||||
|
||||
const notification = new NotificationResponse({
|
||||
type: NotificationType.AutoConfirmMember,
|
||||
payload: {
|
||||
UserId: mockUser1,
|
||||
TargetUserId: "target-user-id",
|
||||
OrganizationId: "org-id",
|
||||
},
|
||||
contextId: "different-app-id",
|
||||
});
|
||||
|
||||
await sut["processNotification"](notification, mockUser1);
|
||||
|
||||
expect(autoConfirmService.autoConfirmUser).toHaveBeenCalledWith(
|
||||
mockUser1,
|
||||
"target-user-id",
|
||||
"org-id",
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop.
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { LogoutReason } from "@bitwarden/auth/common";
|
||||
import { AutomaticUserConfirmationService } from "@bitwarden/auto-confirm";
|
||||
import { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { PolicyData } from "@bitwarden/common/admin-console/models/data/policy.data";
|
||||
import { AuthRequestAnsweringService } from "@bitwarden/common/auth/abstractions/auth-request-answering/auth-request-answering.service.abstraction";
|
||||
@@ -49,6 +50,7 @@ export const DISABLED_NOTIFICATIONS_URL = "http://-";
|
||||
|
||||
export const AllowedMultiUserNotificationTypes = new Set<NotificationType>([
|
||||
NotificationType.AuthRequest,
|
||||
NotificationType.AutoConfirmMember,
|
||||
]);
|
||||
|
||||
export class DefaultServerNotificationsService implements ServerNotificationsService {
|
||||
@@ -70,6 +72,7 @@ export class DefaultServerNotificationsService implements ServerNotificationsSer
|
||||
private readonly authRequestAnsweringService: AuthRequestAnsweringService,
|
||||
private readonly configService: ConfigService,
|
||||
private readonly policyService: InternalPolicyService,
|
||||
private autoConfirmService: AutomaticUserConfirmationService,
|
||||
) {
|
||||
this.notifications$ = this.accountService.accounts$.pipe(
|
||||
map((accounts: Record<UserId, AccountInfo>): Set<UserId> => {
|
||||
@@ -292,6 +295,13 @@ export class DefaultServerNotificationsService implements ServerNotificationsSer
|
||||
case NotificationType.SyncPolicy:
|
||||
await this.policyService.syncPolicy(PolicyData.fromPolicy(notification.payload.policy));
|
||||
break;
|
||||
case NotificationType.AutoConfirmMember:
|
||||
await this.autoConfirmService.autoConfirmUser(
|
||||
notification.payload.userId,
|
||||
notification.payload.targetUserId,
|
||||
notification.payload.organizationId,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user