Преглед на файлове

flusso invito utente e reset pwd

FabioFratini преди 7 месеца
родител
ревизия
44e90a9601

+ 125 - 0
app/Console/Commands/CleanupPasswordResets.php

@@ -0,0 +1,125 @@
+<?php
+
+namespace App\Console\Commands;
+
+
+use Illuminate\Console\Command;
+use Illuminate\Support\Facades\DB;
+use Carbon\Carbon;
+use Illuminate\Support\Facades\Hash;
+use Illuminate\Support\Facades\Log;
+
+class CleanupPasswordResets extends Command
+{
+    protected $signature = 'password:cleanup';
+    protected $description = 'Clean up expired password reset tokens';
+
+    public function handle()
+    {
+        try {
+            $masterConfig = [
+                'driver' => 'mysql',
+                'host' => env('DB_HOST', '127.0.0.1'),
+                'port' => env('DB_PORT', '3306'),
+                'database' => env('DB_DATABASE'),
+                'username' => env('DB_USERNAME'),
+                'password' => env('DB_PASSWORD'),
+                'charset' => 'utf8mb4',
+                'collation' => 'utf8mb4_unicode_ci',
+                'prefix' => '',
+                'strict' => true,
+                'engine' => null,
+            ];
+
+            config(['database.connections.master_cleanup' => $masterConfig]);
+
+            // Delete tokens older than 24 hours
+            $deletedCount = DB::connection('master_cleanup')
+                ->table('password_resets')
+                ->where('created_at', '<', Carbon::now()->subHours(24))
+                ->delete();
+
+            DB::purge('master_cleanup');
+
+            $this->info("Cleaned up {$deletedCount} expired password reset tokens.");
+
+        } catch (\Exception $e) {
+            $this->error("Failed to cleanup password reset tokens: " . $e->getMessage());
+        }
+    }
+}
+
+// ENHANCED AUTHENTICATION FUNCTION
+// Update your existing authentication function to handle password resets
+
+function authenticateUser($email, $password)
+{
+    try {
+        // Step 1: Get user from master database (current default connection)
+        $masterUser = DB::table('users')->where('email', $email)->first();
+
+        if (!$masterUser) {
+            Log::info('User not found in master database', ['email' => $email]);
+            return false;
+        }
+
+        // Step 2: Check password in master database
+        if (!Hash::check($password, $masterUser->password)) {
+            Log::info('Password incorrect in master database', ['email' => $email]);
+            return false;
+        }
+
+        // Step 3: Set up tenant connection
+        $tenantConfig = [
+            'driver' => 'mysql',
+            'host' => env('DB_HOST', '127.0.0.1'),
+            'port' => env('DB_PORT', '3306'),
+            'database' => $masterUser->tenant_database,
+            'username' => $masterUser->tenant_username,
+            'password' => $masterUser->tenant_password,
+            'charset' => 'utf8mb4',
+            'collation' => 'utf8mb4_unicode_ci',
+            'prefix' => '',
+            'strict' => true,
+            'engine' => null,
+        ];
+
+        // Add tenant connection to config
+        config(['database.connections.tenant_check' => $tenantConfig]);
+
+        // Step 4: Check user in tenant database
+        $tenantUser = DB::connection('tenant_check')->table('users')->where('email', $email)->first();
+
+        if (!$tenantUser) {
+            Log::info('User not found in tenant database', [
+                'email' => $email,
+                'tenant_db' => $masterUser->tenant_database
+            ]);
+            return false;
+        }
+
+        // Step 5: Check password in tenant database
+        if (!Hash::check($password, $tenantUser->password)) {
+            Log::info('Password incorrect in tenant database', [
+                'email' => $email,
+                'tenant_db' => $masterUser->tenant_database
+            ]);
+            return false;
+        }
+
+        Log::info('Authentication successful in both databases', [
+            'email' => $email,
+            'tenant_db' => $masterUser->tenant_database
+        ]);
+
+        return $masterUser;
+
+    } catch (\Exception $e) {
+        Log::error('Authentication error', [
+            'email' => $email,
+            'error' => $e->getMessage(),
+            'trace' => $e->getTraceAsString()
+        ]);
+        return false;
+    }
+}

+ 1 - 1
app/Console/Kernel.php

