import * as lf from "@distilled.cloud/aws/lakeformation"; import * as Effect from "effect/Effect"; import * as Schedule from "effect/Schedule"; import * as Stream from "effect/Stream"; /** * Bounded retry through `ConcurrentModificationException` — Lake Formation * serializes grant/revoke/settings mutations and rejects concurrent writers. * Explicitly typed so declaration emit does not widen the provider layer (see * PATTERNS §7 on inlined `Effect.retry`). */ export const retryWhileConcurrentModification = < A, E extends { _tag: string }, R, >( self: Effect.Effect, ): Effect.Effect => Effect.retry(self, { while: (e) => e._tag === "ConcurrentModificationException", schedule: Schedule.max([Schedule.fixed("2 seconds"), Schedule.recurs(10)]), }); /** * Bounded retry through `InvalidLakeFormationPrincipal` — a freshly-created * IAM role/user takes ~10s to propagate before Lake Formation accepts it as a * principal ("Invalid principal, arn: ..." surfaced as a typed synthetic * error via the distilled patch). Explicitly typed for the same * declaration-emit reason as above. */ export const retryWhileInvalidPrincipal = ( self: Effect.Effect, ): Effect.Effect => Effect.retry(self, { while: (e) => e._tag === "InvalidLakeFormationPrincipal", schedule: Schedule.max([Schedule.fixed("3 seconds"), Schedule.recurs(10)]), }); /** * Observe the permissions a specific principal holds on a resource. * * IMPORTANT: this deliberately lists by `Resource` only and filters entries * client-side by exact principal identifier. Listing with a `Principal` * filter makes Lake Formation fold the `IAM_ALLOWED_PRINCIPALS` group grant * (typically `ALL`) into the principal's effective permissions, which would * make the reconciler believe `ALL` was granted directly (verified live). */ export const observePrincipalPermissions = Effect.fn( "AWS.LakeFormation.observePrincipalPermissions", )(function* ( principal: string, resource: lf.Resource, catalogId: string | undefined, ) { const pages = yield* lf.listPermissions .pages({ CatalogId: catalogId, Resource: resource }) .pipe(Stream.runCollect); const entries = Array.from(pages) .flatMap((page) => page.PrincipalResourcePermissions ?? []) .filter((e) => e.Principal?.DataLakePrincipalIdentifier === principal); const permissions = [ ...new Set(entries.flatMap((e) => e.Permissions ?? [])), ].sort(); const permissionsWithGrantOption = [ ...new Set(entries.flatMap((e) => e.PermissionsWithGrantOption ?? [])), ].sort(); return { permissions, permissionsWithGrantOption }; });