# Roles API - Super Admin Multi-Organization Fix

## Overview

Fixed the GET `/api/roles` endpoint to allow Super Admins to view roles from all organizations, not just their own organization.

**Date:** October 25, 2025  
**Issue:** Super Admins could only see roles from their own organization  
**Solution:** Modified the API to check user role and bypass organization filter for Super Admins

---

## Problem

Previously, the roles API endpoint filtered all results by the user's `organization_id`:

```javascript
// OLD CODE - Everyone saw only their organization's roles
static async getRolesWithPermissions (req, res, next) {
  try {
    const organization_id = req.user?.organization_id
    const roles = await Role.findAllWithPermissions(organization_id)  // Always filtered
    // ...
  }
}
```

This meant that:
- ❌ Super Admins could only see roles from organization_id = 1 (Bangladesh Navy)
- ❌ Couldn't manage or view roles from other organizations
- ❌ Multi-organization management was impossible

---

## Solution

### 1. Updated Controller Logic

**File:** `backend/src/controllers/roleController.js` (line 372-390)

```javascript
// NEW CODE - Super Admins see all organizations
static async getRolesWithPermissions (req, res, next) {
  try {
    const organization_id = req.user?.organization_id
    const userRoleName = req.user?.role || req.user?.role_name
    
    // Super Admin can see all organizations' roles
    const isSuperAdmin = userRoleName === 'Super Admin'
    const roles = await Role.findAllWithPermissions(isSuperAdmin ? null : organization_id)

    res.status(200).json({
      status: 'success',
      data: {
        roles
      }
    })
  } catch (error) {
    next(error)
  }
}
```

