# πŸ‘€ TABELLA USERS ## Autenticazione e collegamento agli amministratori --- ## 🎯 **CARATTERISTICHE TABELLA** ### **Scopo**: Tabella per autenticazione utenti collegati agli amministratori tramite `amministratore_id` ### **Principio**: Ogni user appartiene a un amministratore specifico - **nessun dato cross-administrator** --- ## πŸ—„οΈ **STRUTTURA TABELLA** ```sql CREATE TABLE users ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, amministratore_id BIGINT UNSIGNED NOT NULL, -- FK verso amministratori -- Dati autenticazione username VARCHAR(100) NOT NULL, -- Username univoco per amministratore email VARCHAR(255) NOT NULL, email_verified_at TIMESTAMP NULL, password VARCHAR(255) NOT NULL, remember_token VARCHAR(100) NULL, -- Dati personali nome VARCHAR(255) NOT NULL, cognome VARCHAR(255) NOT NULL, telefono VARCHAR(20) NULL, -- Ruoli e permessi (semplificato con enum) ruolo ENUM('admin', 'collaboratore', 'visualizzatore') DEFAULT 'collaboratore', -- Configurazioni utente is_active BOOLEAN DEFAULT TRUE, ultimo_accesso TIMESTAMP NULL, preferenze JSON NULL, -- Preferenze UI/UX -- Reset password password_reset_token VARCHAR(100) NULL, password_reset_expires_at TIMESTAMP NULL, -- Audit created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, -- Foreign Key FOREIGN KEY (amministratore_id) REFERENCES amministratori(id) ON DELETE CASCADE, -- Indici UNIQUE KEY unique_username_per_admin (amministratore_id, username), UNIQUE KEY unique_email_per_admin (amministratore_id, email), INDEX idx_amministratore_id (amministratore_id), INDEX idx_ruolo (ruolo), INDEX idx_active (is_active), INDEX idx_ultimo_accesso (ultimo_accesso) ); ``` --- ## πŸ” **AUTHENTICATION PATTERNS** ### **Login Multi-Tenant**: L'accesso richiede: 1. **Username/Email** 2. **Password** 3. **Codice Amministratore** (8 cifre) ```php // Login form deve avere: // - username/email // - password // - codice_amministratore (8 cifre) public function authenticate(LoginRequest $request) { $credentials = $request->only(['username', 'password']); $codiceAmministratore = $request->input('codice_amministratore'); // Trova amministratore dal codice $amministratore = Amministratore::where('codice_univoco', $codiceAmministratore) ->where('is_active', true) ->first(); if (!$amministratore) { throw ValidationException::withMessages([ 'codice_amministratore' => 'Codice amministratore non valido.' ]); } // Trova user nell'ambito dell'amministratore $user = User::where('amministratore_id', $amministratore->id) ->where(function($query) use ($credentials) { $query->where('username', $credentials['username']) ->orWhere('email', $credentials['username']); }) ->where('is_active', true) ->first(); if (!$user || !Hash::check($credentials['password'], $user->password)) { throw ValidationException::withMessages([ 'username' => 'Credenziali non valide.' ]); } Auth::login($user); $user->updateUltimoAccesso(); return redirect()->intended('/dashboard'); } ``` --- ## πŸ—οΈ **MIGRATION LARAVEL** ### **File**: `database/migrations/2024_08_05_000002_create_users_table.php` ```php id(); $table->foreignId('amministratore_id') ->constrained('amministratori') ->onDelete('cascade'); // Dati autenticazione $table->string('username', 100); $table->string('email'); $table->timestamp('email_verified_at')->nullable(); $table->string('password'); $table->rememberToken(); // Dati personali $table->string('nome'); $table->string('cognome'); $table->string('telefono', 20)->nullable(); // Ruoli e permessi $table->enum('ruolo', ['admin', 'collaboratore', 'visualizzatore']) ->default('collaboratore'); // Configurazioni utente $table->boolean('is_active')->default(true); $table->timestamp('ultimo_accesso')->nullable(); $table->json('preferenze')->nullable(); // Reset password $table->string('password_reset_token', 100)->nullable(); $table->timestamp('password_reset_expires_at')->nullable(); $table->timestamps(); // Indici e vincoli $table->unique(['amministratore_id', 'username'], 'unique_username_per_admin'); $table->unique(['amministratore_id', 'email'], 'unique_email_per_admin'); $table->index(['amministratore_id']); $table->index(['ruolo']); $table->index(['is_active']); $table->index(['ultimo_accesso']); }); } public function down(): void { Schema::dropIfExists('users'); } }; ``` --- ## πŸ“Š **MODEL ELOQUENT** ### **File**: `app/Models/User.php` ```php 'datetime', 'ultimo_accesso' => 'datetime', 'password_reset_expires_at' => 'datetime', 'is_active' => 'boolean', 'preferenze' => 'array', ]; // Relazioni public function amministratore(): BelongsTo { return $this->belongsTo(Amministratore::class); } // Scope public function scopeActive(Builder $query): Builder { return $query->where('is_active', true); } public function scopeByRuolo(Builder $query, string $ruolo): Builder { return $query->where('ruolo', $ruolo); } public function scopeForAmministratore(Builder $query, int $amministratoreId): Builder { return $query->where('amministratore_id', $amministratoreId); } // Accessors public function getNomeCompletoAttribute(): string { return "{$this->nome} {$this->cognome}"; } public function getIsAdminAttribute(): bool { return $this->ruolo === 'admin'; } public function getCanEditAttribute(): bool { return in_array($this->ruolo, ['admin', 'collaboratore']); } public function getCanViewOnlyAttribute(): bool { return $this->ruolo === 'visualizzatore'; } // Helper Methods public function hasPermission(string $permission): bool { $permissions = [ 'admin' => ['view', 'create', 'edit', 'delete', 'manage_users'], 'collaboratore' => ['view', 'create', 'edit'], 'visualizzatore' => ['view'] ]; return in_array($permission, $permissions[$this->ruolo] ?? []); } public function updateUltimoAccesso(): void { $this->update(['ultimo_accesso' => now()]); } public function generatePasswordResetToken(): string { $token = bin2hex(random_bytes(32)); $this->update([ 'password_reset_token' => $token, 'password_reset_expires_at' => now()->addHours(24) ]); return $token; } public function isPasswordResetTokenValid(string $token): bool { return $this->password_reset_token === $token && $this->password_reset_expires_at && $this->password_reset_expires_at->isFuture(); } public function clearPasswordResetToken(): void { $this->update([ 'password_reset_token' => null, 'password_reset_expires_at' => null ]); } } ``` --- ## 🌱 **SEEDER DATI DEMO** ### **File**: `database/seeders/UsersSeeder.php` ```php $admin->id, 'username' => 'admin', 'email' => $admin->email, 'password' => Hash::make('password123'), 'nome' => $admin->nome, 'cognome' => $admin->cognome, 'telefono' => $admin->telefono, 'ruolo' => 'admin', 'is_active' => true, 'email_verified_at' => now(), ]); // User collaboratore User::create([ 'amministratore_id' => $admin->id, 'username' => 'collaboratore1', 'email' => "collaboratore.{$admin->codice_univoco}@netgescon.it", 'password' => Hash::make('password123'), 'nome' => 'Marco', 'cognome' => 'Collaboratore', 'telefono' => '3331112233', 'ruolo' => 'collaboratore', 'is_active' => true, 'email_verified_at' => now(), ]); // User visualizzatore User::create([ 'amministratore_id' => $admin->id, 'username' => 'viewer1', 'email' => "viewer.{$admin->codice_univoco}@netgescon.it", 'password' => Hash::make('password123'), 'nome' => 'Luca', 'cognome' => 'Viewer', 'ruolo' => 'visualizzatore', 'is_active' => true, 'email_verified_at' => now(), ]); } $totalUsers = User::count(); $this->command->info("βœ… Creati {$totalUsers} users per gli amministratori"); } } ``` --- ## πŸŽ›οΈ **MIDDLEWARE TENANT** ### **File**: `app/Http/Middleware/TenantScope.php` ```php route('login'); } $user = Auth::user(); // Verifica che l'amministratore sia attivo if (!$user->amministratore || !$user->amministratore->is_active) { Auth::logout(); return redirect()->route('login') ->withErrors(['account' => 'Account amministratore non attivo.']); } // Imposta il tenant globale per le query app()->singleton('current_amministratore', function () use ($user) { return $user->amministratore; }); return $next($request); } } ``` --- ## πŸ“‹ **FORM LOGIN** ### **File**: `resources/views/auth/login.blade.php` ```blade @extends('layouts.guest') @section('content')

Accesso NetGescon

@csrf
@error('codice_amministratore')

{{ $message }}

@enderror
@error('username')

{{ $message }}

@enderror
@error('password')

{{ $message }}

@enderror
@endsection ``` --- ## 🎯 **UTILIZZO PATTERN** ### **Accesso ai Dati Tenant**: ```php // Nel controller - sempre partire dall'utente loggato $amministratore = auth()->user()->amministratore; // Query scope automatic $stabili = Stabile::where('amministratore_id', $amministratore->id)->get(); // Helper globale $amministratore = app('current_amministratore'); ``` ### **Permessi per Ruolo**: ```php // Nel controller if (!auth()->user()->hasPermission('edit')) { abort(403, 'Non hai il permesso per modificare questi dati.'); } // Nel blade @can('edit') Modifica @endcan ``` --- **🎯 TABELLA USERS DEFINITA** **Con autenticazione multi-tenant, ruoli e collegamento amministratori** **πŸ“– Prossimo: Tabelle collegate (stabili, gestioni, unitΓ )**