# Roles & Permissions System Documentation

## Overview

This document describes the enhanced Role-Based Access Control (RBAC) system implemented in the application. The system provides comprehensive permission management with caching, hierarchical roles, direct user permissions, and audit logging.

---

## Table of Contents

1. [Architecture](#architecture)
2. [Database Structure](#database-structure)
3. [Core Components](#core-components)
4. [Features](#features)
5. [Usage Examples](#usage-examples)
6. [API Endpoints](#api-endpoints)
7. [Artisan Commands](#artisan-commands)
8. [Best Practices](#best-practices)

---

## Architecture

```
┌─────────────────────────────────────────────────────────────────┐
│                         Request Flow                             │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  Request → JWT Auth → AdminMiddleware → PermissionMiddleware    │
│                                              │                   │
│                                              ▼                   │
│                                   ┌─────────────────────┐       │
│                                   │ PermissionCacheService│      │
│                                   └──────────┬──────────┘       │
│                                              │                   │
│                              ┌───────────────┼───────────────┐  │
│                              ▼               ▼               ▼  │
│                         ┌────────┐    ┌───────────┐   ┌──────┐ │
│                         │  Role  │    │  Direct   │   │Cache │ │
│                         │Permissions│  │Permissions│   │      │ │
│                         └────────┘    └───────────┘   └──────┘ │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘
```

---

## Database Structure

### Tables

#### `roles`
| Column | Type | Description |
|--------|------|-------------|
| id | bigint | Primary key |
| parent_id | bigint | Parent role for hierarchy (nullable) |
| level | int | Hierarchy level (0 = root) |
| deleted_at | timestamp | Soft delete |
| created_at | timestamp | Creation time |
| updated_at | timestamp | Last update |

#### `role_translations`
| Column | Type | Description |
|--------|------|-------------|
| id | bigint | Primary key |
| role_id | bigint | Foreign key to roles |
| name | string | Role name |
| slug | string | URL-friendly name |
| desc | text | Description |
| locale | string | Language code (ar, en) |

#### `permissions`
| Column | Type | Description |
|--------|------|-------------|
| id | bigint | Primary key |
| front_route_name | string | Frontend route identifier |
| back_route_name | string | Backend route name |
| icon | string | Icon class |
| is_control_permission | boolean | Control permission flag |
| prefix | string | Namespace prefix (Admin, Organizer) |
| module | string | Module grouping |
| sort_order | int | Display order |

#### `permission_user` (Direct Permissions)
| Column | Type | Description |
|--------|------|-------------|
| id | bigint | Primary key |
| user_id | bigint | Foreign key to users |
| permission_id | bigint | Foreign key to permissions |
| type | enum | 'grant' or 'deny' |
| expires_at | timestamp | Expiration time (nullable) |

#### `permission_audit_logs`
| Column | Type | Description |
|--------|------|-------------|
| id | bigint | Primary key |
| auditable_type | string | Model class (User, Role) |
| auditable_id | bigint | Model ID |
| user_id | bigint | Who made the change |
| action | string | Action type |
| old_values | json | Previous state |
| new_values | json | New state |
| ip_address | string | Client IP |
| user_agent | string | Browser info |

---

## Core Components

### 1. HasPermission Trait

Location: `app/Traits/HasPermission.php`

This trait is used by the User model to provide permission checking capabilities.

```php
use App\Traits\HasPermission;

class User extends Model
{
    use HasPermission;
}
```

#### Available Methods

| Method | Description |
|--------|-------------|
| `permissions()` | Get all user permissions (cached) |
| `hasPermission($permission)` | Check single permission |
| `hasAnyPermission($permissions)` | Check if has any of given permissions |
| `hasAllPermissions($permissions)` | Check if has all given permissions |
| `hasWildcardPermission($route)` | Check with wildcard support |
| `hasRole($role)` | Check if user has specific role |
| `hasAnyRole($roles)` | Check if user has any of given roles |
| `isSuperAdmin()` | Check if user is super admin |
| `grantPermission($ids)` | Grant direct permissions |
| `denyPermission($ids)` | Deny permissions (override role) |
| `revokePermission($ids)` | Remove direct permissions |
| `clearPermissionCache()` | Clear user's permission cache |

### 2. PermissionCacheService

Location: `app/Services/Permission/PermissionCacheService.php`

Handles all permission caching operations.

```php
use App\Services\Permission\PermissionCacheService;

$cacheService = app(PermissionCacheService::class);

// Get cached permissions
$permissions = $cacheService->getUserPermissions($user);

// Clear caches
$cacheService->clearUserCache($user);
$cacheService->clearRoleCache($role);
$cacheService->clearAllCaches();
```

### 3. PermissionAuditService

Location: `app/Services/Permission/PermissionAuditService.php`

Logs all permission changes for auditing.

```php
use App\Services\Permission\PermissionAuditService;

$auditService = app(PermissionAuditService::class);

// Log role permission changes
$auditService->logRolePermissionsUpdated($role, $oldPermissions, $newPermissions);

// Log role assignment
$auditService->logRoleAssigned($user, $oldRole, $newRole);

// Log direct permission grant
$auditService->logDirectPermissionGranted($user, $permissionIds);
```

### 4. Permission Middleware

Location: `app/Http/Middleware/Permission.php`

Protects routes based on user permissions.

```php
// In routes
Route::middleware(['auth:api', 'admin', 'permission'])->group(function () {
    // Protected routes
});

// Or with specific permission
Route::get('/users', 'UserController@index')
    ->middleware('permission:admin.users.index');
```

### 5. Policy Classes

Location: `app/Policies/`

- `RolePolicy.php` - Authorization for Role operations
- `PermissionPolicy.php` - Authorization for Permission operations
- `UserPolicy.php` - Authorization for User operations

```php
// In Controller
public function update(Request $request, Role $role)
{
    $this->authorize('update', $role);
    // ...
}

// Or in Blade
@can('update', $role)
    <button>Edit</button>
@endcan
```

---

## Features

### 1. Permission Caching

All permissions are cached for 1 hour to improve performance.

```php
// Permissions are automatically cached
$user->permissions(); // First call: database query + cache
$user->permissions(); // Subsequent calls: from cache

// Cache is automatically cleared when:
// - Role permissions are updated
// - User's role is changed
// - Direct permissions are modified

// Manual cache clearing
$user->clearPermissionCache();
```

### 2. Hierarchical Roles

Roles can inherit permissions from parent roles.

```php
// Create hierarchy
$adminRole = Role::find(1);
$managerRole = Role::find(2);

$managerRole->setParent($adminRole);

// Manager now inherits all Admin permissions
$managerRole->getAllPermissions(); // Includes parent permissions

// Get hierarchy info
$managerRole->getAncestors();   // All parent roles
$managerRole->getDescendants(); // All child roles
$managerRole->level;            // Hierarchy level (0 = root)
```

### 3. Direct User Permissions

Grant or deny permissions directly to users, bypassing roles.

```php
// Grant permission directly
$user->grantPermission(1);
$user->grantPermission([1, 2, 3]);

// Grant with expiration
$user->grantPermission(1, '2025-12-31 23:59:59');

// Deny permission (overrides role permissions)
$user->denyPermission(5);

// Revoke direct permission
$user->revokePermission(1);

// Sync all direct permissions
$user->syncDirectPermissions([1, 2, 3]);
```

### 4. Wildcard Permissions

Support for wildcard permission matching.

```php
// If user has 'admin.users.*' permission
$user->hasWildcardPermission('admin.users.index');  // true
$user->hasWildcardPermission('admin.users.store');  // true
$user->hasWildcardPermission('admin.users.delete'); // true

// If user has 'admin.*' permission
$user->hasWildcardPermission('admin.users.index');   // true
$user->hasWildcardPermission('admin.roles.update');  // true
```

### 5. Audit Logging

All permission changes are automatically logged.

```php
// Query audit logs
use App\Models\PermissionAuditLog;

// Get logs for specific user
$logs = PermissionAuditLog::forUser($userId)->get();

// Get logs for specific role
$logs = PermissionAuditLog::forRole($roleId)->get();

// Get recent logs (last 30 days)
$logs = PermissionAuditLog::recent(30)->get();

// Log entry structure
[
    'auditable_type' => 'App\Models\Role',
    'auditable_id' => 1,
    'user_id' => 5,  // Who made the change
    'action' => 'role_permissions_updated',
    'old_values' => ['permissions' => [1, 2, 3]],
    'new_values' => ['permissions' => [1, 2, 3, 4, 5]],
    'ip_address' => '192.168.1.1',
    'user_agent' => 'Mozilla/5.0...',
    'created_at' => '2025-01-15 10:30:00'
]
```

### 6. Permission Modules

Permissions can be grouped by module for better organization.

```php
// Get permissions grouped by module
$grouped = Permission::getGroupedByModule('Admin');
// Returns: ['users' => [...], 'roles' => [...], 'settings' => [...]]

// Get all available modules
$modules = Permission::getModules('Admin');
// Returns: ['users', 'roles', 'settings', 'reports']

// Filter by module
$permissions = Permission::byModule('users')->get();
```

---

## Usage Examples

### Check User Permission

```php
// Simple check
if ($user->hasPermission('admin.users.index')) {
    // User can view users
}

// Check multiple (any)
if ($user->hasAnyPermission(['admin.users.index', 'admin.users.show'])) {
    // User can view users
}

// Check multiple (all)
if ($user->hasAllPermissions(['admin.users.index', 'admin.users.store'])) {
    // User can view AND create users
}

// With wildcard
if ($user->hasWildcardPermission('admin.users.delete')) {
    // Checks: admin.users.delete, admin.users.*, admin.*
}
```

### Create Role with Hierarchy

```php
// Create parent role
$adminRole = Role::create([
    'ar' => ['name' => 'مدير النظام', 'desc' => 'صلاحيات كاملة'],
    'en' => ['name' => 'System Admin', 'desc' => 'Full access'],
]);

// Assign all permissions
$adminRole->syncPermissions(Permission::pluck('id')->toArray());

// Create child role
$managerRole = Role::create([
    'ar' => ['name' => 'مدير', 'desc' => 'صلاحيات محدودة'],
    'en' => ['name' => 'Manager', 'desc' => 'Limited access'],
]);

// Set parent (inherits admin permissions)
$managerRole->setParent($adminRole);

// Add specific permissions (in addition to inherited)
$managerRole->assignPermission([10, 11, 12]);
```

### Grant Temporary Permission

```php
// Grant permission for 7 days
$user->grantPermission(
    permissionIds: [15, 16],
    expiresAt: now()->addDays(7)->toDateTimeString()
);

// The permission will automatically not apply after expiration
```

### Clone a Role

```php
// Via API
POST /api/admin/roles/1/clone
{
    "name": {
        "ar": "نسخة من المدير",
        "en": "Copy of Manager"
    }
}

// Via Code
$originalRole = Role::find(1);
$newRole = $originalRole->replicate();
$newRole->push();
$newRole->permissions()->attach($originalRole->permissions->pluck('id'));
```

---

## API Endpoints

### Roles

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/api/admin/roles` | List roles (paginated) |
| GET | `/api/admin/role-not-paginated` | List all roles |
| GET | `/api/admin/roles/tree` | Get role hierarchy tree |
| GET | `/api/admin/roles/{id}` | Get single role |
| POST | `/api/admin/roles` | Create role |
| PUT | `/api/admin/roles/{id}` | Update role |
| DELETE | `/api/admin/roles/{id}` | Delete role |
| POST | `/api/admin/roles/{id}/clone` | Clone role |

### Permissions

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/api/admin/permissions` | List permissions (paginated) |
| GET | `/api/admin/permission-not-paginated` | List all permissions |

### Request/Response Examples

#### Create Role

```http
POST /api/admin/roles
Content-Type: application/json
Authorization: Bearer {token}

{
    "ar": {
        "name": "محرر المحتوى",
        "desc": "يمكنه تحرير المحتوى فقط"
    },
    "en": {
        "name": "Content Editor",
        "desc": "Can only edit content"
    },
    "parent_id": 2,
    "permissions": [10, 11, 12, 13]
}
```

#### Response

```json
{
    "status": "success",
    "message": "Role created successfully",
    "data": {
        "id": 5,
        "name": "Content Editor",
        "desc": "Can only edit content",
        "level": 1,
        "parent_id": 2,
        "parent": {
            "id": 2,
            "name": "Manager"
        },
        "permissions_count": 4,
        "permissions": [...]
    }
}
```

---

## Artisan Commands

### Clear Permission Cache

```bash
# Clear all permission caches
php artisan permission:clear-cache

# Clear cache for specific user
php artisan permission:clear-cache --user=1

# Clear cache for specific role (and all its users)
php artisan permission:clear-cache --role=2
```

---

## Best Practices

### 1. Always Use Caching

The system automatically caches permissions. Never bypass the cache unless absolutely necessary.

```php
// Good - uses cache
$user->hasPermission('admin.users.index');

// Avoid - direct database query
$user->role->permissions()->where('back_route_name', 'admin.users.index')->exists();
```

### 2. Use Policies for Complex Authorization

```php
// Good - uses Policy
$this->authorize('update', $role);

// Instead of manual checks everywhere
if (!$user->hasPermission('admin.roles.update')) {
    abort(403);
}
```

### 3. Leverage Hierarchical Roles

Instead of duplicating permissions, create a role hierarchy.

```php
// Good - inherit from parent
$editorRole->setParent($adminRole);

// Avoid - duplicate permissions
$editorRole->syncPermissions($adminRole->permissions->pluck('id')->toArray());
```

### 4. Use Direct Permissions Sparingly

Direct permissions should be used for exceptions, not as the primary authorization method.

```php
// Good - temporary access
$user->grantPermission(5, now()->addDays(7));

// Avoid - using direct permissions instead of roles
$user->grantPermission([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
```

### 5. Monitor Audit Logs

Regularly review audit logs for security.

```php
// Get suspicious activity
$suspiciousLogs = PermissionAuditLog::where('action', 'role_permissions_updated')
    ->where('created_at', '>=', now()->subDay())
    ->get();
```

---

## Migration

To apply all the improvements, run:

```bash
php artisan migrate
```

This will:
- Add `parent_id` and `level` to `roles` table
- Add `module` and `sort_order` to `permissions` table
- Create `permission_user` table for direct permissions
- Create `permission_audit_logs` table for auditing

---

## Files Reference

| File | Purpose |
|------|---------|
| `app/Traits/HasPermission.php` | User permission methods |
| `app/Models/Role.php` | Role model with hierarchy |
| `app/Models/Permission.php` | Permission model |
| `app/Models/PermissionAuditLog.php` | Audit log model |
| `app/Services/Permission/PermissionCacheService.php` | Caching service |
| `app/Services/Permission/PermissionAuditService.php` | Audit service |
| `app/Http/Middleware/Permission.php` | Route protection |
| `app/Policies/RolePolicy.php` | Role authorization |
| `app/Policies/PermissionPolicy.php` | Permission authorization |
| `app/Policies/UserPolicy.php` | User authorization |
| `app/Console/Commands/ClearPermissionCache.php` | Cache clear command |

---

## Support

For questions or issues, please contact the development team.