netgescon-master/docs/06-SISTEMA-MULTI-RUOLO.md
Pikappa2 480e7eafbd 🎯 NETGESCON - Setup iniziale repository completo
📋 Commit iniziale con:
-  Documentazione unificata in docs/
-  Codice Laravel in netgescon-laravel/
-  Script automazione in scripts/
-  Configurazione sync rsync
-  Struttura organizzata e pulita

🔄 Versione: 2025.07.19-1644
🎯 Sistema pronto per Git distribuito
2025-07-19 16:44:47 +02:00

30 KiB

6. SISTEMA MULTI-RUOLO - GUIDA COMPLETA

📋 INDICE CAPITOLO


6.1 Architettura Ruoli e Permessi

Gerarchia Ruoli NetGescon

SuperAdmin
├── Accesso completo sistema
├── Gestione utenti e ruoli
├── Configurazione globale
├── Archivi base (comuni)
└── Monitoring e logs

Admin
├── Gestione completa stabili assegnati
├── CRUD completo tutti i dati
├── Contabilità e documenti
├── Comunicazioni condomini
└── Backup e reports

Amministratore
├── Gestione stabili assegnati
├── CRUD limitato (no delete critici)
├── Contabilità base
├── Documenti e comunicazioni
└── Reports stabili

Condomino
├── Visualizzazione dati personali
├── Documenti unità di proprietà
├── Comunicazioni con amministratore
├── Consultazione movimenti
└── Modifica profilo personale

Matrice Permessi Dettagliata

FUNZIONALITÀ           | SuperAdmin | Admin | Amministratore | Condomino
-----------------------|------------|-------|----------------|----------
Gestione Utenti        |     ✅     |   ❌   |       ❌       |    ❌
Comuni Italiani        |     ✅     |   ❌   |       ❌       |    ❌
Stabili (CRUD)         |     ✅     |   ✅   |       ✅       |    ❌
Unità Immobiliari      |     ✅     |   ✅   |       ✅       |    👁️
Soggetti (CRUD)        |     ✅     |   ✅   |       ✅       |    ❌
Documenti (Upload)     |     ✅     |   ✅   |       ✅       |    ❌
Documenti (Download)   |     ✅     |   ✅   |       ✅       |    ✅
Contabilità (CRUD)     |     ✅     |   ✅   |       ⚠️       |    👁️
Tickets (Gestione)     |     ✅     |   ✅   |       ✅       |    📝
Backup Sistema         |     ✅     |   ✅   |       ❌       |    ❌
Reports Avanzati       |     ✅     |   ✅   |       ⚠️       |    ❌

Legenda: = Completo, ⚠️ = Limitato, 👁️ = Solo visualizzazione, 📝 = Creazione/Risposta, = Negato


6.2 Configurazione Spatie Permission

Installazione Package

composer require spatie/laravel-permission
php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"
php artisan migrate

Configurazione

File: config/permission.php

<?php

return [
    'models' => [
        'permission' => Spatie\Permission\Models\Permission::class,
        'role' => Spatie\Permission\Models\Role::class,
    ],
    
    'table_names' => [
        'roles' => 'roles',
        'permissions' => 'permissions',
        'model_has_permissions' => 'model_has_permissions',
        'model_has_roles' => 'model_has_roles',
        'role_has_permissions' => 'role_has_permissions',
    ],
    
    'column_names' => [
        'role_pivot_key' => null,
        'permission_pivot_key' => null,
        'model_morph_key' => 'model_id',
        'team_foreign_key' => 'team_id',
    ],
    
    'register_permission_check_method' => true,
    'teams' => false,
    'use_passport_client_credentials' => false,
    'display_permission_in_exception' => false,
    'display_role_in_exception' => false,
    'enable_wildcard_permission' => false,
    'cache' => [
        'expiration_time' => \DateInterval::createFromDateString('24 hours'),
        'key' => 'spatie.permission.cache',
        'store' => 'default',
    ],
];

