feat: add role-based permission system
This commit is contained in:
@@ -0,0 +1,56 @@
|
|||||||
|
export type Role =
|
||||||
|
| 'sysadmin'
|
||||||
|
| 'director'
|
||||||
|
| 'foster_coordinator'
|
||||||
|
| 'volunteer_manager'
|
||||||
|
| 'vet_liaison'
|
||||||
|
| 'content_editor'
|
||||||
|
| 'applications_manager'
|
||||||
|
| 'viewer'
|
||||||
|
| 'foster';
|
||||||
|
|
||||||
|
export type Permission =
|
||||||
|
| 'dashboard'
|
||||||
|
| 'pets'
|
||||||
|
| 'pets_view'
|
||||||
|
| 'applications'
|
||||||
|
| 'sponsors'
|
||||||
|
| 'donations'
|
||||||
|
| 'expenses'
|
||||||
|
| 'vets'
|
||||||
|
| 'medical'
|
||||||
|
| 'events'
|
||||||
|
| 'content'
|
||||||
|
| 'volunteers'
|
||||||
|
| 'users'
|
||||||
|
| 'shop'
|
||||||
|
| 'import'
|
||||||
|
| 'system';
|
||||||
|
|
||||||
|
const ROLE_PERMISSIONS: Record<Role, Permission[]> = {
|
||||||
|
sysadmin: [
|
||||||
|
'dashboard', 'pets', 'pets_view', 'applications', 'sponsors',
|
||||||
|
'donations', 'expenses', 'vets', 'medical', 'events', 'content',
|
||||||
|
'volunteers', 'users', 'shop', 'import', 'system'
|
||||||
|
],
|
||||||
|
director: [
|
||||||
|
'dashboard', 'pets', 'pets_view', 'applications', 'sponsors',
|
||||||
|
'donations', 'expenses', 'vets', 'medical', 'events', 'content',
|
||||||
|
'volunteers', 'users', 'shop', 'import'
|
||||||
|
],
|
||||||
|
foster_coordinator: ['dashboard', 'pets', 'pets_view', 'applications'],
|
||||||
|
volunteer_manager: ['dashboard', 'volunteers', 'events'],
|
||||||
|
vet_liaison: ['dashboard', 'pets', 'pets_view', 'vets', 'medical', 'expenses'],
|
||||||
|
content_editor: ['dashboard', 'sponsors', 'content', 'events'],
|
||||||
|
applications_manager: ['dashboard', 'applications', 'pets_view'],
|
||||||
|
viewer: ['dashboard'],
|
||||||
|
foster: ['dashboard']
|
||||||
|
};
|
||||||
|
|
||||||
|
export function hasPermission(role: Role, permission: Permission): boolean {
|
||||||
|
return ROLE_PERMISSIONS[role]?.includes(permission) ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function canViewPets(role: Role): boolean {
|
||||||
|
return hasPermission(role, 'pets') || hasPermission(role, 'pets_view');
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import { hasPermission, canViewPets, type Role, type Permission } from '$lib/roles';
|
||||||
|
|
||||||
|
describe('roles', () => {
|
||||||
|
it('sysadmin has all permissions', () => {
|
||||||
|
const perms: Permission[] = [
|
||||||
|
'dashboard', 'pets', 'pets_view', 'applications', 'sponsors',
|
||||||
|
'donations', 'expenses', 'vets', 'medical', 'events', 'content',
|
||||||
|
'volunteers', 'users', 'shop', 'import', 'system'
|
||||||
|
];
|
||||||
|
for (const perm of perms) {
|
||||||
|
expect(hasPermission('sysadmin', perm)).toBe(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('director has all except system', () => {
|
||||||
|
expect(hasPermission('director', 'pets')).toBe(true);
|
||||||
|
expect(hasPermission('director', 'system')).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('foster has only dashboard', () => {
|
||||||
|
expect(hasPermission('foster', 'dashboard')).toBe(true);
|
||||||
|
expect(hasPermission('foster', 'pets')).toBe(false);
|
||||||
|
expect(hasPermission('foster', 'users')).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('viewer has only dashboard', () => {
|
||||||
|
expect(hasPermission('viewer', 'dashboard')).toBe(true);
|
||||||
|
expect(hasPermission('viewer', 'pets')).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('applications_manager can view pets but not edit', () => {
|
||||||
|
expect(hasPermission('applications_manager', 'applications')).toBe(true);
|
||||||
|
expect(hasPermission('applications_manager', 'pets_view')).toBe(true);
|
||||||
|
expect(hasPermission('applications_manager', 'pets')).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('canViewPets returns true for roles with pets or pets_view', () => {
|
||||||
|
expect(canViewPets('sysadmin')).toBe(true);
|
||||||
|
expect(canViewPets('applications_manager')).toBe(true);
|
||||||
|
expect(canViewPets('viewer')).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user