@@ -15,7 +15,7 @@ class Kernel extends ConsoleKernel
      */
     protected function schedule(Schedule $schedule)
     {
-        // $schedule->command('inspire')->hourly();
+        $schedule->command('password:cleanup')->dailyAt('02:00');
     }
 
     /**

+ 443 - 0
app/Http/Controllers/PasswordResetController.php

@@ -0,0 +1,443 @@
+<?php
+
+namespace App\Http\Controllers;
+
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Hash;
+use Illuminate\Support\Facades\Mail;
+use Illuminate\Support\Facades\Log;
+use Illuminate\Support\Str;
+use Carbon\Carbon;
+
+class PasswordResetController extends Controller
+{
+    /**
+     * Show password reset request form
+     */
+    public function showResetRequestForm()
+    {
+        return view('auth.password-reset-request');
+    }
+
+    /**
+     * Send password reset email
+     */
+    public function sendResetEmail(Request $request)
+    {
+        $request->validate([
+            'email' => 'required|email'
+        ], [
+            'email.required' => 'La mail è obbligatoria',
+            'email.email' => 'Inserisci un indirizzo email valido'
+        ]);
+
+        $this->logSecurityEvent('reset_requested', $request->email);
+
+        try {
+            $user = $this->findUserInMasterDatabase($request->email);
+
+            if (!$user) {
+                $this->logSecurityEvent('reset_requested_invalid_email', $request->email);
+                return back()->with('success', 'Se l\'email esiste nel sistema, riceverai le istruzioni per il reset della password.');
+            }
+
+            if ($this->hasRecentResetRequest($request->email)) {
+                $this->logSecurityEvent('reset_requested_too_soon', $request->email);
+                return back()->with('error', 'È già stata inviata una richiesta di reset per questa email. Controlla la tua casella di posta o riprova più tardi.');
+            }
+
+            $token = Str::random(64);
+
+            $this->storeResetToken($request->email, $token);
+
+            $this->sendPasswordResetEmail($request->email, $token, $user);
+
+            $this->logSecurityEvent('reset_email_sent', $request->email);
+
+            return back()->with('success', 'Se l\'email esiste nel sistema, riceverai le istruzioni per il reset della password.');
+        } catch (\Exception $e) {
+            $this->logSecurityEvent('reset_request_failed', $request->email, ['error' => $e->getMessage()]);
+            return back()->with('error', 'Errore durante l\'invio dell\'email. Riprova più tardi.');
+        }
+    }
+
+    /**
+     * Show password reset form
+     */
+    public function showResetForm($token)
+    {
+        return view('auth.password-reset-form', ['token' => $token]);
+    }
+
+    /**
+     * Reset password
+     */
+    public function resetPassword(Request $request)
+    {
+        $request->validate([
+            'token' => 'required',
+            'email' => 'required|email',
+            'password' => 'required|min:6|confirmed'
+        ], [
+            'email.required' => 'La mail è obbligatoria',
+            'email.email' => 'Inserisci un indirizzo email valido',
+            'password.required' => 'La password è obbligatoria',
+            'password.min' => 'La password deve essere di almeno 6 caratteri',
+            'password.confirmed' => 'Le password non coincidono'
+        ]);
+
+        try {
+            $resetRecord = $this->verifyResetToken($request->email, $request->token);
+
+            if (!$resetRecord) {
+                return back()->with('error', 'Token non valido o scaduto.');
+            }
+
+            $user = $this->findUserInMasterDatabase($request->email);
+
+            if (!$user) {
+                return back()->with('error', 'Utente non trovato.');
+            }
+
+            $this->updatePasswordInBothDatabases($request->email, $request->password, $user);
+
+            $this->deleteResetToken($request->email);
+
+            Log::info('Password reset completed', [
+                'email' => $request->email
+            ]);
+
+            return redirect('/')->with('success', 'Password aggiornata con successo. Puoi ora effettuare il login.');
+        } catch (\Exception $e) {
+            Log::error('Password reset failed', [
+                'email' => $request->email,
+                'error' => $e->getMessage()
+            ]);
+
+            return back()->with('error', 'Errore durante il reset della password. Riprova più tardi.');
+        }
+    }
+
+    /**
+     * Find user in master database
+     */
+    private function findUserInMasterDatabase($email)
+    {
+        try {
+            $masterConfig = [
+                'driver' => 'mysql',
+                'host' => env('DB_HOST', '127.0.0.1'),
+                'port' => env('DB_PORT', '3306'),
+                'database' => env('DB_DATABASE'),
+                'username' => env('DB_USERNAME'),
+                'password' => env('DB_PASSWORD'),
+                'charset' => 'utf8mb4',
+                'collation' => 'utf8mb4_unicode_ci',
+                'prefix' => '',
+                'strict' => true,
+                'engine' => null,
+            ];
+
+            config(['database.connections.master_reset' => $masterConfig]);
+
+            $user = DB::connection('master_reset')
+                ->table('users')
+                ->where('email', $email)
+                ->first();
+
+            DB::purge('master_reset');
+
+            return $user;
+        } catch (\Exception $e) {
+            Log::error('Failed to find user in master database', [
+                'email' => $email,
+                'error' => $e->getMessage()
+            ]);
+            return null;
+        }
+    }
+
+    /**
+     * Store reset token in master database
+     */
+    private function storeResetToken($email, $token)
+    {
+        try {
+            $masterConfig = [
+                'driver' => 'mysql',
+                'host' => env('DB_HOST', '127.0.0.1'),
+                'port' => env('DB_PORT', '3306'),
+                'database' => env('DB_DATABASE'),
+                'username' => env('DB_USERNAME'),
+                'password' => env('DB_PASSWORD'),
+                'charset' => 'utf8mb4',
+                'collation' => 'utf8mb4_unicode_ci',
+                'prefix' => '',
+                'strict' => true,
+                'engine' => null,
+            ];
+
+            config(['database.connections.master_reset_token' => $masterConfig]);
+
+            DB::connection('master_reset_token')
+                ->table('password_resets')
+                ->where('email', $email)
+                ->delete();
+
+            DB::connection('master_reset_token')
+                ->table('password_resets')
+                ->insert([
+                    'email' => $email,
+                    'token' => Hash::make($token),
+                    'created_at' => Carbon::now()
+                ]);
+
+            DB::purge('master_reset_token');
+        } catch (\Exception $e) {
+            Log::error('Failed to store reset token', [
+                'email' => $email,
+                'error' => $e->getMessage()
+            ]);
+            throw $e;
+        }
+    }
+
+    /**
+     * Verify reset token
+     */
+    private function verifyResetToken($email, $token)
+    {
+        try {
+            $masterConfig = [
+                'driver' => 'mysql',
+                'host' => env('DB_HOST', '127.0.0.1'),
+                'port' => env('DB_PORT', '3306'),
+                'database' => env('DB_DATABASE'),
+                'username' => env('DB_USERNAME'),
+                'password' => env('DB_PASSWORD'),
+                'charset' => 'utf8mb4',
+                'collation' => 'utf8mb4_unicode_ci',
+                'prefix' => '',
+                'strict' => true,
+                'engine' => null,
+            ];
+
+            config(['database.connections.master_verify' => $masterConfig]);
+
+            $resetRecord = DB::connection('master_verify')
+                ->table('password_resets')
+                ->where('email', $email)
+                ->first();
+
+            DB::purge('master_verify');
+
+            if (!$resetRecord) {
+                return null;
+            }
+
+            $isValidToken = Hash::check($token, $resetRecord->token);
+            $isNotExpired = Carbon::parse($resetRecord->created_at)->addHours(24)->isFuture();
+
+            if ($isValidToken && $isNotExpired) {
+                return $resetRecord;
+            }
+
+            return null;
+        } catch (\Exception $e) {
+            Log::error('Failed to verify reset token', [
+                'email' => $email,
+                'error' => $e->getMessage()
+            ]);
+            return null;
+        }
+    }
+
+    /**
+     * Update password in both master and tenant databases
+     */
+    private function updatePasswordInBothDatabases($email, $newPassword, $masterUser)
+    {
+        $hashedPassword = Hash::make($newPassword);
+
+        try {
+            $masterConfig = [
+                'driver' => 'mysql',
+                'host' => env('DB_HOST', '127.0.0.1'),
+                'port' => env('DB_PORT', '3306'),
+                'database' => env('DB_DATABASE'),
+                'username' => env('DB_USERNAME'),
+                'password' => env('DB_PASSWORD'),
+                'charset' => 'utf8mb4',
+                'collation' => 'utf8mb4_unicode_ci',
+                'prefix' => '',
+                'strict' => true,
+                'engine' => null,
+            ];
+
+            config(['database.connections.master_update' => $masterConfig]);
+
+            DB::connection('master_update')
+                ->table('users')
+                ->where('email', $email)
+                ->update(['password' => $hashedPassword]);
+
+            DB::purge('master_update');
+
+            Log::info('Password updated in master database', ['email' => $email]);
+
+            if ($masterUser->tenant_database) {
+                $tenantConfig = [
+                    'driver' => 'mysql',
+                    'host' => env('DB_HOST', '127.0.0.1'),
+                    'port' => env('DB_PORT', '3306'),
+                    'database' => $masterUser->tenant_database,
+                    'username' => $masterUser->tenant_username,
+                    'password' => $masterUser->tenant_password,
+                    'charset' => 'utf8mb4',
+                    'collation' => 'utf8mb4_unicode_ci',
+                    'prefix' => '',
+                    'strict' => true,
+                    'engine' => null,
+                ];
+
+                config(['database.connections.tenant_update' => $tenantConfig]);
+
+                DB::connection('tenant_update')
+                    ->table('users')
+                    ->where('email', $email)
+                    ->update(['password' => $hashedPassword]);
+
+                DB::purge('tenant_update');
+
+                Log::info('Password updated in tenant database', [
+                    'email' => $email,
+                    'tenant_database' => $masterUser->tenant_database
+                ]);
+            }
+        } catch (\Exception $e) {
+            Log::error('Failed to update password in databases', [
+                'email' => $email,
+                'error' => $e->getMessage()
+            ]);
+            throw $e;
+        }
+    }
+
+    /**
+     * Delete reset token
+     */
+    private function deleteResetToken($email)
+    {
+        try {
+            $masterConfig = [
+                'driver' => 'mysql',
+                'host' => env('DB_HOST', '127.0.0.1'),
+                'port' => env('DB_PORT', '3306'),
+                'database' => env('DB_DATABASE'),
+                'username' => env('DB_USERNAME'),
+                'password' => env('DB_PASSWORD'),
+                'charset' => 'utf8mb4',
+                'collation' => 'utf8mb4_unicode_ci',
+                'prefix' => '',
+                'strict' => true,
+                'engine' => null,
+            ];
+
+            config(['database.connections.master_delete_token' => $masterConfig]);
+
+            DB::connection('master_delete_token')
+                ->table('password_resets')
+                ->where('email', $email)
+                ->delete();
+
+            DB::purge('master_delete_token');
+        } catch (\Exception $e) {
+            Log::error('Failed to delete reset token', [
+                'email' => $email,
+                'error' => $e->getMessage()
+            ]);
+        }
+    }
+
+    /**
+     * Send password reset email
+     */
+    private function sendPasswordResetEmail($email, $token, $user)
+    {
+        try {
+            $resetUrl = url('/password-reset/' . $token . '?email=' . urlencode($email));
+            $companyName = 'Leezard';
+
+            $emailData = [
+                'name' => $user->name,
+                'email' => $email,
+                'reset_url' => $resetUrl,
+                'company' => $companyName,
+                'expires_at' => Carbon::now()->addHours(24)->format('d/m/Y H:i')
+            ];
+
+            Mail::send('emails.password-reset', $emailData, function ($message) use ($email, $companyName, $user) {
+                $message->to($email, $user->name)
+                    ->subject('Reset Password -  Leezard')
+                    ->from(config('mail.from.address'), config('mail.from.name'));
+            });
+
+            Log::info('Password reset email sent', ['email' => $email]);
+        } catch (\Exception $e) {
+            Log::error('Failed to send password reset email', [
+                'email' => $email,
+                'error' => $e->getMessage()
+            ]);
+            throw $e;
+        }
+    }
+
+    private function logSecurityEvent($event, $email, $additionalData = [])
+    {
+        Log::info('Password Reset Security Event', array_merge([
+            'event' => $event,
+            'email' => $email,
+            'ip' => request()->ip(),
+            'user_agent' => request()->userAgent(),
+            'timestamp' => now()
+        ], $additionalData));
+    }
+
+    private function hasRecentResetRequest($email)
+    {
+        try {
+            $masterConfig = [
+                'driver' => 'mysql',
+                'host' => env('DB_HOST', '127.0.0.1'),
+                'port' => env('DB_PORT', '3306'),
+                'database' => env('DB_DATABASE'),
+                'username' => env('DB_USERNAME'),
+                'password' => env('DB_PASSWORD'),
+                'charset' => 'utf8mb4',
+                'collation' => 'utf8mb4_unicode_ci',
+                'prefix' => '',
+                'strict' => true,
+                'engine' => null,
+            ];
+
+            config(['database.connections.master_check_recent' => $masterConfig]);
+
+            $recentRequest = DB::connection('master_check_recent')
+                ->table('password_resets')
+                ->where('email', $email)
+                ->where('created_at', '>', Carbon::now()->subMinutes(5)) // 5 minutes cooldown
+                ->first();
+
+            DB::purge('master_check_recent');
+
+            return $recentRequest !== null;
+        } catch (\Exception $e) {
+            Log::error('Failed to check recent reset requests', [
+                'email' => $email,
+                'error' => $e->getMessage()
+            ]);
+            return false;
+        }
+    }
+}

