Revert OAuth2.0
This commit is contained in:
@@ -7,6 +7,7 @@ namespace App\Console\Commands;
|
||||
use App\Models\AppSetting;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\View;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class ExportHelpPdf extends Command
|
||||
{
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum GoogleService: string
|
||||
{
|
||||
case Gmail = 'gmail';
|
||||
case Drive = 'drive';
|
||||
case Calendar = 'calendar';
|
||||
|
||||
public function scope(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::Gmail => 'https://mail.google.com/',
|
||||
self::Drive => 'https://www.googleapis.com/auth/drive',
|
||||
self::Calendar => 'https://www.googleapis.com/auth/calendar',
|
||||
};
|
||||
}
|
||||
|
||||
public function label(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::Gmail => 'Gmail (lettura e invio email)',
|
||||
self::Drive => 'Google Drive (file)',
|
||||
self::Calendar => 'Google Calendar (eventi)',
|
||||
};
|
||||
}
|
||||
|
||||
public function icon(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::Gmail => 'fa-google',
|
||||
self::Drive => 'fa-google-drive',
|
||||
self::Calendar => 'fa-calendar-alt',
|
||||
};
|
||||
}
|
||||
|
||||
public function hexColor(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::Gmail => '#EA4335',
|
||||
self::Drive => '#34A853',
|
||||
self::Calendar => '#4285F4',
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ namespace App\Http\Controllers\Admin;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\AppSetting;
|
||||
use App\Services\BackupService;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\View\View;
|
||||
@@ -108,4 +109,10 @@ class BackupController extends Controller
|
||||
->with('success', 'Configurazione backup automatico salvata.');
|
||||
}
|
||||
|
||||
public function downloadMigrationScript(): JsonResponse
|
||||
{
|
||||
$script = view('admin.backup.migration-script')->render();
|
||||
|
||||
return response()->json(['script' => $script]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,12 +4,14 @@ namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\ActivityLog;
|
||||
use App\Models\EmailSetting;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Password;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Symfony\Component\Mime\Address;
|
||||
|
||||
class AuthController extends Controller
|
||||
{
|
||||
|
||||
@@ -134,6 +134,16 @@ class DocumentoController extends Controller
|
||||
));
|
||||
}
|
||||
|
||||
public function create()
|
||||
{
|
||||
$this->authorizeWrite('documenti');
|
||||
$individui = Individuo::orderBy('cognome')->orderBy('nome')->get();
|
||||
$gruppi = Gruppo::orderBy('nome')->get();
|
||||
$eventi = Evento::orderBy('nome_evento')->get();
|
||||
$tipologie = TipologiaDocumento::attive();
|
||||
return view('documenti.create', compact('individui', 'gruppi', 'eventi', 'tipologie'));
|
||||
}
|
||||
|
||||
public function edit($documento)
|
||||
{
|
||||
$this->authorizeWrite('documenti');
|
||||
|
||||
@@ -12,6 +12,7 @@ use App\Models\Gruppo;
|
||||
use App\Models\Individuo;
|
||||
use App\Models\Documento;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
@@ -401,10 +402,6 @@ class EmailController extends Controller
|
||||
|
||||
$this->ensureFoldersExist();
|
||||
|
||||
if ($settings->isOauth()) {
|
||||
return $this->syncEmailsViaOAuth($settings);
|
||||
}
|
||||
|
||||
$mailbox = $this->connectImap($settings);
|
||||
|
||||
\Illuminate\Support\Facades\Log::info('IMAP connection established', ['host' => $settings->imap_host]);
|
||||
@@ -526,10 +523,6 @@ class EmailController extends Controller
|
||||
$encryption
|
||||
);
|
||||
|
||||
if ($settings->isOauth()) {
|
||||
$dsn .= '&auth_mode=xoauth2';
|
||||
}
|
||||
|
||||
try {
|
||||
$transport = \Symfony\Component\Mailer\Transport::fromDsn($dsn);
|
||||
$mailer = new \Symfony\Component\Mailer\Mailer($transport);
|
||||
@@ -650,10 +643,6 @@ class EmailController extends Controller
|
||||
throw new \Exception('Configurazione IMAP non valida');
|
||||
}
|
||||
|
||||
if ($settings->isOauth()) {
|
||||
return $this->connectOAuthImap($settings);
|
||||
}
|
||||
|
||||
$encryption = match ($settings->imap_encryption) {
|
||||
'ssl' => 'ssl',
|
||||
'tls' => 'tls',
|
||||
@@ -670,27 +659,6 @@ class EmailController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
private function connectOAuthImap($settings): \Webklex\PHPIMAP\Client
|
||||
{
|
||||
$token = $settings->getOAuthAccessToken();
|
||||
if (!$token) {
|
||||
throw new \Exception('Impossibile ottenere il token OAuth per IMAP');
|
||||
}
|
||||
|
||||
$client = new \Webklex\PHPIMAP\Client([
|
||||
'host' => $settings->imap_host,
|
||||
'port' => $settings->imap_port ?? 993,
|
||||
'encryption' => $settings->imap_encryption ?? 'ssl',
|
||||
'validate_cert' => false,
|
||||
'username' => $settings->imap_username,
|
||||
'password' => $token,
|
||||
'authentication' => 'oauth',
|
||||
]);
|
||||
$client->connect();
|
||||
|
||||
return $client;
|
||||
}
|
||||
|
||||
private function processImapMessage($imapMessage, $imapFolder)
|
||||
{
|
||||
$folderType = match ($imapFolder) {
|
||||
@@ -749,133 +717,6 @@ class EmailController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private function syncEmailsViaOAuth($settings): bool
|
||||
{
|
||||
$this->ensureFoldersExist();
|
||||
|
||||
$client = $this->connectOAuthImap($settings);
|
||||
|
||||
$folderMappings = [
|
||||
'INBOX' => 'inbox',
|
||||
'Sent' => 'sent',
|
||||
'[Gmail]/Sent Mail' => 'sent',
|
||||
'Sent Mail' => 'sent',
|
||||
'Drafts' => 'drafts',
|
||||
'[Gmail]/Drafts' => 'drafts',
|
||||
'Trash' => 'trash',
|
||||
'[Gmail]/Trash' => 'trash',
|
||||
];
|
||||
|
||||
$totalNew = 0;
|
||||
|
||||
foreach (array_keys($folderMappings) as $imapFolderName) {
|
||||
try {
|
||||
$folder = $client->getFolder($imapFolderName);
|
||||
if (!$folder) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$messages = $folder->messages()
|
||||
->setFetchFlags(true)
|
||||
->setFetchBody(true)
|
||||
->limit(100)
|
||||
->get();
|
||||
|
||||
foreach ($messages as $imapMessage) {
|
||||
$messageId = $imapMessage->getMessageId() ?? uniqid('msg_');
|
||||
$existing = EmailMessage::where('message_id', $messageId)->first();
|
||||
if ($existing) {
|
||||
continue;
|
||||
}
|
||||
$this->processWebklexMessage($imapMessage, $imapFolderName);
|
||||
$totalNew++;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
\Illuminate\Support\Facades\Log::error('Error processing OAuth IMAP folder', ['folder' => $imapFolderName, 'error' => $e->getMessage()]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$client->disconnect();
|
||||
\Illuminate\Support\Facades\Log::info('OAuth email sync completed', ['new' => $totalNew]);
|
||||
return true;
|
||||
}
|
||||
|
||||
private function processWebklexMessage($imapMessage, $imapFolder)
|
||||
{
|
||||
$folderType = match ($imapFolder) {
|
||||
'INBOX', 'inbox' => 'inbox',
|
||||
'Sent', 'sent', '[Gmail]/Sent Mail' => 'sent',
|
||||
'Drafts', 'drafts', '[Gmail]/Drafts' => 'drafts',
|
||||
'Trash', 'trash', '[Gmail]/Trash' => 'trash',
|
||||
'Archive', 'archive', '[Gmail]/All Mail' => 'archive',
|
||||
'Starred', 'starred', '[Gmail]/Starred' => 'starred',
|
||||
default => 'inbox',
|
||||
};
|
||||
|
||||
$folder = EmailFolder::where('type', $folderType)->first();
|
||||
if (!$folder) return;
|
||||
|
||||
$messageId = $imapMessage->getMessageId() ?? uniqid('msg_');
|
||||
$existing = EmailMessage::where('message_id', $messageId)->first();
|
||||
if ($existing) return;
|
||||
|
||||
$from = $imapMessage->getFrom();
|
||||
$to = $imapMessage->getTo();
|
||||
|
||||
$fromName = '';
|
||||
$fromEmail = '';
|
||||
if ($from && $from->first()) {
|
||||
$fromName = $from->first()->personal ?? '';
|
||||
$fromEmail = ($from->first()->mailbox ?? '') . '@' . ($from->first()->host ?? '');
|
||||
}
|
||||
|
||||
$toEmail = '';
|
||||
if ($to && $to->first()) {
|
||||
$toEmail = ($to->first()->mailbox ?? '') . '@' . ($to->first()->host ?? '');
|
||||
}
|
||||
|
||||
$message = EmailMessage::create([
|
||||
'email_folder_id' => $folder->id,
|
||||
'message_id' => $messageId,
|
||||
'subject' => mb_substr($imapMessage->getSubject() ?? '(senza oggetto)', 0, 255),
|
||||
'from_name' => mb_substr($fromName, 0, 255),
|
||||
'from_email' => mb_substr($fromEmail, 0, 255),
|
||||
'to_email' => mb_substr($toEmail, 0, 255),
|
||||
'body_text' => $imapMessage->getTextBody() ?? '',
|
||||
'body_html' => $imapMessage->getHTMLBody() ?? '',
|
||||
'is_read' => !($imapMessage->getFlags()['UNSEEN'] ?? false),
|
||||
'is_starred' => !empty($imapMessage->getFlags()['FLAGGED']),
|
||||
'is_sent' => $folderType === 'sent',
|
||||
'received_at' => $imapMessage->getDate() ?? now(),
|
||||
'imap_uid' => $imapMessage->getUid() ?? null,
|
||||
]);
|
||||
|
||||
$attachments = $imapMessage->getAttachments();
|
||||
if ($attachments && count($attachments) > 0) {
|
||||
foreach ($attachments as $attachment) {
|
||||
try {
|
||||
$filename = $attachment->getName() ?? 'attachment';
|
||||
$contents = $attachment->getContent();
|
||||
$path = 'email-attachments/' . \Illuminate\Support\Str::uuid() . '_' . $filename;
|
||||
\Illuminate\Support\Facades\Storage::put($path, $contents);
|
||||
|
||||
EmailAttachment::create([
|
||||
'email_message_id' => $message->id,
|
||||
'filename' => $filename,
|
||||
'mime_type' => $attachment->getMimeType() ?? 'application/octet-stream',
|
||||
'size' => strlen($contents),
|
||||
'file_path' => $path,
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
\Illuminate\Support\Facades\Log::error('Error saving OAuth attachment', ['error' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function ensureFoldersExist()
|
||||
{
|
||||
$systemFolders = EmailFolder::getSystemFolders();
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Enums\GoogleService;
|
||||
use App\Services\GoogleOAuthService;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class GoogleOAuthController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
private readonly GoogleOAuthService $oauthService,
|
||||
) {}
|
||||
|
||||
public function connect(GoogleService $service): RedirectResponse
|
||||
{
|
||||
$url = $this->oauthService->getAuthorizationUrl($service);
|
||||
return redirect()->away($url);
|
||||
}
|
||||
|
||||
public function callback(Request $request): RedirectResponse
|
||||
{
|
||||
$request->validate([
|
||||
'code' => 'required|string',
|
||||
'state' => 'required|string|in:' . implode(',', array_column(GoogleService::cases(), 'value')),
|
||||
]);
|
||||
|
||||
$service = GoogleService::from($request->state);
|
||||
|
||||
try {
|
||||
$this->oauthService->handleCallback($request->code, $service);
|
||||
return redirect()->route('impostazioni.index', ['#google-services'])
|
||||
->with('success', "Google {$service->label()} connected successfully.");
|
||||
} catch (\Exception $e) {
|
||||
return redirect()->route('impostazioni.index', ['#google-services'])
|
||||
->with('error', 'Google OAuth error: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function revoke(GoogleService $service): RedirectResponse
|
||||
{
|
||||
try {
|
||||
$this->oauthService->revoke($service);
|
||||
return redirect()->route('impostazioni.index', ['#google-services'])
|
||||
->with('success', "Google {$service->label()} revoked successfully.");
|
||||
} catch (\Exception $e) {
|
||||
return redirect()->route('impostazioni.index', ['#google-services'])
|
||||
->with('error', 'Revocation error: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function status(): View
|
||||
{
|
||||
$services = [];
|
||||
foreach (GoogleService::cases() as $service) {
|
||||
$services[] = [
|
||||
'service' => $service,
|
||||
'authorized' => $this->oauthService->isAuthorized($service),
|
||||
];
|
||||
}
|
||||
|
||||
return view('google-oauth.status', ['services' => $services]);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,8 @@
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Gruppo;
|
||||
use App\Models\Individuo;
|
||||
use App\Models\Ruolo;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class GruppoMembroController extends Controller
|
||||
|
||||
@@ -10,6 +10,7 @@ use App\Models\Gruppo;
|
||||
use App\Models\Individuo;
|
||||
use App\Models\EmailMessage;
|
||||
use App\Models\MailingList;
|
||||
use App\Models\Notifica;
|
||||
use App\Services\BackupService;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ use App\Models\TipologiaDocumento;
|
||||
use App\Models\TipologiaEvento;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Individuo;
|
||||
use App\Models\Contatto;
|
||||
use App\Models\Documento;
|
||||
use App\Models\Gruppo;
|
||||
use App\Models\Comune;
|
||||
|
||||
@@ -10,6 +10,7 @@ use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\View\View;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
|
||||
class StorageRepositoryController extends Controller
|
||||
@@ -18,7 +19,20 @@ class StorageRepositoryController extends Controller
|
||||
private readonly StorageRepositoryService $repoService
|
||||
) {}
|
||||
|
||||
public function store(Request $request): RedirectResponse
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorizeWrite('settings');
|
||||
|
||||
$repositories = StorageRepository::orderBy('ordine')->get();
|
||||
|
||||
if ($request->ajax()) {
|
||||
return response()->json($repositories);
|
||||
}
|
||||
|
||||
return view('storage-repositories.index', compact('repositories'));
|
||||
}
|
||||
|
||||
public function store(Request $request): RedirectResponse
|
||||
{
|
||||
$this->authorizeWrite('settings');
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class EmailAttachment extends Model
|
||||
{
|
||||
|
||||
@@ -4,8 +4,6 @@ namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use App\Enums\GoogleService;
|
||||
use App\Services\GoogleOAuthService;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
|
||||
class EmailSetting extends Model
|
||||
@@ -16,7 +14,7 @@ class EmailSetting extends Model
|
||||
'imap_host', 'imap_port', 'imap_encryption', 'imap_username', 'imap_password',
|
||||
'smtp_host', 'smtp_port', 'smtp_encryption', 'smtp_username', 'smtp_password',
|
||||
'email_address', 'email_name', 'reply_to', 'sync_interval_minutes',
|
||||
'last_sync_at', 'is_active', 'signature', 'signature_enabled', 'auth_method'
|
||||
'last_sync_at', 'is_active', 'signature', 'signature_enabled'
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
@@ -43,9 +41,6 @@ class EmailSetting extends Model
|
||||
|
||||
public function getDecryptedPassword(): string
|
||||
{
|
||||
if ($this->isOauth()) {
|
||||
return $this->getOAuthAccessToken() ?? '';
|
||||
}
|
||||
if (empty($this->imap_password)) {
|
||||
return '';
|
||||
}
|
||||
@@ -58,9 +53,6 @@ class EmailSetting extends Model
|
||||
|
||||
public function getDecryptedSmtpPassword(): string
|
||||
{
|
||||
if ($this->isOauth()) {
|
||||
return $this->getOAuthAccessToken() ?? '';
|
||||
}
|
||||
if (empty($this->smtp_password)) {
|
||||
return $this->getDecryptedPassword();
|
||||
}
|
||||
@@ -71,26 +63,6 @@ class EmailSetting extends Model
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function isOauth(): bool
|
||||
{
|
||||
return $this->auth_method === 'oauth';
|
||||
}
|
||||
|
||||
public function getOAuthAccessToken(): ?string
|
||||
{
|
||||
if (!$this->isOauth()) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
$oauth = app(GoogleOAuthService::class);
|
||||
return $oauth->getAccessToken(GoogleService::Gmail);
|
||||
} catch (\Exception $e) {
|
||||
\Illuminate\Support\Facades\Log::error('OAuth token refresh failed: ' . $e->getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public function getSmtpConfig(): array
|
||||
{
|
||||
return [
|
||||
@@ -108,10 +80,6 @@ class EmailSetting extends Model
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($this->isOauth()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
$encryption = match ($this->imap_encryption) {
|
||||
'ssl' => 'ssl',
|
||||
@@ -135,10 +103,6 @@ class EmailSetting extends Model
|
||||
public function testConnection(): array
|
||||
{
|
||||
try {
|
||||
if ($this->isOauth()) {
|
||||
return $this->testOAuthImapConnection();
|
||||
}
|
||||
|
||||
$mailbox = $this->getImapClient();
|
||||
|
||||
if (!$mailbox) {
|
||||
@@ -161,32 +125,6 @@ class EmailSetting extends Model
|
||||
}
|
||||
}
|
||||
|
||||
private function testOAuthImapConnection(): array
|
||||
{
|
||||
try {
|
||||
$token = $this->getOAuthAccessToken();
|
||||
if (!$token) {
|
||||
return ['success' => false, 'message' => 'Impossibile ottenere il token OAuth. Verifica la connessione Google.'];
|
||||
}
|
||||
|
||||
$client = new \Webklex\PHPIMAP\Client([
|
||||
'host' => $this->imap_host,
|
||||
'port' => $this->imap_port ?? 993,
|
||||
'encryption' => $this->imap_encryption ?? 'ssl',
|
||||
'validate_cert' => false,
|
||||
'username' => $this->imap_username,
|
||||
'password' => $token,
|
||||
'authentication' => 'oauth',
|
||||
]);
|
||||
$client->connect();
|
||||
$client->disconnect();
|
||||
|
||||
return ['success' => true, 'message' => 'Connessione IMAP OAuth riuscita! Server: ' . $this->imap_host];
|
||||
} catch (\Exception $e) {
|
||||
return ['success' => false, 'message' => 'Errore connessione IMAP OAuth: ' . $e->getMessage()];
|
||||
}
|
||||
}
|
||||
|
||||
public static function getActive()
|
||||
{
|
||||
return self::where('is_active', true)->first();
|
||||
|
||||
@@ -6,6 +6,7 @@ use App\Traits\HasTagsLight;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class Evento extends Model
|
||||
{
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\GoogleService;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
|
||||
class GoogleOAuthConnection extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'service',
|
||||
'client_id',
|
||||
'client_secret',
|
||||
'refresh_token',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'service' => GoogleService::class,
|
||||
];
|
||||
}
|
||||
|
||||
public function getClientSecretAttribute(?string $value): ?string
|
||||
{
|
||||
return $value ? Crypt::decryptString($value) : null;
|
||||
}
|
||||
|
||||
public function setClientSecretAttribute(?string $value): void
|
||||
{
|
||||
$this->attributes['client_secret'] = $value ? Crypt::encryptString($value) : null;
|
||||
}
|
||||
|
||||
public function getRefreshTokenAttribute(?string $value): ?string
|
||||
{
|
||||
return $value ? Crypt::decryptString($value) : null;
|
||||
}
|
||||
|
||||
public function setRefreshTokenAttribute(?string $value): void
|
||||
{
|
||||
$this->attributes['refresh_token'] = $value ? Crypt::encryptString($value) : null;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ namespace App\Providers;
|
||||
|
||||
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Spatie\Permission\Models\Permission;
|
||||
|
||||
class AuthServiceProvider extends ServiceProvider
|
||||
|
||||
@@ -5,8 +5,11 @@ declare(strict_types=1);
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\AppSetting;
|
||||
use App\Models\Notifica;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use RuntimeException;
|
||||
use ZipArchive;
|
||||
|
||||
|
||||
@@ -4,9 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Enums\GoogleService;
|
||||
use App\Models\CalendarioConnessione;
|
||||
use App\Services\GoogleOAuthService;
|
||||
use App\Models\Evento;
|
||||
use Carbon\Carbon;
|
||||
use Google\Client as GoogleClient;
|
||||
@@ -329,16 +327,6 @@ class GoogleCalendarSyncService
|
||||
|
||||
private function buildClient(CalendarioConnessione $connessione): GoogleClient
|
||||
{
|
||||
try {
|
||||
$oauth = app(GoogleOAuthService::class);
|
||||
$connection = $oauth->getConnection(GoogleService::Calendar);
|
||||
if ($connection) {
|
||||
return $oauth->buildClient(GoogleService::Calendar);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
\Illuminate\Support\Facades\Log::warning('Central Calendar OAuth failed, falling back: ' . $e->getMessage());
|
||||
}
|
||||
|
||||
$config = $connessione->getDecryptedConfig();
|
||||
|
||||
$client = new GoogleClient();
|
||||
|
||||
@@ -1,150 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Enums\GoogleService;
|
||||
use App\Models\GoogleOAuthConnection;
|
||||
use Google\Client;
|
||||
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class GoogleOAuthService
|
||||
{
|
||||
private const TOKEN_URI = 'https://oauth2.googleapis.com/token';
|
||||
|
||||
private static function getRedirectUri(): string
|
||||
{
|
||||
$subfolder = config('app.url');
|
||||
return rtrim($subfolder, '/') . '/auth/google/callback';
|
||||
}
|
||||
|
||||
public function getAuthorizationUrl(GoogleService $service): string
|
||||
{
|
||||
$client = $this->createClient($service);
|
||||
$client->setRedirectUri(self::getRedirectUri());
|
||||
$client->setState($service->value);
|
||||
$client->setAccessType('offline');
|
||||
$client->setPrompt('consent');
|
||||
$client->setIncludeGrantedScopes(true);
|
||||
|
||||
return $client->createAuthUrl();
|
||||
}
|
||||
|
||||
public function handleCallback(string $code, GoogleService $service): GoogleOAuthConnection
|
||||
{
|
||||
$client = $this->createClient($service);
|
||||
$client->setRedirectUri(self::getRedirectUri());
|
||||
|
||||
$token = $client->fetchAccessTokenWithAuthCode($code);
|
||||
|
||||
if (isset($token['error'])) {
|
||||
throw new \RuntimeException('Google OAuth error: ' . ($token['error_description'] ?? $token['error']));
|
||||
}
|
||||
|
||||
if (!isset($token['refresh_token'])) {
|
||||
throw new \RuntimeException('No refresh token returned. Ensure access_type=offline and prompt=consent are set.');
|
||||
}
|
||||
|
||||
$clientId = $client->getClientId();
|
||||
$clientSecret = $client->getClientSecret();
|
||||
|
||||
return GoogleOAuthConnection::updateOrCreate(
|
||||
['service' => $service->value],
|
||||
[
|
||||
'client_id' => $clientId,
|
||||
'client_secret' => $clientSecret,
|
||||
'refresh_token' => $token['refresh_token'],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function revoke(GoogleService $service): void
|
||||
{
|
||||
$connection = $this->getConnection($service);
|
||||
if (!$connection) {
|
||||
return;
|
||||
}
|
||||
|
||||
$client = $this->createClient($service);
|
||||
$client->revokeToken();
|
||||
$connection->delete();
|
||||
}
|
||||
|
||||
public function getAccessToken(GoogleService $service): ?string
|
||||
{
|
||||
$connection = $this->getConnection($service);
|
||||
if (!$connection) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
$client = $this->createClient($service);
|
||||
$client->setAccessToken([
|
||||
'access_token' => '',
|
||||
'refresh_token' => $connection->refresh_token,
|
||||
'client_id' => $connection->client_id,
|
||||
'client_secret' => $connection->client_secret,
|
||||
]);
|
||||
|
||||
if ($client->isAccessTokenExpired()) {
|
||||
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
|
||||
$newToken = $client->getAccessToken();
|
||||
if (isset($newToken['refresh_token'])) {
|
||||
$connection->refresh_token = $newToken['refresh_token'];
|
||||
$connection->save();
|
||||
}
|
||||
}
|
||||
|
||||
$accessToken = $client->getAccessToken();
|
||||
return $accessToken['access_token'] ?? null;
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Google OAuth token refresh failed for ' . $service->value . ': ' . $e->getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public function isAuthorized(GoogleService $service): bool
|
||||
{
|
||||
return $this->getConnection($service) !== null;
|
||||
}
|
||||
|
||||
public function getConnection(GoogleService $service): ?GoogleOAuthConnection
|
||||
{
|
||||
return GoogleOAuthConnection::where('service', $service->value)->first();
|
||||
}
|
||||
|
||||
public function buildClient(GoogleService $service): Client
|
||||
{
|
||||
$client = $this->createClient($service);
|
||||
$connection = $this->getConnection($service);
|
||||
|
||||
if ($connection) {
|
||||
$client->setAccessToken([
|
||||
'access_token' => '',
|
||||
'refresh_token' => $connection->refresh_token,
|
||||
'client_id' => $connection->client_id,
|
||||
'client_secret' => $connection->client_secret,
|
||||
]);
|
||||
|
||||
if ($client->isAccessTokenExpired()) {
|
||||
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
|
||||
}
|
||||
}
|
||||
|
||||
return $client;
|
||||
}
|
||||
|
||||
private function createClient(GoogleService $service): Client
|
||||
{
|
||||
$client = new Client();
|
||||
$client->setApplicationName(config('app.name'));
|
||||
$client->setScopes([$service->scope()]);
|
||||
$client->setClientId(config('services.google.client_id'));
|
||||
$client->setClientSecret(config('services.google.client_secret'));
|
||||
$client->setRedirectUri(self::getRedirectUri());
|
||||
|
||||
return $client;
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Enums\GoogleService;
|
||||
use App\Models\StorageRepository;
|
||||
use App\Services\GoogleOAuthService;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use League\Flysystem\Filesystem;
|
||||
@@ -71,20 +69,6 @@ class StorageRepositoryService
|
||||
private function checkGoogleDriveApiStatus(array $config): ?string
|
||||
{
|
||||
try {
|
||||
try {
|
||||
$oauth = app(GoogleOAuthService::class);
|
||||
$connection = $oauth->getConnection(GoogleService::Drive);
|
||||
if ($connection) {
|
||||
$client = $oauth->buildClient(GoogleService::Drive);
|
||||
$service = new \Google_Service_Drive($client);
|
||||
$rootId = $config['root_folder_id'] ?? 'root';
|
||||
$service->files->get($rootId, ['fields' => 'id']);
|
||||
return null;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// fall through to config-based auth
|
||||
}
|
||||
|
||||
$client = new \Google_Client();
|
||||
$client->setClientId($config['client_id']);
|
||||
$client->setClientSecret($config['client_secret']);
|
||||
@@ -260,18 +244,6 @@ class StorageRepositoryService
|
||||
{
|
||||
$config = $repo->getDecryptedConfig();
|
||||
|
||||
try {
|
||||
$oauth = app(GoogleOAuthService::class);
|
||||
$connection = $oauth->getConnection(GoogleService::Drive);
|
||||
if ($connection) {
|
||||
$client = $oauth->buildClient(GoogleService::Drive);
|
||||
$service = new \Google_Service_Drive($client);
|
||||
return $this->readGoogleDriveFileWithService($service, $config, $normalizedPath, $basename);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
\Illuminate\Support\Facades\Log::warning('Central Google Drive OAuth failed in readGoogleDriveFile, falling back: ' . $e->getMessage());
|
||||
}
|
||||
|
||||
$client = new \Google_Client();
|
||||
$client->setClientId($config['client_id']);
|
||||
$client->setClientSecret($config['client_secret']);
|
||||
@@ -311,40 +283,6 @@ class StorageRepositoryService
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
private function readGoogleDriveFileWithService(\Google_Service_Drive $service, array $config, string $normalizedPath, string $basename): array
|
||||
{
|
||||
$rootFolderId = $config['root_folder_id'] ?? 'root';
|
||||
|
||||
$fileId = $this->findGoogleDriveFileId($service, $rootFolderId, $normalizedPath);
|
||||
$file = $service->files->get($fileId, ['fields' => 'id,name,mimeType,size']);
|
||||
|
||||
$mimeType = $file->getMimeType();
|
||||
|
||||
if ($mimeType && str_starts_with($mimeType, 'application/vnd.google-apps.')) {
|
||||
return $this->exportGoogleDriveDoc($service, $fileId, $mimeType, $basename);
|
||||
}
|
||||
|
||||
$response = $service->files->get($fileId, ['alt' => 'media']);
|
||||
$body = $response->getBody();
|
||||
$stream = $body instanceof \Psr\Http\Message\StreamInterface ? $body->detach() : null;
|
||||
|
||||
if (!$stream) {
|
||||
$stream = fopen('php://temp', 'r+');
|
||||
fwrite($stream, (string) $body);
|
||||
rewind($stream);
|
||||
}
|
||||
|
||||
$fileSize = $file->getSize();
|
||||
|
||||
return [
|
||||
'stream' => $stream,
|
||||
'mimeType' => $mimeType,
|
||||
'basename' => $basename,
|
||||
'fileSize' => $fileSize !== null ? (int) $fileSize : null,
|
||||
];
|
||||
}
|
||||
|
||||
private function exportGoogleDriveDoc(\Google_Service_Drive $service, string $fileId, string $googleMimeType, string $originalBasename): array
|
||||
{
|
||||
$exportMap = [
|
||||
|
||||
Reference in New Issue
Block a user