# Role-Based Access Control (RBAC) Implementation

## Overview

The system now supports **three user roles** for granular access control:

| Role | Access Level | Use Case |
|------|--------------|----------|
| **USER** | Standard | Default role for all users - create/view own requests |
| **MANAGEMENT** | Enhanced Read | View all requests across organization (read-only) |
| **ADMIN** | Full Access | System configuration, user management, all workflows |

---

## User Roles

### 1. USER (Default)
**Permissions:**
- ✅ Create new workflow requests
- ✅ View own requests
- ✅ Participate in assigned workflows (as approver/spectator)
- ✅ Add work notes to requests they're involved in
- ✅ Upload documents to own requests
- ❌ Cannot view other users' requests (unless added as participant)
- ❌ Cannot access system configuration
- ❌ Cannot manage users or roles

**Use Case:** Regular employees creating and managing their workflow requests

---

### 2. MANAGEMENT
**Permissions:**
- ✅ All USER permissions
- ✅ View ALL requests across organization (read-only)
- ✅ Access comprehensive dashboards with organization-wide analytics
- ✅ Export reports across all departments
- ✅ View TAT performance metrics for all approvers
- ❌ Cannot approve/reject requests (unless explicitly added as approver)
- ❌ Cannot modify system configuration
- ❌ Cannot manage user roles

**Use Case:** Department heads, managers, auditors needing visibility into all workflows

---

### 3. ADMIN
**Permissions:**
- ✅ All MANAGEMENT permissions
- ✅ All USER permissions
- ✅ Manage system configuration
- ✅ Assign user roles
- ✅ Manage holiday calendar
- ✅ Configure email/notification settings
- ✅ Access audit logs
- ✅ Manage AI provider settings

**Use Case:** System administrators, IT staff managing the workflow platform

---

## Database Schema

### Migration Applied

```sql
-- Create ENUM type for roles
CREATE TYPE user_role_enum AS ENUM ('USER', 'MANAGEMENT', 'ADMIN');

-- Add role column to users table
ALTER TABLE users 
ADD COLUMN role user_role_enum NOT NULL DEFAULT 'USER';

-- Migrate existing data
UPDATE users 
SET role = CASE 
  WHEN is_admin = true THEN 'ADMIN'
  ELSE 'USER'
END;

-- Create index for performance
CREATE INDEX idx_users_role ON users(role);
```

### Updated Users Table

```
users {
    uuid user_id PK
    varchar email UK
    varchar display_name
    varchar department
    varchar designation
    boolean is_active
    user_role_enum role ← NEW FIELD
    boolean is_admin ← DEPRECATED (kept for compatibility)
    timestamp created_at
    timestamp updated_at
}
```

---

## Backend Implementation

### Model (User.ts)

```typescript
export type UserRole = 'USER' | 'MANAGEMENT' | 'ADMIN';

interface UserAttributes {
  // ... other fields
  role: UserRole; // RBAC role
  isAdmin: boolean; // DEPRECATED
}

class User extends Model<UserAttributes> {
  public role!: UserRole;
  
  // Helper methods
  public isUserRole(): boolean {
    return this.role === 'USER';
  }
  
  public isManagementRole(): boolean {
    return this.role === 'MANAGEMENT';
  }
  
  public isAdminRole(): boolean {
    return this.role === 'ADMIN';
  }
  
  public hasManagementAccess(): boolean {
    return this.role === 'MANAGEMENT' || this.role === 'ADMIN';
  }
  
  public hasAdminAccess(): boolean {
    return this.role === 'ADMIN';
  }
}
```

---

## Middleware Usage

### 1. Require Admin Only

```typescript
import { requireAdmin } from '@middlewares/authorization.middleware';

// Only ADMIN can access
router.post('/admin/config', authenticate, requireAdmin, adminController.updateConfig);
router.post('/admin/users/:userId/role', authenticate, requireAdmin, adminController.updateUserRole);
router.post('/admin/holidays', authenticate, requireAdmin, adminController.addHoliday);
```

### 2. Require Management or Admin

