UNPKG

@cloud-copilot/iam-lens

Version:

Visibility in IAM in and across AWS accounts

400 lines 16.9 kB
import { type TopLevelConfig } from '@cloud-copilot/iam-collect'; import { type ClientFactoryPlugin } from '../collect/collect.js'; import { type S3AbacOverride } from '../utils/s3Abac.js'; import { type LightRequestAnalysis } from './requestAnalysis.js'; import { type WhoCanPrincipalScope, type WhoCanResponse } from './whoCan.js'; import { type WorkerBootstrapPlugin } from './workerBootstrapPlugin.js'; export type { WorkerBootstrapPlugin } from './workerBootstrapPlugin.js'; /** * Configuration for creating a WhoCanProcessor. These settings are fixed * for the lifetime of the processor and baked into worker threads at creation time. */ export interface WhoCanProcessorConfig { /** The collect configurations for loading IAM data. */ collectConfigs: TopLevelConfig[]; /** The AWS partition to use (e.g. 'aws', 'aws-cn'). */ partition: string; tuning?: { /** * The number of worker threads to use beyond the main thread. Defaults to number of CPUs - 1. */ workerThreads?: number; /** * The concurrency level for processing simulations on the main thread. Defaults to 50. */ mainThreadConcurrency?: number; /** * The concurrency level for processing simulations on worker threads. * This is the value for EACH worker. * Defaults to 50. */ perWorkerConcurrency?: number; /** * The concurrency level for the shared preparation queue (account/principal fetches * across all active requests). Defaults to min(50, max(1, number of CPUs * 2)). */ preparationConcurrency?: number; /** * The maximum number of requests that may be actively expanded into scenarios * at once. Later requests remain as lightweight entries in pendingRequests. * Defaults to 30. */ maxRequestsInProgress?: number; }; /** Optional plugin to wrap the collect client with a custom implementation. */ clientFactoryPlugin?: ClientFactoryPlugin; /** * Optional plugin that runs once per worker thread at startup before any work * is processed. Use this for loading instrumentation, initializing logging * context, or other worker-lifetime setup. If the bootstrap function throws, * the worker fails and the processor surfaces the error. */ workerBootstrapPlugin?: WorkerBootstrapPlugin; /** An override for S3 ABAC being enabled when checking access to S3 Bucket resources. */ s3AbacOverride?: S3AbacOverride; /** Whether workers should collect grant details for allowed simulations. */ collectGrantDetails?: boolean; /** * Async callback invoked when a request settles (succeeds or fails). The processor * awaits this callback before removing the request from active state and admitting * the next pending request. This allows consumers to perform async work with backpressure. * * @param event - The settlement event containing the request ID, original request, * status, and either the result or the error. */ onRequestSettled: (event: WhoCanSettledEvent) => Promise<void>; /** * Optional async callback invoked when an `onRequestSettled` callback throws or rejects. * If this callback itself throws, the error is silently ignored. If not provided, a * warning is logged indicating that a settlement callback failed with no handler defined. * * @param event - The settlement event that was being delivered when the error occurred. * @param error - The error thrown by the `onRequestSettled` callback. */ onSettlementFailure?: (event: WhoCanSettledEvent, error: Error) => Promise<void>; /** * Whether the processor should ignore an existing principal index. Use this with testing. */ ignorePrincipalIndex?: boolean; } /** * Request parameters that vary per whoCan call on a processor. */ export interface WhoCanProcessorRequest { /** The ARN of the resource to check access for. */ resource?: string; /** The account ID the resource belongs to. */ resourceAccount?: string; /** The actions to check access for. */ actions: string[]; /** Whether to sort the results for consistent output. */ sort?: boolean; /** * Optional callback to filter which denied simulations should include detailed * denial analysis. If provided, deny details are collected for this request. * If the callback returns true for a given denial, the full deny details are * included in the response. If omitted, no deny details are collected for this request. * * @param details - A lightweight summary of the denied simulation. * @returns true to include full deny details for this denial. */ denyDetailsCallback?: (details: LightRequestAnalysis) => boolean; /** Optional scope to limit the set of principals tested. */ principalScope?: WhoCanPrincipalScope; /** Optional context keys to consider strict when running simulations. */ strictContextKeys?: string[]; } /** * Event delivered to the onRequestSettled callback when a request completes * (either successfully or with an error). */ export type WhoCanSettledEvent = WhoCanSettledSuccess | WhoCanSettledError; /** * Settlement event for a successfully completed request. */ export interface WhoCanSettledSuccess { /** Discriminator for the settlement outcome. */ status: 'fulfilled'; /** The unique ID assigned when the request was enqueued. */ requestId: string; /** The original request that was enqueued. */ request: WhoCanProcessorRequest; /** The whoCan result for this request. */ result: WhoCanResponse; } /** * Settlement event for a request that failed during preparation or simulation. */ export interface WhoCanSettledError { /** Discriminator for the settlement outcome. */ status: 'rejected'; /** The unique ID assigned when the request was enqueued. */ requestId: string; /** The original request that was enqueued. */ request: WhoCanProcessorRequest; /** The error that caused the request to fail. */ error: Error; } /** * A queue-first bulk processor that accepts many whoCan requests, expands * scenarios on the main thread, and feeds a shared simulation scheduler used * by worker threads and an optional main-thread runner. * * Results are delivered through the {@link WhoCanProcessorConfig.onRequestSettled} * callback as each request completes — they are not stored inside the processor. * * Use {@link enqueueWhoCan} to submit requests, then {@link waitForIdle} to * wait for all work to complete. Call {@link shutdown} when done to terminate * worker threads. */ export declare class WhoCanProcessor { private workers; private collectClient; private config; private isShutdown; private workersDead; private pendingRequests; private activeRequestOrder; private requestStates; private admissionPumpRunning; private draining; private preparationQueue; private idleWaiters; private mainThreadWorker; private fatalError?; private shutdownPromise?; private constructor(); /** * Waits for every worker to post a `{ type: 'ready' }` message. If any * worker fails (error, non-zero exit, or explicit `startupError` message) * the remaining workers are terminated and the returned promise rejects. * * @param workers - The worker instances to wait on. */ private static awaitWorkersReady; /** * Creates a new WhoCanProcessor with worker threads, a shared cache, and * lifetime-scoped message routing. The processor is ready to accept requests * immediately after creation. * * @param config - The configuration for the processor, including collect configs, * partition, simulation options, tuning, and the onRequestSettled callback. * @returns a new WhoCanProcessor instance */ static create(config: WhoCanProcessorConfig): Promise<WhoCanProcessor>; /** * Enqueues a whoCan request for processing. Returns a unique request ID * that will appear in the corresponding {@link WhoCanSettledEvent}. * * This method never activates a request directly — it appends to * pendingRequests and signals the admission pump. * * @param request - The whoCan request parameters. * @returns the unique request ID assigned to this request. * @throws if the processor is shut down or draining via waitForIdle. */ enqueueWhoCan(request: WhoCanProcessorRequest): string; /** * Returns a promise that resolves when all pending and active work has * completed and all onRequestSettled callbacks have finished. * * While draining, new calls to {@link enqueueWhoCan} will throw. Once * the drain completes, the processor re-opens for new enqueues. * * @returns a promise that resolves when idle, or rejects if a worker crashes. */ waitForIdle(): Promise<void>; /** * Shuts down the processor by rejecting all pending requests, waiting for * active requests to settle, and terminating all worker threads. * * This method is idempotent — calling it multiple times is safe. */ shutdown(): Promise<void>; /** * Internal shutdown implementation. Rejects pending requests, waits for * active requests to drain, then terminates workers. */ private executeShutdown; /** * Installs lifetime-scoped message, error, and exit listeners on all workers. * Message listeners route simulation results and deny-detail checks to the * correct request state using requestId. Error/exit listeners detect crashes * and mark the processor as fatally failed. */ private installLifetimeWorkerListeners; /** * Routes a message from a worker thread to the appropriate handler based * on message type and requestId. * * @param msg - The message received from the worker. * @param worker - The worker that sent the message. */ private handleWorkerMessage; /** * Creates the main-thread simulation runner if mainThreadConcurrency > 0. * The runner pulls from the FIFO scheduler and routes results by requestId. */ private createMainThreadRunner; /** * Dequeues the next simulation scenario using FIFO request priority. * Prefers the oldest active request that has ready scenarios. If the oldest * is temporarily empty (still preparing), falls back to the next request * with ready scenarios so cores do not idle. * * @returns the next work item, or undefined if no scenarios are ready. */ private dequeueNextScenario; /** * Notifies all simulation consumers (workers and main thread) that new * work may be available in the scheduler. */ private notifySimulationConsumers; /** * Wakes the admission pump to process pending requests. If the pump is * already running, this is a no-op — the running pump will pick up new * pending requests on its next iteration. */ private wakeAdmissionPump; /** * The admission pump loop. Drains pendingRequests into active processing * up to maxRequestsInProgress. Only one instance of this loop runs at a time, * guarded by admissionPumpRunning. */ private runAdmissionPump; /** * Creates a fresh RequestState for an admitted request. * * @param submitted - The submitted request to create state for. * @returns the new RequestState. */ private createRequestState; /** * Enqueues the root preparation job for a request. This job performs resource * account resolution, resource policy lookup, action expansion, principal scope * handling, and then enqueues follow-up preparation jobs to enumerate principals. * * @param state - The request state to prepare. */ private enqueueRootPreparation; /** * Executes the root preparation for a request: resolves the resource account, * fetches the resource policy, expands actions, determines which accounts and * principals to check, and enqueues follow-up preparation jobs. * * @param state - The request state to prepare. */ private executeRootPreparation; /** * Handles a simulation result from a worker or the main thread runner. * Routes the result to the correct request state and checks for completion. * * @param requestId - The ID of the request this result belongs to. * @param result - The simulation job result. */ private handleSimulationResult; /** * Handles a checkDenyDetails request from a worker thread. Looks up the * request's denyDetailsCallback and responds. * * @param requestId - The ID of the request. * @param checkId - The unique check ID for this deny-details round trip. * @param lightAnalysis - The light analysis to pass to the callback. * @param worker - The worker to respond to. */ private handleCheckDenyDetails; /** * Handles a deny details result from a worker thread. Decrements the * pending deny-details counter and checks for request completion. * * @param requestId - The ID of the request. * @param denyDetail - The deny detail to store. */ private handleDenyDetailsResult; /** * Checks whether a request has completed all preparation and simulation work. * If so, settles the request as successful. * * @param state - The request state to check. */ private checkRequestCompletion; /** * Settles a request as successful: builds the WhoCanResponse, awaits * onRequestSettled, removes the request from active state, and wakes * the admission pump. * * @param state - The request state to settle. */ private settleRequestAsSuccess; /** * Settles a request as failed: invokes onRequestSettled with the error * immediately, but keeps the request in active state until all in-flight * work drains (created === completed). Late results for settled requests * are discarded but still counted so the drain completes. * * @param state - The request state to settle. * @param error - The error that caused the failure. */ private settleRequestAsError; /** * Invokes the onRequestSettled callback and routes any errors through * {@link handleSettlementFailure}. * * @param event - The settlement event to deliver. */ private invokeSettledCallback; /** * Awaits the onRequestSettled callback, then removes the request from * active state and wakes the admission pump. Used for successful settlements * where all work is already complete. * * @param state - The request state being settled. * @param event - The settlement event to deliver. */ private invokeSettledCallbackAndCleanup; /** * Checks whether a settled request has fully drained: the onRequestSettled * callback has been awaited, all preparation jobs have finished, all * simulation results have been received, and all deny-detail round trips * have completed. Only then is the request removed from active state. * * @param state - The request state to check. */ private checkRequestDrain; /** * Removes a request from active state, wakes the admission pump to fill * the freed slot, and checks if the processor is now idle. * * @param state - The request state to remove. */ private removeFromActiveState; /** * Returns true if the processor has no pending, active, or in-flight work. * * @returns true if fully idle. */ private isIdle; /** * Checks whether the processor has become idle and resolves or rejects the * waitForIdle promise if so. */ private checkIdle; /** * Handles an error thrown by the `onRequestSettled` callback. If the * `onSettlementFailure` callback is defined, it is invoked with the event * and error; any error it throws is silently ignored. If not defined, a * warning is logged. * * @param event - The settlement event that was being delivered. * @param err - The raw error thrown by the `onRequestSettled` callback. */ private handleSettlementFailure; /** * Handles an unexpected worker failure by marking the processor as dead, * terminating remaining workers, and rejecting all active and pending requests. * * @param error - The error that caused the worker failure. */ private handleWorkerFailure; } //# sourceMappingURL=WhoCanProcessor.d.ts.map