Registrazione Middleware

File: app/Http/Kernel.php

protected $routeMiddleware = [
    // ...existing middleware...
    'role' => \Spatie\Permission\Middlewares\RoleMiddleware::class,
    'permission' => \Spatie\Permission\Middlewares\PermissionMiddleware::class,
    'role_or_permission' => \Spatie\Permission\Middlewares\RoleOrPermissionMiddleware::class,
];

6.3 Modelli e Relazioni

Modello User Esteso

File: app/Models/User.php

<?php

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
use Spatie\Permission\Traits\HasRoles;

class User extends Authenticatable
{
    use HasApiTokens, HasRoles, Notifiable;

    protected $fillable = [
        'name',
        'email',
        'password',
        'is_active',
        'last_login_at',
        'profile_photo_path',
        'phone',
        'address',
    ];

    protected $hidden = [
        'password',
        'remember_token',
    ];

    protected $casts = [
        'email_verified_at' => 'datetime',
        'last_login_at' => 'datetime',
        'is_active' => 'boolean',
    ];

    // Relazioni
    public function stabiliAmministrati()
    {
        return $this->hasMany(Stabile::class, 'amministratore_id');
    }

    public function unitaImmobiliari()
    {
        return $this->belongsToMany(UnitaImmobiliare::class, 'diritti_reali')
                    ->withPivot('tipo_diritto', 'quota_proprieta', 'data_inizio', 'data_fine')
                    ->withTimestamps();
    }

    public function tickets()
    {
        return $this->hasMany(Ticket::class);
    }

    // Helper methods
    public function isSuperAdmin(): bool
    {
        return $this->hasRole('super-admin');
    }

    public function isAdmin(): bool
    {
        return $this->hasRole('admin');
    }

    public function isAmministratore(): bool
    {
        return $this->hasRole('amministratore');
    }

    public function isCondomino(): bool
    {
        return $this->hasRole('condomino');
    }

    public function canManageStabile(Stabile $stabile): bool
    {
        if ($this->isSuperAdmin()) {
            return true;
        }

        if ($this->isAdmin() || $this->isAmministratore()) {
            return $this->stabiliAmministrati()->where('id', $stabile->id)->exists();
        }

        return false;
    }

    public function canAccessUnita(UnitaImmobiliare $unita): bool
    {
        if ($this->isSuperAdmin()) {
            return true;
        }

        if ($this->isAdmin() || $this->isAmministratore()) {
            return $this->canManageStabile($unita->stabile);
        }

        if ($this->isCondomino()) {
            return $this->unitaImmobiliari()->where('id', $unita->id)->exists();
        }

        return false;
    }

    public function getDisplayRoleName(): string
    {
        $roleNames = [
            'super-admin' => 'Super Amministratore',
            'admin' => 'Amministratore',
            'amministratore' => 'Amministratore Condominio',
            'condomino' => 'Condomino',
        ];

        $role = $this->getRoleNames()->first();
        return $roleNames[$role] ?? 'Utente';
    }

    public function getRoleColor(): string
    {
        $roleColors = [
            'super-admin' => 'danger',
            'admin' => 'primary',
            'amministratore' => 'warning',
            'condomino' => 'success',
        ];

        $role = $this->getRoleNames()->first();
        return $roleColors[$role] ?? 'secondary';
    }
}

Modello Stabile con Controlli Accesso

File: app/Models/Stabile.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;

class Stabile extends Model
{
    protected $fillable = [
        'denominazione',
        'indirizzo',
        'comune_id',
        'cap',
        'codice_fiscale',
        'partita_iva',
        'amministratore_id',
        'is_active',
        'note',
    ];

    protected $casts = [
        'is_active' => 'boolean',
    ];

    // Relazioni
    public function amministratore(): BelongsTo
    {
        return $this->belongsTo(User::class, 'amministratore_id');
    }

    public function comune(): BelongsTo
    {
        return $this->belongsTo(ComuneItaliano::class, 'comune_id');
    }

    public function unitaImmobiliari(): HasMany
    {
        return $this->hasMany(UnitaImmobiliare::class);
    }

    public function documenti(): HasMany
    {
        return $this->hasMany(Documento::class);
    }

    public function tickets(): HasMany
    {
        return $this->hasMany(Ticket::class);
    }

    public function movimentiBancari(): HasMany
    {
        return $this->hasMany(MovimentoBancario::class);
    }

    // Scope per filtri di accesso
    public function scopeAccessibleBy($query, User $user)
    {
        if ($user->isSuperAdmin()) {
            return $query;
        }

        if ($user->isAdmin() || $user->isAmministratore()) {
            return $query->where('amministratore_id', $user->id);
        }

        if ($user->isCondomino()) {
            return $query->whereHas('unitaImmobiliari.soggetti', function($q) use ($user) {
                $q->where('users.id', $user->id);
            });
        }

        return $query->whereRaw('1 = 0'); // Nessun accesso
    }

    // Helper methods
    public function getIndirizzoCompleto(): string
    {
        $indirizzo = $this->indirizzo;
        
        if ($this->comune) {
            $indirizzo .= ', ' . $this->comune->denominazione;
        }
        
        if ($this->cap) {
            $indirizzo .= ' (' . $this->cap . ')';
        }
        
        return $indirizzo;
    }

    public function getTotalUnita(): int
    {
        return $this->unitaImmobiliari()->count();
    }

    public function getTotalMillesimi(): float
    {
        return $this->unitaImmobiliari()->sum('millesimi_proprieta');
    }
}

6.4 Seeder Ruoli e Permessi

Seeder Completo

File: database/seeders/RolesAndPermissionsSeeder.php

<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;
use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;

class RolesAndPermissionsSeeder extends Seeder
{
    public function run(): void
    {
        // Reset cached roles and permissions
        app()[\Spatie\Permission\PermissionRegistrar::class]->forgetCachedPermissions();

        // Definizione permessi organizzati per area
        $permissions = [
            // Gestione Utenti
            'users.view',
            'users.create',
            'users.edit',
            'users.delete',
            'users.assign-roles',
            'users.toggle-status',

            // Gestione Stabili
            'stabili.view',
            'stabili.create',
            'stabili.edit',
            'stabili.delete',
            'stabili.assign-admin',

            // Gestione Unità Immobiliari
            'unita.view',
            'unita.create',
            'unita.edit',
            'unita.delete',
            'unita.assign-soggetti',

            // Gestione Soggetti
            'soggetti.view',
            'soggetti.create',
            'soggetti.edit',
            'soggetti.delete',
            'soggetti.merge',

            // Gestione Documenti
            'documenti.view',
            'documenti.upload',
            'documenti.download',
            'documenti.delete',
            'documenti.share',

            // Gestione Contabilità
            'contabilita.view',
            'contabilita.create',
            'contabilita.edit',
            'contabilita.delete',
            'contabilita.reports',
            'contabilita.export',

            // Gestione Tickets
            'tickets.view',
            'tickets.create',
            'tickets.edit',
            'tickets.delete',
            'tickets.assign',
            'tickets.close',

            // Configurazione Sistema
            'system.config',
            'system.backup',
            'system.logs',
            'system.maintenance',

            // Archivi Base
            'comuni.view',
            'comuni.edit',
            'comuni.import',
        ];

        // Crea tutti i permessi
        foreach ($permissions as $permission) {
            Permission::create(['name' => $permission]);
        }

        // Creazione ruoli
        $superAdmin = Role::create(['name' => 'super-admin']);
        $admin = Role::create(['name' => 'admin']);
        $amministratore = Role::create(['name' => 'amministratore']);
        $condomino = Role::create(['name' => 'condomino']);

        // SuperAdmin: tutti i permessi
        $superAdmin->givePermissionTo(Permission::all());

        // Admin: tutti tranne configurazione sistema
        $admin->givePermissionTo([
            'stabili.view', 'stabili.create', 'stabili.edit', 'stabili.delete',
            'unita.view', 'unita.create', 'unita.edit', 'unita.delete', 'unita.assign-soggetti',
            'soggetti.view', 'soggetti.create', 'soggetti.edit', 'soggetti.delete', 'soggetti.merge',
            'documenti.view', 'documenti.upload', 'documenti.download', 'documenti.delete', 'documenti.share',
            'contabilita.view', 'contabilita.create', 'contabilita.edit', 'contabilita.delete', 'contabilita.reports', 'contabilita.export',
            'tickets.view', 'tickets.create', 'tickets.edit', 'tickets.delete', 'tickets.assign', 'tickets.close',
            'system.backup', 'system.logs',
        ]);

        // Amministratore: gestione limitata
        $amministratore->givePermissionTo([
            'stabili.view', 'stabili.edit',
            'unita.view', 'unita.create', 'unita.edit', 'unita.assign-soggetti',
            'soggetti.view', 'soggetti.create', 'soggetti.edit',
            'documenti.view', 'documenti.upload', 'documenti.download', 'documenti.share',
            'contabilita.view', 'contabilita.create', 'contabilita.edit', 'contabilita.reports',
            'tickets.view', 'tickets.create', 'tickets.edit', 'tickets.assign', 'tickets.close',
        ]);

        // Condomino: solo visualizzazione e interazione limitata
        $condomino->givePermissionTo([
            'unita.view',
            'documenti.view', 'documenti.download',
            'contabilita.view',
            'tickets.view', 'tickets.create',
        ]);
    }
}

