Skip to content
File

Blob: src/worker/routes/test-fixtures.ts

typescript118 lines
1import { generatePat, normalizeScopes } from "@/worker/auth/pats";
2import { authObject } from "@/worker/auth/session-cookie";
3import { createControlPlaneDb } from "@/worker/db/d1/client";
4import { bootstrapSubject, createPatProjection } from "@/worker/db/d1/repository";
5import type { AppContext } from "@/worker/types";
6 
7type DavFixtureInput = {
8 sub: string;
9 email: string;
10 displayName: string;
11 scopes: string[];
12 patName: string;
13 path: "/files/" | "/calendars/";
14 storage: "files" | "calendars";
15};
16 
17function fixturesEnabled(env: Env): boolean {
18 return (env as unknown as Record<string, string | undefined>).DAB_ENABLE_TEST_FIXTURES === "1";
19}
20 
21function fixtureInput(fixture: string): DavFixtureInput | null {
22 switch (fixture) {
23 case "litmus":
24 return {
25 sub: "litmus-subject",
26 email: "litmus@example.test",
27 displayName: "litmus",
28 scopes: ["files.full"],
29 patName: "litmus",
30 path: "/files/",
31 storage: "files",
32 };
33 case "caldav":
34 return {
35 sub: "caldav-tester-subject",
36 email: "caldav-tester@example.test",
37 displayName: "caldav tester",
38 scopes: ["caldav.full"],
39 patName: "caldav tester",
40 path: "/calendars/",
41 storage: "calendars",
42 };
43 default:
44 return null;
45 }
46}
47 
48async function createDavFixture(c: AppContext, input: DavFixtureInput) {
49 if (!fixturesEnabled(c.env) || c.get("hostInfo").kind !== "control") return c.notFound();
50 
51 const nowMs = Date.now();
52 const { subject } = await bootstrapSubject(
53 createControlPlaneDb(c.env.DAV_CONTROL_PLANE),
54 {
55 sub: input.sub,
56 email: input.email,
57 displayName: input.displayName,
58 },
59 nowMs,
60 );
61 if (input.storage === "files") {
62 await c.env.FILE_DAV.getByName(subject.storageId).ensureInitialized({
63 subjectId: subject.id,
64 storageId: subject.storageId,
65 nowMs,
66 });
67 } else {
68 await c.env.CAL_DAV.getByName(subject.storageId).ensureInitialized({
69 subjectId: subject.id,
70 nowMs,
71 });
72 }
73 
74 const scopes = normalizeScopes(input.scopes);
75 if (!scopes) return c.json({ error: "invalid_scopes" }, 500);
76 
77 const generated = await generatePat();
78 const pat = await authObject(c.env, subject.storageId).createPat({
79 id: generated.id,
80 name: input.patName,
81 tokenDigest: generated.tokenDigest,
82 scopes,
83 createdAtMs: nowMs,
84 expiresAtMs: null,
85 revokedAtMs: null,
86 lastUsedAtMs: null,
87 });
88 await createPatProjection(createControlPlaneDb(c.env.DAV_CONTROL_PLANE), {
89 id: pat.id,
90 subjectId: subject.id,
91 name: pat.name,
92 scopes: pat.scopes,
93 createdAtMs: pat.createdAtMs,
94 expiresAtMs: pat.expiresAtMs,
95 revokedAtMs: pat.revokedAtMs,
96 lastUsedAtMs: pat.lastUsedAtMs,
97 });
98 
99 const requestUrl = new URL(c.req.url);
100 const port = requestUrl.port ? `:${requestUrl.port}` : "";
101 const davHost = `${subject.hostLabel}${c.env.SUBJECT_HOST_SUFFIX}`;
102 return c.json({
103 url: `${requestUrl.protocol}//${davHost}${port}${input.path}`,
104 username: subject.hostLabel,
105 pat: generated.token,
106 subject_id: subject.id,
107 host_label: subject.hostLabel,
108 });
109}
110 
111export async function handleTestFixtureRequest(c: AppContext): Promise<Response> {
112 const fixture = c.req.param("fixture");
113 if (!fixture) return c.notFound();
114 const input = fixtureInput(fixture);
115 if (!input) return c.notFound();
116 return await createDavFixture(c, input);
117}