+ 131 - 16
app/Http/Livewire/Profile.php

@@ -1,11 +1,13 @@
 <?php
 
 namespace App\Http\Livewire;
+
 use Illuminate\Support\Facades\Auth;
 use Illuminate\Support\Facades\Hash;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Log;
 use Livewire\Component;
 use App\Http\Middleware\TenantMiddleware;
-use AWS\CRT\Log;
 
 class Profile extends Component
 {
@@ -17,6 +19,7 @@ class Profile extends Component
     public $telefono;
     public $cellulare;
     public $password;
+
     public function boot()
     {
         app(TenantMiddleware::class)->setupTenantConnection();
@@ -33,7 +36,6 @@ class Profile extends Component
         $this->cellulare = $user->cellulare;
     }
 
-
     public function enableEditMode()
     {
         $this->editMode = true;
@@ -49,31 +51,140 @@ class Profile extends Component
         ]);
 
         $currentUser = Auth::user();
-        $user = \App\Models\User::findOrFail($currentUser->id);
-        $user->name = $this->name;
-        $user->cognome = $this->cognome;
-        $user->email = $this->email;
-        $user->telefono = $this->telefono;
-        $user->cellulare = $this->cellulare;
-
-        if (!empty($this->password)) {
-            $user->password = Hash::make($this->password);
+
+        try {
+            DB::beginTransaction();
+
+            $user = \App\Models\User::findOrFail($currentUser->id);
+            $oldEmail = $user->email;
+            $passwordChanged = !empty($this->password);
+            $emailChanged = $oldEmail !== $this->email;
+
+            $user->name = $this->name;
+            $user->cognome = $this->cognome;
+            $user->email = $this->email;
+            $user->telefono = $this->telefono;
+            $user->cellulare = $this->cellulare;
+
+            if ($passwordChanged) {
+                $user->password = Hash::make($this->password);
+            }
+
+            $user->save();
+
+            Log::info('Updated user in tenant database', [
+                'user_id' => $user->id,
+                'tenant_database' => DB::connection()->getDatabaseName(),
+                'email_changed' => $emailChanged,
+                'password_changed' => $passwordChanged
+            ]);
+
+            if ($emailChanged || $passwordChanged || $currentUser->name !== $this->name) {
+                $this->updateMasterDatabase($currentUser, $oldEmail, $passwordChanged);
+            }
+
+            DB::commit();
+
+            session()->flash('message', 'Profilo aggiornato con successo!');
+            $this->editMode = false;
+            $this->password = '';
+
+        } catch (\Exception $e) {
+            DB::rollBack();
+
+            Log::error('Profile update failed', [
+                'user_id' => $currentUser->id,
+                'error' => $e->getMessage(),
+                'trace' => $e->getTraceAsString()
+            ]);
+
+            session()->flash('error', 'Errore durante l\'aggiornamento: ' . $e->getMessage());
         }
+    }
 
