# 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)
