- m365_callback.php: OAuth-state-Validierung gegen Login-CSRF - includes/Crypto.php: Verschlüsselung-at-rest für UniFi-Passwörter (AES-256-GCM/libsodium) mit Klartext-Fallback für Bestandsinstallationen - install.php: APP_KEY-Generierung + Reinstall nur mit Admin-Session - Auth.php: absolutes Session-Timeout (SESSION_LIFETIME) durchsetzen - index.php: CSRF + Throttle auch für anonyme öffentliche Voucher-Erstellung - UniFiController.php: createVoucher liefert nicht mehr den falschen Code bei parallelen Erstellungen (note-Match statt blindes reset()) - display_errors in allen Entry-Points deaktiviert, log_errors aktiviert - test.php & m365_debug.php hinter requireAdmin() (Info-Leak) - m365_debug.php: abgeschnittene/kaputte Datei vervollständigt
129 lines
No EOL
4.8 KiB
PHP
129 lines
No EOL
4.8 KiB
PHP
<?php
|
|
error_reporting(E_ALL);
|
|
ini_set('display_errors', 0);
|
|
ini_set('log_errors', 1);
|
|
|
|
require_once __DIR__ . '/config.php';
|
|
require_once __DIR__ . '/includes/Database.php';
|
|
require_once __DIR__ . '/includes/Auth.php';
|
|
|
|
session_start();
|
|
|
|
$db = Database::getInstance();
|
|
$auth = new Auth();
|
|
|
|
// M365 Einstellungen abrufen
|
|
$clientId = $db->getSetting('m365_client_id', '');
|
|
$clientSecret = $db->getSetting('m365_client_secret', '');
|
|
$tenantId = $db->getSetting('m365_tenant_id', '');
|
|
|
|
if (empty($clientId) || empty($clientSecret) || empty($tenantId)) {
|
|
die('Microsoft 365 ist nicht konfiguriert. Bitte kontaktieren Sie Ihren Administrator. <a href="login.php">Zurück zum Login</a>');
|
|
}
|
|
|
|
// Dynamische Redirect URI
|
|
$protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http';
|
|
$host = $_SERVER['HTTP_HOST'];
|
|
$scriptPath = dirname($_SERVER['SCRIPT_NAME']);
|
|
$scriptPath = $scriptPath === '/' ? '' : $scriptPath;
|
|
$redirectUri = $protocol . '://' . $host . $scriptPath . '/m365_callback.php';
|
|
|
|
// Fehlerbehandlung
|
|
if (isset($_GET['error'])) {
|
|
$error = htmlspecialchars($_GET['error']);
|
|
$errorDesc = htmlspecialchars($_GET['error_description'] ?? 'Unbekannter Fehler');
|
|
die("Microsoft 365 Login-Fehler: $error<br>$errorDesc<br><a href='login.php'>Zurück zum Login</a>");
|
|
}
|
|
|
|
// Authorization Code erhalten
|
|
if (isset($_GET['code'])) {
|
|
// OAuth-State validieren (CSRF-Schutz). Der State wurde in login.php erzeugt
|
|
// und in der Session hinterlegt; er muss exakt zurueckkommen.
|
|
$sessionState = $_SESSION['m365_state'] ?? '';
|
|
$returnedState = $_GET['state'] ?? '';
|
|
unset($_SESSION['m365_state']); // One-Time-Token, nach Pruefung verbrauchen
|
|
|
|
if ($sessionState === '' || !hash_equals($sessionState, $returnedState)) {
|
|
die("Ungültiger oder fehlender Sicherheits-Token (OAuth state). Bitte erneut anmelden.<br><a href='login.php'>Zurück zum Login</a>");
|
|
}
|
|
|
|
$code = $_GET['code'];
|
|
|
|
// Token anfordern
|
|
$tokenUrl = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token";
|
|
|
|
$postData = [
|
|
'client_id' => $clientId,
|
|
'client_secret' => $clientSecret,
|
|
'code' => $code,
|
|
'redirect_uri' => $redirectUri,
|
|
'grant_type' => 'authorization_code',
|
|
'scope' => 'openid profile email User.Read'
|
|
];
|
|
|
|
$ch = curl_init($tokenUrl);
|
|
curl_setopt($ch, CURLOPT_POST, true);
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData));
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/x-www-form-urlencoded']);
|
|
|
|
$response = curl_exec($ch);
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
|
|
if ($httpCode !== 200) {
|
|
die("Fehler beim Token-Abruf (HTTP $httpCode): " . htmlspecialchars($response) . "<br><a href='login.php'>Zurück zum Login</a>");
|
|
}
|
|
|
|
$tokenData = json_decode($response, true);
|
|
|
|
if (!isset($tokenData['access_token'])) {
|
|
die("Kein Access Token erhalten: " . htmlspecialchars($response) . "<br><a href='login.php'>Zurück zum Login</a>");
|
|
}
|
|
|
|
$accessToken = $tokenData['access_token'];
|
|
|
|
// Benutzer-Informationen abrufen
|
|
$userUrl = 'https://graph.microsoft.com/v1.0/me';
|
|
|
|
$ch = curl_init($userUrl);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
|
'Authorization: Bearer ' . $accessToken,
|
|
'Content-Type: application/json'
|
|
]);
|
|
|
|
$userResponse = curl_exec($ch);
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
|
|
if ($httpCode !== 200) {
|
|
die("Fehler beim Abrufen der Benutzer-Daten (HTTP $httpCode): " . htmlspecialchars($userResponse) . "<br><a href='login.php'>Zurück zum Login</a>");
|
|
}
|
|
|
|
$userData = json_decode($userResponse, true);
|
|
|
|
if (!isset($userData['id']) || !isset($userData['mail'])) {
|
|
die("Ungültige Benutzer-Daten erhalten: " . htmlspecialchars($userResponse) . "<br><a href='login.php'>Zurück zum Login</a>");
|
|
}
|
|
|
|
// Benutzer einloggen oder anlegen
|
|
$microsoftUser = [
|
|
'id' => $userData['id'],
|
|
'email' => $userData['mail'] ?? $userData['userPrincipalName'],
|
|
'name' => $userData['displayName'] ?? $userData['givenName'] . ' ' . $userData['surname']
|
|
];
|
|
|
|
try {
|
|
$auth->loginWithMicrosoft($microsoftUser);
|
|
header('Location: index.php');
|
|
exit;
|
|
} catch (Exception $e) {
|
|
die("Login-Fehler: " . $e->getMessage() . "<br><a href='login.php'>Zurück zum Login</a>");
|
|
}
|
|
|
|
} else {
|
|
// Keine Authorization Code - Redirect zu Microsoft Login
|
|
die("Kein Authorization Code erhalten.<br><a href='login.php'>Zurück zum Login</a>");
|
|
}
|
|
?>
|