127 lines
3.9 KiB
TypeScript
127 lines
3.9 KiB
TypeScript
import { z } from "zod"
|
|
|
|
export const roleSchema = z.enum(["owner", "support", "admin", "editor", "manager"])
|
|
export const permissionScopeSchema = z.enum(["own", "team", "global"])
|
|
|
|
export const permissionSchema = z.enum([
|
|
"dashboard:read",
|
|
"roadmap:read",
|
|
"pages:read",
|
|
"pages:write",
|
|
"pages:publish",
|
|
"navigation:read",
|
|
"navigation:write",
|
|
"media:read",
|
|
"media:write",
|
|
"media:refine",
|
|
"users:read",
|
|
"users:write",
|
|
"users:manage_roles",
|
|
"commissions:read",
|
|
"commissions:write",
|
|
"commissions:transition",
|
|
"banner:read",
|
|
"banner:write",
|
|
"news:read",
|
|
"news:write",
|
|
"news:publish",
|
|
])
|
|
|
|
export type Role = z.infer<typeof roleSchema>
|
|
export type Permission = z.infer<typeof permissionSchema>
|
|
export type PermissionScope = z.infer<typeof permissionScopeSchema>
|
|
|
|
export type PermissionGrant = {
|
|
permission: Permission
|
|
scopes: PermissionScope[]
|
|
}
|
|
|
|
const allPermissions = permissionSchema.options
|
|
|
|
const allGlobalGrants: PermissionGrant[] = allPermissions.map((permission) => ({
|
|
permission,
|
|
scopes: ["global"],
|
|
}))
|
|
|
|
export const permissionMatrix: Record<Role, PermissionGrant[]> = {
|
|
owner: allGlobalGrants,
|
|
support: allGlobalGrants,
|
|
admin: allGlobalGrants,
|
|
manager: [
|
|
{ permission: "dashboard:read", scopes: ["global"] },
|
|
{ permission: "roadmap:read", scopes: ["global"] },
|
|
{ permission: "pages:read", scopes: ["global"] },
|
|
{ permission: "pages:write", scopes: ["global"] },
|
|
{ permission: "pages:publish", scopes: ["global"] },
|
|
{ permission: "navigation:read", scopes: ["global"] },
|
|
{ permission: "navigation:write", scopes: ["global"] },
|
|
{ permission: "media:read", scopes: ["global"] },
|
|
{ permission: "media:write", scopes: ["global"] },
|
|
{ permission: "media:refine", scopes: ["global"] },
|
|
{ permission: "users:read", scopes: ["global"] },
|
|
{ permission: "users:write", scopes: ["team"] },
|
|
{ permission: "commissions:read", scopes: ["global"] },
|
|
{ permission: "commissions:write", scopes: ["global"] },
|
|
{ permission: "commissions:transition", scopes: ["global"] },
|
|
{ permission: "banner:read", scopes: ["global"] },
|
|
{ permission: "banner:write", scopes: ["global"] },
|
|
{ permission: "news:read", scopes: ["global"] },
|
|
{ permission: "news:write", scopes: ["global"] },
|
|
{ permission: "news:publish", scopes: ["global"] },
|
|
],
|
|
editor: [
|
|
{ permission: "dashboard:read", scopes: ["global"] },
|
|
{ permission: "pages:read", scopes: ["team"] },
|
|
{ permission: "pages:write", scopes: ["team"] },
|
|
{ permission: "pages:publish", scopes: ["own"] },
|
|
{ permission: "navigation:read", scopes: ["team"] },
|
|
{ permission: "navigation:write", scopes: ["team"] },
|
|
{ permission: "media:read", scopes: ["team"] },
|
|
{ permission: "media:write", scopes: ["team"] },
|
|
{ permission: "media:refine", scopes: ["team"] },
|
|
{ permission: "users:read", scopes: ["own"] },
|
|
{ permission: "commissions:read", scopes: ["own"] },
|
|
{ permission: "commissions:write", scopes: ["own"] },
|
|
{ permission: "commissions:transition", scopes: ["own"] },
|
|
{ permission: "banner:read", scopes: ["global"] },
|
|
{ permission: "news:read", scopes: ["team"] },
|
|
{ permission: "news:write", scopes: ["team"] },
|
|
{ permission: "news:publish", scopes: ["own"] },
|
|
],
|
|
}
|
|
|
|
const scopeWeight: Record<PermissionScope, number> = {
|
|
own: 1,
|
|
team: 2,
|
|
global: 3,
|
|
}
|
|
|
|
export function normalizeRole(input: string | null | undefined): Role | null {
|
|
if (!input) {
|
|
return null
|
|
}
|
|
|
|
const parsed = roleSchema.safeParse(input.toLowerCase())
|
|
|
|
if (!parsed.success) {
|
|
return null
|
|
}
|
|
|
|
return parsed.data
|
|
}
|
|
|
|
export function hasPermission(
|
|
role: Role,
|
|
permission: Permission,
|
|
scope: PermissionScope = "global",
|
|
): boolean {
|
|
const grants = permissionMatrix[role]
|
|
const grant = grants.find((item) => item.permission === permission)
|
|
|
|
if (!grant) {
|
|
return false
|
|
}
|
|
|
|
return grant.scopes.some((grantedScope) => scopeWeight[grantedScope] >= scopeWeight[scope])
|
|
}
|