Skip to content

Commit 3b2fa75

Browse files
committed
improve bearerCredentials
1 parent 3293cbc commit 3b2fa75

2 files changed

Lines changed: 255 additions & 157 deletions

File tree

apps/webapp/test/rbacFallbackBranch.test.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,26 @@ async function createEnv(
6262
});
6363
}
6464

65+
describe("RBAC fallback — root key resolution", () => {
66+
it("reports an unknown root key as not found", async () => {
67+
const prisma = {
68+
runtimeEnvironment: { findFirst: vi.fn().mockResolvedValue(null) },
69+
revokedApiKey: { findFirst: vi.fn().mockResolvedValue(null) },
70+
} as unknown as PrismaClient;
71+
const rbac = makeController(prisma);
72+
73+
const result = await rbac.authenticateBearer(bearerRequest("tr_prod_unknown"));
74+
75+
expect(result).toMatchObject({
76+
ok: false,
77+
resolution: {
78+
credentialKind: "root_api_key",
79+
lookupPath: "not_found",
80+
},
81+
});
82+
});
83+
});
84+
6585
describe("RBAC fallback — DEVELOPMENT branch pivot", () => {
6686
postgresTest("pivots to the named branch, carrying the parent's api key", async ({ prisma }) => {
6787
const { organization, project, orgMember } = await createTestOrgProjectWithMember(prisma);
@@ -145,6 +165,7 @@ describe("RBAC fallback — DEVELOPMENT branch pivot", () => {
145165
expect(result.ok).toBe(false);
146166
if (result.ok) return;
147167
expect(result.status).toBe(401);
168+
expect(result.error).toBe("No matching branch env");
148169
}
149170
);
150171
});
@@ -277,6 +298,34 @@ describe("RBAC fallback — additional keys", () => {
277298
expect(result.subject).toMatchObject({ type: "apiKey", restricted: false });
278299
});
279300

301+
postgresTest("does not record use when branch resolution fails", async ({ prisma }) => {
302+
const { organization, project, orgMember, user } = await createTestOrgProjectWithMember(prisma);
303+
const rbac = makeController(prisma);
304+
const devRoot = await createEnv(prisma, project.id, organization.id, {
305+
type: "DEVELOPMENT",
306+
orgMemberId: orgMember.id,
307+
});
308+
const additional = generateAdditionalApiKey("DEVELOPMENT").apiKey;
309+
const created = await prisma.apiKey.create({
310+
data: {
311+
name: "Branch key",
312+
keyHash: createHash("sha256").update(additional).digest("hex"),
313+
lastFour: additional.slice(-4),
314+
runtimeEnvironmentId: devRoot.id,
315+
createdByUserId: user.id,
316+
presetId: null,
317+
scopes: ["admin"],
318+
},
319+
});
320+
321+
const result = await rbac.authenticateBearer(bearerRequest(additional, "missing-branch"));
322+
323+
expect(result).toMatchObject({ ok: false, status: 401, error: "No matching branch env" });
324+
await expect(
325+
prisma.apiKey.findUnique({ where: { id: created.id }, select: { lastUsedAt: true } })
326+
).resolves.toEqual({ lastUsedAt: null });
327+
});
328+
280329
postgresTest("treats empty stored scopes as restricted and deny-all", async ({ prisma }) => {
281330
const { organization, project, orgMember, user } = await createTestOrgProjectWithMember(prisma);
282331
const rbac = makeController(prisma);
@@ -481,6 +530,27 @@ describe("RBAC fallback — additional key permissions", () => {
481530
});
482531

483532
describe("RBAC fallback — branch header guards", () => {
533+
postgresTest("preview environments require a branch header", async ({ prisma }) => {
534+
const { organization, project } = await createTestOrgProjectWithMember(prisma);
535+
const rbac = makeController(prisma);
536+
const previewParent = await createEnv(prisma, project.id, organization.id, {
537+
type: "PREVIEW",
538+
isBranchableEnvironment: true,
539+
});
540+
541+
const result = await rbac.authenticateBearer(bearerRequest(previewParent.apiKey));
542+
543+
expect(result).toMatchObject({
544+
ok: false,
545+
status: 401,
546+
error: "x-trigger-branch header required for preview env",
547+
resolution: {
548+
credentialKind: "root_api_key",
549+
lookupPath: "root_current",
550+
},
551+
});
552+
});
553+
484554
// The "default" sentinel is DEVELOPMENT-only: it maps the dev root env to its
485555
// (branchless) self. For PREVIEW, "default" is an ordinary branch name, so a
486556
// PREVIEW branch literally named "default" is reachable and the request pivots

0 commit comments

Comments
 (0)