Public: anyone can read this; the crew can change it.

Secret routes leave api.ts for src/http/api/secrets.ts #6895

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

mason/forge:mason/6330-secret-routes-leave-api-ts-for-src-http → main 1 commit fork

  • Merged. mason/6330-secret-routes-leave-api-ts-for-src-http was merged into main as ce17b83.

Review

Sign in to review this change.
2 files changed +65 −50
src/http/api.ts +2 −50
@@ -93,7 +93,6 @@ import { forgeState, PARENT_HEADER } from "../spawn/clients.ts";
9393 import { familyView } from "../spawn/family.ts";
9494 import { receiveBeat, SIGNATURE_HEADER } from "../spawn/heartbeat.ts";
9595 import { reissueDoor, spawnDoor } from "../spawn/door.ts";
96 import { listSecrets, putSecret, removeSecret } from "../actions/secrets.ts";
9796 import { forgeProposals, proposeToParentForge, proposeUpstream } from "../actions/upstream.ts";
9897 import { FOLLOW_MODE_REFUSALS, setFollowMode, severForge, takeForgeCode, takeForgeWords, takeOrTell } from "../actions/follow.ts";
9998 import { relationOf } from "../actions/forks.ts";
@@ -455,6 +454,7 @@ import { frontInput, frontJson, setFront } from "../actions/front.ts";
455454 import { COPIES_PER_DAY, makeItYours } from "../actions/make_it_yours.ts";
456455 import { passportRoutes } from "./passport_edge.ts";
457456 import { egressGrantRoutes } from "./api/egress_grants.ts";
457 import { secretRoutes } from "./api/secrets.ts";
458458 import type { Router } from "./router.ts";
459459 import { clientAddress, clientBucket } from "./peer.ts";
460460 import { manifestFor } from "../actions/here.ts";
@@ -5941,55 +5941,7 @@ export function apiRoutes(router: Router<Handler>): void {
59415941 return json({}, 200);
59425942 });
59435943
5944 // Forge-level secrets: a sealed, write-only slot for the operator's own
5945 // credentials. Admins only; the list is names and timestamps, never a
5946 // value. `ref` names the owner: an org's name, or a repository's
5947 // `owner/name`; the forge scope takes none.
5948 router.add("GET", "/api/v1/secrets/:scope", (ctx) => {
5949 notePhase(ctx.request, "session", takeSessionTime(ctx.request));
5950 const denied = requireScope(ctx, "admin");
5951 if (denied) return denied;
5952 const listed = timePhase(ctx.request, "secrets", () =>
5953 listSecrets(
5954 ctx.app,
5955 ctx.auth.user,
5956 ctx.params.scope ?? "",
5957 ctx.url.searchParams.get("ref") ?? "",
5958 ),
5959 );
5960 if (!listed.ok) return respondAccess(listed.error, 422);
5961 return timePhase(ctx.request, "render", () => json({ items: listed.value, next_cursor: null }));
5962 });
5963
5964 router.add("PUT", "/api/v1/secrets/:scope/:name", async (ctx) => {
5965 const denied = requireScope(ctx, "admin");
5966 if (denied) return denied;
5967 const input = await body(ctx.request);
5968 const set = putSecret(
5969 ctx.app,
5970 ctx.auth.user,
5971 ctx.params.scope ?? "",
5972 str(input.ref),
5973 ctx.params.name ?? "",
5974 str(input.value),
5975 );
5976 if (!set.ok) return respondAccess(set.error, 422);
5977 return json(set.value);
5978 });
5979
5980 router.add("DELETE", "/api/v1/secrets/:scope/:name", (ctx) => {
5981 const denied = requireScope(ctx, "admin");
5982 if (denied) return denied;
5983 const removed = removeSecret(
5984 ctx.app,
5985 ctx.auth.user,
5986 ctx.params.scope ?? "",
5987 ctx.url.searchParams.get("ref") ?? "",
5988 ctx.params.name ?? "",
5989 );
5990 if (!removed.ok) return respondAccess(removed.error, 422);
5991 return json({}, 200);
5992 });
5944 secretRoutes(router);
59935945
59945946 // The app's environment: names in, names out, and never a value back.
59955947 // `:var`, not `:name` — `:name` is the repository's.
src/http/api/secrets.ts added +63
@@ -0,0 +1,63 @@
1 /**
2 * Forge-level secrets over the API: `GET /api/v1/secrets/:scope`, and `PUT`
3 * and `DELETE` `/api/v1/secrets/:scope/:name`. Moved out of `api.ts` (#6330);
4 * the rules live in `actions/secrets.ts`.
5 */
6
7 import { listSecrets, putSecret, removeSecret } from "../../actions/secrets.ts";
8 import { notePhase, takeSessionTime, timePhase } from "../../core/route_timing.ts";
9 import type { Handler } from "../context.ts";
10 import { body, json, requireScope, respondAccess, str } from "../respond.ts";
11 import type { Router } from "../router.ts";
12
13 export function secretRoutes(router: Router<Handler>): void {
14 // Forge-level secrets: a sealed, write-only slot for the operator's own
15 // credentials. Admins only; the list is names and timestamps, never a
16 // value. `ref` names the owner: an org's name, or a repository's
17 // `owner/name`; the forge scope takes none.
18 router.add("GET", "/api/v1/secrets/:scope", (ctx) => {
19 notePhase(ctx.request, "session", takeSessionTime(ctx.request));
20 const denied = requireScope(ctx, "admin");
21 if (denied) return denied;
22 const listed = timePhase(ctx.request, "secrets", () =>
23 listSecrets(
24 ctx.app,
25 ctx.auth.user,
26 ctx.params.scope ?? "",
27 ctx.url.searchParams.get("ref") ?? "",
28 ),
29 );
30 if (!listed.ok) return respondAccess(listed.error, 422);
31 return timePhase(ctx.request, "render", () => json({ items: listed.value, next_cursor: null }));
32 });
33
34 router.add("PUT", "/api/v1/secrets/:scope/:name", async (ctx) => {
35 const denied = requireScope(ctx, "admin");
36 if (denied) return denied;
37 const input = await body(ctx.request);
38 const set = putSecret(
39 ctx.app,
40 ctx.auth.user,
41 ctx.params.scope ?? "",
42 str(input.ref),
43 ctx.params.name ?? "",
44 str(input.value),
45 );
46 if (!set.ok) return respondAccess(set.error, 422);
47 return json(set.value);
48 });
49
50 router.add("DELETE", "/api/v1/secrets/:scope/:name", (ctx) => {
51 const denied = requireScope(ctx, "admin");
52 if (denied) return denied;
53 const removed = removeSecret(
54 ctx.app,
55 ctx.auth.user,
56 ctx.params.scope ?? "",
57 ctx.url.searchParams.get("ref") ?? "",
58 ctx.params.name ?? "",
59 );
60 if (!removed.ok) return respondAccess(removed.error, 422);
61 return json({}, 200);
62 });
63 }

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs