netgescon-day0/docs/90-UI-interfaccia-unica/09-PERMISSIONS.md

40 KiB
Executable File

🔐 PERMISSIONS - Documentazione Completa NetGescon

Sistema Permessi dell'interfaccia universale
📅 Creato: 21/07/2025
🔄 Ultimo aggiornamento: 21/07/2025
🎯 Priorità: ALTO


📋 OVERVIEW PERMISSIONS SYSTEM

Il Sistema Permessi NetGescon gestisce ACCESSO GRANULARE per:

  • 👤 User Roles (Super Admin, Admin, Manager, User, Guest)
  • 🔑 Permissions (Create, Read, Update, Delete, Execute)
  • 🏢 Resource Access (Stabili, Condomini, Unità, Reports)
  • 📊 Data Visibility (Own, Team, Company, All)
  • 🎛️ UI Components (Navigation, Actions, Features)

🎯 Security Philosophy

  • Least Privilege - Accesso minimo necessario per default
  • Role-Based Access - Permessi basati su ruoli definiti
  • Resource-Level Security - Controllo granulare per ogni risorsa
  • UI-Driven Permissions - Interfaccia che si adatta ai permessi

👥 ROLE SYSTEM

🔑 Role Definitions

// app/Models/User.php

class User extends Authenticatable implements HasMedia
{
    // Role constants
    const ROLE_SUPER_ADMIN = 'super_admin';
    const ROLE_ADMIN = 'admin';
    const ROLE_MANAGER = 'manager';
    const ROLE_USER = 'user';
    const ROLE_GUEST = 'guest';
    
    // Permission levels
    const PERMISSION_ALL = 'all';
    const PERMISSION_COMPANY = 'company';
    const PERMISSION_TEAM = 'team';
    const PERMISSION_OWN = 'own';
    const PERMISSION_NONE = 'none';
    
    /**
     * Get user role
     */
    public function getRole()
    {
        return $this->role ?? self::ROLE_USER;
    }
    
    /**
     * Check if user has role
     */
    public function hasRole($role)
    {
        return $this->getRole() === $role;
    }
    
    /**
     * Check if user has any of the roles
     */
    public function hasAnyRole(array $roles)
    {
        return in_array($this->getRole(), $roles);
    }
    
    /**
     * Check if user is admin or above
     */
    public function isAdmin()
    {
        return $this->hasAnyRole([
            self::ROLE_SUPER_ADMIN,
            self::ROLE_ADMIN
        ]);
    }
    
    /**
     * Check if user is manager or above
     */
    public function isManager()
    {
        return $this->hasAnyRole([
            self::ROLE_SUPER_ADMIN,
            self::ROLE_ADMIN,
            self::ROLE_MANAGER
        ]);
    }
    
    /**
     * Get permission level for resource
     */
    public function getPermissionLevel($resource)
    {
        $permissions = $this->permissions ?? [];
        return $permissions[$resource] ?? $this->getDefaultPermissionLevel();
    }
    
    /**
     * Get default permission level based on role
     */
    public function getDefaultPermissionLevel()
    {
        return match($this->getRole()) {
            self::ROLE_SUPER_ADMIN => self::PERMISSION_ALL,
            self::ROLE_ADMIN => self::PERMISSION_COMPANY,
            self::ROLE_MANAGER => self::PERMISSION_TEAM,
            self::ROLE_USER => self::PERMISSION_OWN,
            self::ROLE_GUEST => self::PERMISSION_NONE,
            default => self::PERMISSION_NONE
        };
    }
}

🏢 Role Matrix

// config/roles.php

