diff --git a/apps/cli/src/key-management/convert-to-key-connector.command.spec.ts b/apps/cli/src/key-management/convert-to-key-connector.command.spec.ts index 5525dee02c6..6b0d4e6f685 100644 --- a/apps/cli/src/key-management/convert-to-key-connector.command.spec.ts +++ b/apps/cli/src/key-management/convert-to-key-connector.command.spec.ts @@ -130,7 +130,10 @@ describe("ConvertToKeyConnectorCommand", () => { expect(response).not.toBeNull(); expect(response.success).toEqual(true); - expect(keyConnectorService.migrateUser).toHaveBeenCalledWith(userId); + expect(keyConnectorService.migrateUser).toHaveBeenCalledWith( + organization.keyConnectorUrl, + userId, + ); expect(environmentService.setEnvironment).toHaveBeenCalledWith(Region.SelfHosted, { keyConnector: organization.keyConnectorUrl, } as Urls); diff --git a/apps/cli/src/key-management/convert-to-key-connector.command.ts b/apps/cli/src/key-management/convert-to-key-connector.command.ts index a974a1909ec..ff1de744d74 100644 --- a/apps/cli/src/key-management/convert-to-key-connector.command.ts +++ b/apps/cli/src/key-management/convert-to-key-connector.command.ts @@ -64,7 +64,7 @@ export class ConvertToKeyConnectorCommand { if (answer.convert === "remove") { try { - await this.keyConnectorService.migrateUser(this.userId); + await this.keyConnectorService.migrateUser(organization.keyConnectorUrl, this.userId); } catch (e) { await this.logout(); throw e; diff --git a/libs/common/src/key-management/key-connector/abstractions/key-connector.service.ts b/libs/common/src/key-management/key-connector/abstractions/key-connector.service.ts index 2ce691f1c3c..20336112a74 100644 --- a/libs/common/src/key-management/key-connector/abstractions/key-connector.service.ts +++ b/libs/common/src/key-management/key-connector/abstractions/key-connector.service.ts @@ -6,13 +6,13 @@ import { Organization } from "../../../admin-console/models/domain/organization" import { UserId } from "../../../types/guid"; export abstract class KeyConnectorService { - abstract setMasterKeyFromUrl(url: string, userId: UserId): Promise; + abstract setMasterKeyFromUrl(keyConnectorUrl: string, userId: UserId): Promise; abstract getManagingOrganization(userId: UserId): Promise; abstract getUsesKeyConnector(userId: UserId): Promise; - abstract migrateUser(userId: UserId): Promise; + abstract migrateUser(keyConnectorUrl: string, userId: UserId): Promise; abstract convertNewSsoUserToKeyConnector( orgId: string, diff --git a/libs/common/src/key-management/key-connector/services/key-connector.service.spec.ts b/libs/common/src/key-management/key-connector/services/key-connector.service.spec.ts index 568470725a9..fa8be921175 100644 --- a/libs/common/src/key-management/key-connector/services/key-connector.service.spec.ts +++ b/libs/common/src/key-management/key-connector/services/key-connector.service.spec.ts @@ -45,6 +45,8 @@ describe("KeyConnectorService", () => { key: "eO9nVlVl3I3sU6O+CyK0kEkpGtl/auT84Hig2WTXmZtDTqYtKpDvUPfjhgMOHf+KQzx++TVS2AOLYq856Caa7w==", }); + const keyConnectorUrl = "https://key-connector-url.com"; + beforeEach(() => { jest.clearAllMocks(); @@ -124,27 +126,9 @@ describe("KeyConnectorService", () => { it("should return the managing organization with key connector enabled", async () => { // Arrange const orgs = [ - organizationData( - true, - true, - "https://key-connector-url.com", - OrganizationUserType.User, - false, - ), - organizationData( - false, - true, - "https://key-connector-url.com", - OrganizationUserType.User, - false, - ), - organizationData( - true, - false, - "https://key-connector-url.com", - OrganizationUserType.User, - false, - ), + organizationData(true, true, keyConnectorUrl, OrganizationUserType.User, false), + organizationData(false, true, keyConnectorUrl, OrganizationUserType.User, false), + organizationData(true, false, keyConnectorUrl, OrganizationUserType.User, false), organizationData(true, true, "https://other-url.com", OrganizationUserType.User, false), ]; organizationService.organizations$.mockReturnValue(of(orgs)); @@ -159,20 +143,8 @@ describe("KeyConnectorService", () => { it("should return undefined if no managing organization with key connector enabled is found", async () => { // Arrange const orgs = [ - organizationData( - true, - false, - "https://key-connector-url.com", - OrganizationUserType.User, - false, - ), - organizationData( - false, - false, - "https://key-connector-url.com", - OrganizationUserType.User, - false, - ), + organizationData(true, false, keyConnectorUrl, OrganizationUserType.User, false), + organizationData(false, false, keyConnectorUrl, OrganizationUserType.User, false), ]; organizationService.organizations$.mockReturnValue(of(orgs)); @@ -186,8 +158,8 @@ describe("KeyConnectorService", () => { it("should return undefined if user is Owner or Admin", async () => { // Arrange const orgs = [ - organizationData(true, true, "https://key-connector-url.com", 0, false), - organizationData(true, true, "https://key-connector-url.com", 1, false), + organizationData(true, true, keyConnectorUrl, 0, false), + organizationData(true, true, keyConnectorUrl, 1, false), ]; organizationService.organizations$.mockReturnValue(of(orgs)); @@ -201,20 +173,8 @@ describe("KeyConnectorService", () => { it("should return undefined if user is a Provider", async () => { // Arrange const orgs = [ - organizationData( - true, - true, - "https://key-connector-url.com", - OrganizationUserType.User, - true, - ), - organizationData( - false, - true, - "https://key-connector-url.com", - OrganizationUserType.User, - true, - ), + organizationData(true, true, keyConnectorUrl, OrganizationUserType.User, true), + organizationData(false, true, keyConnectorUrl, OrganizationUserType.User, true), ]; organizationService.organizations$.mockReturnValue(of(orgs)); @@ -229,7 +189,7 @@ describe("KeyConnectorService", () => { describe("setMasterKeyFromUrl", () => { it("should set the master key from the provided URL", async () => { // Arrange - const url = "https://key-connector-url.com"; + const url = keyConnectorUrl; apiService.getMasterKeyFromKeyConnector.mockResolvedValue(mockMasterKeyResponse); @@ -247,7 +207,7 @@ describe("KeyConnectorService", () => { it("should handle errors thrown during the process", async () => { // Arrange - const url = "https://key-connector-url.com"; + const url = keyConnectorUrl; const error = new Error("Failed to get master key"); apiService.getMasterKeyFromKeyConnector.mockRejectedValue(error); @@ -267,27 +227,18 @@ describe("KeyConnectorService", () => { describe("migrateUser", () => { it("should migrate the user to the key connector", async () => { // Arrange - const organization = organizationData( - true, - true, - "https://key-connector-url.com", - OrganizationUserType.User, - false, - ); const masterKey = getMockMasterKey(); masterPasswordService.masterKeySubject.next(masterKey); const keyConnectorRequest = new KeyConnectorUserKeyRequest(masterKey.encKeyB64); - jest.spyOn(keyConnectorService, "getManagingOrganization").mockResolvedValue(organization); jest.spyOn(apiService, "postUserKeyToKeyConnector").mockResolvedValue(); // Act - await keyConnectorService.migrateUser(mockUserId); + await keyConnectorService.migrateUser(keyConnectorUrl, mockUserId); // Assert - expect(keyConnectorService.getManagingOrganization).toHaveBeenCalled(); expect(apiService.postUserKeyToKeyConnector).toHaveBeenCalledWith( - organization.keyConnectorUrl, + keyConnectorUrl, keyConnectorRequest, ); expect(apiService.postConvertToKeyConnector).toHaveBeenCalled(); @@ -295,32 +246,21 @@ describe("KeyConnectorService", () => { it("should handle errors thrown during migration", async () => { // Arrange - const organization = organizationData( - true, - true, - "https://key-connector-url.com", - OrganizationUserType.User, - false, - ); const masterKey = getMockMasterKey(); const keyConnectorRequest = new KeyConnectorUserKeyRequest(masterKey.encKeyB64); - const error = new Error("Failed to post user key to key connector"); - organizationService.organizations$.mockReturnValue(of([organization])); - masterPasswordService.masterKeySubject.next(masterKey); - jest.spyOn(keyConnectorService, "getManagingOrganization").mockResolvedValue(organization); + const error = new Error("Failed to post user key to key connector"); jest.spyOn(apiService, "postUserKeyToKeyConnector").mockRejectedValue(error); jest.spyOn(logService, "error"); try { // Act - await keyConnectorService.migrateUser(mockUserId); + await keyConnectorService.migrateUser(keyConnectorUrl, mockUserId); } catch { // Assert expect(logService.error).toHaveBeenCalledWith(error); - expect(keyConnectorService.getManagingOrganization).toHaveBeenCalled(); expect(apiService.postUserKeyToKeyConnector).toHaveBeenCalledWith( - organization.keyConnectorUrl, + keyConnectorUrl, keyConnectorRequest, ); } @@ -332,7 +272,7 @@ describe("KeyConnectorService", () => { const organization = organizationData( true, true, - "https://key-connector-url.com", + keyConnectorUrl, OrganizationUserType.User, false, ); @@ -360,7 +300,7 @@ describe("KeyConnectorService", () => { const organization = organizationData( true, false, - "https://key-connector-url.com", + keyConnectorUrl, OrganizationUserType.User, false, ); @@ -375,7 +315,7 @@ describe("KeyConnectorService", () => { const organization = organizationData( true, true, - "https://key-connector-url.com", + keyConnectorUrl, OrganizationUserType.Admin, false, ); @@ -390,7 +330,7 @@ describe("KeyConnectorService", () => { const organization = organizationData( true, true, - "https://key-connector-url.com", + keyConnectorUrl, OrganizationUserType.Owner, false, ); @@ -405,7 +345,7 @@ describe("KeyConnectorService", () => { const organization = organizationData( true, true, - "https://key-connector-url.com", + keyConnectorUrl, OrganizationUserType.User, true, ); diff --git a/libs/common/src/key-management/key-connector/services/key-connector.service.ts b/libs/common/src/key-management/key-connector/services/key-connector.service.ts index d4b67c0300c..f9e33854cdc 100644 --- a/libs/common/src/key-management/key-connector/services/key-connector.service.ts +++ b/libs/common/src/key-management/key-connector/services/key-connector.service.ts @@ -89,22 +89,12 @@ export class KeyConnectorService implements KeyConnectorServiceAbstraction { ); } - async migrateUser(userId: UserId) { - const organization = await this.getManagingOrganization(userId); - if (organization == null) { - throw new Error( - "[Key Connector service] No key connector enabled organization found, aborting user migration to key connector.", - ); - } - + async migrateUser(keyConnectorUrl: string, userId: UserId) { const masterKey = await firstValueFrom(this.masterPasswordService.masterKey$(userId)); const keyConnectorRequest = new KeyConnectorUserKeyRequest(masterKey.encKeyB64); try { - await this.apiService.postUserKeyToKeyConnector( - organization.keyConnectorUrl, - keyConnectorRequest, - ); + await this.apiService.postUserKeyToKeyConnector(keyConnectorUrl, keyConnectorRequest); } catch (e) { this.handleKeyConnectorError(e); } @@ -115,9 +105,9 @@ export class KeyConnectorService implements KeyConnectorServiceAbstraction { } // TODO: UserKey should be renamed to MasterKey and typed accordingly - async setMasterKeyFromUrl(url: string, userId: UserId) { + async setMasterKeyFromUrl(keyConnectorUrl: string, userId: UserId) { try { - const masterKeyResponse = await this.apiService.getMasterKeyFromKeyConnector(url); + const masterKeyResponse = await this.apiService.getMasterKeyFromKeyConnector(keyConnectorUrl); const keyArr = Utils.fromB64ToArray(masterKeyResponse.key); const masterKey = new SymmetricCryptoKey(keyArr) as MasterKey; await this.masterPasswordService.setMasterKey(masterKey, userId); diff --git a/libs/key-management-ui/src/key-connector/remove-password.component.spec.ts b/libs/key-management-ui/src/key-connector/remove-password.component.spec.ts index 3bedf86c444..41962d8a309 100644 --- a/libs/key-management-ui/src/key-connector/remove-password.component.spec.ts +++ b/libs/key-management-ui/src/key-connector/remove-password.component.spec.ts @@ -22,6 +22,7 @@ describe("RemovePasswordComponent", () => { const organization = { id: "test-organization-id", name: "test-organization-name", + keyConnectorUrl: "https://key-connector-url.com", } as Organization; const accountService = mockAccountServiceWith(userId); @@ -121,7 +122,10 @@ describe("RemovePasswordComponent", () => { await component.convert(); expect(component.continuing).toBe(true); - expect(mockKeyConnectorService.migrateUser).toHaveBeenCalledWith(userId); + expect(mockKeyConnectorService.migrateUser).toHaveBeenCalledWith( + organization.keyConnectorUrl, + userId, + ); expect(mockToastService.showToast).toHaveBeenCalledWith({ variant: "success", message: "removed master password", @@ -137,7 +141,10 @@ describe("RemovePasswordComponent", () => { await component.convert(); expect(component.continuing).toBe(false); - expect(mockKeyConnectorService.migrateUser).toHaveBeenCalledWith(userId); + expect(mockKeyConnectorService.migrateUser).toHaveBeenCalledWith( + organization.keyConnectorUrl, + userId, + ); expect(mockToastService.showToast).toHaveBeenCalledWith({ variant: "error", title: "error occurred", @@ -161,7 +168,10 @@ describe("RemovePasswordComponent", () => { await component.convert(); expect(component.continuing).toBe(false); - expect(mockKeyConnectorService.migrateUser).toHaveBeenCalledWith(userId); + expect(mockKeyConnectorService.migrateUser).toHaveBeenCalledWith( + organization.keyConnectorUrl, + userId, + ); expect(mockToastService.showToast).toHaveBeenCalledWith({ variant: "error", title: "error occurred", diff --git a/libs/key-management-ui/src/key-connector/remove-password.component.ts b/libs/key-management-ui/src/key-connector/remove-password.component.ts index bc94f7d1177..dc2303b9951 100644 --- a/libs/key-management-ui/src/key-connector/remove-password.component.ts +++ b/libs/key-management-ui/src/key-connector/remove-password.component.ts @@ -65,7 +65,10 @@ export class RemovePasswordComponent implements OnInit { this.continuing = true; try { - await this.keyConnectorService.migrateUser(this.activeUserId); + await this.keyConnectorService.migrateUser( + this.organization.keyConnectorUrl, + this.activeUserId, + ); this.toastService.showToast({ variant: "success",