feat: implement RBAC permissions system

Add role-based access control with hierarchy: super_admin > team_admin >
engineer > viewer. Adds is_super_admin boolean to User model (migration 010),
centralized backend permissions module, frontend usePermissions hook, and
UI enforcement (conditional Create/Edit buttons, editor redirect for viewers,
role badge in header). All endpoint admin checks updated from role=="admin"
to is_super_admin.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
chihlasm
2026-02-05 02:42:44 -05:00
parent d7c5c8c9ce
commit 34daa26a67
20 changed files with 428 additions and 65 deletions

View File

@@ -96,6 +96,15 @@ When adding new frontend pages or components, use "ResolutionFlow" for any user-
- Backend `get_refresh_token_payload` dependency extracts refresh token from Authorization header
- Frontend Axios interceptor queues failed requests during refresh, retries after success
- Auth store synced after silent refresh via `setTokens` action
- **RBAC & Permissions:**
- `is_super_admin` boolean on User model (migration 010)
- Role hierarchy: super_admin > team_admin > engineer > viewer
- `role` field values: 'engineer' | 'viewer' (no more 'admin')
- Team Admin = `role='engineer'` + `is_team_admin=True` + valid `team_id`
- Backend centralized: `backend/app/core/permissions.py`
- Frontend hook: `frontend/src/hooks/usePermissions.ts`
- Viewers CAN: browse trees, start sessions, rate steps
- Viewers CANNOT: create/edit trees, steps, tags, categories
- **Session Scratchpad (Floating Overlay):**
- Fixed-position overlay panel (420px wide, 55vh tall) on right edge
- Floating button when collapsed, slide-in panel when expanded
@@ -158,6 +167,7 @@ patherly/
│ │ ├── core/
│ │ │ ├── config.py # Settings (pydantic-settings)
│ │ │ ├── database.py # Async SQLAlchemy
│ │ │ ├── permissions.py # Centralized RBAC (role checks, content guards)
│ │ │ ├── security.py # JWT + password hashing
│ │ │ ├── logging_config.py # Structured logging
│ │ │ └── middleware.py # Request logging
@@ -198,7 +208,7 @@ patherly/
│ │ │ ├── auth.ts
│ │ │ ├── trees.ts
│ │ │ └── sessions.ts
│ │ ├── hooks/ # Custom React hooks (useKeyboardShortcuts)
│ │ ├── hooks/ # Custom React hooks (useKeyboardShortcuts, usePermissions)
│ │ ├── store/
│ │ │ ├── authStore.ts # Zustand auth state
│ │ │ ├── themeStore.ts # Dark/light theme
@@ -496,6 +506,17 @@ if settings.ALLOW_RAILWAY_ORIGINS:
```
When using `allow_origin_regex` for wildcard patterns, also include `allow_origins` for explicit custom domains. The regex alone won't match custom domains like `resolutionflow.com`.
### RBAC Permission Checks
- Backend auth deps: `get_current_user` (any logged-in), `require_engineer_or_admin` (blocks viewers), `require_admin` (super admin only)
- Backend: `is_super_admin` replaces all `role == "admin"` checks. Never use `role == "admin"`.
- Frontend: use `usePermissions()` hook for all role/permission checks
- `TreeListItem` includes `team_id` for frontend permission checks (`author_id` and `team_id` are nullable)
### Alembic Migrations: Test Data State Before Writing WHERE Clauses
Migration 010 had `WHERE role = 'admin'` but the only user already had `role = 'engineer'` (changed by earlier work), so the UPDATE matched zero rows. Always verify actual data values before writing conditional migrations, or use broader conditions.
### findNode Requires Tree Structure Parameter
```tsx
@@ -792,6 +813,7 @@ Position overlay at `right-2` (not `right-0`) so it sits inside the page scrollb
1. Create test database: `docker exec -it patherly_postgres psql -U postgres -c "CREATE DATABASE patherly_test;"`
2. Install dev deps: `pip install -r requirements-dev.txt`
3. Ensure pytest-asyncio version: `pip install pytest-asyncio==0.24.0`
4. If `unrecognized arguments: --cov` error: run with `--override-ini="addopts="` or install `pytest-cov`
### API 500 errors