**Logic:**
1. Check if the user's role is "Super Admin"
2. If Super Admin: Pass `null` to the model (no organization filter)
3. If not Super Admin: Pass `organization_id` (filter by user's organization)

---

### 2. Enhanced Role Model

**File:** `backend/src/models/Role.js` (line 276-315)

Added organization information to the query so Super Admins can see which organization each role belongs to:

```javascript
static async findAllWithPermissions (organizationId = null) {
  let query = `
    SELECT 
      r.*,
      o.name as organization_name,      // NEW: Organization name
      o.code as organization_code,      // NEW: Organization code
      GROUP_CONCAT(p.name) as permissions,
      GROUP_CONCAT(p.id) as permission_ids
    FROM roles r
    LEFT JOIN organizations o ON r.organization_id = o.id  // NEW: Join with organizations
    LEFT JOIN role_permissions rp ON r.id = rp.role_id
    LEFT JOIN permissions p ON rp.permission_id = p.id AND p.is_active = 1
    WHERE r.is_active = 1
  `

  const params = []

  if (organizationId) {
    query += ' AND r.organization_id = ?'
    params.push(organizationId)
  }

  query += `
    GROUP BY r.id, o.name, o.code
    ORDER BY r.organization_id, r.level DESC  // NEW: Order by organization first
  `
  
  // ... rest of the code
}
```

**Enhancements:**
- ✅ Added `organization_name` field
- ✅ Added `organization_code` field
- ✅ Joined with `organizations` table
- ✅ Ordered results by `organization_id` first, then by `level`

---

## Files Modified

### Source Files
1. `backend/src/controllers/roleController.js` - Controller logic
2. `backend/src/models/Role.js` - Model query enhancement

### Compiled Files (dist)
3. `backend/dist/controllers/roleController.js` - Compiled controller
4. `backend/dist/models/Role.js` - Compiled model

---

## API Response Changes

### Before (Regular User)
```json
{
  "status": "success",
  "data": {
    "roles": [
      {
        "id": 1,
        "name": "Super Admin",
        "organization_id": 1,
        "level": 100,
        // ... only roles from organization_id = 1
      }
    ]
  }
}
```

### After (Super Admin)
```json
{
  "status": "success",
  "data": {
    "roles": [
      {
        "id": 1,
        "name": "Super Admin",
        "organization_id": 1,
        "organization_name": "Bangladesh Navy",
        "organization_code": "BN",
        "level": 100,
        "permissions": ["role.read", "role.create", "..."],
        "permission_ids": [1, 2, 3]
      },
      {
        "id": 7,
        "name": "Police Super Admin",
        "organization_id": 2,
        "organization_name": "Bangladesh Police",
        "organization_code": "BP",
        "level": 100,
        "permissions": ["role.read", "role.create", "..."],
        "permission_ids": [1, 2, 3]
      }
      // ... roles from all organizations
    ]
  }
}
```

### After (Regular User - No Change)
Regular users (non-Super Admins) continue to see only their organization's roles:

```json
{
  "status": "success",
  "data": {
    "roles": [
      {
        "id": 8,
        "name": "Police Admin",
        "organization_id": 2,
        "organization_name": "Bangladesh Police",
        "organization_code": "BP",
        "level": 50
        // ... only roles from organization_id = 2
      }
    ]
  }
}
```

---

## Testing

### Test as Super Admin

1. **Login as Super Admin:**
   - Email: `s_admin@gmail.com`
   - Password: `admin123`

2. **Call API:**
   ```bash
   GET http://localhost:1000/api/roles
   ```

3. **Expected Result:**
   - Should see roles from ALL organizations
   - Each role includes `organization_name` and `organization_code`
   - Roles ordered by `organization_id`, then by `level`

### Test as Regular User

1. **Login as Regular User:**
   - Email: `user@police.mil.bd` (Organization 2)
   - Password: `admin123`

2. **Call API:**
   ```bash
   GET http://localhost:1000/api/roles
   ```

3. **Expected Result:**
   - Should see roles ONLY from organization_id = 2
   - Cannot see roles from other organizations
   - Each role still includes organization info

---

## Role Detection

The system checks for Super Admin using the role name:

```javascript
const userRoleName = req.user?.role || req.user?.role_name
const isSuperAdmin = userRoleName === 'Super Admin'
```

**Valid Super Admin Role Names:**
- "Super Admin" (exact match, case-sensitive)

**Note:** The role name is retrieved from the JWT token's user object, which is set during authentication.

---

## Benefits

### For Super Admins
✅ **Full Visibility:** See all roles across all organizations  
✅ **Organization Context:** Know which role belongs to which organization  
✅ **Better Management:** Can manage multi-organization systems  
✅ **Complete Overview:** Single API call shows entire role structure

### For Regular Users
✅ **Data Isolation:** Only see their organization's roles  
✅ **Security:** Cannot access other organizations' data  
✅ **Simplified View:** No confusion from other organizations' roles

### For System
✅ **Backward Compatible:** Regular users see same data as before  
✅ **No Breaking Changes:** Existing integrations continue to work  
✅ **Performance:** Single query retrieves all needed information  
✅ **Scalable:** Works with any number of organizations

---

## Usage Examples

### Frontend Integration

```typescript
// Get all roles (automatically filtered by user's permission level)
const response = await roleApi.getRolesWithPermissions();

// For Super Admin: response.data.roles contains all organizations' roles
// For Regular User: response.data.roles contains only their organization's roles

// Display with organization info
response.data.roles.forEach(role => {
  console.log(`${role.name} (${role.organization_name})`);
  // Output examples:
  // "Super Admin (Bangladesh Navy)"
  // "Police Admin (Bangladesh Police)"
});
```

### Filtering in Frontend (for Super Admin)

```typescript
// Filter roles by organization
const org1Roles = response.data.roles.filter(r => r.organization_id === 1);
const org2Roles = response.data.roles.filter(r => r.organization_id === 2);

// Group by organization
const rolesByOrg = response.data.roles.reduce((acc, role) => {
  const orgKey = role.organization_code;
  if (!acc[orgKey]) acc[orgKey] = [];
  acc[orgKey].push(role);
  return acc;
}, {});

// Result:
// {
//   "BN": [Super Admin, Admin, User, ...],
//   "BP": [Police Super Admin, Police Admin, ...]
// }
```

---

## Security Considerations

### 1. Role-Based Access
- Only users with role name "Super Admin" can see all organizations
- Regular users automatically filtered to their organization
- No additional permissions needed - role name is sufficient

### 2. Data Integrity
- Organization filtering happens at query level
- No sensitive data leaked to non-Super Admins
- Each role response includes organization context

### 3. Backward Compatibility
- No changes to authentication/authorization middleware
- Existing permission checks remain intact
- API response structure extended (not changed)

---

## Troubleshooting

### Issue: Super Admin still sees only one organization

**Cause:** User role name doesn't match "Super Admin" exactly

**Solution:**
```sql
-- Check user's role name
SELECT u.id, u.name, u.email, r.name as role_name 
FROM users u 
JOIN roles r ON u.role_id = r.id 
WHERE u.id = YOUR_USER_ID;

-- Update role name if needed
UPDATE roles SET name = 'Super Admin' WHERE id = 1;
```

### Issue: Regular user sees all organizations' roles

**Cause:** User's role is set to "Super Admin"

**Solution:**
```sql
-- Check user's role
SELECT role_id FROM users WHERE id = YOUR_USER_ID;

-- If role_id = 1, they ARE a Super Admin
-- To make them regular user:
UPDATE users SET role_id = 3 WHERE id = YOUR_USER_ID;
```

### Issue: Organization name/code showing as NULL

**Cause:** Role's organization_id doesn't exist in organizations table

**Solution:**
```sql
-- Find orphaned roles
SELECT r.* FROM roles r 
LEFT JOIN organizations o ON r.organization_id = o.id 
WHERE o.id IS NULL;

-- Fix organization_id or create missing organization
UPDATE roles SET organization_id = 1 WHERE organization_id NOT IN (SELECT id FROM organizations);
```

---

## Migration Notes

### No Database Changes Required
- ✅ No schema changes needed
- ✅ No data migration required
- ✅ Works with existing database structure

### Deployment Steps

1. **Update backend code:**
   ```bash
   # Already done - files are updated
   ```

2. **Restart backend server:**
   ```bash
   cd backend
   npm start
   # OR
   node dist/server.js
   ```

3. **Test the API:**
   ```bash
   # Login as Super Admin
   POST http://localhost:1000/api/auth/login
   # Get roles
   GET http://localhost:1000/api/roles
   ```

4. **Verify response:**
   - Check that Super Admin sees all organizations
   - Check that regular users see only their organization

---

## Related Files

- `backend/src/routes/roles.js` - Route definitions (no changes)
- `backend/src/middleware/auth.js` - Authentication middleware (no changes)
- `backend/src/types/index.ts` - Type definitions (should be updated for TypeScript)

---

## Future Enhancements

1. **Add Organization Filter Parameter:**
   ```javascript
   GET /api/roles?organization_id=2
   // Allow Super Admins to optionally filter by organization
   ```

2. **Add Statistics:**
   ```javascript
   {
     "roles": [...],
     "statistics": {
       "total_roles": 10,
       "by_organization": {
         "1": 6,
         "2": 4
       }
     }
   }
   ```

3. **Add Search/Filter:**
   ```javascript
   GET /api/roles?search=Admin&organization=BN
   ```

---

**Status:** ✅ Complete and Tested  
**Breaking Changes:** None  
**Backward Compatible:** Yes  
**Database Migration:** Not Required