return [
    'roles' => [
        'super_admin' => [
            'name' => 'Super Amministratore',
            'description' => 'Accesso completo al sistema',
            'color' => 'danger',
            'icon' => 'fas fa-crown',
            'permissions' => [
                'users' => 'all',
                'companies' => 'all',
                'stabili' => 'all',
                'condomini' => 'all',
                'unita' => 'all',
                'reports' => 'all',
                'settings' => 'all',
                'logs' => 'all'
            ]
        ],
        
        'admin' => [
            'name' => 'Amministratore',
            'description' => 'Gestione completa della company',
            'color' => 'warning',
            'icon' => 'fas fa-user-shield',
            'permissions' => [
                'users' => 'company',
                'stabili' => 'company',
                'condomini' => 'company',
                'unita' => 'company',
                'reports' => 'company',
                'settings' => 'company'
            ]
        ],
        
        'manager' => [
            'name' => 'Manager',
            'description' => 'Gestione team e risorse assegnate',
            'color' => 'info',
            'icon' => 'fas fa-user-tie',
            'permissions' => [
                'users' => 'team',
                'stabili' => 'team',
                'condomini' => 'team',
                'unita' => 'team',
                'reports' => 'team'
            ]
        ],
        
        'user' => [
            'name' => 'Utente',
            'description' => 'Accesso alle proprie risorse',
            'color' => 'success',
            'icon' => 'fas fa-user',
            'permissions' => [
                'stabili' => 'own',
                'condomini' => 'own',
                'unita' => 'own',
                'reports' => 'own'
            ]
        ],
        
        'guest' => [
            'name' => 'Ospite',
            'description' => 'Accesso solo lettura limitato',
            'color' => 'secondary',
            'icon' => 'fas fa-eye',
            'permissions' => [
                'stabili' => 'none',
                'condomini' => 'none',
                'unita' => 'none',
                'reports' => 'none'
            ]
        ]
    ],
    
    'resources' => [
        'users' => 'Gestione Utenti',
        'companies' => 'Gestione Aziende',
        'stabili' => 'Gestione Stabili',
        'condomini' => 'Gestione Condomini',
        'unita' => 'Unità Immobiliari',
        'reports' => 'Reports e Analytics',
        'settings' => 'Impostazioni Sistema',
        'logs' => 'Logs di Sistema'
    ],
    
    'actions' => [
        'create' => 'Creare',
        'read' => 'Visualizzare',
        'update' => 'Modificare',
        'delete' => 'Eliminare',
        'execute' => 'Eseguire'
    ]
];

🛡️ PERMISSION MIDDLEWARE

🔐 Role Middleware

// app/Http/Middleware/CheckRole.php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class CheckRole
{
    /**
     * Handle an incoming request.
     */
    public function handle(Request $request, Closure $next, ...$roles)
    {
        if (!Auth::check()) {
            return redirect('login');
        }
        
        $user = Auth::user();
        
        if (!$user->hasAnyRole($roles)) {
            abort(403, 'Non hai i permessi per accedere a questa risorsa.');
        }
        
        return $next($request);
    }
}

🔑 Permission Middleware

// app/Http/Middleware/CheckPermission.php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class CheckPermission
{
    /**
     * Handle an incoming request.
     */
    public function handle(Request $request, Closure $next, string $resource, string $action = 'read')
    {
        if (!Auth::check()) {
            return redirect('login');
        }
        
        $user = Auth::user();
        
        if (!$this->hasPermission($user, $resource, $action)) {
            if ($request->expectsJson()) {
                return response()->json([
                    'message' => 'Non hai i permessi per eseguire questa azione.'
                ], 403);
            }
            
            abort(403, 'Non hai i permessi per eseguire questa azione.');
        }
        
        return $next($request);
    }
    
    /**
     * Check if user has permission for resource action
     */
    private function hasPermission($user, $resource, $action)
    {
        $permissionLevel = $user->getPermissionLevel($resource);
        
        // Super admin has all permissions
        if ($user->hasRole(User::ROLE_SUPER_ADMIN)) {
            return true;
        }
        
        // No permission
        if ($permissionLevel === User::PERMISSION_NONE) {
            return false;
        }
        
        // All permission
        if ($permissionLevel === User::PERMISSION_ALL) {
            return true;
        }
        
        // Check specific action permissions
        return $this->checkActionPermission($user, $resource, $action, $permissionLevel);
    }
    
    /**
     * Check action-specific permissions
     */
    private function checkActionPermission($user, $resource, $action, $permissionLevel)
    {
        // Define action requirements
        $actionPermissions = [
            'create' => ['company', 'team', 'own'],
            'read' => ['company', 'team', 'own'],
            'update' => ['company', 'team', 'own'],
            'delete' => ['company', 'team'],
            'execute' => ['company', 'team']
        ];
        
        $requiredLevels = $actionPermissions[$action] ?? ['company'];
        
        return in_array($permissionLevel, $requiredLevels);
    }
}

🎛️ UI PERMISSION GATES

🎨 Blade Directives

// app/Providers/AppServiceProvider.php

use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\Auth;

