false, 'message' => 'Méthode non autorisée']); exit; } // Boot session for CSRF App::boot(); // Validate CSRF token if ( empty($_POST['csrf_token']) || empty($_SESSION['csrf_token']) || !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token']) ) { http_response_code(403); echo json_encode(['success' => false, 'message' => 'Token de sécurité invalide']); exit; } // Validate input $thesisId = isset($_POST['thesis_id']) ? (int)$_POST['thesis_id'] : 0; $email = isset($_POST['email']) ? trim($_POST['email']) : ''; $justification = isset($_POST['justification']) ? trim($_POST['justification']) : ''; if ($thesisId <= 0 || !filter_var($email, FILTER_VALIDATE_EMAIL)) { http_response_code(400); echo json_encode(['success' => false, 'message' => 'Données invalides']); exit; } // Truncate justification to a safe length if (mb_strlen($justification) > 2000) { $justification = mb_substr($justification, 0, 2000); } $db = Database::getInstance(); // Check if thesis exists and is published $thesis = $db->getThesis($thesisId); if (!$thesis || !$thesis['is_published']) { http_response_code(404); echo json_encode(['success' => false, 'message' => 'TFE non trouvé']); exit; } // Check if restricted files feature is enabled if (!$db->isRestrictedFilesEnabled()) { http_response_code(403); echo json_encode(['success' => false, 'message' => 'Cette fonctionnalité est désactivée']); exit; } // Check if thesis actually has restricted access (access_type_id = 2) $accessTypeId = $db->getThesisAccessTypeId($thesisId); if ($accessTypeId !== 2) { http_response_code(400); echo json_encode(['success' => false, 'message' => 'Ce TFE ne nécessite pas d\'accès restreint']); exit; } // Rate limiting: max 3 requests per 10 minutes per IP $rateLimitKey = 'access_request_' . ($_SERVER['REMOTE_ADDR'] ?? 'unknown'); if (!RateLimit::check($rateLimitKey, 3, 600)) { http_response_code(429); echo json_encode(['success' => false, 'message' => 'Trop de requêtes. Veuillez réessayer dans quelques minutes.']); exit; } // Check for existing request $existingRequest = $db->getExistingAccessRequest($thesisId, $email); if ($existingRequest) { if ($existingRequest['status'] === 'approved') { // Re-generate and re-send a fresh 24h token try { $newToken = $db->generateAccessToken((int)$existingRequest['id'], 24); $host = $_SERVER['HTTP_HOST'] ?? 'xamxam.erg.be'; $accessUrl = "https://{$host}/validate-access?token={$newToken}&thesis={$thesisId}"; $subject = "Accès (renvoi) - TFE : " . $thesis['title']; $body = buildAutoApprovalEmail($thesis['title'], $thesis['authors'] ?? '', $accessUrl); $plain = htmlToPlain($body); SmtpRelay::send($db, $email, $subject, $body, $plain); echo json_encode([ 'success' => true, 'message' => 'Un nouvel email d\'accès vous a été envoyé.', 'status' => 'resent', ]); } catch (Exception $e) { error_log('Access request resend failed: ' . $e->getMessage()); echo json_encode(['success' => true, 'message' => 'Votre accès est déjà approuvé. Si vous n\'avez pas reçu l\'email, contactez l\'administrateur.']); } exit; } if ($existingRequest['status'] === 'pending') { http_response_code(400); echo json_encode([ 'success' => false, 'message' => 'Une demande est déjà en cours de traitement pour cet email.', 'status' => 'already_pending', ]); exit; } } // Determine if auto-approved (ERG domain) $emailLower = strtolower($email); $isErgEmail = ( str_ends_with($emailLower, '@erg.school') || str_ends_with($emailLower, '@erg.be') ); $host = $_SERVER['HTTP_HOST'] ?? 'xamxam.erg.be'; try { if ($isErgEmail) { // Auto-approve: create request + short-lived 24h token $requestId = $db->createFileAccessRequest($thesisId, $email, null); $token = $db->approveAccessRequest($requestId, null, 24); $accessUrl = "https://{$host}/validate-access?token={$token}&thesis={$thesisId}"; $subject = "Accès accordé - TFE : " . $thesis['title']; $body = buildAutoApprovalEmail($thesis['title'], $thesis['authors'] ?? '', $accessUrl); $plain = htmlToPlain($body); SmtpRelay::send($db, $email, $subject, $body, $plain); http_response_code(200); echo json_encode([ 'success' => true, 'message' => 'Un email avec le lien d\'accès vous a été envoyé. Le lien est valable 24 heures.', 'status' => 'auto_approved', ]); } else { // External email: justification required, create pending request if (empty($justification)) { http_response_code(400); echo json_encode([ 'success' => false, 'message' => 'Une justification est requise pour les emails externes à l\'ERG.', 'status' => 'justification_required', ]); exit; } $requestId = $db->createFileAccessRequest($thesisId, $email, $justification); // Notify admins $thesisYear = $thesis['year'] ?? ''; $subject = "Nouvelle demande d'accès - TFE : " . $thesis['title']; $body = buildAdminNotificationEmail( $thesis['title'], $thesis['authors'] ?? '', $thesisYear, $email, $justification, $host ); $plain = htmlToPlain($body); $settings = SmtpRelay::getSettings($db); if (!empty($settings['from_email'])) { SmtpRelay::send($db, $settings['from_email'], $subject, $body, $plain); } http_response_code(200); echo json_encode([ 'success' => true, 'message' => 'Votre demande a été envoyée et sera examinée par un administrateur.', 'status' => 'pending', ]); } } catch (Exception $e) { error_log('Access request failed: ' . $e->getMessage()); http_response_code(500); echo json_encode(['success' => false, 'message' => 'Erreur lors du traitement de la demande']); } // ───────────────────────────────────────────────────────────────────────────── // Email template helpers // ───────────────────────────────────────────────────────────────────────────── /** * Build the auto-approval email sent to ERG-domain users. * The link leads to a confirmation page (GET) that then requires a POST to redeem. */ function buildAutoApprovalEmail(string $title, string $authors, string $accessUrl): string { $safeTitle = htmlspecialchars($title, ENT_QUOTES); $safeAuthors = htmlspecialchars($authors, ENT_QUOTES); $safeUrl = htmlspecialchars($accessUrl, ENT_QUOTES); return <<
Bonjour,
Votre demande d'accès au TFE suivant a été automatiquement approuvée :
Cliquez sur le bouton ci-dessous pour activer l'accès aux fichiers de ce TFE :
Ce lien est valable 24 heures et à usage unique. L'accès sur votre appareil sera ensuite conservé 30 jours.
Cordialement,
L'équipe XAMXAM – ERG
Une nouvelle demande d'accès a été soumise :
Connectez-vous au panneau d'administration pour approuver ou rejeter cette demande.