```typescript
import { requireManagement } from '@middlewares/authorization.middleware';

// MANAGEMENT and ADMIN can access (read-only for management)
router.get('/reports/all-requests', authenticate, requireManagement, reportController.getAllRequests);
router.get('/analytics/department', authenticate, requireManagement, analyticsController.getDepartmentStats);
router.get('/dashboard/organization', authenticate, requireManagement, dashboardController.getOrgWideStats);
```

### 3. Flexible Role Checking

```typescript
import { requireRole } from '@middlewares/authorization.middleware';

// Multiple role options
router.get('/workflows/search', authenticate, requireRole(['MANAGEMENT', 'ADMIN']), workflowController.search);
router.post('/workflows/export', authenticate, requireRole(['MANAGEMENT', 'ADMIN']), workflowController.export);

// Any authenticated user
router.get('/profile', authenticate, requireRole(['USER', 'MANAGEMENT', 'ADMIN']), userController.getProfile);
```

### 4. Programmatic Role Checking in Controllers

```typescript
import { hasManagementAccess, hasAdminAccess } from '@middlewares/authorization.middleware';

export async function getWorkflows(req: Request, res: Response) {
  const user = req.user;
  
  // Management and Admin can see ALL workflows
  if (hasManagementAccess(user)) {
    const allWorkflows = await WorkflowRequest.findAll();
    return res.json({ success: true, data: allWorkflows });
  }
  
  // Regular users only see their own workflows
  const userWorkflows = await WorkflowRequest.findAll({
    where: { initiatorId: user.userId }
  });
  
  return res.json({ success: true, data: userWorkflows });
}
```

---

## Example Route Implementations

### Admin Routes (ADMIN only)

```typescript
// src/routes/admin.routes.ts
import { Router } from 'express';
import { authenticate } from '@middlewares/auth.middleware';
import { requireAdmin } from '@middlewares/authorization.middleware';
import * as adminController from '@controllers/admin.controller';

const router = Router();

// All admin routes require ADMIN role
router.use(authenticate, requireAdmin);

// System configuration
router.get('/config', adminController.getConfig);
router.put('/config', adminController.updateConfig);

// User role management
router.put('/users/:userId/role', adminController.updateUserRole);
router.get('/users/admins', adminController.getAllAdmins);
router.get('/users/management', adminController.getAllManagement);

// Holiday management
router.post('/holidays', adminController.createHoliday);
router.delete('/holidays/:holidayId', adminController.deleteHoliday);

export default router;
```

### Management Routes (MANAGEMENT + ADMIN)

```typescript
// src/routes/management.routes.ts
import { Router } from 'express';
import { authenticate } from '@middlewares/auth.middleware';
import { requireManagement } from '@middlewares/authorization.middleware';
import * as managementController from '@controllers/management.controller';

const router = Router();

// All management routes require MANAGEMENT or ADMIN role
router.use(authenticate, requireManagement);

// Organization-wide dashboards (read-only)
router.get('/dashboard/organization', managementController.getOrgDashboard);
router.get('/requests/all', managementController.getAllRequests);
router.get('/analytics/tat-performance', managementController.getTATPerformance);
router.get('/analytics/approver-stats', managementController.getApproverStats);
router.get('/reports/export', managementController.exportReports);

// Department-wise analytics
router.get('/analytics/department/:deptName', managementController.getDepartmentAnalytics);

export default router;
```

### Workflow Routes (Mixed Permissions)

```typescript
// src/routes/workflow.routes.ts
import { Router } from 'express';
import { authenticate } from '@middlewares/auth.middleware';
import { requireManagement, requireRole } from '@middlewares/authorization.middleware';
import * as workflowController from '@controllers/workflow.controller';

const router = Router();

// USER: Create own request (all roles can do this)
router.post('/workflows', authenticate, workflowController.create);

// USER: View own requests (filtered by role in controller)
router.get('/workflows/my-requests', authenticate, workflowController.getMyRequests);

// MANAGEMENT + ADMIN: Search all requests
router.get('/workflows/search', authenticate, requireManagement, workflowController.searchAll);

// ADMIN: Delete workflow
router.delete('/workflows/:id', authenticate, requireRole(['ADMIN']), workflowController.delete);

export default router;
```

---

## Controller Implementation Examples

### Example 1: Dashboard with Role-Based Data