Seeder Utenti Base

File: database/seeders/UserSeeder.php

<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Hash;
use App\Models\User;

class UserSeeder extends Seeder
{
    public function run(): void
    {
        // SuperAdmin
        $superAdmin = User::create([
            'name' => 'Super Amministratore',
            'email' => 'superadmin@netgescon.it',
            'password' => Hash::make('password'),
            'is_active' => true,
            'email_verified_at' => now(),
        ]);
        $superAdmin->assignRole('super-admin');

        // Admin
        $admin = User::create([
            'name' => 'Amministratore Principale',
            'email' => 'admin@netgescon.it',
            'password' => Hash::make('password'),
            'is_active' => true,
            'email_verified_at' => now(),
        ]);
        $admin->assignRole('admin');

        // Amministratore Condominio
        $amministratore = User::create([
            'name' => 'Mario Rossi',
            'email' => 'mario.rossi@netgescon.it',
            'password' => Hash::make('password'),
            'is_active' => true,
            'email_verified_at' => now(),
        ]);
        $amministratore->assignRole('amministratore');

        // Condomino
        $condomino = User::create([
            'name' => 'Luigi Bianchi',
            'email' => 'luigi.bianchi@netgescon.it',
            'password' => Hash::make('password'),
            'is_active' => true,
            'email_verified_at' => now(),
        ]);
        $condomino->assignRole('condomino');
    }
}

6.5 Middleware di Protezione

Middleware Personalizzato

File: app/Http/Middleware/RoleAccessMiddleware.php

<?php

namespace App\Http\Middleware;

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

class RoleAccessMiddleware
{
    public function handle(Request $request, Closure $next, ...$roles)
    {
        if (!Auth::check()) {
            return redirect()->route('login');
        }

        $user = Auth::user();

        // Verifica se utente è attivo
        if (!$user->is_active) {
            Auth::logout();
            return redirect()->route('login')
                ->with('error', 'Account disattivato. Contatta l\'amministratore.');
        }

        // Verifica ruoli
        if (!$user->hasAnyRole($roles)) {
            abort(403, 'Accesso non autorizzato per questo ruolo.');
        }

        // Aggiorna ultimo login
        $user->update(['last_login_at' => now()]);

        return $next($request);
    }
}

Middleware Permessi Specifici

File: app/Http/Middleware/PermissionMiddleware.php

<?php

namespace App\Http\Middleware;

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

class PermissionMiddleware
{
    public function handle(Request $request, Closure $next, $permission)
    {
        if (!Auth::check()) {
            return redirect()->route('login');
        }

        $user = Auth::user();

        if (!$user->can($permission)) {
            if ($request->ajax()) {
                return response()->json([
                    'error' => 'Permesso negato',
                    'message' => "Non hai i permessi per: {$permission}"
                ], 403);
            }

            abort(403, "Permesso negato: {$permission}");
        }

        return $next($request);
    }
}

6.6 Controllers per Ruoli

Controller SuperAdmin

File: app/Http/Controllers/SuperAdmin/SuperAdminDashboardController.php

<?php

namespace App\Http\Controllers\SuperAdmin;

use App\Http\Controllers\Controller;
use App\Models\User;
use App\Models\Stabile;
use App\Models\ComuneItaliano;
use App\Models\Ticket;

class SuperAdminDashboardController extends Controller
{
    public function __construct()
    {
        $this->middleware(['auth', 'role:super-admin']);
    }

    public function index()
    {
        $stats = [
            'users_total' => User::count(),
            'users_active' => User::where('is_active', true)->count(),
            'stabili_total' => Stabile::count(),
            'stabili_active' => Stabile::where('is_active', true)->count(),
            'comuni_loaded' => ComuneItaliano::count(),
            'tickets_open' => Ticket::where('stato', 'aperto')->count(),
            'users_by_role' => User::join('model_has_roles', 'users.id', '=', 'model_has_roles.model_id')
                ->join('roles', 'model_has_roles.role_id', '=', 'roles.id')
                ->selectRaw('roles.name, COUNT(*) as count')
                ->groupBy('roles.name')
                ->pluck('count', 'name')
                ->toArray(),
        ];

        return view('superadmin.dashboard', [
            'pageTitle' => 'Dashboard Super Amministratore',
            'stats' => $stats,
        ]);
    }
}

Controller Admin

File: app/Http/Controllers/Admin/AdminDashboardController.php

<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Models\Stabile;
use App\Models\UnitaImmobiliare;
use App\Models\Documento;
use App\Models\Ticket;
use App\Models\MovimentoBancario;
use Illuminate\Support\Facades\Auth;

class AdminDashboardController extends Controller
{
    public function __construct()
    {
        $this->middleware(['auth', 'role:admin|amministratore']);
    }

    public function index()
    {
        $user = Auth::user();
        $stabiliIds = $user->stabiliAmministrati()->pluck('id');

        $stats = [
            'stabili_count' => $stabiliIds->count(),
            'stabili_attivi' => $user->stabiliAmministrati()->where('is_active', true)->count(),
            'unita_total' => UnitaImmobiliare::whereIn('stabile_id', $stabiliIds)->count(),
            'documenti_count' => Documento::whereIn('stabile_id', $stabiliIds)->count(),
            'documenti_mese' => Documento::whereIn('stabile_id', $stabiliIds)
                ->whereMonth('created_at', now()->month)
                ->count(),
            'tickets_aperti' => Ticket::whereIn('stabile_id', $stabiliIds)
                ->where('stato', 'aperto')->count(),
            'tickets_totali' => Ticket::whereIn('stabile_id', $stabiliIds)->count(),
            'saldo_totale' => MovimentoBancario::whereIn('stabile_id', $stabiliIds)
                ->sum('importo'),
            'condomini_count' => UnitaImmobiliare::whereIn('stabile_id', $stabiliIds)
                ->has('soggetti')->count(),
            'proprietari_count' => UnitaImmobiliare::whereIn('stabile_id', $stabiliIds)
                ->whereHas('soggetti', function($q) {
                    $q->where('tipo_diritto', 'proprietario');
                })->count(),
            'movimenti_count' => MovimentoBancario::whereIn('stabile_id', $stabiliIds)->count(),
        ];

        return view('admin.dashboard', [
            'pageTitle' => 'Dashboard Amministrazione',
            'stats' => $stats,
            'user' => $user,
        ]);
    }
}

