Files
ipal-kit/src/modules/access/predicates.ts
T

45 lines
1.6 KiB
TypeScript

import type { Role } from './types.js'
import { ROLE_HIERARCHY } from './types.js'
/**
* The plugin can't know the client's generated User type, and Payload types
* `req.user` loosely (UntypedUser | null). Predicates therefore accept an
* unknown-ish user and read `roles` defensively — no assumptions about shape
* beyond an optional roles array.
*/
type MaybeUser = { roles?: null | Role[] } | null | Record<string, unknown> | undefined
/** Safely extracts the roles array from a loosely-typed user. */
function getRoles(user: MaybeUser): Role[] {
if (!user || typeof user !== 'object') {return []}
const roles = (user as { roles?: unknown }).roles
if (!Array.isArray(roles)) {return []}
return roles.filter((role): role is Role => ROLE_HIERARCHY.includes(role as Role))
}
/** Highest-privilege role index the user holds, or -1 if none. */
function highestRoleIndex(user: MaybeUser): number {
const roles = getRoles(user)
if (!roles.length) {return -1}
return Math.max(...roles.map((role) => ROLE_HIERARCHY.indexOf(role)))
}
/**
* True if the user holds at least the given role in the hierarchy.
* admin satisfies 'editor' and 'user'; editor satisfies 'user'.
*/
export function hasMinimumRole(user: MaybeUser, minimum: Role): boolean {
return highestRoleIndex(user) >= ROLE_HIERARCHY.indexOf(minimum)
}
/** True if the user is an admin. */
export function isAdmin(user: MaybeUser): boolean {
return hasMinimumRole(user, 'admin')
}
/** True if the user is an editor or higher (editor, admin). */
export function isEditor(user: MaybeUser): boolean {
return hasMinimumRole(user, 'editor')
}