Files
llmapikey/lib/auth/admin-allowlist.js
T
tiennm99 616f133989 feat: add gated admin console for api_keys registry (list/search/filter/revoke/mint)
- env-allowlist authz via ADMIN_GITHUB_USER_IDS on numeric provider_id (no migration)
- server-side re-gated revoke + manual-mint actions
- parameterized search/filter/paginate queries
- shared mint-key extraction (DRY) from generate-key
- notFound() for non-admins (404 never leaks route existence)
- 3 unit-test suites (authz/queries/integration)
2026-06-13 21:16:57 +07:00

37 lines
1.3 KiB
JavaScript

/**
* Pure admin-allowlist logic. No `server-only` guard and no network I/O — only
* an env read — so it is unit-testable under plain node, mirroring
* `lib/keys/key-format.js`. The session-resolving gate (`requireAdminIdentity`)
* lives in `lib/auth/is-admin.js`, which adds the `server-only` boundary.
*
* Authz is keyed on the numeric, immutable GitHub `provider_id` — the same
* identity anchor used for key ownership — never the mutable login.
*/
/**
* Parse the comma-separated allowlist env value into trimmed, non-empty ids.
*
* @param {string|undefined|null} raw e.g. "12345, 67890"
* @returns {string[]} numeric provider_id strings; `[]` when empty/unset.
*/
export function parseAdminIds(raw) {
return String(raw ?? "")
.split(",")
.map((s) => s.trim())
.filter(Boolean);
}
/**
* True iff the identity's numeric GitHub id is in `ADMIN_GITHUB_USER_IDS`.
* Compared as exact strings — never a substring/prefix match, so "4" can never
* match "42". Empty/unset env ⇒ zero admins (fail-closed).
*
* @param {{ githubUserId?: string }|null|undefined} identity
* @returns {boolean}
*/
export function isAdmin(identity) {
const id = identity?.githubUserId;
if (!id) return false;
return parseAdminIds(process.env.ADMIN_GITHUB_USER_IDS).includes(id);
}