resolveAmministratore(); if (! $amministratore instanceof Amministratore) { abort(403, 'Amministratore non disponibile per questa sessione.'); } $google = Arr::get($amministratore->impostazioni ?? [], 'google', []); $store = app(GoogleAccountStore::class); $accountKey = $store->normalizeAccountKey((string) request()->query('account_key', '')); $accountLabel = trim((string) request()->query('label', '')); $account = $store->get($amministratore, $accountKey !== '' ? $accountKey : null); $returnTo = trim((string) request()->query('return_to', '')); $clientId = (string) ($google['client_id'] ?? config('services.google.client_id')); $clientSecret = (string) ($google['client_secret'] ?? config('services.google.client_secret')); $redirectUri = (string) ($google['redirect_uri'] ?? config('services.google.redirect')); if ($clientId === '' || $clientSecret === '' || $redirectUri === '') { return redirect('/admin-filament/impostazioni/scheda-amministratore') ->with('error', 'Configura Client ID, Client Secret e Redirect URI in Scheda amministratore.'); } $state = bin2hex(random_bytes(20)); session()->put('google_oauth_context', [ 'state' => $state, 'account_key' => $accountKey, 'label' => $accountLabel, 'return_to' => str_starts_with($returnTo, '/') ? $returnTo : null, ]); $queryParams = [ 'client_id' => $clientId, 'redirect_uri' => $redirectUri, 'response_type' => 'code', 'scope' => implode(' ', self::GOOGLE_SCOPES), 'access_type' => 'offline', 'include_granted_scopes' => 'true', 'state' => $state, ]; $refreshToken = trim((string) ($account['refresh_token'] ?? '')); if ($refreshToken === '') { $queryParams['prompt'] = 'consent'; } if (! empty($account['email'])) { $queryParams['login_hint'] = (string) $account['email']; } $query = http_build_query($queryParams); return redirect()->away('https://accounts.google.com/o/oauth2/v2/auth?' . $query); } public function callback(): RedirectResponse { $amministratore = $this->resolveAmministratore(); if (! $amministratore instanceof Amministratore) { abort(403, 'Amministratore non disponibile per questa sessione.'); } $google = Arr::get($amministratore->impostazioni ?? [], 'google', []); $store = app(GoogleAccountStore::class); $clientId = (string) ($google['client_id'] ?? config('services.google.client_id')); $clientSecret = (string) ($google['client_secret'] ?? config('services.google.client_secret')); $redirectUri = (string) ($google['redirect_uri'] ?? config('services.google.redirect')); try { $oauthContext = (array) session()->pull('google_oauth_context', []); $redirectTo = $this->resolveRedirectTarget($oauthContext); $incomingState = (string) request()->query('state', ''); $sessionState = (string) ($oauthContext['state'] ?? ''); if ($incomingState === '' || $sessionState === '' || ! hash_equals($sessionState, $incomingState)) { return redirect($redirectTo) ->with('error', 'Stato OAuth non valido. Riprova il collegamento Google.'); } $code = (string) request()->query('code', ''); if ($code === '') { return redirect($redirectTo) ->with('error', 'Google non ha restituito il codice di autorizzazione.'); } $tokenResponse = Http::asForm()->post('https://oauth2.googleapis.com/token', [ 'client_id' => $clientId, 'client_secret' => $clientSecret, 'redirect_uri' => $redirectUri, 'grant_type' => 'authorization_code', 'code' => $code, ]); if (! $tokenResponse->successful()) { $message = (string) (Arr::get($tokenResponse->json(), 'error_description') ?: Arr::get($tokenResponse->json(), 'error.message') ?: 'token_exchange_failed'); return redirect($redirectTo) ->with('error', 'Errore scambio token Google: ' . $message); } $payload = $tokenResponse->json(); $accessToken = (string) Arr::get($payload, 'access_token', ''); $refreshToken = (string) Arr::get($payload, 'refresh_token', ''); $expiresIn = (int) Arr::get($payload, 'expires_in', 3600); $profileResponse = Http::withToken($accessToken) ->acceptJson() ->get('https://openidconnect.googleapis.com/v1/userinfo'); $profile = $profileResponse->successful() ? $profileResponse->json() : []; $googleUserId = (string) Arr::get($profile, 'sub', ''); $email = (string) Arr::get($profile, 'email', ''); $name = (string) Arr::get($profile, 'name', ''); $requestedKey = $store->normalizeAccountKey((string) ($oauthContext['account_key'] ?? '')); $accountKey = $requestedKey !== '' ? $requestedKey : $store->normalizeAccountKey($googleUserId !== '' ? $googleUserId : ($email !== '' ? $email : 'primary')); $accountLabel = trim((string) ($oauthContext['label'] ?? '')) ?: ($name !== '' ? $name : $email); $existing = $store->get($amministratore, $accountKey); if ($refreshToken === '') { $refreshToken = trim((string) ($existing['refresh_token'] ?? '')); } $setDefault = $existing === null && ! $store->isScopedAccountKey($accountKey); $store->upsertAccount( $amministratore, $accountKey, [ 'connected' => true, 'label' => $accountLabel, 'email' => $email, 'name' => $name, 'google_user_id' => $googleUserId, 'access_token' => $accessToken, 'refresh_token' => $refreshToken, 'expires_in' => $expiresIn, 'connected_at' => (string) ($existing['connected_at'] ?? now()->toDateTimeString()), 'refreshed_at' => now()->toDateTimeString(), 'scopes' => self::GOOGLE_SCOPES, ], $setDefault ); return redirect($redirectTo) ->with('status', 'Account Google collegato con successo.'); } catch (Throwable $e) { Log::error('Google OAuth callback failed', [ 'admin_id' => $amministratore->id, 'error' => $e->getMessage(), ]); return redirect($this->resolveRedirectTarget((array) session()->get('google_oauth_context', []))) ->with('error', 'Errore durante il collegamento Google: ' . $e->getMessage()); } } public function disconnect(): RedirectResponse { $amministratore = $this->resolveAmministratore(); if (! $amministratore instanceof Amministratore) { abort(403, 'Amministratore non disponibile per questa sessione.'); } app(GoogleAccountStore::class)->disconnectAccount( $amministratore, (string) request()->input('account_key', request()->query('account_key', '')) ); $returnTo = trim((string) request()->input('return_to', request()->query('return_to', ''))); return redirect($returnTo !== '' && str_starts_with($returnTo, '/') ? $returnTo : '/admin-filament/impostazioni/scheda-amministratore') ->with('status', 'Collegamento Google rimosso.'); } private function resolveAmministratore(): ?Amministratore { $user = Auth::user(); if (! $user instanceof User) { return null; } $amministratore = $user->amministratore; if (! $amministratore instanceof Amministratore && $user->hasAnyRole(['super-admin', 'admin'])) { $stabile = StabileContext::getActiveStabile($user); if ($stabile instanceof Stabile && $stabile->amministratore instanceof Amministratore) { $amministratore = $stabile->amministratore; } } if (! $amministratore instanceof Amministratore && $user->hasRole('fornitore')) { $fornitore = $this->resolveCurrentUserSupplier($user); if ($fornitore instanceof Fornitore && $fornitore->amministratore instanceof Amministratore) { $amministratore = $fornitore->amministratore; } } return $amministratore instanceof Amministratore ? $amministratore : null; } private function resolveCurrentUserSupplier(User $user): ?Fornitore { $email = mb_strtolower(trim((string) $user->email)); if ($email === '') { return $this->resolveCurrentUserSupplierFromCollaboratore($user); } $fornitore = Fornitore::query() ->whereRaw('LOWER(email) = ?', [$email]) ->first(); if ($fornitore instanceof Fornitore) { return $fornitore; } return $this->resolveCurrentUserSupplierFromCollaboratore($user); } private function resolveCurrentUserSupplierFromCollaboratore(User $user): ?Fornitore { $dipendente = FornitoreDipendente::query() ->where('attivo', true) ->where(function ($query) use ($user): void { $query->where('user_id', (int) $user->id); $email = mb_strtolower(trim((string) $user->email)); if ($email !== '') { $query->orWhereRaw('LOWER(email) = ?', [$email]); } }) ->latest('id') ->first(); if (! $dipendente instanceof FornitoreDipendente) { return null; } return Fornitore::query()->find((int) $dipendente->fornitore_id); } /** * @param array $oauthContext */ private function resolveRedirectTarget(array $oauthContext): string { $returnTo = trim((string) ($oauthContext['return_to'] ?? '')); return $returnTo !== '' && str_starts_with($returnTo, '/') ? $returnTo : '/admin-filament/impostazioni/scheda-amministratore'; } }