@adtl/gateway 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/startup.ts CHANGED
@@ -14,7 +14,8 @@ import { parse as parseYaml } from "yaml";
14
14
  import { ZodError } from "zod";
15
15
  import { ApprovalBroker, type Notifier } from "./approvals.js";
16
16
  import { JsonlAuditLog } from "./audit.js";
17
- import type { Principal } from "./authorizer.js";
17
+ import type { Authorizer, Principal } from "./authorizer.js";
18
+ import { CedarAuthorizer } from "./cedar.js";
18
19
  import {
19
20
  GatewayConfigSchema,
20
21
  expandApprovals,
@@ -170,6 +171,23 @@ export function gatewayPrincipal(cfg: GatewayConfig, env: NodeJS.ProcessEnv): Pr
170
171
  return { type: p.type, id, ...(p.claims ? { claims: p.claims } : {}) };
171
172
  }
172
173
 
174
+ /**
175
+ * The configured authorizers, in the order they are asked: roles, then cedar.
176
+ * Cedar files are read and parsed here, so a bad policy fails startup.
177
+ */
178
+ export async function gatewayAuthorizers(cfg: GatewayConfig, baseDir: string): Promise<Authorizer[]> {
179
+ const out: Authorizer[] = [];
180
+ if (cfg.roles) out.push(new RoleAuthorizer(cfg.roles));
181
+ if (cfg.cedar) {
182
+ try {
183
+ out.push(await CedarAuthorizer.load(cfg.cedar, (p) => resolve(baseDir, p)));
184
+ } catch (err) {
185
+ throw new GatewayConfigError((err as Error).message);
186
+ }
187
+ }
188
+ return out;
189
+ }
190
+
173
191
  /**
174
192
  * Under Claude Code, which passes `CLAUDE_CODE_SESSION_ID` to stdio MCP
175
193
  * servers, the gateway uses that id and keeps session labels in files, so the
@@ -193,6 +211,7 @@ export async function startGateway(
193
211
  // Config problems that need no process: fail before starting any.
194
212
  const approverMembers = approverRoles(loaded.labels, loaded.gateway);
195
213
  const baseDir = dirname(loaded.path);
214
+ const authorizers = await gatewayAuthorizers(loaded.gateway, baseDir);
196
215
  const log = opts.logger ?? { info: (m: string) => console.error(`[adtl-gateway] ${m}`), warn: (m: string) => console.error(`[adtl-gateway] warning: ${m}`) };
197
216
  const clients: Client[] = [];
198
217
  const closeAll = () => Promise.allSettled(clients.map((c) => c.close())).then(() => undefined);
@@ -260,13 +279,14 @@ export async function startGateway(
260
279
  log.info(`roles: ${Object.keys(roles).join(", ")} (caller: ${principal?.id ?? "anonymous"})`);
261
280
  if (!principal) log.warn('roles are set but gateway.principal is not: only roles with members ["*"] apply');
262
281
  }
282
+ for (const a of authorizers) if (a.name !== "roles") log.info(`authorizer: ${a.name}`);
263
283
  const gateway = new AdtlGateway({
264
284
  ...(shared ? { sessionId: shared.sessionId } : {}),
265
285
  upstreams,
266
286
  invoker,
267
287
  ...(auditLog ? { audit: auditLog } : {}),
268
288
  ...(approvals ? { approvals } : {}),
269
- ...(roles ? { authorizer: new RoleAuthorizer(roles) } : {}),
289
+ ...(authorizers.length ? { authorizer: authorizers } : {}),
270
290
  ...(principal ? { principal } : {}),
271
291
  ...(approverMembers.size ? { approverRoles: approverMembers } : {}),
272
292
  ...(loaded.gateway.approvals?.elicitation ? { elicitApprovals: true } : {}),