1import * as kms from "@distilled.cloud/aws/kms";
2import * as Effect from "effect/Effect";
3import * as Layer from "effect/Layer";
4import * as Binding from "../../Binding.ts";
5import { isBindingHost } from "../Lambda/Function.ts";
6import { keyLabel, keyPolicyStatement, type KeyLike } from "./KeyBinding.ts";
7import { ReEncrypt, type ReEncryptRequest } from "./ReEncrypt.ts";
8
9/**
10 * Bespoke (not scaffolded): ReEncrypt spans two keys — `kms:ReEncryptTo` on
11 * the destination and `kms:ReEncryptFrom` on the source — and injects
12 * `DestinationKeyId` (plus `SourceKeyId` when a source key is bound).
13 */
14export const ReEncryptHttp = Layer.effect(
15 ReEncrypt,
16 Effect.gen(function* () {
17 const reEncrypt = yield* kms.reEncrypt;
18
19 return Effect.fn(function* (destination: KeyLike, source?: KeyLike) {
20 const from = source ?? destination;
21 const DestinationKeyId =
22 typeof destination === "string"
23 ? Effect.succeed<string>(destination)
24 : yield* destination.keyId;
25 const SourceKeyId =
26 typeof from === "string"
27 ? Effect.succeed<string>(from)
28 : yield* from.keyId;
29 if (!globalThis.__ALCHEMY_RUNTIME__) {
30 const host = yield* Binding.Host;
31 if (isBindingHost(host)) {
32 yield* host.bind`Allow(${host}, AWS.KMS.ReEncrypt(${destination}, ${from}))`(
33 {
34 policyStatements:
35 source === undefined
36 ? [
37 keyPolicyStatement(
38 ["kms:ReEncryptFrom", "kms:ReEncryptTo"],
39 destination,
40 ),
41 ]
42 : [
43 keyPolicyStatement("kms:ReEncryptTo", destination),
44 keyPolicyStatement("kms:ReEncryptFrom", from),
45 ],
46 },
47 );
48 }
49 }
50 return Effect.fn(`AWS.KMS.ReEncrypt(${keyLabel(destination)})`)(
51 function* (request: ReEncryptRequest) {
52 const destinationKeyId = yield* DestinationKeyId;
53 // Always pin the source key (AWS best practice for symmetric
54 // ciphertexts) — and required for alias-bound IAM, where the
55 // `kms:RequestAlias` condition only matches when the alias appears
56 // in the request.
57 const sourceKeyId = yield* SourceKeyId;
58 return yield* reEncrypt({
59 ...request,
60 SourceKeyId: sourceKeyId,
61 DestinationKeyId: destinationKeyId,
62 });
63 },
64 );
65 });
66 }),
67);