Controller Condomino

File: app/Http/Controllers/Condomino/CondominoDashboardController.php

<?php

namespace App\Http\Controllers\Condomino;

use App\Http\Controllers\Controller;
use App\Models\UnitaImmobiliare;
use App\Models\Documento;
use App\Models\Ticket;
use App\Models\MovimentoBancario;
use Illuminate\Support\Facades\Auth;

class CondominoDashboardController extends Controller
{
    public function __construct()
    {
        $this->middleware(['auth', 'role:condomino']);
    }

    public function index()
    {
        $user = Auth::user();
        $unitaIds = $user->unitaImmobiliari()->pluck('unita_immobiliari.id');
        $stabiliIds = $user->unitaImmobiliari()->pluck('stabile_id');

        $stats = [
            'unita_count' => $unitaIds->count(),
            'documenti_count' => Documento::whereIn('stabile_id', $stabiliIds)->count(),
            'tickets_aperti' => Ticket::where('user_id', $user->id)
                ->where('stato', 'aperto')->count(),
            'tickets_totali' => Ticket::where('user_id', $user->id)->count(),
            'movimenti_mese' => MovimentoBancario::whereIn('stabile_id', $stabiliIds)
                ->whereMonth('data_movimento', now()->month)
                ->count(),
        ];

        $unita = $user->unitaImmobiliari()
            ->with(['stabile', 'stabile.comune'])
            ->get();

        return view('condomino.dashboard', [
            'pageTitle' => 'Area Condomino',
            'stats' => $stats,
            'unita' => $unita,
            'user' => $user,
        ]);
    }
}

6.7 Dashboard Condizionali

Router Principale

File: app/Http/Controllers/DashboardController.php

<?php

namespace App\Http\Controllers;

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

class DashboardController extends Controller
{
    public function __construct()
    {
        $this->middleware('auth');
    }

    public function index()
    {
        $user = Auth::user();

        // Redirect in base al ruolo
        if ($user->hasRole('super-admin')) {
            return redirect()->route('superadmin.dashboard');
        }

        if ($user->hasAnyRole(['admin', 'amministratore'])) {
            return redirect()->route('admin.dashboard');
        }

        if ($user->hasRole('condomino')) {
            return redirect()->route('condomino.dashboard');
        }

        // Fallback per utenti senza ruolo
        return view('dashboard.no-role', [
            'pageTitle' => 'Accesso Negato',
            'message' => 'Nessun ruolo assegnato. Contatta l\'amministratore.',
        ]);
    }
}

6.8 Gestione Utenti

Form Assegnazione Ruoli

File: resources/views/superadmin/users/edit-roles.blade.php

<form action="{{ route('superadmin.users.update-roles', $user) }}" method="POST">
    @csrf
    @method('PUT')
    
    <div class="card">
        <div class="card-header">
            <h5>Gestione Ruoli: {{ $user->name }}</h5>
        </div>
        <div class="card-body">
            <div class="row">
                @foreach($roles as $role)
                    <div class="col-md-6 mb-3">
                        <div class="form-check">
                            <input class="form-check-input" type="checkbox" 
                                   name="roles[]" value="{{ $role->name }}" 
                                   id="role_{{ $role->id }}"
                                   {{ $user->hasRole($role->name) ? 'checked' : '' }}>
                            <label class="form-check-label" for="role_{{ $role->id }}">
                                <strong>{{ $role->name }}</strong>
                                <small class="text-muted d-block">
                                    {{ $roleDescriptions[$role->name] ?? '' }}
                                </small>
                            </label>
                        </div>
                    </div>
                @endforeach
            </div>
        </div>
        <div class="card-footer">
            <button type="submit" class="btn btn-primary">
                <i class="fas fa-save me-2"></i>Salva Ruoli
            </button>
            <a href="{{ route('superadmin.users.index') }}" class="btn btn-secondary">
                <i class="fas fa-times me-2"></i>Annulla
            </a>
        </div>
    </div>
