281 lines
11 KiB
PHP
281 lines
11 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\GoogleAccountStore;
|
|
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 = [
|
|
...GoogleAccountStore::SCOPES,
|
|
];
|
|
|
|
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', []);
|
|
$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<string, mixed> $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';
|
|
}
|
|
}
|