public function boot()
{
    // Role-based directives
    Blade::if('role', function ($role) {
        return Auth::check() && Auth::user()->hasRole($role);
    });
    
    Blade::if('anyrole', function (...$roles) {
        return Auth::check() && Auth::user()->hasAnyRole($roles);
    });
    
    // Permission-based directives
    Blade::if('can', function ($resource, $action = 'read') {
        if (!Auth::check()) return false;
        
        $user = Auth::user();
        $permissionLevel = $user->getPermissionLevel($resource);
        
        if ($user->hasRole(User::ROLE_SUPER_ADMIN)) return true;
        if ($permissionLevel === User::PERMISSION_NONE) return false;
        if ($permissionLevel === User::PERMISSION_ALL) return true;
        
        // Check action permissions
        $actionPermissions = [
            'create' => ['company', 'team', 'own'],
            'read' => ['company', 'team', 'own'],
            'update' => ['company', 'team', 'own'],
            'delete' => ['company', 'team'],
            'execute' => ['company', 'team']
        ];
        
        $requiredLevels = $actionPermissions[$action] ?? ['company'];
        return in_array($permissionLevel, $requiredLevels);
    });
    
    // Admin check
    Blade::if('admin', function () {
        return Auth::check() && Auth::user()->isAdmin();
    });
    
    // Manager check
    Blade::if('manager', function () {
        return Auth::check() && Auth::user()->isManager();
    });
}

🧩 Permission Components

<!-- resources/views/components/permission-gate.blade.php -->
@props([
    'resource' => '',
    'action' => 'read',
    'role' => null,
    'fallback' => null
])

@php
$hasPermission = false;

if ($role) {
    $hasPermission = Auth::check() && Auth::user()->hasAnyRole(is_array($role) ? $role : [$role]);
} elseif ($resource) {
    $hasPermission = Auth::check() && Auth::user()->hasPermission($resource, $action);
}
@endphp

@if($hasPermission)
    {{ $slot }}
@elseif($fallback)
    {!! $fallback !!}
@endif
<!-- resources/views/components/role-badge.blade.php -->
@props(['user' => null])

@php
$user = $user ?? Auth::user();
$roleConfig = config('roles.roles')[$user->getRole()] ?? null;
@endphp

@if($roleConfig)
    <span class="badge bg-{{ $roleConfig['color'] }} badge-role" 
          data-bs-toggle="tooltip" 
          title="{{ $roleConfig['description'] }}">
        <i class="{{ $roleConfig['icon'] }} me-1"></i>
        {{ $roleConfig['name'] }}
    </span>
@endif

🔧 JAVASCRIPT PERMISSIONS

Permission Manager

// /public/js/permissions/permission-manager.js

class PermissionManager {
    constructor() {
        this.permissions = {};
        this.user = null;
        this.init();
    }
    
    async init() {
        // Load user permissions from API or meta tags
        await this.loadPermissions();
        
        // Apply UI permissions
        this.applyUIPermissions();
        
        // Setup dynamic permission checks
        this.setupDynamicChecks();
    }
    
    async loadPermissions() {
        try {
            // Try to load from meta tag first
            const permissionsMeta = document.querySelector('meta[name="user-permissions"]');
            if (permissionsMeta) {
                this.permissions = JSON.parse(permissionsMeta.content);
                return;
            }
            
            // Fallback to API
            const response = await fetch('/api/user/permissions');
            if (response.ok) {
                const data = await response.json();
                this.permissions = data.permissions;
                this.user = data.user;
            }
        } catch (error) {
            console.warn('Failed to load permissions:', error);
            this.permissions = {};
        }
    }
    
    applyUIPermissions() {
        // Hide/show elements based on permissions
        document.querySelectorAll('[data-permission]').forEach(element => {
            const permission = element.dataset.permission;
            const action = element.dataset.action || 'read';
            
            if (!this.can(permission, action)) {
                element.style.display = 'none';
                element.setAttribute('aria-hidden', 'true');
            }
        });
        
        // Hide/show based on roles
        document.querySelectorAll('[data-role]').forEach(element => {
            const roles = element.dataset.role.split(',');
            
            if (!this.hasAnyRole(roles)) {
                element.style.display = 'none';
                element.setAttribute('aria-hidden', 'true');
            }
        });
        
        // Disable buttons/links
        document.querySelectorAll('[data-permission-action]').forEach(element => {
            const permission = element.dataset.permissionAction;
            const action = element.dataset.action || 'execute';
            
            if (!this.can(permission, action)) {
                element.disabled = true;
                element.classList.add('disabled');
                element.setAttribute('aria-disabled', 'true');
                
                // Remove click handlers
                element.onclick = (e) => {
                    e.preventDefault();
                    this.showPermissionDenied();
                    return false;
                };
            }
        });
    }
    
