Files
dockhand-upstream/src/lib/server/schedule-execution-access-core.ts
T

62 lines
2.7 KiB
TypeScript

/**
* Pure permission mapping for reading schedule_executions rows. No imports, so
* it stays unit-testable without the DB/auth module chain.
*
* A schedule execution's row (its errorMessage, details, and full logs) is only
* as sensitive as the thing it ran, so reading it is gated by the SAME resource
* permission that gates the underlying feature -- NOT a single blanket
* 'schedules:view'. That would either over-restrict (a backups-only viewer
* loses backup run stats) or under-restrict (a schedules-only viewer reads
* compose stderr from a stack deploy they can't otherwise see). Deploy stderr
* can carry redacted-but-not-guaranteed-clean output, hence the care.
*/
export type ScheduleExecResource = 'stacks' | 'backups' | 'schedules';
/** Every schedule type that maps to a NON-generic resource. Types not listed here
* fall through to 'schedules' in resourceForScheduleType. Kept here (not inlined)
* so the list gate can enumerate which types a resource-scoped caller may see. */
const NON_GENERIC_TYPES: Record<string, ScheduleExecResource> = {
stack_deploy: 'stacks',
git_stack_sync: 'stacks',
backup: 'backups',
restore: 'backups'
};
/**
* The resource whose `:view` permission a caller must hold to read executions of
* this schedule type. Unknown/unrecognized types fall back to 'schedules' -- the
* generic bucket, never a weaker one.
*/
export function resourceForScheduleType(scheduleType: string): ScheduleExecResource {
return NON_GENERIC_TYPES[scheduleType] ?? 'schedules';
}
/**
* The concrete schedule types whose executions a caller may read given which
* resource :view permissions they hold. Used to bound a NO-scheduleType-filter
* list query so a caller never receives errorMessage/details of a type they lack
* permission for.
*
* Returns null when 'schedules' AND 'stacks' AND 'backups' are all viewable --
* i.e. every type is allowed, so no type filter is needed (null = "do not
* filter", matching getScheduleExecutions' scheduleTypes semantics). Otherwise
* returns the explicit allow-list: the always-generic types when 'schedules' is
* held, plus each NON_GENERIC type whose resource is held.
*
* `allKnownTypes` is passed in (the caller has the ScheduleType union) so this
* stays dependency-free and testable.
*/
export function viewableScheduleTypes(
viewable: { schedules: boolean; stacks: boolean; backups: boolean },
allKnownTypes: readonly string[]
): string[] | null {
if (viewable.schedules && viewable.stacks && viewable.backups) return null;
const allowed: string[] = [];
for (const t of allKnownTypes) {
const resource = resourceForScheduleType(t);
if (viewable[resource]) allowed.push(t);
}
return allowed;
}