</form>

Controller Gestione Ruoli

File: app/Http/Controllers/SuperAdmin/UserRoleController.php

<?php

namespace App\Http\Controllers\SuperAdmin;

use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Http\Request;
use Spatie\Permission\Models\Role;

class UserRoleController extends Controller
{
    public function __construct()
    {
        $this->middleware(['auth', 'role:super-admin']);
    }

    public function editRoles(User $user)
    {
        $roles = Role::all();
        $roleDescriptions = [
            'super-admin' => 'Accesso completo al sistema',
            'admin' => 'Gestione completa stabili assegnati',
            'amministratore' => 'Gestione limitata stabili assegnati',
            'condomino' => 'Visualizzazione dati personali',
        ];

        return view('superadmin.users.edit-roles', compact('user', 'roles', 'roleDescriptions'));
    }

    public function updateRoles(Request $request, User $user)
    {
        $request->validate([
            'roles' => 'array',
            'roles.*' => 'exists:roles,name',
        ]);

        $user->syncRoles($request->get('roles', []));

        return redirect()->route('superadmin.users.index')
            ->with('success', "Ruoli aggiornati per {$user->name}");
    }
}

6.9 Esempi Pratici

Verifica Permessi in Blade

@can('stabili.create')
    <a href="{{ route('admin.stabili.create') }}" class="btn btn-primary">
        <i class="fas fa-plus me-2"></i>Nuovo Stabile
    </a>
@endcan

@role('super-admin')
    <div class="alert alert-info">
        <i class="fas fa-crown me-2"></i>
        Modalità Super Amministratore attiva
    </div>
@endrole

@hasrole('admin|amministratore')
    <div class="card">
        <div class="card-header">Pannello Gestione</div>
        <!-- Contenuto per admin -->
    </div>
@endhasrole

Controllo Accesso in Controller

public function show(Stabile $stabile)
{
    $user = Auth::user();
    
    // Verifica accesso al singolo stabile
    if (!$user->canManageStabile($stabile)) {
        abort(403, 'Non puoi accedere a questo stabile');
    }
    
    return view('admin.stabili.show', compact('stabile'));
}

Policy per Modelli

File: app/Policies/StabilePolicy.php

<?php

namespace App\Policies;

use App\Models\Stabile;
use App\Models\User;

class StabilePolicy
{
    public function viewAny(User $user): bool
    {
        return $user->can('stabili.view');
    }

    public function view(User $user, Stabile $stabile): bool
    {
        return $user->can('stabili.view') && $user->canManageStabile($stabile);
    }

    public function create(User $user): bool
    {
        return $user->can('stabili.create');
    }

    public function update(User $user, Stabile $stabile): bool
    {
        return $user->can('stabili.edit') && $user->canManageStabile($stabile);
    }

    public function delete(User $user, Stabile $stabile): bool
    {
        return $user->can('stabili.delete') && $user->canManageStabile($stabile);
    }
}

📝 COMPLETATO: Capitolo 6 - Sistema Multi-Ruolo

Questo capitolo fornisce una guida completa per:

  • Configurare architettura ruoli e permessi
  • Implementare Spatie Permission
  • Creare modelli con controlli accesso
  • Configurare seeder ruoli e permessi
  • Implementare middleware protezione
  • Creare controllers per ogni ruolo
  • Gestire dashboard condizionali
  • Amministrare utenti e ruoli
  • Esempi pratici implementazione

🔄 Prossimo capitolo: Database e Strutture Avanzate