-        $user->save();
+    /**
+     * Update user information in master database
+     */
+    private function updateMasterDatabase($currentUser, $oldEmail, $passwordChanged)
+    {
+        try {
+            // Store current tenant connection info
+            $currentConnection = DB::getDefaultConnection();
+            $currentDatabase = DB::connection()->getDatabaseName();
 
-        session()->flash('message', 'Profilo aggiornato con successo!');
+            Log::info('Updating master database', [
+                'current_connection' => $currentConnection,
+                'current_database' => $currentDatabase,
+                'user_id' => $currentUser->id,
+                'old_email' => $oldEmail,
+                'new_email' => $this->email
+            ]);
 
-        $this->editMode = false;
+            $masterConfig = [
+                'driver' => 'mysql',
+                'host' => env('DB_HOST', '127.0.0.1'),
+                'port' => env('DB_PORT', '3306'),
+                'database' => env('DB_DATABASE'),
+                'username' => env('DB_USERNAME'),
+                'password' => env('DB_PASSWORD'),
+                'charset' => 'utf8mb4',
+                'collation' => 'utf8mb4_unicode_ci',
+                'prefix' => '',
+                'strict' => true,
+                'engine' => null,
+            ];
+
+            config(['database.connections.master_temp' => $masterConfig]);
+
+            $updateData = [
+                'name' => $this->name,
+                'email' => $this->email
+            ];
+
+            if ($passwordChanged) {
+                $updateData['password'] = Hash::make($this->password);
+            }
+
+            $updated = DB::connection('master_temp')
+                ->table('users')
+                ->where('email', $oldEmail)
+                ->update($updateData);
+
+            if ($updated) {
+                Log::info('Successfully updated user in master database', [
+                    'old_email' => $oldEmail,
+                    'new_email' => $this->email,
+                    'password_changed' => $passwordChanged
+                ]);
+            } else {
+                Log::warning('No user found in master database to update', [
+                    'email' => $oldEmail
+                ]);
+            }
+
+            config(['database.default' => $currentConnection]);
+            DB::purge('master_temp');
+
+        } catch (\Exception $e) {
+            Log::error('Failed to update master database', [
+                'error' => $e->getMessage(),
+                'user_id' => $currentUser->id,
+                'old_email' => $oldEmail,
+                'new_email' => $this->email
+            ]);
+        }
     }
 
     public function cancel()
     {
         $this->editMode = false;
         $this->password = '';
+
+        $this->mount();
     }
 
-    private function resetInputFields(){
+    private function resetInputFields()
+    {
         $this->name = '';
         $this->cognome = '';
         $this->email = '';
@@ -81,5 +192,9 @@ class Profile extends Component
         $this->cellulare = '';
         $this->password = '';
     }
-}
 
+    public function render()
+    {
+        return view('livewire.profile');
+    }
+}

+ 321 - 12
app/Http/Livewire/User.php

@@ -4,6 +4,8 @@ namespace App\Http\Livewire;
 
 use Illuminate\Support\Facades\Log;
 use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Hash;
+use Illuminate\Support\Facades\Mail;
 use Livewire\Component;
 use App\Http\Middleware\TenantMiddleware;
 use Illuminate\Support\Facades\Auth;
@@ -16,7 +18,7 @@ class User extends Component
         $this->logCurrentDatabase('After tenant connection setup in boot()');
     }
 
-    public $records, $name, $cognome, $email, $password, $oldPassword, $level, $enabled, $dataId, $update = false, $add = false;
+    public $records, $name, $cognome, $email, $password, $oldPassword, $level, $enabled, $dataId, $update = false, $add = false, $oldEmail = null;
     public $userExists = false;
 
     protected $rules = [
@@ -59,6 +61,240 @@ class User extends Component
         }
     }
 
