forked from Yara724/api
fix claim validation and expert branch scoping
This commit is contained in:
55
src/fanavaran/fanavaran-location.service.spec.ts
Normal file
55
src/fanavaran/fanavaran-location.service.spec.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { ForbiddenException } from "@nestjs/common";
|
||||
import { FanavaranLocationService } from "./fanavaran-location.service";
|
||||
|
||||
describe("FanavaranLocationService branch scope", () => {
|
||||
const makerId = "maker-1";
|
||||
const reviewerId = "reviewer-1";
|
||||
|
||||
const createService = (makerBranch?: string, reviewerBranch?: string) =>
|
||||
new FanavaranLocationService(
|
||||
{
|
||||
findById: jest.fn().mockResolvedValue(
|
||||
makerBranch ? { branchId: makerBranch } : {},
|
||||
),
|
||||
} as any,
|
||||
{
|
||||
findById: jest.fn().mockResolvedValue(
|
||||
reviewerBranch ? { branchId: reviewerBranch } : {},
|
||||
),
|
||||
} as any,
|
||||
);
|
||||
|
||||
it("allows a reviewer to view a file from the same branch", async () => {
|
||||
const service = createService("branch-1", "branch-1");
|
||||
|
||||
await expect(
|
||||
service.assertMakerReviewerBranchCompatible({
|
||||
fileMakerId: makerId,
|
||||
fileReviewerId: reviewerId,
|
||||
}),
|
||||
).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it("rejects a reviewer from another branch", async () => {
|
||||
const service = createService("branch-1", "branch-2");
|
||||
|
||||
await expect(
|
||||
service.assertMakerReviewerBranchCompatible({
|
||||
fileMakerId: makerId,
|
||||
fileReviewerId: reviewerId,
|
||||
}),
|
||||
).rejects.toBeInstanceOf(ForbiddenException);
|
||||
});
|
||||
|
||||
it("prefers the immutable case branch snapshot", async () => {
|
||||
const service = createService("old-branch", "branch-2");
|
||||
|
||||
await expect(
|
||||
service.assertMakerReviewerBranchCompatible({
|
||||
fileMakerId: makerId,
|
||||
fileReviewerId: reviewerId,
|
||||
caseBranchId: "branch-2",
|
||||
}),
|
||||
).resolves.toBeUndefined();
|
||||
});
|
||||
});
|
||||
@@ -113,6 +113,54 @@ export class FanavaranLocationService {
|
||||
}
|
||||
}
|
||||
|
||||
async fileMakerBranchId(
|
||||
fileMakerId: string | null | undefined,
|
||||
): Promise<string | undefined> {
|
||||
if (!fileMakerId) return undefined;
|
||||
const doc = await this.fileMakerDbService.findById(String(fileMakerId));
|
||||
const branchId = (doc as any)?.branchId;
|
||||
return branchId ? String(branchId) : undefined;
|
||||
}
|
||||
|
||||
async fileReviewerBranchId(
|
||||
fileReviewerId: string | null | undefined,
|
||||
): Promise<string | undefined> {
|
||||
if (!fileReviewerId) return undefined;
|
||||
const doc = await this.fileReviewerDbService.findById(
|
||||
String(fileReviewerId),
|
||||
);
|
||||
const branchId = (doc as any)?.branchId;
|
||||
return branchId ? String(branchId) : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* V4/V5 case visibility is branch-scoped by the FileMaker who created it.
|
||||
* Missing branch assignments are denied instead of widening visibility.
|
||||
*/
|
||||
async assertMakerReviewerBranchCompatible(input: {
|
||||
fileMakerId?: string | null;
|
||||
fileReviewerId?: string | null;
|
||||
caseBranchId?: string | null;
|
||||
}): Promise<void> {
|
||||
const reviewerBranchId = await this.fileReviewerBranchId(
|
||||
input.fileReviewerId,
|
||||
);
|
||||
const caseBranchId =
|
||||
(input.caseBranchId ? String(input.caseBranchId) : undefined) ??
|
||||
(await this.fileMakerBranchId(input.fileMakerId));
|
||||
|
||||
if (!reviewerBranchId) {
|
||||
throw new ForbiddenException(
|
||||
"FileReviewer account is not assigned to a branch.",
|
||||
);
|
||||
}
|
||||
if (!caseBranchId || caseBranchId !== reviewerBranchId) {
|
||||
throw new ForbiddenException(
|
||||
"This file belongs to another branch.",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private async loadPrimaryLocationId(
|
||||
userId: string | null | undefined,
|
||||
kind: "maker" | "reviewer",
|
||||
|
||||
Reference in New Issue
Block a user