feat(platform): harden access scoping and delivery baseline

This commit is contained in:
2026-03-30 00:27:31 +02:00
parent 00b936fa1f
commit 819345acfa
109 changed files with 26142 additions and 8081 deletions
@@ -112,10 +112,10 @@ describe("resource router CRUD", () => {
vi.clearAllMocks();
});
// ─── list ─────────────────────────────────────────────────────────────────
// ─── listStaff ────────────────────────────────────────────────────────────
describe("list", () => {
it("returns paginated results with total count", async () => {
describe("listStaff", () => {
it("returns paginated results with total count for staff callers", async () => {
const db = {
resource: {
findMany: vi.fn().mockResolvedValue([sampleResource]),
@@ -123,15 +123,15 @@ describe("resource router CRUD", () => {
},
};
const caller = createProtectedCaller(db);
const result = await caller.list({ limit: 50 });
const caller = createManagerCaller(db);
const result = await caller.listStaff({ limit: 50 });
expect(result.resources).toHaveLength(1);
expect(result.resources[0]?.displayName).toBe("Alice");
expect(db.resource.findMany).toHaveBeenCalled();
});
it("applies search filter", async () => {
it("applies search filter for staff callers", async () => {
const db = {
resource: {
findMany: vi.fn().mockResolvedValue([]),
@@ -139,8 +139,8 @@ describe("resource router CRUD", () => {
},
};
const caller = createProtectedCaller(db);
await caller.list({ search: "Alice", limit: 50 });
const caller = createManagerCaller(db);
await caller.listStaff({ search: "Alice", limit: 50 });
expect(db.resource.findMany).toHaveBeenCalled();
});
@@ -152,7 +152,8 @@ describe("resource router CRUD", () => {
it("returns correct resource", async () => {
const db = {
resource: {
findUnique: vi.fn().mockResolvedValue(sampleResource),
findFirst: vi.fn().mockResolvedValue({ id: "res_1" }),
findUnique: vi.fn().mockResolvedValue({ ...sampleResource, userId: "user_1" }),
findMany: vi.fn().mockResolvedValue([]),
},
systemSettings: {
@@ -170,6 +171,7 @@ describe("resource router CRUD", () => {
it("throws NOT_FOUND when resource does not exist", async () => {
const db = {
resource: {
findFirst: vi.fn().mockResolvedValue({ id: "res_1" }),
findUnique: vi.fn().mockResolvedValue(null),
findMany: vi.fn().mockResolvedValue([]),
},
@@ -188,6 +190,7 @@ describe("resource router CRUD", () => {
const ownedResource = { ...sampleResource, userId: "user_1" };
const db = {
resource: {
findFirst: vi.fn().mockResolvedValue({ id: "res_1" }),
findUnique: vi.fn().mockResolvedValue(ownedResource),
findMany: vi.fn().mockResolvedValue([]),
},
@@ -201,6 +204,21 @@ describe("resource router CRUD", () => {
expect(result.isOwnedByCurrentUser).toBe(true);
});
it("rejects foreign resources for regular users", async () => {
const db = {
resource: {
findFirst: vi.fn().mockResolvedValue({ id: "res_own" }),
findUnique: vi.fn().mockResolvedValue(sampleResource),
findMany: vi.fn().mockResolvedValue([]),
},
};
const caller = createProtectedCaller(db);
await expect(caller.getById({ id: "res_1" })).rejects.toThrow(
expect.objectContaining({ code: "FORBIDDEN" }),
);
});
});
// ─── create ───────────────────────────────────────────────────────────────
@@ -349,6 +367,7 @@ describe("resource router CRUD", () => {
it("returns expected shape with key fields", async () => {
const db = {
resource: {
findFirst: vi.fn().mockResolvedValue({ id: "res_1" }),
findUnique: vi.fn().mockResolvedValue({
id: "res_1",
displayName: "Alice",
@@ -387,7 +406,10 @@ describe("resource router CRUD", () => {
it("throws NOT_FOUND for missing resource", async () => {
const db = {
resource: { findUnique: vi.fn().mockResolvedValue(null) },
resource: {
findFirst: vi.fn().mockResolvedValue({ id: "res_1" }),
findUnique: vi.fn().mockResolvedValue(null),
},
systemSettings: { findUnique: vi.fn().mockResolvedValue(null) },
};
@@ -396,6 +418,37 @@ describe("resource router CRUD", () => {
expect.objectContaining({ code: "NOT_FOUND" }),
);
});
it("rejects foreign hover-card access for regular users", async () => {
const db = {
resource: {
findFirst: vi.fn().mockResolvedValue({ id: "res_own" }),
findUnique: vi.fn().mockResolvedValue({
id: "res_1",
displayName: "Alice",
eid: "E-001",
email: "alice@example.com",
chapter: "CGI",
lcrCents: 5000,
ucrCents: 9000,
currency: "EUR",
chargeabilityTarget: 80,
skills: [],
availability: { monday: 8, tuesday: 8, wednesday: 8, thursday: 8, friday: 8 },
isActive: true,
areaRole: null,
country: null,
managementLevel: null,
resourceType: null,
}),
},
};
const caller = createProtectedCaller(db);
await expect(caller.getHoverCard({ id: "res_1" })).rejects.toThrow(
expect.objectContaining({ code: "FORBIDDEN" }),
);
});
});
// ─── importSkillMatrix ────────────────────────────────────────────────────