1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-19 17:53:39 +00:00

[PM-28264] Consolidate and update the UI for key connector migration/confirmation (#17642)

* Consolidate the RemovePasswordComponent

* Add getting confirmation details for confirm key connector

* Add missing message
This commit is contained in:
Thomas Avery
2025-12-10 15:24:20 -06:00
committed by GitHub
parent 93640e65e3
commit fe4895d97e
30 changed files with 496 additions and 206 deletions

View File

@@ -0,0 +1,7 @@
import { KeyConnectorConfirmationDetailsResponse } from "../models/response/key-connector-confirmation-details.response";
export abstract class KeyConnectorApiService {
abstract getConfirmationDetails(
orgSsoIdentifier: string,
): Promise<KeyConnectorConfirmationDetailsResponse>;
}

View File

@@ -1,3 +1,4 @@
export interface KeyConnectorDomainConfirmation {
keyConnectorUrl: string;
organizationSsoIdentifier: string;
}

View File

@@ -0,0 +1,10 @@
import { BaseResponse } from "../../../../models/response/base.response";
export class KeyConnectorConfirmationDetailsResponse extends BaseResponse {
organizationName: string;
constructor(response: any) {
super(response);
this.organizationName = this.getResponseProperty("OrganizationName");
}
}

View File

@@ -0,0 +1,54 @@
import { mock, MockProxy } from "jest-mock-extended";
import { ApiService } from "../../../abstractions/api.service";
import { KeyConnectorConfirmationDetailsResponse } from "../models/response/key-connector-confirmation-details.response";
import { DefaultKeyConnectorApiService } from "./default-key-connector-api.service";
describe("DefaultKeyConnectorApiService", () => {
let apiService: MockProxy<ApiService>;
let sut: DefaultKeyConnectorApiService;
beforeEach(() => {
apiService = mock<ApiService>();
sut = new DefaultKeyConnectorApiService(apiService);
});
describe("getConfirmationDetails", () => {
it("encodes orgSsoIdentifier in URL", async () => {
const orgSsoIdentifier = "test org/with special@chars";
const expectedEncodedIdentifier = encodeURIComponent(orgSsoIdentifier);
const mockResponse = {};
apiService.send.mockResolvedValue(mockResponse);
await sut.getConfirmationDetails(orgSsoIdentifier);
expect(apiService.send).toHaveBeenCalledWith(
"GET",
`/accounts/key-connector/confirmation-details/${expectedEncodedIdentifier}`,
null,
true,
true,
);
});
it("returns expected response", async () => {
const orgSsoIdentifier = "test-org";
const expectedOrgName = "example";
const mockResponse = { OrganizationName: expectedOrgName };
apiService.send.mockResolvedValue(mockResponse);
const result = await sut.getConfirmationDetails(orgSsoIdentifier);
expect(result).toBeInstanceOf(KeyConnectorConfirmationDetailsResponse);
expect(result.organizationName).toBe(expectedOrgName);
expect(apiService.send).toHaveBeenCalledWith(
"GET",
"/accounts/key-connector/confirmation-details/test-org",
null,
true,
true,
);
});
});
});

View File

@@ -0,0 +1,20 @@
import { ApiService } from "../../../abstractions/api.service";
import { KeyConnectorApiService } from "../abstractions/key-connector-api.service";
import { KeyConnectorConfirmationDetailsResponse } from "../models/response/key-connector-confirmation-details.response";
export class DefaultKeyConnectorApiService implements KeyConnectorApiService {
constructor(private apiService: ApiService) {}
async getConfirmationDetails(
orgSsoIdentifier: string,
): Promise<KeyConnectorConfirmationDetailsResponse> {
const r = await this.apiService.send(
"GET",
"/accounts/key-connector/confirmation-details/" + encodeURIComponent(orgSsoIdentifier),
null,
true,
true,
);
return new KeyConnectorConfirmationDetailsResponse(r);
}
}

View File

@@ -603,7 +603,10 @@ describe("KeyConnectorService", () => {
const data$ = keyConnectorService.requiresDomainConfirmation$(mockUserId);
const data = await firstValueFrom(data$);
expect(data).toEqual({ keyConnectorUrl: conversion.keyConnectorUrl });
expect(data).toEqual({
keyConnectorUrl: conversion.keyConnectorUrl,
organizationSsoIdentifier: conversion.organizationId,
});
});
it("should return observable of null value when no data is set", async () => {

View File

@@ -202,9 +202,16 @@ export class KeyConnectorService implements KeyConnectorServiceAbstraction {
}
requiresDomainConfirmation$(userId: UserId): Observable<KeyConnectorDomainConfirmation | null> {
return this.stateProvider
.getUserState$(NEW_SSO_USER_KEY_CONNECTOR_CONVERSION, userId)
.pipe(map((data) => (data != null ? { keyConnectorUrl: data.keyConnectorUrl } : null)));
return this.stateProvider.getUserState$(NEW_SSO_USER_KEY_CONNECTOR_CONVERSION, userId).pipe(
map((data) =>
data != null
? {
keyConnectorUrl: data.keyConnectorUrl,
organizationSsoIdentifier: data.organizationId,
}
: null,
),
);
}
private handleKeyConnectorError(e: any) {