# Connector Authorization System - Implementation Guide

## Overview

This document describes the comprehensive authorization system implemented for the Connector module in the SuiteX iPaaS platform. The system uses **direct permission checks** via `User->hasPermission('can_manage_ipass')` to control access to connector operations based on the user's role permissions.

## Date: November 7, 2025

---

## Architecture

### 1. **Direct Permission Checks**

The authorization system uses a **simple and direct approach**:

```php
if (!auth()->user()->hasPermission('can_manage_ipass')) {
    abort(403, 'Unauthorized action.');
}
```

This checks the **`can_manage_ipass`** permission on the user's current role without the overhead of Laravel Policies.

#### Why Not Policies?

We opted for **direct permission checks** instead of Laravel Policies because:
- ✅ **Simpler**: Only one permission to check, no complex logic needed
- ✅ **More maintainable**: Less abstraction layers
- ✅ **Easier to debug**: Authorization logic is inline and visible
- ✅ **Performance**: No additional Policy resolution overhead

#### Security Logging

Authorization checks include comprehensive logging:
- Logs **warning** when unauthorized access is attempted
- Captures user ID, role ID, connector ID, action, and IP address
- Helps with security audits and compliance

### 2. **Permission Model** (Database Schema)

#### `roles` table (tenant database)
```sql
can_manage_ipass TINYINT(1) NOT NULL DEFAULT 0
```

#### `tenant_roles` table (central database)
```sql
can_manage_ipass BOOLEAN DEFAULT false
```

#### User-Role Relationship
- Users have a `current_role_id` that references their active role
- `User::hasPermission($permission)` checks the current role's permission flag
- Returns `true` if `role->can_manage_ipass == true`

---

## Implementation Details

### ConnectorRequest Authorization

The `ConnectorRequest` Form Request handles authorization **before validation**:

```php
public function authorize(): bool
{
    // 1. Check authentication
    if (!auth()->check()) {
        Log::warning('Unauthenticated connector request attempt');
        return false;
    }

    $user = auth()->user();

    // 2. Check permission directly
    $hasPermission = $user->hasPermission('can_manage_ipass');
    
    if (!$hasPermission) {
        Log::warning('Unauthorized connector access attempt', [
            'user_id' => $user->id,
            'action' => $this->isMethod('POST') ? 'create' : 'update',
            'ip' => $this->ip()
        ]);
    }
    
    return $hasPermission;
}
```

**Key Features:**
- Simple and direct permission check
- Logs unauthorized attempts with context (user ID, action, IP address)
- Returns `false` immediately if user doesn't have permission

### Controller Authorization

The `ConnectorController` uses **direct permission checks** in each method:

```php
// In index(), create(), show(), edit(), destroy()
if (!auth()->user()->hasPermission('can_manage_ipass')) {
    abort(403, 'Unauthorized action.');
}
```

**Layered Security:**
- `ConnectorRequest->authorize()` runs first (for `store()` and `update()`)
- Controller authorization adds a second layer for view/edit/delete operations
- Both layers check the same permission, ensuring consistency

---

## Permission Flow

```
1. User makes request → ConnectorController
                          ↓
2. Controller checks auth()->user()->hasPermission('can_manage_ipass')
                          ↓
3. User->currentRole->can_manage_ipass
                          ↓
4. Return true/false
                          ↓
5. If false: abort(403) or return false
   If true: proceed to controller logic
```

---

## Security Features

### 1. **Comprehensive Logging**
- All authorization failures are logged with context
- Security audit trail for compliance
- Helps identify potential security threats

### 2. **Multi-Layer Authorization**
- FormRequest authorization (before validation)
- Controller authorization (before business logic)
- Prevents bypassing authorization by calling actions directly

### 3. **Role-Based Access Control (RBAC)**
- Centralized permission management via roles
- Easy to audit who has access to iPaaS features
- Scalable for future permission granularity

### 4. **Secure by Default**
- All methods return `false` unless permission is explicitly granted
- Logs warnings on unauthorized attempts
- No hardcoded bypasses or superuser exceptions