```typescript
// src/controllers/dashboard.controller.ts
import { hasManagementAccess } from '@middlewares/authorization.middleware';

export async function getDashboard(req: Request, res: Response) {
  const user = req.user;
  
  // MANAGEMENT and ADMIN: See organization-wide stats
  if (hasManagementAccess(user)) {
    const stats = await dashboardService.getOrganizationStats();
    return res.json({
      success: true,
      data: {
        ...stats,
        scope: 'organization', // Indicates full visibility
        userRole: user.role
      }
    });
  }
  
  // USER: See only personal stats
  const stats = await dashboardService.getUserStats(user.userId);
  return res.json({
    success: true,
    data: {
      ...stats,
      scope: 'personal', // Indicates limited visibility
      userRole: user.role
    }
  });
}
```

### Example 2: User Role Update (ADMIN only)

```typescript
// src/controllers/admin.controller.ts
export async function updateUserRole(req: Request, res: Response) {
  const { userId } = req.params;
  const { role } = req.body;
  
  // Validate role
  if (!['USER', 'MANAGEMENT', 'ADMIN'].includes(role)) {
    return res.status(400).json({
      success: false,
      error: 'Invalid role. Must be USER, MANAGEMENT, or ADMIN'
    });
  }
  
  // Update user role
  const user = await User.findByPk(userId);
  if (!user) {
    return res.status(404).json({
      success: false,
      error: 'User not found'
    });
  }
  
  const oldRole = user.role;
  user.role = role;
  
  // Sync is_admin for backward compatibility
  user.isAdmin = (role === 'ADMIN');
  await user.save();
  
  // Log role change
  console.log(`✅ User role updated: ${user.email} - ${oldRole} → ${role}`);
  
  return res.json({
    success: true,
    message: `User role updated from ${oldRole} to ${role}`,
    data: {
      userId: user.userId,
      email: user.email,
      role: user.role
    }
  });
}
```

---

## Frontend Integration

### Update Auth Context

```typescript
// Frontend: src/contexts/AuthContext.tsx
interface User {
  userId: string;
  email: string;
  displayName: string;
  role: 'USER' | 'MANAGEMENT' | 'ADMIN'; // ← Add role
}

// Helper functions
export function isAdmin(user: User | null): boolean {
  return user?.role === 'ADMIN';
}

export function isManagement(user: User | null): boolean {
  return user?.role === 'MANAGEMENT' || user?.role === 'ADMIN';
}

export function hasManagementAccess(user: User | null): boolean {
  return user?.role === 'MANAGEMENT' || user?.role === 'ADMIN';
}
```

### Role-Based UI Rendering

```typescript
// Show admin menu only for ADMIN
{user?.role === 'ADMIN' && (
  <NavItem to="/admin/config">
    <Settings /> System Configuration
  </NavItem>
)}

// Show management dashboard for MANAGEMENT and ADMIN
{(user?.role === 'MANAGEMENT' || user?.role === 'ADMIN') && (
  <NavItem to="/dashboard/organization">
    <TrendingUp /> Organization Dashboard
  </NavItem>
)}

// Show all requests for MANAGEMENT and ADMIN
{hasManagementAccess(user) && (
  <NavItem to="/requests/all">
    <FileText /> All Requests
  </NavItem>
)}
```

---

## Migration Guide

### Running the Migration

```bash
# Run migration to add role column
npm run migrate

# Verify migration
psql -d royal_enfield_db -c "SELECT email, role, is_admin FROM users LIMIT 10;"
```

### Expected Results

```
Before Migration:
+-------------------------+-----------+
| email                   | is_admin  |
+-------------------------+-----------+
| admin@{{APP_DOMAIN}}  | true      |
| user1@{{APP_DOMAIN}}  | false     |
+-------------------------+-----------+

After Migration:
+-------------------------+-----------+-----------+
| email                   | role      | is_admin  |
+-------------------------+-----------+-----------+
| admin@{{APP_DOMAIN}}  | ADMIN     | true      |
| user1@{{APP_DOMAIN}}  | USER      | false     |
+-------------------------+-----------+-----------+
```

---

## Assigning Roles

### Via SQL (Direct Database)

