1import * as Effect from "effect/Effect";
2import * as Binding from "../../Binding.ts";
3import * as Output from "../../Output.ts";
4import type { PolicyStatement } from "../IAM/Policy.ts";
5import { isBindingHost } from "../Lambda/Function.ts";
6import type { SigningProfile } from "./SigningProfile.ts";
7
8/**
9 * Shared scaffolding for the AWS Signer HTTP runtime bindings.
10 *
11 * NOT exported from `index.ts` — every `{Op}Http.ts` in this service is a
12 * thin `Layer.effect(Cap, make…HttpBinding({ … }))` over one of the two
13 * builders below. Everything except the operation and the IAM action list is
14 * boilerplate.
15 *
16 * Signer authorizes profile-addressed actions against the *signing profile*
17 * ARN (`arn:…:/signing-profiles/name`) and its version-qualified variant
18 * (`arn:…:/signing-profiles/name/version`), so the profile-scoped builder
19 * grants on both. Job-addressed actions (describe/list/revoke a signing job)
20 * target job ids chosen per request at runtime, so those bindings are
21 * account-level and grant on `Resource: ["*"]`.
22 */
23const profilePolicyStatement = (
24 profile: SigningProfile,
25 actions: readonly string[],
26) => ({
27 Effect: "Allow" as const,
28 Action: [...actions],
29 Resource: [
30 Output.interpolate`${profile.arn}`,
31 // The version-qualified profile ARN (`…/signing-profiles/name/version`).
32 Output.map(profile.arn, (arn) => `${arn}/*`),
33 ],
34});
35
36/**
37 * Build the impl Effect for an operation whose input carries a `profileName`
38 * field: the runtime callable injects the bound {@link SigningProfile}'s name
39 * and the deploy-time half grants `actions` on the profile ARN (and its
40 * version-qualified pattern).
41 */
42export const makeSignerProfileHttpBinding = <
43 I extends { profileName?: string },
44 A,
45 E,
46 R,
47>(options: {
48 /** Fully-qualified binding tag, e.g. `AWS.Signer.StartSigningJob`. */
49 tag: string;
50 /** The distilled operation; `profileName` is injected from the profile. */
51 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
52 /** IAM actions granted on the profile ARN + version pattern. */
53 actions: readonly string[];
54}) =>
55 Effect.gen(function* () {
56 const op = yield* options.operation;
57
58 return Effect.fn(function* (profile: SigningProfile) {
59 // Outputs yield a DEFERRED effect — resolve again per invocation below.
60 const ProfileName = yield* profile.profileName;
61 if (!globalThis.__ALCHEMY_RUNTIME__) {
62 const host = yield* Binding.Host;
63 if (isBindingHost(host)) {
64 yield* host.bind`Allow(${host}, ${options.tag}(${profile}))`({
65 policyStatements: [
66 profilePolicyStatement(profile, options.actions),
67 ],
68 });
69 }
70 }
71 return Effect.fn(`${options.tag}(${profile.LogicalId})`)(function* (
72 request: Omit<I, "profileName">,
73 ) {
74 return yield* op({
75 ...request,
76 profileName: yield* ProfileName,
77 } as I);
78 });
79 });
80 });
81
82/**
83 * Build the impl Effect for an account-level Signer operation (signing jobs
84 * are addressed by runtime-chosen job ids; platforms are AWS-managed read-only
85 * catalog entries): the binding takes no resource argument and the deploy-time
86 * half grants `actions` on `Resource: ["*"]`.
87 */
88export const makeSignerHttpBinding = <I, A, E, R>(options: {
89 /** Fully-qualified binding tag, e.g. `AWS.Signer.DescribeSigningJob`. */
90 tag: string;
91 /** The distilled operation, invoked with the caller's request as-is. */
92 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
93 /**
94 * IAM actions granted on `Resource: ["*"]` (the target jobs/platforms are
95 * chosen per request at runtime and unknowable at deploy time).
96 */
97 actions: readonly string[];
98}) =>
99 Effect.gen(function* () {
100 const op = yield* options.operation;
101
102 return Effect.fn(function* () {
103 if (!globalThis.__ALCHEMY_RUNTIME__) {
104 const host = yield* Binding.Host;
105 if (isBindingHost(host)) {
106 const policyStatements: PolicyStatement[] = [
107 {
108 Effect: "Allow",
109 Action: [...options.actions],
110 Resource: ["*"],
111 },
112 ];
113 yield* host.bind`Allow(${host}, ${options.tag}())`({
114 policyStatements,
115 });
116 }
117 }
118 return Effect.fn(options.tag)(function* (request?: I) {
119 return yield* op((request ?? {}) as I);
120 });
121 });
122 });