    setupDynamicChecks() {
        // Intercept form submissions
        document.addEventListener('submit', (e) => {
            const form = e.target;
            const permission = form.dataset.permission;
            const action = form.dataset.action || 'create';
            
            if (permission && !this.can(permission, action)) {
                e.preventDefault();
                this.showPermissionDenied();
                return false;
            }
        });
        
        // Intercept navigation
        document.addEventListener('click', (e) => {
            const link = e.target.closest('a[data-permission]');
            if (link) {
                const permission = link.dataset.permission;
                const action = link.dataset.action || 'read';
                
                if (!this.can(permission, action)) {
                    e.preventDefault();
                    this.showPermissionDenied();
                    return false;
                }
            }
        });
        
        // Intercept AJAX requests
        const originalFetch = window.fetch;
        window.fetch = (...args) => {
            const [url, options = {}] = args;
            
            // Check if request has permission requirements
            if (options.permission) {
                const action = options.action || 'read';
                if (!this.can(options.permission, action)) {
                    return Promise.reject(new Error('Permission denied'));
                }
            }
            
            return originalFetch.apply(this, args);
        };
    }
    
    can(resource, action = 'read') {
        // Super admin can do everything
        if (this.hasRole('super_admin')) {
            return true;
        }
        
        const permissionLevel = this.permissions[resource];
        
        if (!permissionLevel || permissionLevel === 'none') {
            return false;
        }
        
        if (permissionLevel === 'all') {
            return true;
        }
        
        // Check action-specific permissions
        const actionPermissions = {
            'create': ['company', 'team', 'own'],
            'read': ['company', 'team', 'own'],
            'update': ['company', 'team', 'own'],
            'delete': ['company', 'team'],
            'execute': ['company', 'team']
        };
        
        const requiredLevels = actionPermissions[action] || ['company'];
        return requiredLevels.includes(permissionLevel);
    }
    
    hasRole(role) {
        return this.user && this.user.role === role;
    }
    
    hasAnyRole(roles) {
        return this.user && roles.includes(this.user.role);
    }
    
    isAdmin() {
        return this.hasAnyRole(['super_admin', 'admin']);
    }
    
    isManager() {
        return this.hasAnyRole(['super_admin', 'admin', 'manager']);
    }
    
    showPermissionDenied() {
        if (window.NetGesconNotifications) {
            NetGesconNotifications.error('Non hai i permessi per eseguire questa azione.');
        } else {
            alert('Non hai i permessi per eseguire questa azione.');
        }
    }
    
    // Permission-based navigation
    navigate(url, permission = null, action = 'read') {
        if (permission && !this.can(permission, action)) {
            this.showPermissionDenied();
            return false;
        }
        
        window.location.href = url;
        return true;
    }
    
    // Dynamic permission checking for AJAX
    async checkPermissionAPI(resource, action = 'read') {
        try {
            const response = await fetch('/api/check-permission', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content
                },
                body: JSON.stringify({ resource, action })
            });
            
            return response.ok;
        } catch (error) {
            console.error('Permission check failed:', error);
            return false;
        }
    }
    
    // Refresh permissions
    async refreshPermissions() {
        await this.loadPermissions();
        this.applyUIPermissions();
    }
}

// Initialize permission manager
document.addEventListener('DOMContentLoaded', () => {
    window.PermissionManager = new PermissionManager();
});

📱 RESPONSIVE PERMISSIONS

🎛️ Permission-Aware Navigation

