# ADR-006: Role-Based Access Control for Admin Principles **Status**: Accepted **Date**: 2025-12-08 **Deciders**: DSS Architecture Team (via Zen Swarm 3-Cycle Analysis) **Related**: ADMIN_PRINCIPLES.md v2.0.0, PRINCIPLES.md v2.2.0 ## Context The DSS Admin layer requires team-specific access control to enable role-based dashboards and one-click operations. Four distinct teams need different capabilities: - **Admin Team**: System configuration, project creation, user/role management - **UI Team**: Figma sync, QuickWins analysis, regression tools - **UX Team**: Component/token/icon management, Figma plugin customization - **QA Team**: Component testing, ESRE, issue creation The existing role system (`admin`, `designer`, `developer`, `viewer`) was generic and didn't align with team-specific workflows described in Admin Principle #6 (Role-Based Configuration & Visibility). ## Decision We implemented a comprehensive RBAC layer with: ### 1. Team-Aligned Role System - Replaced generic roles with team-specific roles: `admin`, `ui_team`, `ux_team`, `qa_team` - Added `team_id` field to User model for multi-team organization support - Default role: `ui_team` (safest non-admin default) ### 2. Granular Permission Model Created `TeamPermissions` table with: - **Triple**: (role, resource, action) with unique constraint - **Actions**: CRUD (create, read, update, delete) - **Resources**: figma, components, tokens, icons, analysis, metrics, issues, testing - **Admin Bypass**: Admins have implicit all-permissions without database checks ### 3. Middleware Architecture - `authenticateToken`: JWT validation (existing) - `authorizeRole(allowedRoles)`: Simple role checking (existing, updated) - `requirePermission(resource, action)`: Granular permission checking (new) ### 4. Admin API Endpoints Four new endpoints in `/api/admin/roles/`: - `POST /assign`: Assign role to user (admin-only) - `GET /permissions/:role`: View role permissions (self or admin) - `PUT /permissions/:role`: Enable/disable permissions (admin-only) - `GET /users/:user_id/permissions`: View effective user permissions (self or admin) ### 5. Database Migration Strategy Used temporary column pattern to safely migrate PostgreSQL ENUMs: ``` Users.role (old enum) → Users.role_new (new enum) → drop old → rename new → set constraints ``` This avoids casting errors and allows atomic rollback. ## Alternatives Considered ### Alternative 1: Attribute-Based Access Control (ABAC) **Rejected**: Too complex for current needs. RBAC with granular permissions provides sufficient flexibility. ### Alternative 2: Single Admin Role with Feature Flags **Rejected**: Doesn't support team-specific audit trails or permission boundaries required by Admin Principle #3 (Accountability). ### Alternative 3: External Authorization Service (e.g., Ory Keto, Casbin) **Rejected**: Adds operational complexity and external dependency. In-database RBAC is sufficient for DSS scale. ## Consequences ### Positive - **Team Alignment**: Roles now match organizational structure - **Granular Control**: Permissions can be toggled per team without code changes - **Audit Trail**: TeamPermissions changes logged in database - **Migration Safety**: Temporary column pattern allows zero-downtime deployment - **Admin Lockout Prevention**: Admin bypass ensures system recoverability ### Negative - **Performance**: N+1 query on every `requirePermission` check (see Mitigation) - **Complexity**: Additional table and migration logic - **Language Mismatch**: Implemented in JavaScript instead of TypeScript (technical debt) ### Neutral - **Breaking Change**: Old roles mapped to new roles during migration (graceful upgrade path) ## Mitigation Strategies ### Critical: Redis Caching (Next Step) To address the N+1 performance issue, Track 1.5 will implement: - Redis cache for permissions: `rbac:permissions:{role}` (TTL: 1 hour) - Cache invalidation on permission updates - Fallback to database on cache miss ### Security Hardening - Remove hardcoded JWT secret fallback (`process.env.JWT_SECRET || 'your-secret-key'`) - Throw error on app startup if JWT_SECRET undefined ### TypeScript Migration (Future) - Convert models and middleware to TypeScript for type safety - Add `.d.ts` definitions for Sequelize models ## Implementation Details **Files Modified:** - `server/src/models/User.js`: Added role enum and team_id field - `server/src/middleware/auth.js`: Added requirePermission middleware **Files Created:** - `server/src/models/TeamPermissions.js`: Permission model - `server/src/routes/admin/roles.js`: Role management API - `server/migrations/20251208000000-add-rbac.js`: Database migration **Default Permissions Seeded:** - **ui_team**: sync_figma, view_figma, quickwins, regression, view_metrics - **ux_team**: view/update components/tokens/icons, customize_figma_plugin, view_metrics - **qa_team**: test_components, create_issue, view_metrics, run_esre ## Validation **Cycle 1 (ThinkDeep)**: Architecture validated with "almost_certain" confidence **Cycle 2 (Gemini 3 Pro)**: Code generation completed **Cycle 3 (Gemini 3 Pro Review)**: Security, architecture, performance, code quality validated **Review Findings:** - ✅ Migration strategy: Excellent - ✅ Database design: Solid (unique constraints, transactions) - ✅ API design: RESTful, consistent response format - ⚠️ Performance: Redis caching required - ⚠️ Security: Remove hardcoded secret fallback - ⚠️ Architecture: TypeScript conversion recommended ## Next Steps 1. **Track 1.5**: Implement Redis caching for permissions (immediate) 2. **Track 2**: Configuration Schema (system/project/user tiers) 3. **Track 3**: Component Registry (automatic discovery) 4. **Track 4**: OAuth Integration (Figma/Atlassian) ## References - [ADMIN_PRINCIPLES.md](../../docs/01_core/ADMIN_PRINCIPLES.md) - Principle #6 - [API_CONTRACTS.md](../../docs/01_core/API_CONTRACTS.md) - Admin endpoints - [PRINCIPLES.md](../../docs/01_core/PRINCIPLES.md) - Core principle #5 (Operations as Tools) - Zen Swarm Analysis: ThinkDeep 3-step workflow (2025-12-08)