---

## Usage Examples

### Checking Permissions in Blade Views

```blade
@if(auth()->user()->hasPermission('can_manage_ipass'))
    <a href="{{ route('connector.create') }}" class="btn">
        Create Connector
    </a>
@endif

@if(auth()->user()->hasPermission('can_manage_ipass'))
    <a href="{{ route('connector.edit', $connector->id) }}" class="btn">
        Edit
    </a>
@endif

@if(auth()->user()->hasPermission('can_manage_ipass'))
    <form action="{{ route('connector.destroy', $connector->id) }}" method="POST">
        @csrf
        @method('DELETE')
        <button type="submit" class="btn btn-danger">Delete</button>
    </form>
@endif
```

### Checking Permissions in Controllers

```php
// Check before showing button/link
if (auth()->user()->hasPermission('can_manage_ipass')) {
    // Show create button
}

// Explicit authorization
if (!auth()->user()->hasPermission('can_manage_ipass')) {
    abort(403, 'Unauthorized action.');
}

// Authorization with custom response
if (!auth()->user()->hasPermission('can_manage_ipass')) {
    return response()->json(['error' => 'Unauthorized'], 403);
}
```

### Checking Permissions in Livewire Components

```php
class ConnectorForm extends Component
{
    public function mount($connectorId = null)
    {
        if (!auth()->user()->hasPermission('can_manage_ipass')) {
            abort(403, 'Unauthorized action.');
        }
        
        // Load connector if editing
        if ($connectorId) {
            $this->connector = Connector::findOrFail($connectorId);
        }
    }
}
```

---

## Testing Authorization

### Unit Tests for Permission Checks

```php
use Tests\TestCase;
use App\Models\User;
use Domain\Roles\Models\Role;
use Domain\Ipaas\Connectors\Models\Connector;

class ConnectorAuthorizationTest extends TestCase
{
    /** @test */
    public function user_with_can_manage_ipass_has_permission()
    {
        $role = Role::factory()->create(['can_manage_ipass' => true]);
        $user = User::factory()->create(['current_role_id' => $role->id]);
        
        $this->assertTrue($user->hasPermission('can_manage_ipass'));
    }

    /** @test */
    public function user_without_can_manage_ipass_does_not_have_permission()
    {
        $role = Role::factory()->create(['can_manage_ipass' => false]);
        $user = User::factory()->create(['current_role_id' => $role->id]);
        
        $this->assertFalse($user->hasPermission('can_manage_ipass'));
    }

    /** @test */
    public function user_without_role_does_not_have_permission()
    {
        $user = User::factory()->create(['current_role_id' => null]);
        
        $this->assertFalse($user->hasPermission('can_manage_ipass'));
    }
}
```

### Feature Tests for Controller

```php
use Tests\TestCase;
use App\Models\User;
use Domain\Roles\Models\Role;
use Domain\Ipaas\Connectors\Models\Connector;

class ConnectorControllerAuthorizationTest extends TestCase
{
    /** @test */
    public function authorized_user_can_access_connector_index()
    {
        $role = Role::factory()->create(['can_manage_ipass' => true]);
        $user = User::factory()->create(['current_role_id' => $role->id]);
        
        $response = $this->actingAs($user)->get(route('connector.index'));
        
        $response->assertStatus(200);
    }

    /** @test */
    public function unauthorized_user_cannot_access_connector_index()
    {
        $role = Role::factory()->create(['can_manage_ipass' => false]);
        $user = User::factory()->create(['current_role_id' => $role->id]);
        
        $response = $this->actingAs($user)->get(route('connector.index'));
        
        $response->assertStatus(403);
    }

    /** @test */
    public function unauthorized_user_cannot_create_connector()
    {
        $role = Role::factory()->create(['can_manage_ipass' => false]);
        $user = User::factory()->create(['current_role_id' => $role->id]);
        
        $response = $this->actingAs($user)->postJson(route('connector.store'), [
            'name' => 'Test Connector',
            'base_url' => 'https://api.example.com',
            // ... other fields
        ]);
        
        $response->assertStatus(403);
    }
}
```