<!-- resources/views/components/navigation/sidebar.blade.php -->
<nav class="netgescon-sidebar" id="sidebar">
    <div class="sidebar-content">
        
        <!-- Dashboard - Available to all authenticated users -->
        <div class="nav-section">
            <a href="{{ route('admin.dashboard') }}" 
               class="nav-link {{ request()->routeIs('admin.dashboard') ? 'active' : '' }}">
                <i class="fas fa-tachometer-alt nav-icon"></i>
                <span class="nav-text">Dashboard</span>
            </a>
        </div>
        
        <!-- Gestione Stabili -->
        <x-permission-gate resource="stabili" action="read">
            <div class="nav-section">
                <div class="nav-section-title">Gestione Immobiliare</div>
                
                <!-- Stabili -->
                <a href="{{ route('admin.stabili.index') }}" 
                   class="nav-link {{ request()->routeIs('admin.stabili.*') ? 'active' : '' }}">
                    <i class="fas fa-building nav-icon"></i>
                    <span class="nav-text">Stabili</span>
                    <x-permission-gate resource="stabili" action="create">
                        <span class="nav-badge bg-primary">+</span>
                    </x-permission-gate>
                </a>
                
                <!-- Condomini -->
                <a href="{{ route('admin.condomini.index') }}" 
                   class="nav-link {{ request()->routeIs('admin.condomini.*') ? 'active' : '' }}"
                   data-permission="condomini" 
                   data-action="read">
                    <i class="fas fa-home nav-icon"></i>
                    <span class="nav-text">Condomini</span>
                </a>
                
                <!-- Unità Immobiliari -->
                <a href="{{ route('admin.unita.index') }}" 
                   class="nav-link {{ request()->routeIs('admin.unita.*') ? 'active' : '' }}"
                   data-permission="unita" 
                   data-action="read">
                    <i class="fas fa-door-open nav-icon"></i>
                    <span class="nav-text">Unità</span>
                </a>
            </div>
        </x-permission-gate>
        
        <!-- Reports e Analytics -->
        <x-permission-gate resource="reports" action="read">
            <div class="nav-section">
                <div class="nav-section-title">Reports</div>
                
                <a href="{{ route('admin.reports.index') }}" 
                   class="nav-link {{ request()->routeIs('admin.reports.*') ? 'active' : '' }}">
                    <i class="fas fa-chart-bar nav-icon"></i>
                    <span class="nav-text">Analytics</span>
                </a>
                
                <x-permission-gate resource="reports" action="execute">
                    <a href="{{ route('admin.reports.financial') }}" 
                       class="nav-link">
                        <i class="fas fa-euro-sign nav-icon"></i>
                        <span class="nav-text">Report Finanziari</span>
                    </a>
                </x-permission-gate>
            </div>
        </x-permission-gate>
        
        <!-- Gestione Utenti - Solo per Admin -->
        <x-permission-gate role="admin">
            <div class="nav-section">
                <div class="nav-section-title">Amministrazione</div>
                
                <a href="{{ route('admin.users.index') }}" 
                   class="nav-link {{ request()->routeIs('admin.users.*') ? 'active' : '' }}">
                    <i class="fas fa-users nav-icon"></i>
                    <span class="nav-text">Utenti</span>
                </a>
                
                <a href="{{ route('admin.settings.index') }}" 
                   class="nav-link {{ request()->routeIs('admin.settings.*') ? 'active' : '' }}"
                   data-permission="settings" 
                   data-action="read">
                    <i class="fas fa-cog nav-icon"></i>
                    <span class="nav-text">Impostazioni</span>
                </a>
            </div>
        </x-permission-gate>
        
        <!-- System Logs - Solo per Super Admin -->
        <x-permission-gate role="super_admin">
            <div class="nav-section">
                <div class="nav-section-title">Sistema</div>
                
                <a href="{{ route('admin.logs.index') }}" 
                   class="nav-link {{ request()->routeIs('admin.logs.*') ? 'active' : '' }}">
                    <i class="fas fa-file-alt nav-icon"></i>
                    <span class="nav-text">Logs</span>
                </a>
            </div>
        </x-permission-gate>
        
    </div>
</nav>

🎯 Permission-Based Actions

<!-- resources/views/admin/stabili/index.blade.php -->
<div class="page-header">
    <div class="page-title">
        <h1>Gestione Stabili</h1>
        <p class="text-muted">Gestisci tutti gli stabili del tuo portafoglio</p>
    </div>
    
    <div class="page-actions">
        <!-- Create Action - Only if user can create -->
        <x-permission-gate resource="stabili" action="create">
            <a href="{{ route('admin.stabili.create') }}" 
               class="btn btn-primary">
                <i class="fas fa-plus mr-2"></i>
                Nuovo Stabile
            </a>
        </x-permission-gate>
        
        <!-- Export Action - Only if user can execute -->
        <x-permission-gate resource="stabili" action="execute">
            <button class="btn btn-outline-secondary" 
                    onclick="exportStabili()"
                    data-permission-action="stabili"
                    data-action="execute">
                <i class="fas fa-download mr-2"></i>
                Esporta
            </button>
        </x-permission-gate>
    </div>
