Add permissions and RBAC planning doc

Granular permission system: domain.action pattern (accounts.view, pos.edit,
repairs.admin). Custom roles composed of permissions. Default system roles
(Admin, Manager, Sales Associate, Technician, Instructor, Viewer). Migration
plan from current role enum. API and frontend guard patterns.
This commit is contained in:
Ryan Moon
2026-03-28 16:06:23 -05:00
parent e65175ef19
commit 693121ec14

View File

@@ -0,0 +1,275 @@
Music Store Management Platform
Permissions & Role-Based Access Control
Version 1.0 | Draft
# 1. Overview
The platform uses a permission-based access control system. Permissions are granular actions (view accounts, edit inventory, approve tax exemptions). Roles are named collections of permissions. Stores can create custom roles by selecting which permissions to include. The system ships with default roles that cover common staff configurations.
# 2. Permission Structure
Each permission follows the pattern: `{domain}.{action}`
## 2.1 Actions
Action | Description
view | Read-only access — list, search, view detail
edit | Create, update, soft-delete
admin | Full control — includes configuration, approval workflows, destructive actions
manage | Alias for edit + admin (shorthand for full domain control)
## 2.2 Domains & Permissions
Domain | Permissions | Description
accounts | accounts.view, accounts.edit, accounts.admin | Customer accounts, members, payment methods
inventory | inventory.view, inventory.edit, inventory.admin | Products, categories, suppliers, stock
pos | pos.view, pos.edit, pos.admin | Transactions, cash drawer, discounts
rentals | rentals.view, rentals.edit, rentals.admin | Rental contracts, fleet, billing
lessons | lessons.view, lessons.edit, lessons.admin | Scheduling, enrollment, attendance
repairs | repairs.view, repairs.edit, repairs.admin | Repair tickets, parts, labor
accounting | accounting.view, accounting.edit, accounting.admin | Journal entries, chart of accounts, reports
personnel | personnel.view, personnel.edit, personnel.admin | Time clock, scheduling, payroll
files | files.view, files.upload, files.delete | File storage operations
email | email.view, email.send, email.admin | Email logs, send mass email, manage templates
settings | settings.view, settings.edit | Store settings, locations, tax rates
users | users.view, users.edit, users.admin | User accounts, roles, permissions
reports | reports.view, reports.export | View reports, export data
# 3. What Each Action Controls
## 3.1 accounts
Permission | Allows
accounts.view | List/search accounts, view account detail, view members, view payment methods, view tax exemptions
accounts.edit | Create/update accounts, add/edit/move members, add payment methods, create tax exemption requests
accounts.admin | Delete accounts, delete members, approve/revoke tax exemptions, set primary member, manage identifiers
## 3.2 inventory
Permission | Allows
inventory.view | List/search products, view detail, view stock levels, view categories/suppliers
inventory.edit | Create/update products, receive stock, update inventory units, manage categories/suppliers
inventory.admin | Delete products, adjust stock (cycle counts), manage lookup values (custom statuses/conditions), bulk operations
## 3.3 pos
Permission | Allows
pos.view | View transaction history, view cash drawer sessions
pos.edit | Process sales, take payments, apply discounts (within threshold), open/close drawer
pos.admin | Void transactions, override prices below min_price, apply discounts above threshold, manage discount rules
## 3.4 rentals
Permission | Allows
rentals.view | View rental contracts, fleet inventory, billing history
rentals.edit | Create rentals, process returns, manage fleet, generate agreements
rentals.admin | Override rental terms, adjust equity, cancel contracts, approve buyouts
## 3.5 repairs
Permission | Allows
repairs.view | View repair tickets, parts inventory, technician assignments
repairs.edit | Create tickets, log parts/labor, update status, manage repair parts inventory
repairs.admin | Override estimates, adjust billing, manage repair templates, delete tickets
## 3.6 files
Permission | Allows
files.view | View/download files
files.upload | Upload files
files.delete | Delete files
## 3.7 users
Permission | Allows
users.view | List users, view roles
users.edit | Create/update users, assign roles
users.admin | Create/modify roles, manage permissions, delete users
# 4. Database Schema
## 4.1 permission
System-defined. Seeded on first run. Not editable by stores.
Column | Type | Notes
id | uuid PK |
slug | varchar | Unique. e.g. accounts.view, pos.edit
domain | varchar | e.g. accounts, pos, inventory
action | varchar | e.g. view, edit, admin
description | varchar | Human-readable description
created_at | timestamptz |
## 4.2 role
Store-defined roles. System provides defaults, stores can create custom.
Column | Type | Notes
id | uuid PK |
company_id | uuid FK | Tenant scoping
name | varchar | Role display name. e.g. "Sales Associate", "Repair Tech"
slug | varchar | Unique per company. e.g. sales_associate
description | text |
is_system | boolean | System roles can't be deleted (admin, manager, staff, technician, instructor)
is_active | boolean |
created_at | timestamptz |
updated_at | timestamptz |
## 4.3 role_permission
Many-to-many: which permissions a role has.
Column | Type | Notes
id | uuid PK |
role_id | uuid FK |
permission_id | uuid FK |
created_at | timestamptz |
Unique constraint on (role_id, permission_id).
## 4.4 user_role
Replaces the current `role` enum on the user table. Users can have multiple roles.
Column | Type | Notes
id | uuid PK |
user_id | uuid FK |
role_id | uuid FK |
assigned_by | uuid FK | Who assigned this role
created_at | timestamptz |
Unique constraint on (user_id, role_id).
# 5. Default Roles
## 5.1 System Roles (seeded per company)
Role | Permissions | Typical User
Admin | All permissions | Store owner, IT admin
Manager | All except users.admin, settings.edit | Assistant manager, department head
Sales Associate | accounts.view, accounts.edit, pos.view, pos.edit, inventory.view, rentals.view, files.view, files.upload | Front counter staff
Technician | repairs.view, repairs.edit, inventory.view, files.view, files.upload | Repair technician
Instructor | lessons.view, lessons.edit, accounts.view | Music teacher
Viewer | *.view (all view permissions) | Read-only access, auditors
## 5.2 Custom Role Example
A store could create:
**"School Sales Rep"**
- accounts.view, accounts.edit
- rentals.view, rentals.edit
- pos.view, pos.edit
- inventory.view
This gives them everything they need to work with school accounts and rental contracts without access to repairs, lessons, or admin functions.
# 6. Permission Checking
## 6.1 API Route Guards
```typescript
// Single permission
app.get('/accounts', {
preHandler: [app.authenticate, app.requirePermission('accounts.view')]
}, handler)
// Any of multiple permissions
app.post('/transactions', {
preHandler: [app.authenticate, app.requirePermission('pos.edit')]
}, handler)
// Admin-level action
app.post('/tax-exemptions/:id/approve', {
preHandler: [app.authenticate, app.requirePermission('accounts.admin')]
}, handler)
```
## 6.2 Frontend Guards
```typescript
// Route level
{
beforeLoad: () => requirePermission('accounts.view'),
component: AccountsList,
}
// Component level
{hasPermission('accounts.edit') && <Button>New Account</Button>}
```
## 6.3 Resolution
When checking a permission:
1. Get all roles assigned to the user
2. Get all permissions for those roles
3. Check if the required permission is in the set
4. Cache the permission set in memory for the request duration (loaded on auth)
# 7. Migration Plan
## 7.1 Current State
The `user` table has a `role` enum column with values: admin, manager, staff, technician, instructor. This is checked via `app.requireRole()` in a few places.
## 7.2 Migration Steps
1. Create permission, role, role_permission, user_role tables
2. Seed system permissions (all domain.action combinations)
3. Seed default roles with their permission mappings
4. Migrate existing users: map current `role` enum to the equivalent new role
5. Add `app.requirePermission()` decorator alongside existing `app.requireRole()`
6. Gradually replace `requireRole()` calls with `requirePermission()` in routes
7. Drop the `role` enum column from user table (after full migration)
8. Update frontend to check permissions instead of roles
## 7.3 Backward Compatibility
During migration, both `requireRole()` and `requirePermission()` work. The JWT payload includes both `role` (legacy) and `permissions` (new array of slugs). Once all routes are migrated, the legacy role field can be removed.
# 8. Admin UI
## 8.1 Roles Page
- List all roles (system + custom)
- Create custom role: name, description, select permissions via checkboxes grouped by domain
- Edit custom role permissions
- View which users are assigned to each role
- System roles show as locked (can't delete, can edit description)
## 8.2 User Management
- Assign/remove roles from users
- View effective permissions for a user (union of all role permissions)
- Quick-assign: dropdown of available roles when creating/editing a user
# 9. Business Rules
- Every user must have at least one role
- Admin role cannot be removed from the last admin user in a company
- System roles can be customized (add/remove permissions) but not deleted
- Custom roles are company-scoped
- Permission checks are additive — if any assigned role has the permission, the user has it
- New permissions added in updates are automatically added to the Admin role
- Role changes take effect on next login (JWT refresh)
- Audit log records all role assignments and permission changes