---

## Granting iPaaS Permissions to Users

### Via Database (Direct)

```sql
-- Update a specific role to allow iPaaS management
UPDATE roles SET can_manage_ipass = 1 WHERE id = 1;

-- Create a new role with iPaaS permissions
INSERT INTO roles (title, `key`, can_manage_ipass, created_at, updated_at)
VALUES ('iPaaS Manager', 'ipaas_manager', 1, NOW(), NOW());
```

### Via Admin Interface (Recommended)

1. Navigate to **Roles Management** in the admin panel
2. Edit the desired role
3. Check the **"Can Manage iPaaS"** checkbox
4. Save the role

### Via Seeder

```php
use Domain\Roles\Models\Role;

class RoleSeeder extends Seeder
{
    public function run()
    {
        Role::create([
            'title' => 'iPaaS Administrator',
            'key' => 'ipaas_admin',
            'can_manage_ipass' => true,
            'can_create' => true,
            'can_read' => true,
            'can_update' => true,
            'can_delete' => true,
        ]);
    }
}
```

---

## Future Enhancements

### 1. **Granular Permissions**
Instead of a single `can_manage_ipass` flag, implement:
- `can_create_connectors`
- `can_update_connectors`
- `can_delete_connectors`
- `can_view_connectors`
- `can_execute_flows`

### 2. **Tenant-Scoped Permissions**
- Verify the connector belongs to the user's current tenant
- Prevent cross-tenant access
- Add tenant_id checks to Policy methods

### 3. **Ownership-Based Permissions**
- Track connector creators (`created_by` column)
- Allow users to manage only their own connectors
- Add "Admin" override for viewing all connectors

### 4. **Permission Caching**
- Cache role permissions to reduce database queries
- Use Laravel's `Cache` facade with role-based keys
- Invalidate cache on role updates

---

## Troubleshooting

### "This action is unauthorized" Error

**Symptoms:** User receives 403 error when trying to access connectors

**Causes:**
1. User's current role doesn't have `can_manage_ipass = true`
2. User has no `current_role_id` assigned
3. User model's `hasPermission()` method not working correctly

**Solutions:**
1. Grant permission via Artisan command:
   ```bash
   php artisan ipaas:grant-permission {tenant_id} all
   # or for specific user:
   php artisan ipaas:grant-permission {tenant_id} user@example.com
   ```
2. Update the role directly:
   ```sql
   UPDATE roles SET can_manage_ipass = 1 WHERE id = {role_id}
   ```
3. Assign a role to the user:
   ```sql
   UPDATE users SET current_role_id = {role_id} WHERE id = {user_id}
   ```

### Permission Check Not Working

**Symptoms:** Authorization always passes/fails regardless of role

**Causes:**
1. `User->currentRole` relationship not loaded
2. Role doesn't have `can_manage_ipass` column
3. Database connection issue (wrong tenant)

**Solutions:**
1. Verify `User` model has `currentRole()` relationship
2. Check database schema has `can_manage_ipass` column
3. Ensure correct tenant database is being used

### Logging Not Working

**Symptoms:** No authorization logs in storage/logs

**Causes:**
1. Log channel not configured
2. Permissions issue on log directory
3. Log level too high

**Solutions:**
1. Check `config/logging.php` configuration
2. Ensure `storage/logs` is writable: `chmod -R 775 storage/logs`
3. Set log level to `debug` or `info` in `.env`

---

## Summary

The Connector Authorization System provides:

✅ **Role-based access control** via `can_manage_ipass` permission  
✅ **Multi-layer security** (FormRequest + Controller)  
✅ **Comprehensive audit logging** for security compliance  
✅ **Simple and maintainable** direct permission checks  
✅ **Scalable architecture** for future enhancements  
✅ **Artisan command** for easy permission management

All connector operations are now protected by this authorization system, ensuring only authorized users with the `can_manage_ipass` permission can create, view, update, or delete connectors.