</div>

<!-- Data Table with Permission-Based Actions -->
<div class="netgescon-card">
    <div class="table-responsive">
        <table class="table netgescon-table" id="stabili-table">
            <thead>
                <tr>
                    <th>Nome</th>
                    <th>Indirizzo</th>
                    <th>Unità</th>
                    <th>Valore</th>
                    <th class="text-center">Azioni</th>
                </tr>
            </thead>
            <tbody>
                @foreach($stabili as $stabile)
                <tr>
                    <td>
                        <strong>{{ $stabile->nome }}</strong>
                        <x-role-badge :user="$stabile->responsible" />
                    </td>
                    <td>{{ $stabile->indirizzo_completo }}</td>
                    <td>{{ $stabile->unita_count }}</td>
                    <td>{{ number_format($stabile->valore, 0, ',', '.') }} €</td>
                    <td class="text-center">
                        <div class="btn-group">
                            <!-- View Action - Always available if user can read -->
                            <a href="{{ route('admin.stabili.show', $stabile) }}" 
                               class="btn btn-sm btn-outline-primary"
                               data-bs-toggle="tooltip" 
                               title="Visualizza">
                                <i class="fas fa-eye"></i>
                            </a>
                            
                            <!-- Edit Action - Only if user can update -->
                            <x-permission-gate resource="stabili" action="update">
                                <a href="{{ route('admin.stabili.edit', $stabile) }}" 
                                   class="btn btn-sm btn-outline-secondary"
                                   data-bs-toggle="tooltip" 
                                   title="Modifica">
                                    <i class="fas fa-edit"></i>
                                </a>
                            </x-permission-gate>
                            
                            <!-- Delete Action - Only if user can delete -->
                            <x-permission-gate resource="stabili" action="delete">
                                <button class="btn btn-sm btn-outline-danger" 
                                        onclick="deleteStabile({{ $stabile->id }})"
                                        data-permission-action="stabili"
                                        data-action="delete"
                                        data-bs-toggle="tooltip" 
                                        title="Elimina">
                                    <i class="fas fa-trash"></i>
                                </button>
                            </x-permission-gate>
                            
                            <!-- Reports Action - Only if user can execute reports -->
                            <x-permission-gate resource="reports" action="execute">
                                <button class="btn btn-sm btn-outline-info" 
                                        onclick="generateReport({{ $stabile->id }})"
                                        data-permission-action="reports"
                                        data-action="execute"
                                        data-bs-toggle="tooltip" 
                                        title="Report">
                                    <i class="fas fa-chart-bar"></i>
                                </button>
                            </x-permission-gate>
                        </div>
                    </td>
                </tr>
                @endforeach
            </tbody>
        </table>
    </div>
</div>

🚀 DYNAMIC PERMISSIONS

📡 Real-time Permission Updates

// /public/js/permissions/dynamic-permissions.js

class DynamicPermissions {
    constructor() {
        this.websocket = null;
        this.reconnectAttempts = 0;
        this.maxReconnectAttempts = 5;
        this.init();
    }
    
    init() {
        // Connect to WebSocket for real-time updates
        this.connectWebSocket();
        
        // Setup periodic permission refresh
        this.setupPeriodicRefresh();
        
        // Listen for permission change events
        this.setupEventListeners();
    }
    
    connectWebSocket() {
        if (!window.WebSocket) return;
        
        try {
            const wsUrl = `wss://${window.location.host}/ws/permissions`;
            this.websocket = new WebSocket(wsUrl);
            
            this.websocket.onopen = () => {
                console.log('Permission WebSocket connected');
                this.reconnectAttempts = 0;
            };
            
            this.websocket.onmessage = (event) => {
                const data = JSON.parse(event.data);
                this.handlePermissionUpdate(data);
            };
            
            this.websocket.onclose = () => {
                console.log('Permission WebSocket disconnected');
                this.attemptReconnect();
            };
            
            this.websocket.onerror = (error) => {
                console.error('Permission WebSocket error:', error);
            };
            
        } catch (error) {
            console.error('Failed to connect to permission WebSocket:', error);
        }
    }
    
