add OAUth2.0
This commit is contained in:
@@ -7,7 +7,6 @@ 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;
|
||||
@@ -109,10 +108,4 @@ 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,14 +4,12 @@ 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,16 +134,6 @@ 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,7 +12,6 @@ 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;
|
||||
|
||||
@@ -402,6 +401,10 @@ 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]);
|
||||
@@ -523,6 +526,10 @@ 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);
|
||||
@@ -643,6 +650,10 @@ 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',
|
||||
@@ -659,6 +670,27 @@ 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) {
|
||||
@@ -717,6 +749,133 @@ 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();
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
<?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,8 +3,6 @@
|
||||
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,7 +10,6 @@ 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,7 +14,6 @@ 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,7 +3,6 @@
|
||||
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,7 +10,6 @@ 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
|
||||
@@ -19,20 +18,7 @@ class StorageRepositoryController extends Controller
|
||||
private readonly StorageRepositoryService $repoService
|
||||
) {}
|
||||
|
||||
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
|
||||
public function store(Request $request): RedirectResponse
|
||||
{
|
||||
$this->authorizeWrite('settings');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user