45 lines
1.6 KiB
TypeScript
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')
|
|
}
|