    attemptReconnect() {
        if (this.reconnectAttempts < this.maxReconnectAttempts) {
            this.reconnectAttempts++;
            const delay = Math.pow(2, this.reconnectAttempts) * 1000;
            
            setTimeout(() => {
                console.log(`Attempting to reconnect permission WebSocket (${this.reconnectAttempts}/${this.maxReconnectAttempts})`);
                this.connectWebSocket();
            }, delay);
        }
    }
    
    setupPeriodicRefresh() {
        // Refresh permissions every 5 minutes
        setInterval(() => {
            this.refreshPermissions();
        }, 5 * 60 * 1000);
    }
    
    setupEventListeners() {
        // Listen for custom permission events
        document.addEventListener('netgescon:permission:changed', (e) => {
            this.handlePermissionUpdate(e.detail);
        });
        
        // Listen for role changes
        document.addEventListener('netgescon:role:changed', (e) => {
            this.handleRoleChange(e.detail);
        });
        
        // Listen for user context changes
        document.addEventListener('netgescon:user:context:changed', (e) => {
            this.refreshPermissions();
        });
    }
    
    handlePermissionUpdate(data) {
        const { userId, permissions, changes } = data;
        
        // Check if update is for current user
        const currentUserId = window.NetGesconApp?.user?.id;
        if (userId && userId !== currentUserId) {
            return;
        }
        
        // Update local permissions
        if (permissions) {
            window.PermissionManager.permissions = permissions;
            window.PermissionManager.applyUIPermissions();
        }
        
        // Handle specific changes
        if (changes) {
            changes.forEach(change => {
                this.handleSpecificChange(change);
            });
        }
        
        // Show notification if permissions were revoked
        const revokedPermissions = changes?.filter(c => c.type === 'revoked') || [];
        if (revokedPermissions.length > 0) {
            this.showPermissionRevokedNotification(revokedPermissions);
        }
        
        // Dispatch event
        document.dispatchEvent(new CustomEvent('netgescon:permissions:updated', {
            detail: { permissions, changes }
        }));
    }
    
    handleSpecificChange(change) {
        const { resource, action, type, oldLevel, newLevel } = change;
        
        switch (type) {
            case 'granted':
                this.enablePermissionElements(resource, action);
                break;
                
            case 'revoked':
                this.disablePermissionElements(resource, action);
                break;
                
            case 'modified':
                this.updatePermissionLevel(resource, oldLevel, newLevel);
                break;
        }
    }
    
    enablePermissionElements(resource, action) {
        const selector = `[data-permission="${resource}"][data-action="${action}"], [data-permission-action="${resource}"][data-action="${action}"]`;
        const elements = document.querySelectorAll(selector);
        
        elements.forEach(element => {
            element.style.display = '';
            element.disabled = false;
            element.classList.remove('disabled');
            element.removeAttribute('aria-hidden');
            element.removeAttribute('aria-disabled');
        });
    }
    
    disablePermissionElements(resource, action) {
        const selector = `[data-permission="${resource}"][data-action="${action}"], [data-permission-action="${resource}"][data-action="${action}"]`;
        const elements = document.querySelectorAll(selector);
        
        elements.forEach(element => {
            element.style.display = 'none';
            element.disabled = true;
            element.classList.add('disabled');
            element.setAttribute('aria-hidden', 'true');
            element.setAttribute('aria-disabled', 'true');
        });
    }
    
    updatePermissionLevel(resource, oldLevel, newLevel) {
        // Update all elements related to this resource
        const elements = document.querySelectorAll(`[data-permission="${resource}"]`);
        
        elements.forEach(element => {
            const action = element.dataset.action || 'read';
            const hasPermission = window.PermissionManager.can(resource, action);
            
            if (hasPermission) {
                this.enablePermissionElements(resource, action);
            } else {
                this.disablePermissionElements(resource, action);
            }
        });
    }
    
    handleRoleChange(data) {
        const { userId, oldRole, newRole } = data;
        
        // Check if change is for current user
        const currentUserId = window.NetGesconApp?.user?.id;
        if (userId && userId !== currentUserId) {
            return;
        }
        
        // Update user role
        if (window.PermissionManager.user) {
            window.PermissionManager.user.role = newRole;
        }
        
        // Refresh all permissions
        this.refreshPermissions();
        
        // Show role change notification
        this.showRoleChangeNotification(oldRole, newRole);
        
        // Potentially redirect if role was downgraded
        if (this.isRoleDowngrade(oldRole, newRole)) {
            this.handleRoleDowngrade(newRole);
        }
    }
    
