mirror of
https://github.com/bitwarden/browser
synced 2025-12-15 07:43:35 +00:00
fix(EmergencyAccess): [Auth/PM-23860] - Restore contact removal functionality (#15666)
* PM-23860 - EmergencyAccessService - convert types from response model to actual constructed type to avoid structural typing issue at runtime * PM-23860 - EmergencyAccessService tests - add tests to both methods to prevent this from being possible again.
This commit is contained in:
@@ -5,6 +5,10 @@ import { KdfType } from "@bitwarden/key-management";
|
||||
|
||||
import { EmergencyAccessStatusType } from "../enums/emergency-access-status-type";
|
||||
import { EmergencyAccessType } from "../enums/emergency-access-type";
|
||||
import {
|
||||
EmergencyAccessGranteeDetailsResponse,
|
||||
EmergencyAccessGrantorDetailsResponse,
|
||||
} from "../response/emergency-access.response";
|
||||
|
||||
export class GranteeEmergencyAccess {
|
||||
id: string;
|
||||
@@ -16,6 +20,24 @@ export class GranteeEmergencyAccess {
|
||||
waitTimeDays: number;
|
||||
creationDate: string;
|
||||
avatarColor: string;
|
||||
|
||||
constructor(partial: Partial<GranteeEmergencyAccess> = {}) {
|
||||
Object.assign(this, partial);
|
||||
}
|
||||
|
||||
static fromResponse(response: EmergencyAccessGranteeDetailsResponse) {
|
||||
return new GranteeEmergencyAccess({
|
||||
id: response.id,
|
||||
granteeId: response.granteeId,
|
||||
name: response.name,
|
||||
email: response.email,
|
||||
type: response.type,
|
||||
status: response.status,
|
||||
waitTimeDays: response.waitTimeDays,
|
||||
creationDate: response.creationDate,
|
||||
avatarColor: response.avatarColor,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class GrantorEmergencyAccess {
|
||||
@@ -28,6 +50,24 @@ export class GrantorEmergencyAccess {
|
||||
waitTimeDays: number;
|
||||
creationDate: string;
|
||||
avatarColor: string;
|
||||
|
||||
constructor(partial: Partial<GrantorEmergencyAccess> = {}) {
|
||||
Object.assign(this, partial);
|
||||
}
|
||||
|
||||
static fromResponse(response: EmergencyAccessGrantorDetailsResponse) {
|
||||
return new GrantorEmergencyAccess({
|
||||
id: response.id,
|
||||
grantorId: response.grantorId,
|
||||
name: response.name,
|
||||
email: response.email,
|
||||
type: response.type,
|
||||
status: response.status,
|
||||
waitTimeDays: response.waitTimeDays,
|
||||
creationDate: response.creationDate,
|
||||
avatarColor: response.avatarColor,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class TakeoverTypeEmergencyAccess {
|
||||
|
||||
@@ -22,9 +22,11 @@ import { KdfType, KeyService } from "@bitwarden/key-management";
|
||||
|
||||
import { EmergencyAccessStatusType } from "../enums/emergency-access-status-type";
|
||||
import { EmergencyAccessType } from "../enums/emergency-access-type";
|
||||
import { GranteeEmergencyAccess, GrantorEmergencyAccess } from "../models/emergency-access";
|
||||
import { EmergencyAccessPasswordRequest } from "../request/emergency-access-password.request";
|
||||
import {
|
||||
EmergencyAccessGranteeDetailsResponse,
|
||||
EmergencyAccessGrantorDetailsResponse,
|
||||
EmergencyAccessTakeoverResponse,
|
||||
} from "../response/emergency-access.response";
|
||||
|
||||
@@ -242,11 +244,19 @@ describe("EmergencyAccessService", () => {
|
||||
|
||||
const mockEmergencyAccess = {
|
||||
data: [
|
||||
createMockEmergencyAccess("0", "EA 0", EmergencyAccessStatusType.Invited),
|
||||
createMockEmergencyAccess("1", "EA 1", EmergencyAccessStatusType.Accepted),
|
||||
createMockEmergencyAccess("2", "EA 2", EmergencyAccessStatusType.Confirmed),
|
||||
createMockEmergencyAccess("3", "EA 3", EmergencyAccessStatusType.RecoveryInitiated),
|
||||
createMockEmergencyAccess("4", "EA 4", EmergencyAccessStatusType.RecoveryApproved),
|
||||
createMockEmergencyAccessGranteeDetails("0", "EA 0", EmergencyAccessStatusType.Invited),
|
||||
createMockEmergencyAccessGranteeDetails("1", "EA 1", EmergencyAccessStatusType.Accepted),
|
||||
createMockEmergencyAccessGranteeDetails("2", "EA 2", EmergencyAccessStatusType.Confirmed),
|
||||
createMockEmergencyAccessGranteeDetails(
|
||||
"3",
|
||||
"EA 3",
|
||||
EmergencyAccessStatusType.RecoveryInitiated,
|
||||
),
|
||||
createMockEmergencyAccessGranteeDetails(
|
||||
"4",
|
||||
"EA 4",
|
||||
EmergencyAccessStatusType.RecoveryApproved,
|
||||
),
|
||||
],
|
||||
} as ListResponse<EmergencyAccessGranteeDetailsResponse>;
|
||||
|
||||
@@ -295,9 +305,113 @@ describe("EmergencyAccessService", () => {
|
||||
).rejects.toThrow("New user key is required for rotation.");
|
||||
});
|
||||
});
|
||||
|
||||
describe("getEmergencyAccessTrusted", () => {
|
||||
it("should return an empty array if no emergency access is granted", async () => {
|
||||
emergencyAccessApiService.getEmergencyAccessTrusted.mockResolvedValue({
|
||||
data: [],
|
||||
} as ListResponse<EmergencyAccessGranteeDetailsResponse>);
|
||||
|
||||
const result = await emergencyAccessService.getEmergencyAccessTrusted();
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it("should return an empty array if the API returns an empty response", async () => {
|
||||
emergencyAccessApiService.getEmergencyAccessTrusted.mockResolvedValue(
|
||||
null as unknown as ListResponse<EmergencyAccessGranteeDetailsResponse>,
|
||||
);
|
||||
|
||||
const result = await emergencyAccessService.getEmergencyAccessTrusted();
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it("should return a list of trusted emergency access contacts", async () => {
|
||||
const mockEmergencyAccess = [
|
||||
createMockEmergencyAccessGranteeDetails("1", "EA 1", EmergencyAccessStatusType.Invited),
|
||||
createMockEmergencyAccessGranteeDetails("2", "EA 2", EmergencyAccessStatusType.Invited),
|
||||
createMockEmergencyAccessGranteeDetails("3", "EA 3", EmergencyAccessStatusType.Accepted),
|
||||
createMockEmergencyAccessGranteeDetails("4", "EA 4", EmergencyAccessStatusType.Confirmed),
|
||||
createMockEmergencyAccessGranteeDetails(
|
||||
"5",
|
||||
"EA 5",
|
||||
EmergencyAccessStatusType.RecoveryInitiated,
|
||||
),
|
||||
];
|
||||
emergencyAccessApiService.getEmergencyAccessTrusted.mockResolvedValue({
|
||||
data: mockEmergencyAccess,
|
||||
} as ListResponse<EmergencyAccessGranteeDetailsResponse>);
|
||||
|
||||
const result = await emergencyAccessService.getEmergencyAccessTrusted();
|
||||
|
||||
expect(result).toHaveLength(mockEmergencyAccess.length);
|
||||
|
||||
result.forEach((access, index) => {
|
||||
expect(access).toBeInstanceOf(GranteeEmergencyAccess);
|
||||
|
||||
expect(access.id).toBe(mockEmergencyAccess[index].id);
|
||||
expect(access.name).toBe(mockEmergencyAccess[index].name);
|
||||
expect(access.status).toBe(mockEmergencyAccess[index].status);
|
||||
expect(access.type).toBe(mockEmergencyAccess[index].type);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("getEmergencyAccessGranted", () => {
|
||||
it("should return an empty array if no emergency access is granted", async () => {
|
||||
emergencyAccessApiService.getEmergencyAccessGranted.mockResolvedValue({
|
||||
data: [],
|
||||
} as ListResponse<EmergencyAccessGrantorDetailsResponse>);
|
||||
|
||||
const result = await emergencyAccessService.getEmergencyAccessGranted();
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it("should return an empty array if the API returns an empty response", async () => {
|
||||
emergencyAccessApiService.getEmergencyAccessGranted.mockResolvedValue(
|
||||
null as unknown as ListResponse<EmergencyAccessGrantorDetailsResponse>,
|
||||
);
|
||||
|
||||
const result = await emergencyAccessService.getEmergencyAccessGranted();
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it("should return a list of granted emergency access contacts", async () => {
|
||||
const mockEmergencyAccess = [
|
||||
createMockEmergencyAccessGrantorDetails("1", "EA 1", EmergencyAccessStatusType.Invited),
|
||||
createMockEmergencyAccessGrantorDetails("2", "EA 2", EmergencyAccessStatusType.Invited),
|
||||
createMockEmergencyAccessGrantorDetails("3", "EA 3", EmergencyAccessStatusType.Accepted),
|
||||
createMockEmergencyAccessGrantorDetails("4", "EA 4", EmergencyAccessStatusType.Confirmed),
|
||||
createMockEmergencyAccessGrantorDetails(
|
||||
"5",
|
||||
"EA 5",
|
||||
EmergencyAccessStatusType.RecoveryInitiated,
|
||||
),
|
||||
];
|
||||
emergencyAccessApiService.getEmergencyAccessGranted.mockResolvedValue({
|
||||
data: mockEmergencyAccess,
|
||||
} as ListResponse<EmergencyAccessGrantorDetailsResponse>);
|
||||
|
||||
const result = await emergencyAccessService.getEmergencyAccessGranted();
|
||||
|
||||
expect(result).toHaveLength(mockEmergencyAccess.length);
|
||||
|
||||
result.forEach((access, index) => {
|
||||
expect(access).toBeInstanceOf(GrantorEmergencyAccess);
|
||||
|
||||
expect(access.id).toBe(mockEmergencyAccess[index].id);
|
||||
expect(access.name).toBe(mockEmergencyAccess[index].name);
|
||||
expect(access.status).toBe(mockEmergencyAccess[index].status);
|
||||
expect(access.type).toBe(mockEmergencyAccess[index].type);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function createMockEmergencyAccess(
|
||||
function createMockEmergencyAccessGranteeDetails(
|
||||
id: string,
|
||||
name: string,
|
||||
status: EmergencyAccessStatusType,
|
||||
@@ -309,3 +423,16 @@ function createMockEmergencyAccess(
|
||||
emergencyAccess.status = status;
|
||||
return emergencyAccess;
|
||||
}
|
||||
|
||||
function createMockEmergencyAccessGrantorDetails(
|
||||
id: string,
|
||||
name: string,
|
||||
status: EmergencyAccessStatusType,
|
||||
): EmergencyAccessGrantorDetailsResponse {
|
||||
const emergencyAccess = new EmergencyAccessGrantorDetailsResponse({});
|
||||
emergencyAccess.id = id;
|
||||
emergencyAccess.name = name;
|
||||
emergencyAccess.type = 0;
|
||||
emergencyAccess.status = status;
|
||||
return emergencyAccess;
|
||||
}
|
||||
|
||||
@@ -77,14 +77,22 @@ export class EmergencyAccessService
|
||||
* Gets all emergency access that the user has been granted.
|
||||
*/
|
||||
async getEmergencyAccessTrusted(): Promise<GranteeEmergencyAccess[]> {
|
||||
return (await this.emergencyAccessApiService.getEmergencyAccessTrusted()).data;
|
||||
const listResponse = await this.emergencyAccessApiService.getEmergencyAccessTrusted();
|
||||
if (!listResponse || listResponse.data.length === 0) {
|
||||
return [];
|
||||
}
|
||||
return listResponse.data.map((response) => GranteeEmergencyAccess.fromResponse(response));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all emergency access that the user has granted.
|
||||
*/
|
||||
async getEmergencyAccessGranted(): Promise<GrantorEmergencyAccess[]> {
|
||||
return (await this.emergencyAccessApiService.getEmergencyAccessGranted()).data;
|
||||
const listResponse = await this.emergencyAccessApiService.getEmergencyAccessGranted();
|
||||
if (!listResponse || listResponse.data.length === 0) {
|
||||
return [];
|
||||
}
|
||||
return listResponse.data.map((response) => GrantorEmergencyAccess.fromResponse(response));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user