diff --git a/src/lib/roles.ts b/src/lib/roles.ts new file mode 100644 index 0000000..1f9c269 --- /dev/null +++ b/src/lib/roles.ts @@ -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 = { + 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'); +} diff --git a/tests/unit/roles.test.ts b/tests/unit/roles.test.ts new file mode 100644 index 0000000..74025ef --- /dev/null +++ b/tests/unit/roles.test.ts @@ -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); + }); +});