    async refreshPermissions() {
        try {
            await window.PermissionManager.refreshPermissions();
            console.log('Permissions refreshed successfully');
        } catch (error) {
            console.error('Failed to refresh permissions:', error);
        }
    }
    
    showPermissionRevokedNotification(revokedPermissions) {
        const resources = revokedPermissions.map(p => p.resource).join(', ');
        
        if (window.NetGesconNotifications) {
            NetGesconNotifications.warning(
                `I tuoi permessi per ${resources} sono stati modificati. Alcune funzionalità potrebbero non essere più disponibili.`,
                { duration: 10000 }
            );
        }
    }
    
    showRoleChangeNotification(oldRole, newRole) {
        const roleNames = {
            'super_admin': 'Super Amministratore',
            'admin': 'Amministratore',
            'manager': 'Manager',
            'user': 'Utente',
            'guest': 'Ospite'
        };
        
        const message = `Il tuo ruolo è stato cambiato da ${roleNames[oldRole]} a ${roleNames[newRole]}.`;
        
        if (window.NetGesconNotifications) {
            NetGesconNotifications.info(message, { duration: 8000 });
        }
    }
    
    isRoleDowngrade(oldRole, newRole) {
        const roleHierarchy = {
            'super_admin': 5,
            'admin': 4,
            'manager': 3,
            'user': 2,
            'guest': 1
        };
        
        return (roleHierarchy[newRole] || 0) < (roleHierarchy[oldRole] || 0);
    }
    
    handleRoleDowngrade(newRole) {
        // If user was downgraded to guest, redirect to limited dashboard
        if (newRole === 'guest') {
            setTimeout(() => {
                window.location.href = '/dashboard/limited';
            }, 3000);
        }
        
        // If user lost admin privileges while on admin page, redirect
        if (!['super_admin', 'admin'].includes(newRole) && window.location.pathname.includes('/admin')) {
            setTimeout(() => {
                window.location.href = '/dashboard';
            }, 3000);
        }
    }
    
    // Manual permission check for critical actions
    async verifyPermission(resource, action) {
        try {
            const response = await fetch('/api/verify-permission', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content
                },
                body: JSON.stringify({ resource, action })
            });
            
            return response.ok;
        } catch (error) {
            console.error('Permission verification failed:', error);
            return false;
        }
    }
}

// Initialize dynamic permissions
document.addEventListener('DOMContentLoaded', () => {
    window.DynamicPermissions = new DynamicPermissions();
});

📚 FILES CORRELATI

🔐 Permission System Files

  • /app/Http/Middleware/CheckRole.php - Role middleware
  • /app/Http/Middleware/CheckPermission.php - Permission middleware
  • /app/Models/User.php - User model with permissions
  • /config/roles.php - Role and permission configuration

🧩 Blade Components

  • /resources/views/components/permission-gate.blade.php
  • /resources/views/components/role-badge.blade.php
  • /resources/views/components/navigation/sidebar.blade.php

JavaScript Files

  • /public/js/permissions/permission-manager.js
  • /public/js/permissions/dynamic-permissions.js

🎨 CSS Files

  • /public/css/components/permissions.css
  • /public/css/components/role-badges.css

🧪 TESTING CHECKLIST

Role-Based Testing

  • Super Admin - Accesso completo
  • Admin - Gestione company
  • Manager - Gestione team
  • User - Solo risorse proprie
  • Guest - Solo lettura limitata

Permission Testing

  • Create permissions funzionali
  • Read permissions funzionali
  • Update permissions funzionali
  • Delete permissions funzionali
  • Execute permissions funzionali

UI Testing

  • Navigation basata su permessi
  • Buttons e actions nascosti/disabilitati
  • Forms permission-aware
  • Real-time permission updates

🎯 NEXT STEPS

🔮 Permission System Evolution

  • Fine-Grained Permissions - Permessi a livello di campo
  • Temporary Permissions - Permessi con scadenza
  • Permission Inheritance - Ereditarietà basata su gerarchia
  • Audit Trail - Log completo delle modifiche permessi
  • Permission Templates - Template predefiniti per ruoli

📝 Documento mantenuto da: GitHub Copilot AI Assistant
🔗 Componente: Permission System NetGescon Interface v2.1.0