| 1 | import * as lf from "@distilled.cloud/aws/lakeformation"; |
| 2 | import * as Effect from "effect/Effect"; |
| 3 | import * as Schedule from "effect/Schedule"; |
| 4 | import * as Stream from "effect/Stream"; |
| 5 | |
| 6 | /** |
| 7 | * Bounded retry through `ConcurrentModificationException` — Lake Formation |
| 8 | * serializes grant/revoke/settings mutations and rejects concurrent writers. |
| 9 | * Explicitly typed so declaration emit does not widen the provider layer (see |
| 10 | * PATTERNS §7 on inlined `Effect.retry`). |
| 11 | */ |
| 12 | export const retryWhileConcurrentModification = < |
| 13 | A, |
| 14 | E extends { _tag: string }, |
| 15 | R, |
| 16 | >( |
| 17 | self: Effect.Effect<A, E, R>, |
| 18 | ): Effect.Effect<A, E, R> => |
| 19 | Effect.retry(self, { |
| 20 | while: (e) => e._tag === "ConcurrentModificationException", |
| 21 | schedule: Schedule.max([Schedule.fixed("2 seconds"), Schedule.recurs(10)]), |
| 22 | }); |
| 23 | |
| 24 | /** |
| 25 | * Bounded retry through `InvalidLakeFormationPrincipal` — a freshly-created |
| 26 | * IAM role/user takes ~10s to propagate before Lake Formation accepts it as a |
| 27 | * principal ("Invalid principal, arn: ..." surfaced as a typed synthetic |
| 28 | * error via the distilled patch). Explicitly typed for the same |
| 29 | * declaration-emit reason as above. |
| 30 | */ |
| 31 | export const retryWhileInvalidPrincipal = <A, E extends { _tag: string }, R>( |
| 32 | self: Effect.Effect<A, E, R>, |
| 33 | ): Effect.Effect<A, E, R> => |
| 34 | Effect.retry(self, { |
| 35 | while: (e) => e._tag === "InvalidLakeFormationPrincipal", |
| 36 | schedule: Schedule.max([Schedule.fixed("3 seconds"), Schedule.recurs(10)]), |
| 37 | }); |
| 38 | |
| 39 | /** |
| 40 | * Observe the permissions a specific principal holds on a resource. |
| 41 | * |
| 42 | * IMPORTANT: this deliberately lists by `Resource` only and filters entries |
| 43 | * client-side by exact principal identifier. Listing with a `Principal` |
| 44 | * filter makes Lake Formation fold the `IAM_ALLOWED_PRINCIPALS` group grant |
| 45 | * (typically `ALL`) into the principal's effective permissions, which would |
| 46 | * make the reconciler believe `ALL` was granted directly (verified live). |
| 47 | */ |
| 48 | export const observePrincipalPermissions = Effect.fn( |
| 49 | "AWS.LakeFormation.observePrincipalPermissions", |
| 50 | )(function* ( |
| 51 | principal: string, |
| 52 | resource: lf.Resource, |
| 53 | catalogId: string | undefined, |
| 54 | ) { |
| 55 | const pages = yield* lf.listPermissions |
| 56 | .pages({ CatalogId: catalogId, Resource: resource }) |
| 57 | .pipe(Stream.runCollect); |
| 58 | const entries = Array.from(pages) |
| 59 | .flatMap((page) => page.PrincipalResourcePermissions ?? []) |
| 60 | .filter((e) => e.Principal?.DataLakePrincipalIdentifier === principal); |
| 61 | const permissions = [ |
| 62 | ...new Set(entries.flatMap((e) => e.Permissions ?? [])), |
| 63 | ].sort(); |
| 64 | const permissionsWithGrantOption = [ |
| 65 | ...new Set(entries.flatMap((e) => e.PermissionsWithGrantOption ?? [])), |
| 66 | ].sort(); |
| 67 | return { permissions, permissionsWithGrantOption }; |
| 68 | }); |