```sql
-- Make user a MANAGEMENT role
UPDATE users 
SET role = 'MANAGEMENT', is_admin = false 
WHERE email = 'manager@{{APP_DOMAIN}}';

-- Make user an ADMIN role
UPDATE users 
SET role = 'ADMIN', is_admin = true 
WHERE email = 'admin@{{APP_DOMAIN}}';

-- Revert to USER role
UPDATE users 
SET role = 'USER', is_admin = false 
WHERE email = 'user@{{APP_DOMAIN}}';
```

### Via API (Admin Endpoint)

```bash
# Update user role (ADMIN only)
POST /api/v1/admin/users/:userId/role
Authorization: Bearer <admin-token>
Content-Type: application/json

{
  "role": "MANAGEMENT"
}
```

---

## Testing

### Test Scenarios

```typescript
describe('RBAC Tests', () => {
  test('USER cannot access admin config', async () => {
    const response = await request(app)
      .get('/api/v1/admin/config')
      .set('Authorization', `Bearer ${userToken}`);
    
    expect(response.status).toBe(403);
    expect(response.body.error).toContain('Admin access required');
  });
  
  test('MANAGEMENT can view all requests', async () => {
    const response = await request(app)
      .get('/api/v1/management/requests/all')
      .set('Authorization', `Bearer ${managementToken}`);
    
    expect(response.status).toBe(200);
    expect(response.body.data).toBeInstanceOf(Array);
  });
  
  test('ADMIN can update user roles', async () => {
    const response = await request(app)
      .put(`/api/v1/admin/users/${userId}/role`)
      .set('Authorization', `Bearer ${adminToken}`)
      .send({ role: 'MANAGEMENT' });
    
    expect(response.status).toBe(200);
    expect(response.body.data.role).toBe('MANAGEMENT');
  });
});
```

---

## Best Practices

### 1. Always Use Role Column

```typescript
// ✅ GOOD: Use new role system
if (user.role === 'ADMIN') {
  // Admin logic
}

// ❌ BAD: Don't use deprecated is_admin
if (user.isAdmin) {
  // Deprecated approach
}
```

### 2. Use Helper Functions

```typescript
// ✅ GOOD: Use provided helpers
if (user.hasManagementAccess()) {
  // Management or Admin logic
}

// ❌ BAD: Manual checking
if (user.role === 'MANAGEMENT' || user.role === 'ADMIN') {
  // Verbose
}
```

### 3. Route Protection

```typescript
// ✅ GOOD: Clear role requirements
router.get('/sensitive-data', authenticate, requireManagement, controller.getData);

// ❌ BAD: Role checking in controller only
router.get('/sensitive-data', authenticate, controller.getData); // No middleware check
```

---

## Backward Compatibility

The `is_admin` field is **DEPRECATED** but kept for backward compatibility:

- ✅ Existing code using `is_admin` will continue to work
- ✅ Migration automatically syncs `role` and `is_admin`
- ⚠️ New code should use `role` instead of `is_admin`
- 📅 `is_admin` will be removed in future version

### Sync Logic

```typescript
// When updating role, sync is_admin
user.role = 'ADMIN';
user.isAdmin = true; // Auto-sync

user.role = 'USER';
user.isAdmin = false; // Auto-sync
```

---

## Quick Reference

| Task | Code Example |
|------|--------------|
| Check if ADMIN | `user.role === 'ADMIN'` or `user.isAdminRole()` |
| Check if MANAGEMENT | `user.role === 'MANAGEMENT'` or `user.isManagementRole()` |
| Check if USER | `user.role === 'USER'` or `user.isUserRole()` |
| Check Management+ | `user.hasManagementAccess()` |
| Middleware: Admin only | `requireAdmin` |
| Middleware: Management+ | `requireManagement` |
| Middleware: Custom roles | `requireRole(['ADMIN', 'MANAGEMENT'])` |
| Update role (SQL) | `UPDATE users SET role = 'MANAGEMENT' WHERE email = '...'` |
| Update role (API) | `PUT /admin/users/:userId/role { role: 'MANAGEMENT' }` |

---

## Support

For questions or issues:
- Check migration logs: `logs/migration.log`
- Review user roles: `SELECT email, role FROM users;`
- Test role access: Use provided test scenarios

**Migration File:** `src/migrations/20251112-add-user-roles.ts`  
**Model File:** `src/models/User.ts`  
**Middleware File:** `src/middlewares/authorization.middleware.ts`

