netgescon-day0/app/Http/Controllers/Integrations/GoogleOAuthController.php

239 lines
9.5 KiB
PHP

<?php
namespace App\Http\Controllers\Integrations;
use App\Http\Controllers\Controller;
use App\Models\Amministratore;
use App\Models\Fornitore;
use App\Models\FornitoreDipendente;
use App\Models\Stabile;
use App\Models\User;
use App\Support\StabileContext;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Throwable;
class GoogleOAuthController extends Controller
{
private const GOOGLE_SCOPES = [
'openid',
'email',
'profile',
'https://www.googleapis.com/auth/calendar.events',
'https://www.googleapis.com/auth/contacts',
'https://www.googleapis.com/auth/drive.file',
];
public function redirect(): RedirectResponse
{
$amministratore = $this->resolveAmministratore();
if (! $amministratore instanceof Amministratore) {
abort(403, 'Amministratore non disponibile per questa sessione.');
}
$google = Arr::get($amministratore->impostazioni ?? [], 'google', []);
$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_state', $state);
$query = http_build_query([
'client_id' => $clientId,
'redirect_uri' => $redirectUri,
'response_type' => 'code',
'scope' => implode(' ', self::GOOGLE_SCOPES),
'access_type' => 'offline',
'prompt' => 'consent',
'include_granted_scopes' => 'true',
'state' => $state,
]);
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', []);
$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 {
$incomingState = (string) request()->query('state', '');
$sessionState = (string) session()->pull('google_oauth_state', '');
if ($incomingState === '' || $sessionState === '' || ! hash_equals($sessionState, $incomingState)) {
return redirect('/admin-filament/impostazioni/scheda-amministratore')
->with('error', 'Stato OAuth non valido. Riprova il collegamento Google.');
}
$code = (string) request()->query('code', '');
if ($code === '') {
return redirect('/admin-filament/impostazioni/scheda-amministratore')
->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('/admin-filament/impostazioni/scheda-amministratore')
->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', '');
$impostazioni = $amministratore->impostazioni ?? [];
$impostazioni['google'] = array_merge($google, [
'oauth' => [
'connected' => true,
'email' => $email,
'name' => $name,
'google_user_id' => $googleUserId,
'access_token' => $accessToken,
'refresh_token' => $refreshToken,
'expires_in' => $expiresIn,
'connected_at' => now()->toDateTimeString(),
],
]);
$amministratore->impostazioni = $impostazioni;
$amministratore->save();
return redirect('/admin-filament/impostazioni/scheda-amministratore')
->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('/admin-filament/impostazioni/scheda-amministratore')
->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.');
}
$impostazioni = $amministratore->impostazioni ?? [];
$google = Arr::get($impostazioni, 'google', []);
$google['oauth'] = [
'connected' => false,
'disconnected_at' => now()->toDateTimeString(),
];
$impostazioni['google'] = $google;
$amministratore->impostazioni = $impostazioni;
$amministratore->save();
return redirect('/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);
}
}