+    /**
+     * Create or update user in master database
+     */
+    private function syncUserToMasterDatabase($userData, $action = 'create', $oldEmail = null)
+    {
+        try {
+            Log::info('Syncing user to master database', [
+                'action' => $action,
+                'email' => $userData['email'],
+                'old_email' => $oldEmail
+            ]);
+
+            $masterConfig = [
+                'driver' => 'mysql',
+                'host' => env('DB_HOST', '127.0.0.1'),
+                'port' => env('DB_PORT', '3306'),
+                'database' => env('DB_DATABASE'),
+                'username' => env('DB_USERNAME'),
+                'password' => env('DB_PASSWORD'),
+                'charset' => 'utf8mb4',
+                'collation' => 'utf8mb4_unicode_ci',
+                'prefix' => '',
+                'strict' => true,
+                'engine' => null,
+            ];
+
+            config(['database.connections.master_sync' => $masterConfig]);
+
+            $currentUser = Auth::user();
+
+            $masterData = [
+                'name' => $userData['name'],
+                'email' => $userData['email'],
+                'password' => $userData['password'],
+                'tenant_database' => $currentUser->tenant_database,
+                'tenant_username' => $currentUser->tenant_username,
+                'tenant_password' => $currentUser->tenant_password,
+                'tenant_host' => '127.0.0.1',
+                'created_at' => now(),
+                'updated_at' => now()
+            ];
+
+            if ($action === 'create') {
+                $inserted = DB::connection('master_sync')
+                    ->table('users')
+                    ->insert($masterData);
+
+                if ($inserted) {
+                    Log::info('Successfully created user in master database', [
+                        'email' => $userData['email'],
+                        'tenant_database' => $currentUser->tenant_database
+                    ]);
+                    return true;
+                } else {
+                    Log::warning('Failed to create user in master database', [
+                        'email' => $userData['email']
+                    ]);
+                    return false;
+                }
+            } elseif ($action === 'update') {
+                $searchEmail = $oldEmail ?: $userData['email'];
+
+                unset($masterData['created_at']);
+
+                $updated = DB::connection('master_sync')
+                    ->table('users')
+                    ->where('email', $searchEmail)
+                    ->update($masterData);
+
+                if ($updated) {
+                    Log::info('Successfully updated user in master database', [
+                        'old_email' => $searchEmail,
+                        'new_email' => $userData['email']
+                    ]);
+                    return true;
+                } else {
+                    Log::warning('No user found in master database to update', [
+                        'search_email' => $searchEmail
+                    ]);
+                    return false;
+                }
+            }
+        } catch (\Exception $e) {
+            Log::error('Failed to sync user to master database', [
+                'action' => $action,
+                'email' => $userData['email'],
+                'error' => $e->getMessage(),
+                'trace' => $e->getTraceAsString()
+            ]);
+            return false;
+        } finally {
+            // Clean up the temporary connection
+            try {
+                DB::purge('master_sync');
+            } catch (\Exception $e) {
+                // Ignore cleanup errors
+            }
+        }
+    }
+
+    /**
+     * Delete user from master database
+     */
+    private function deleteUserFromMasterDatabase($email)
+    {
+        try {
+            Log::info('Deleting user from master database', [
+                'email' => $email
+            ]);
+
+            $masterConfig = [
+                'driver' => 'mysql',
+                'host' => env('DB_HOST', '127.0.0.1'),
+                'port' => env('DB_PORT', '3306'),
+                'database' => env('DB_DATABASE'),
+                'username' => env('DB_USERNAME'),
+                'password' => env('DB_PASSWORD'),
+                'charset' => 'utf8mb4',
+                'collation' => 'utf8mb4_unicode_ci',
+                'prefix' => '',
+                'strict' => true,
+                'engine' => null,
+            ];
+
+            config(['database.connections.master_delete' => $masterConfig]);
+
+            $deleted = DB::connection('master_delete')
+                ->table('users')
+                ->where('email', $email)
+                ->delete();
+
+            if ($deleted) {
+                Log::info('Successfully deleted user from master database', [
+                    'email' => $email,
+                    'rows_affected' => $deleted
+                ]);
+                return true;
+            } else {
+                Log::warning('No user found in master database to delete', [
+                    'email' => $email
+                ]);
+                return false;
+            }
+        } catch (\Exception $e) {
+            Log::error('Failed to delete user from master database', [
+                'email' => $email,
+                'error' => $e->getMessage(),
+                'trace' => $e->getTraceAsString()
+            ]);
+            return false;
+        } finally {
+            try {
+                DB::purge('master_delete');
+            } catch (\Exception $e) {
+            }
+        }
+    }
+
+    /**
+     * Send welcome email to new user
+     */
+    private function sendWelcomeEmail($userData, $plainPassword)
+    {
+        try {
+            $currentUser = Auth::user();
+            $companyName = 'Leezard';
+
+            Log::info('Preparing to send welcome email', [
+                'recipient' => $userData['email'],
+                'company' => $companyName,
+                'mail_from' => config('mail.from.address'),
+                'mail_host' => config('mail.mailers.smtp.host'),
+                'mail_port' => config('mail.mailers.smtp.port')
+            ]);
+
+            $emailData = [
+                'name' => $userData['name'],
+                'cognome' => $userData['cognome'],
+                'email' => $userData['email'],
+                'password' => $plainPassword,
+                'level' => $userData['level'],
+                'company' => $companyName,
+                'login_url' => url('/'),
+                'created_by' => $currentUser->name
+            ];
+
+            try {
+                $viewContent = view('emails.welcome-user', $emailData)->render();
+                Log::info('Email template rendered successfully', ['template_length' => strlen($viewContent)]);
+            } catch (\Exception $viewException) {
+                Log::error('Email template rendering failed', ['error' => $viewException->getMessage()]);
+                throw new \Exception('Email template error: ' . $viewException->getMessage());
+            }
+
+            Mail::send('emails.welcome-user', $emailData, function ($message) use ($userData, $companyName) {
+                $message->to($userData['email'], $userData['name'] . ' ' . $userData['cognome'])
+                    ->subject('Benvenuto su Leezard - Account Creato')
+                    ->from(config('mail.from.address'), config('mail.from.name'));
+
+                if (env('MAIL_CCN')) {
+                    $message->bcc(env('MAIL_CCN'));
+                }
+            });
+
+            Log::info('Welcome email sent successfully', [
+                'recipient' => $userData['email'],
+                'company' => $companyName,
+                'subject' => 'Benvenuto in ' . $companyName . ' - Account Creato'
+            ]);
+
+            return true;
+        } catch (\Swift_TransportException $e) {
+            Log::error('SMTP Transport error when sending welcome email', [
+                'recipient' => $userData['email'],
+                'error' => $e->getMessage(),
+                'mail_config' => [
+                    'host' => config('mail.mailers.smtp.host'),
+                    'port' => config('mail.mailers.smtp.port'),
+                    'encryption' => config('mail.mailers.smtp.encryption'),
+                    'username' => config('mail.mailers.smtp.username')
+                ]
+            ]);
+            return false;
+        } catch (\Exception $e) {
+            Log::error('General error when sending welcome email', [
+                'recipient' => $userData['email'],
+                'error' => $e->getMessage(),
+                'trace' => $e->getTraceAsString()
+            ]);
+            return false;
+        }
+    }
+
+
     public function resetFields()
     {
         $this->name = '';
@@ -127,18 +363,22 @@ class User extends Component
         $this->logCurrentDatabase('Before creating user in store()');
 
         try {
+            $plainPassword = $this->password;
+
+            $hashedPassword = bcrypt($this->password);
+
             $user = \App\Models\User::create([
                 'name' => $this->name,
                 'cognome' => $this->cognome,
                 'email' => $this->email,
-                'password' => bcrypt($this->password),
+                'password' => $hashedPassword,
                 'level' => $this->level,
                 'enabled' => $this->enabled
             ]);
 
-            $this->logCurrentDatabase('After creating user in store()');
+            $this->logCurrentDatabase('After creating user in tenant database');
 
-            Log::info('User created successfully', [
+            Log::info('User created successfully in tenant database', [
                 'user_id' => $user->id,
                 'name' => $this->name,
                 'cognome' => $this->cognome,
@@ -148,7 +388,32 @@ class User extends Component
                 'database' => DB::connection()->getDatabaseName()
             ]);
 
-            session()->flash('success', 'Utente creato');
+            $masterSyncSuccess = $this->syncUserToMasterDatabase([
+                'name' => $this->name,
+                'cognome' => $this->cognome,
+                'email' => $this->email,
+                'password' => $hashedPassword,
+                'level' => $this->level,
+                'enabled' => $this->enabled
+            ], 'create');
+
+            if ($masterSyncSuccess) {
+                $emailSent = $this->sendWelcomeEmail([
+                    'name' => $this->name,
+                    'cognome' => $this->cognome,
+                    'email' => $this->email,
+                    'level' => $this->level
+                ], $plainPassword);
+
+                if ($emailSent) {
+                    session()->flash('success', 'Utente creato e email di benvenuto inviata');
+                } else {
+                    session()->flash('success', 'Utente creato ma errore nell\'invio email');
+                }
+            } else {
+                session()->flash('success', 'Utente creato nel database tenant ma errore nella sincronizzazione master');
+            }
+
             $this->resetFields();
             $this->add = false;
         } catch (\Exception $ex) {
@@ -191,6 +456,7 @@ class User extends Component
                 $this->add = false;
                 $this->enabled = $user->enabled;
                 $this->userExists = true;
+                $this->oldEmail = $user->email;
             }
 
             Log::info('User edit loaded', [
@@ -218,30 +484,65 @@ class User extends Component
     {
         $this->logCurrentDatabase('Start of update() method');
 
-        $this->validate();
+        $rules = [
+            'name' => 'required',
+            'cognome' => 'required',
+            'email' => 'required|email',
+            'password' => 'nullable|min:6'
+        ];
+
+        $this->validate($rules, $this->messages);
 
         try {
-            \App\Models\User::whereId($this->dataId)->update([
+            $currentUser = \App\Models\User::findOrFail($this->dataId);
+            $oldEmail = $currentUser->email;
+            $oldName = $currentUser->name;
+
+            $updateData = [
                 'name' => $this->name,
                 'cognome' => $this->cognome,
                 'email' => $this->email,
-                'password' => bcrypt($this->password),
                 'level' => $this->level,
                 'enabled' => $this->enabled
-            ]);
+            ];
+
+            $passwordChanged = !empty($this->password);
+            if ($passwordChanged) {
+                $hashedPassword = bcrypt($this->password);
+                $updateData['password'] = $hashedPassword;
+            }
+
+            \App\Models\User::whereId($this->dataId)->update($updateData);
 
             $this->logCurrentDatabase('After updating user');
 
-            Log::info('User updated successfully', [
+            Log::info('User updated successfully in tenant database', [
                 'user_id' => $this->dataId,
                 'name' => $this->name,
                 'cognome' => $this->cognome,
                 'email' => $this->email,
                 'level' => $this->level,
                 'enabled' => $this->enabled,
+                'password_changed' => $passwordChanged,
                 'database' => DB::connection()->getDatabaseName()
             ]);
 
+            $emailChanged = $oldEmail !== $this->email;
+            $nameChanged = $oldName !== $this->name;
+
+            if ($emailChanged || $nameChanged || $passwordChanged) {
+                $masterData = [
+                    'name' => $this->name,
+                    'email' => $this->email
+                ];
+
+                if ($passwordChanged) {
+                    $masterData['password'] = $hashedPassword;
+                }
+
+                $this->syncUserToMasterDatabase($masterData, 'update', $oldEmail);
+            }
+
             session()->flash('success', 'Dato aggiornato');
             $this->resetFields();
             $this->update = false;
@@ -274,15 +575,23 @@ class User extends Component
         $this->logCurrentDatabase('Start of delete() method');
 
         try {
-            \App\Models\User::find($id)->delete();
+            $user = \App\Models\User::find($id);
+            $userEmail = $user ? $user->email : null;
+
+            $user->delete();
 
             $this->logCurrentDatabase('After deleting user');
 
-            Log::info('User deleted successfully', [
+            Log::info('User deleted successfully from tenant database', [
                 'user_id' => $id,
+                'user_email' => $userEmail,
                 'database' => DB::connection()->getDatabaseName()
             ]);
 
+            if ($userEmail) {
+                $this->deleteUserFromMasterDatabase($userEmail);
+            }
+
             session()->flash('success', "Dato eliminato");
         } catch (\Exception $e) {
             $this->logCurrentDatabase('Error in delete() method');

+ 25 - 0
app/Http/Middleware/PasswordResetThrottle.php

@@ -0,0 +1,25 @@
+<?php
+
+namespace App\Http\Middleware;
+
+use Closure;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\RateLimiter;
+use Symfony\Component\HttpFoundation\Response;
+
+class PasswordResetThrottle
+{
+    public function handle(Request $request, Closure $next): Response
+    {
+        $key = 'password-reset:' . $request->ip();
+
+        if (RateLimiter::tooManyAttempts($key, 5)) {
+            $seconds = RateLimiter::availableIn($key);
+            return back()->with('error', "Troppi tentativi. Riprova tra {$seconds} secondi.");
+        }
+
+        RateLimiter::hit($key, 3600); // 1 hour
+
+        return $next($request);
+    }
+}

+ 72 - 0
app/Services/MultiTenantAuthService.php

@@ -0,0 +1,72 @@
+<?php
+namespace App\Services;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Log;
+use Illuminate\Support\Facades\Hash;
+class MultiTenantAuthService
+{
+    /**
+     * Authenticate user against both master and tenant databases
+     */
+    public static function authenticate($email, $password)
+    {
+        try {
+            $masterUser = DB::connection('mysql')->table('users')
+                ->where('email', $email)
+                ->first();
+
+            if (!$masterUser) {
+                Log::info('User not found in master database', ['email' => $email]);
+                return false;
+            }
+
+            if (!Hash::check($password, $masterUser->password)) {
+                Log::info('Password mismatch in master database', ['email' => $email]);
+                return false;
+            }
+
+            config(['database.connections.temp_tenant' => [
+                'driver' => 'mysql',
+                'host' => '127.0.0.1',
+                'port' => '3306',
+                'database' => $masterUser->tenant_database,
+                'username' => $masterUser->tenant_username,
+                'password' => $masterUser->tenant_password,
+            ]]);
+
+            $tenantUser = DB::connection('temp_tenant')->table('users')
+                ->where('email', $email)
+                ->first();
+
+            if (!$tenantUser) {
+                Log::info('User not found in tenant database', [
+                    'email' => $email,
+                    'tenant_db' => $masterUser->tenant_database
+                ]);
+                return false;
+            }
+
+            if (!Hash::check($password, $tenantUser->password)) {
+                Log::info('Password mismatch in tenant database', [
+                    'email' => $email,
+                    'tenant_db' => $masterUser->tenant_database
+                ]);
+                return false;
+            }
+
+            Log::info('Authentication successful for both databases', [
+                'email' => $email,
+                'tenant_db' => $masterUser->tenant_database
+            ]);
+
+            return $masterUser;
+
+        } catch (\Exception $e) {
+            Log::error('Authentication error', [
+                'email' => $email,
+                'error' => $e->getMessage()
+            ]);
+            return false;
+        }
+    }
+}

+ 21 - 0
database/migrations/2025_06_20_084530_create_password_resets_table.php

@@ -0,0 +1,21 @@
+<?php
+use App\Database\Migrations\MasterMigration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends MasterMigration
+{
+    public function up()
+    {
+        Schema::create('password_resets', function (Blueprint $table) {
+            $table->string('email')->index();
+            $table->string('token');
+            $table->timestamp('created_at')->nullable();
+        });
+    }
+
+    public function down()
+    {
+        Schema::dropIfExists('password_resets');
+    }
+};

+ 70 - 0
resources/views/auth/password-reset-form.blade.php

@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<html lang="it">
+<head>
+    <meta charset="UTF-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Nuova Password</title>
+    <link rel="stylesheet" href="https://use.typekit.net/dit2bgs.css">
+    <link rel="stylesheet" href="/css/style.css">
+</head>
+<body>
+    <div class="login--box d-flex flex-column min-vh-100 justify-content-center align-items-center">
+        <img src="{{ env('LOGO', '') }}" alt="" class="img-fluid" id="login--logo" style="max-width:400px">
+        <div class="card--ui mt-5">
+            <p class="form--title text-center mb-4">Imposta Nuova Password</p>
+
+            @if(session('success'))
+                <div class="alert alert-success mb-3">{{ session('success') }}</div>
+            @endif
+
+            @if(session('error'))
+                <div class="alert alert-danger mb-3">{{ session('error') }}</div>
+            @endif
+
+            <form action="/password-reset" method="POST">
+                @csrf
+                <input type="hidden" name="token" value="{{ $token }}">
+
+                <div class="form--item input-group mb-3">
+                    <input type="email" class="form-control @error('email') is-invalid @enderror"
+                           placeholder="Email" name="email" value="{{ request('email') }}" required>
+                    <span class="input-group-text"><i class="ico--ui mail"></i></span>
+                    @error('email')
+                        <div class="invalid-feedback">{{ $message }}</div>
+                    @enderror
+                </div>
+
+                <div class="form--item input-group mb-3">
+                    <input type="password" class="form-control @error('password') is-invalid @enderror"
+                           placeholder="Nuova Password" name="password" required>
+                    <span class="input-group-text"><i class="ico--ui lock"></i></span>
+                    @error('password')
+                        <div class="invalid-feedback">{{ $message }}</div>
+                    @enderror
+                </div>
+
+                <div class="form--item input-group mb-3">
+                    <input type="password" class="form-control @error('password_confirmation') is-invalid @enderror"
+                           placeholder="Conferma Password" name="password_confirmation" required>
+                    <span class="input-group-text"><i class="ico--ui lock"></i></span>
+                    @error('password_confirmation')
+                        <div class="invalid-feedback">{{ $message }}</div>
+                    @enderror
+                </div>
+
+                <div class="form--item input-group d-flex align-items-center justify-content-between">
+                    <button type="submit" class="btn--ui">Aggiorna Password</button>
+                </div>
+            </form>
+
+            <div class="credential--recovery d-flex flex-column mt-4">
+                <a href="/">Torna al Login</a>
+            </div>
+        </div>
+    </div>
+
+    <script src="/assets/js/bootstrap.bundle.js"></script>
+    <script src="/assets/js/app.js"></script>
+</body>
+</html>

+ 53 - 0
resources/views/auth/password-reset-request.blade.php

@@ -0,0 +1,53 @@
+<!-- resources/views/password-reset-request.blade.php -->
+<!DOCTYPE html>
+<html lang="it">
+<head>
+    <meta charset="UTF-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Reset Password</title>
+    <link rel="stylesheet" href="https://use.typekit.net/dit2bgs.css">
+    <link rel="stylesheet" href="/css/style.css">
+</head>
+<body>
+    <div class="login--box d-flex flex-column min-vh-100 justify-content-center align-items-center">
+        <img src="{{ env('LOGO', '') }}" alt="" class="img-fluid" id="login--logo" style="max-width:400px">
+        <div class="card--ui mt-5">
+            <p class="form--title text-center mb-4">Reset Password</p>
+            <p class="text-center mb-4">Inserisci la tua email per ricevere le istruzioni di reset</p>
+
+            @if(session('success'))
+                <div class="alert alert-success mb-3">{{ session('success') }}</div>
+            @endif
+
+            @if(session('error'))
+                <div class="alert alert-danger mb-3">{{ session('error') }}</div>
+            @endif
+
+            <form action="/password-reset-request" method="POST">
+                @csrf
+
+                <div class="form--item input-group mb-3">
+                    <input type="email" class="form-control @error('email') is-invalid @enderror"
+                           placeholder="Email" name="email" value="{{ old('email') }}" required>
+                    <span class="input-group-text"><i class="ico--ui mail"></i></span>
+                    @error('email')
+                        <div class="invalid-feedback">{{ $message }}</div>
+                    @enderror
+                </div>
+
+                <div class="form--item input-group d-flex align-items-center justify-content-between">
+                    <button type="submit" class="btn--ui">Invia Reset</button>
+                </div>
+            </form>
+
+            <div class="credential--recovery d-flex flex-column mt-4">
+                <a href="/">Torna al Login</a>
+            </div>
+        </div>
+    </div>
+
+    <script src="/assets/js/bootstrap.bundle.js"></script>
+    <script src="/assets/js/app.js"></script>
+</body>
+</html>

+ 122 - 0
resources/views/emails/password-reset.blade.php

@@ -0,0 +1,122 @@
+<!DOCTYPE html>
+<html lang="it">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Reset Password</title>
+    <style>
+        body {
+            font-family: Arial, sans-serif;
+            line-height: 1.6;
+            color: #333;
+            max-width: 600px;
+            margin: 0 auto;
+            padding: 20px;
+        }
+        .header {
+            background-color: #dc3545;
+            color: white;
+            padding: 20px;
+            text-align: center;
+            border-radius: 8px 8px 0 0;
+        }
+        .content {
+            background-color: #f8f9fa;
+            padding: 30px;
+            border-radius: 0 0 8px 8px;
+        }
+        .reset-button {
+            display: inline-block;
+            background-color: #dc3545;
+            color: white;
+            padding: 15px 30px;
+            text-decoration: none;
+            border-radius: 5px;
+            margin: 20px 0;
+            font-weight: bold;
+            text-align: center;
+        }
+        .reset-url {
+            background-color: white;
+            border: 2px solid #dc3545;
+            border-radius: 8px;
+            padding: 15px;
+            margin: 20px 0;
+            word-break: break-all;
+            font-family: monospace;
+            font-size: 14px;
+        }
+        .warning {
+            background-color: #fff3cd;
+            border: 1px solid #ffeaa7;
+            color: #856404;
+            padding: 15px;
+            border-radius: 5px;
+            margin: 20px 0;
+        }
+        .footer {
+            margin-top: 30px;
+            padding-top: 20px;
+            border-top: 1px solid #dee2e6;
+            font-size: 14px;
+            color: #6c757d;
+        }
+        .security-info {
+            background-color: #d1ecf1;
+            border: 1px solid #bee5eb;
+            color: #0c5460;
+            padding: 15px;
+            border-radius: 5px;
+            margin: 20px 0;
+        }
+    </style>
+</head>
+<body>
+    <div class="header">
+        <h1>🔒 Reset Password</h1>
+    </div>
+
+    <div class="content">
+        <h2>Ciao {{ $name }},</h2>
+
+        <p>Hai richiesto di reimpostare la password per il tuo account in <strong>Leezard</strong>.</p>
+
+        <p>Se hai effettuato questa richiesta, clicca sul pulsante qui sotto per reimpostare la tua password:</p>
+
+        <div style="text-align: center;">
+            <a href="{{ $reset_url }}" class="reset-button">🔑 Reimposta Password</a>
+        </div>
+
+        <div class="warning">
+            <strong>⚠️ Importante:</strong> Questo link scadrà il <strong>{{ $expires_at }}</strong>. Se il link è scaduto, dovrai richiedere un nuovo reset della password.
+        </div>
+
+        <p><strong>Se il pulsante non funziona, copia e incolla questo link nel tuo browser:</strong></p>
+
+        <div class="reset-url">
+            {{ $reset_url }}
+        </div>
+
+        <div class="security-info">
+            <h3>🛡️ Informazioni di Sicurezza:</h3>
+            <ul>
+                <li>Se non hai richiesto questo reset, ignora questa email</li>
+                <li>Non condividere mai questo link con nessuno</li>
+                <li>Scegli una password sicura e unica</li>
+                <li>Il link può essere utilizzato una sola volta</li>
+            </ul>
+        </div>
+
+        <p><strong>Se non hai richiesto questo reset</strong>, la tua password è ancora sicura e non è stata modificata. Puoi ignorare questa email.</p>
+
+        <p>Se hai problemi o domande, contatta l'amministratore del sistema.</p>
+
+        <div class="footer">
+            <p><strong>{{ $company }}</strong></p>
+            <p>Questa email è stata generata automaticamente. Per favore, non rispondere a questo indirizzo.</p>
+            <p>Data richiesta: {{ date('d/m/Y H:i') }}</p>
+            <p>Indirizzo email: {{ $email }}</p>
+        </div>
+    </div>
+</body>
+</html>

+ 138 - 0
resources/views/emails/welcome-user.blade.php

@@ -0,0 +1,138 @@
+<!DOCTYPE html>
+<html lang="it">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Benvenuto - Account Creato</title>
+    <style>
+        body {
+            font-family: Arial, sans-serif;
+            line-height: 1.6;
+            color: #333;
+            max-width: 600px;
+            margin: 0 auto;
+            padding: 20px;
+        }
+        .header {
+            background-color: #007bff;
+            color: white;
+            padding: 20px;
+            text-align: center;
+            border-radius: 8px 8px 0 0;
+        }
+        .content {
+            background-color: #f8f9fa;
+            padding: 30px;
+            border-radius: 0 0 8px 8px;
+        }
+        .credentials-box {
+            background-color: white;
+            border: 2px solid #007bff;
+            border-radius: 8px;
+            padding: 20px;
+            margin: 20px 0;
+        }
+        .credential-item {
+            margin: 10px 0;
+            font-size: 16px;
+        }
+        .credential-label {
+            font-weight: bold;
+            color: #007bff;
+        }
+        .credential-value {
+            background-color: #e9ecef;
+            padding: 5px 10px;
+            border-radius: 4px;
+            font-family: monospace;
+            margin-left: 10px;
+        }
+        .login-button {
+            display: inline-block;
+            background-color: #007bff;
+            color: white;
+            padding: 12px 30px;
+            text-decoration: none;
+            border-radius: 5px;
+            margin: 20px 0;
+            font-weight: bold;
+        }
+        .footer {
+            margin-top: 30px;
+            padding-top: 20px;
+            border-top: 1px solid #dee2e6;
+            font-size: 14px;
+            color: #6c757d;
+        }
+        .warning {
+            background-color: #fff3cd;
+            border: 1px solid #ffeaa7;
+            color: #856404;
+            padding: 15px;
+            border-radius: 5px;
+            margin: 20px 0;
+        }
+    </style>
+</head>
+<body>
+    <div class="header">
+        <h1>🎉 Benvenuto in {{ $company }}!</h1>
+    </div>
+
+    <div class="content">
+        <h2>Ciao {{ $name }} {{ $cognome }},</h2>
+
+        <p>Il tuo account è stato creato con successo da <strong>{{ $created_by }}</strong>. Ora puoi accedere alla piattaforma con le credenziali fornite di seguito.</p>
+
+        <div class="credentials-box">
+            <h3>🔐 Le tue credenziali di accesso:</h3>
+
+            <div class="credential-item">
+                <span class="credential-label">Email:</span>
+                <span class="credential-value">{{ $email }}</span>
+            </div>
+
+            <div class="credential-item">
+                <span class="credential-label">Password:</span>
+                <span class="credential-value">{{ $password }}</span>
+            </div>
+
+            <div class="credential-item">
+                <span class="credential-label">Livello Account:</span>
+                <span class="credential-value">
+                    @if($level == 0)
+                        Amministratore
+                    @elseif($level == 1)
+                        Worker
+                    @else
+                        Istruttore
+                    @endif
+                </span>
+            </div>
+        </div>
+
+        <div class="warning">
+            <strong>⚠️ Importante:</strong> Per motivi di sicurezza, ti consigliamo di cambiare la password al primo accesso. Vai alla sezione "Profilo" dopo aver effettuato il login.
+        </div>
+
+        <div style="text-align: center;">
+            <a href="{{ $login_url }}" class="login-button">🚀 Accedi Ora</a>
+        </div>
+
+        <h3>📋 Cosa puoi fare ora:</h3>
+        <ul>
+            <li>Accedi alla piattaforma usando le credenziali sopra</li>
+            <li>Completa il tuo profilo nella sezione "Profilo"</li>
+            <li>Cambia la password per maggiore sicurezza</li>
+        </ul>
+
+        <p>Se hai domande o problemi nell'accesso, contatta l'amministratore del sistema.</p>
+
+        <div class="footer">
+            <p><strong>{{ $company }}</strong></p>
+            <p>Questa email è stata generata automaticamente. Per favore, non rispondere a questo indirizzo.</p>
+            <p>Data creazione account: {{ date('d/m/Y H:i') }}</p>
+        </div>
+    </div>
+</body>
+</html>

+ 1 - 10
resources/views/login.blade.php

@@ -38,18 +38,9 @@
                 </div>
             </form>
 
-            <!--<span class="divider--form d-block text-center my-3">oppure</span>
-
-            <div class="d-grid gap-2 my-4">
-                <button class="btn--ui fb--login" type="button"><i class="ico--ui fb me-2"></i>Login con Facebook</button>
-                <button class="btn--ui li--login" type="button"><i class="ico--ui li me-2"></i>Login con LinkedIn</button>
-            </div>            -->
-
-            <!--
             <div class="credential--recovery d-flex flex-column mt-4">
-                <a href="#">Hai dimenticato la Password</a>
+                <a href="/password-reset-request">Hai dimenticato la Password?</a>
             </div>
-            -->
 
         </div>
     </div>

+ 22 - 4
routes/web.php

@@ -9,6 +9,8 @@ use Illuminate\Support\Facades\Log;
 use Illuminate\Support\Facades\Mail;
 use Illuminate\Support\Facades\Storage;
 use App\Services\MigrationService;
+use App\Services\MultiTenantAuthService;
+use App\Http\Controllers\PasswordResetController;
 /*
 |--------------------------------------------------------------------------
 | Web Routes
@@ -29,16 +31,31 @@ Route::get('/login', function () {
     return Redirect::to('/');
     // return Redirect::to('/dashboard');
 });
+Route::get('/password-reset-request', [PasswordResetController::class, 'showResetRequestForm'])->name('password.request');
+Route::post('/password-reset-request', [PasswordResetController::class, 'sendResetEmail'])->name('password.email');
+Route::get('/password-reset/{token}', [PasswordResetController::class, 'showResetForm'])->name('password.reset');
+Route::post('/password-reset', [PasswordResetController::class, 'resetPassword'])->name('password.update');
+
 
 Route::post('/login', function () {
+    $email = request('email');
+    $password = request('password');
 
-    if (Auth::attempt(array('email' => $_POST["email"], 'password' => $_POST["password"]))) {
-        return Redirect::to('/dashboard');
-    } else {
-        return Redirect::to('/?error=1');
+    $user = MultiTenantAuthService::authenticate($email, $password);
+
+    if ($user) {
+        $authUser = \App\Models\User::where('email', $email)->first();
+
+        if ($authUser) {
+            Auth::login($authUser);
+            return redirect('/dashboard');
+        }
     }
+
+    return redirect('/?error=1');
 })->name('login');
 
+
 Route::get('/logout', function () {
     session()->forget('currentClient');
 
@@ -89,6 +106,7 @@ Route::group(['middleware' => 'tenant'], function () {
     Route::get('/azienda', \App\Http\Livewire\Azienda::class);
 
 
+
 Route::get('/receipt/{id}', function ($id) {
     $receipt = \App\Models\Receipt::findOrFail($id);
     $pdf = PDF::loadView('receipt', array('receipt' => $receipt));