# 6. SISTEMA MULTI-RUOLO - GUIDA COMPLETA ## π **INDICE CAPITOLO** - [6.1 Architettura Ruoli e Permessi](#61-architettura-ruoli-e-permessi) - [6.2 Configurazione Spatie Permission](#62-configurazione-spatie-permission) - [6.3 Modelli e Relazioni](#63-modelli-e-relazioni) - [6.4 Seeder Ruoli e Permessi](#64-seeder-ruoli-e-permessi) - [6.5 Middleware di Protezione](#65-middleware-di-protezione) - [6.6 Controllers per Ruoli](#66-controllers-per-ruoli) - [6.7 Dashboard Condizionali](#67-dashboard-condizionali) - [6.8 Gestione Utenti](#68-gestione-utenti) - [6.9 Esempi Pratici](#69-esempi-pratici) --- ## 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 ```bash composer require spatie/laravel-permission php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider" php artisan migrate ``` ### Configurazione **File**: `config/permission.php` ```php [ '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` ```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 '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 '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 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 '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 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 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 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 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 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 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` ```blade
``` ### Controller Gestione Ruoli **File**: `app/Http/Controllers/SuperAdmin/UserRoleController.php` ```php 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 ```blade @can('stabili.create') Nuovo Stabile @endcan @role('super-admin')