feat: roles SPA foundations
This commit is contained in:
1 parent
80f7037d3a
commit
907949aa12
180 files changed
+4308
-835
No files matched your search
@@ -0,0 +1,152 @@
|
||||
import type { Access, Grant, RoleRef, Scope } from '../lib/permissions'
|
||||
|
||||
export const CATALOGUE_KEYS = [
|
||||
'content.read',
|
||||
'content.create',
|
||||
'content.update',
|
||||
'content.delete',
|
||||
'content.publish',
|
||||
'schema.view',
|
||||
'schema.edit',
|
||||
'schema.delete',
|
||||
'media.view',
|
||||
'media.upload',
|
||||
'media.delete',
|
||||
'authors.view',
|
||||
'authors.create',
|
||||
'authors.edit',
|
||||
'authors.delete',
|
||||
'activity.view',
|
||||
'activity.export',
|
||||
'users.view',
|
||||
'users.invite',
|
||||
'users.change_role',
|
||||
'users.remove',
|
||||
'settings.view',
|
||||
'settings.edit_general',
|
||||
'settings.manage_sites',
|
||||
'settings.delete_site',
|
||||
'apikeys.view',
|
||||
'apikeys.create',
|
||||
'apikeys.revoke',
|
||||
'roles.view',
|
||||
'roles.manage',
|
||||
'roles.edit_owner_only',
|
||||
'system.apply_updates',
|
||||
'system.edit_retention',
|
||||
'system.transfer_ownership',
|
||||
] as const
|
||||
|
||||
export const OWNER_ONLY_KEYS = [
|
||||
'settings.delete_site',
|
||||
'roles.edit_owner_only',
|
||||
'system.apply_updates',
|
||||
'system.edit_retention',
|
||||
'system.transfer_ownership',
|
||||
] as const
|
||||
|
||||
export const OWNER_ROLE: RoleRef = { id: 1, name: 'Owner', built_in: true, locked: true }
|
||||
export const ADMIN_ROLE: RoleRef = { id: 2, name: 'Admin', built_in: true, locked: false }
|
||||
export const WRITER_ROLE: RoleRef = { id: 3, name: 'Writer', built_in: true, locked: false }
|
||||
|
||||
const WRITER_GRANTS: Grant[] = [
|
||||
{ key: 'content.read', scope: 'all' },
|
||||
{ key: 'content.create', scope: 'all' },
|
||||
{ key: 'content.update', scope: 'all' },
|
||||
{ key: 'schema.view', scope: 'all' },
|
||||
{ key: 'media.view', scope: 'all' },
|
||||
{ key: 'media.upload', scope: 'all' },
|
||||
{ key: 'authors.view', scope: 'all' },
|
||||
{ key: 'authors.edit', scope: 'own' },
|
||||
{ key: 'activity.view', scope: 'own' },
|
||||
]
|
||||
|
||||
function everyKey(skip: readonly string[] = []): Grant[] {
|
||||
return CATALOGUE_KEYS.filter((key) => !skip.includes(key)).map((key) => ({
|
||||
key,
|
||||
scope: 'all' as Scope,
|
||||
}))
|
||||
}
|
||||
|
||||
export function grantsFrom(spec: string): Grant[] {
|
||||
return spec
|
||||
.split(',')
|
||||
.map((part) => part.trim())
|
||||
.filter((part) => part !== '')
|
||||
.map((part) => {
|
||||
const [key, scope = 'all'] = part.split('=')
|
||||
return { key: key!.trim(), scope: scope.trim() as Scope }
|
||||
})
|
||||
}
|
||||
|
||||
export function ownerAccess(): Access {
|
||||
return { role: { ...OWNER_ROLE }, permissions: everyKey() }
|
||||
}
|
||||
|
||||
export function adminAccess(): Access {
|
||||
return { role: { ...ADMIN_ROLE }, permissions: everyKey(OWNER_ONLY_KEYS) }
|
||||
}
|
||||
|
||||
export function writerAccess(): Access {
|
||||
return { role: { ...WRITER_ROLE }, permissions: WRITER_GRANTS.map((grant) => ({ ...grant })) }
|
||||
}
|
||||
|
||||
export function noAccess(name = 'Guest'): Access {
|
||||
return { role: { id: 9, name, built_in: false, locked: false }, permissions: [] }
|
||||
}
|
||||
|
||||
export function customAccess(grants: string | Grant[], name = 'Contributor', id = 7): Access {
|
||||
return {
|
||||
role: { id, name, built_in: false, locked: false },
|
||||
permissions: typeof grants === 'string' ? grantsFrom(grants) : grants,
|
||||
}
|
||||
}
|
||||
|
||||
function withoutKeys(access: Access, drop: (key: string) => boolean): Access {
|
||||
return {
|
||||
role: { ...access.role, locked: false, name: 'Custom' },
|
||||
permissions: access.permissions.filter((grant) => !drop(grant.key)),
|
||||
}
|
||||
}
|
||||
|
||||
export function accessFor(role?: string): Access | undefined {
|
||||
switch (role) {
|
||||
case 'owner':
|
||||
return ownerAccess()
|
||||
case 'admin':
|
||||
return adminAccess()
|
||||
case 'viewer':
|
||||
return customAccess('content.read, settings.view', 'Viewer')
|
||||
case 'keyholder':
|
||||
return customAccess('content.read, apikeys.view=own, apikeys.create', 'Key holder')
|
||||
case 'noschema':
|
||||
return withoutKeys(ownerAccess(), (key) => key.startsWith('schema.'))
|
||||
case 'member':
|
||||
case 'editor':
|
||||
case 'verified':
|
||||
return writerAccess()
|
||||
case 'user':
|
||||
return noAccess()
|
||||
default:
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
export function meBody(role?: string, extra: Record<string, unknown> = {}) {
|
||||
const access = accessFor(role)
|
||||
return { ...(access ? { access } : {}), ...extra }
|
||||
}
|
||||
|
||||
export function roleRefFor(role?: string | null): RoleRef | null {
|
||||
switch (role) {
|
||||
case 'owner':
|
||||
return { ...OWNER_ROLE }
|
||||
case 'admin':
|
||||
return { ...ADMIN_ROLE }
|
||||
case 'member':
|
||||
case 'editor':
|
||||
return { ...WRITER_ROLE }
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user