diff --git a/DeskHub-Webseite/api/public/v1/tickets.php b/DeskHub-Webseite/api/public/v1/tickets.php index 24ca81ff..bd5a80a9 100644 --- a/DeskHub-Webseite/api/public/v1/tickets.php +++ b/DeskHub-Webseite/api/public/v1/tickets.php @@ -57,7 +57,7 @@ if ($method === 'POST') { $note = mb_substr($note, 0, 2000); if (!in_array($ticket['status'], ['resolved', 'closed'], true)) { $now = db_now(); - $pdo->prepare("INSERT INTO ticket_threads (ticket_id, author_type, message, is_internal_note, created_at) VALUES (?, 'system', ?, 1, ?)")->execute([$ticket['id'], e($note), $now]); + $pdo->prepare("INSERT INTO ticket_threads (ticket_id, author_type, message, is_internal_note, created_at) VALUES (?, 'system', ?, 1, ?)")->execute([$ticket['id'], $note, $now]); $pdo->prepare("UPDATE tickets SET status = 'resolved', resolved_at = ?, updated_at = ? WHERE id = ?")->execute([$now, $now, $ticket['id']]); log_activity($pdo, (int) $ticket['id'], null, 'Über die API gelöst: ' . $note); try { webhook_dispatch($pdo, 'ticket.status_changed', (int) $ticket['id'], ['previousStatus' => (string) $ticket['status']]); } catch (Throwable $e) { /* nachrangig */ } diff --git a/DeskHub-Webseite/api/staff/v1/login.php b/DeskHub-Webseite/api/staff/v1/login.php index f3ad1a84..1b01e741 100644 --- a/DeskHub-Webseite/api/staff/v1/login.php +++ b/DeskHub-Webseite/api/staff/v1/login.php @@ -13,7 +13,8 @@ if (!filter_var($email, FILTER_VALIDATE_EMAIL) || $password === '') { api_error(422, 'CREDENTIALS_REQUIRED', 'E-Mail-Adresse und Passwort sind erforderlich.'); } if (!rate_limit_check('mobile_staff_login_ip', client_ip(), 10, 15) - || !rate_limit_check('mobile_staff_login_email', strtolower($email), 5, 15)) { + || !rate_limit_check('mobile_staff_login_email', strtolower($email), login_guard_max_attempts(), login_guard_lock_minutes())) { + login_guard_locked($email, 'app'); api_error(429, 'RATE_LIMITED', 'Zu viele Anmeldeversuche. Bitte versuchen Sie es später erneut.'); } @@ -22,6 +23,7 @@ $stmt = $pdo->prepare('SELECT * FROM staff WHERE LOWER(email) = LOWER(?) AND is_ $stmt->execute([$email]); $staff = $stmt->fetch(); if (!$staff || !password_verify($password, $staff['password_hash'])) { + login_guard_failed($staff ?: null, $email, 'app', $staff ? 'Falsches Passwort' : 'Unbekannte E-Mail-Adresse'); api_error(401, 'INVALID_CREDENTIALS', 'E-Mail-Adresse oder Passwort ist falsch.'); } if (!empty($staff['totp_enabled'])) { @@ -29,6 +31,7 @@ if (!empty($staff['totp_enabled'])) { api_json(['requiresTotp' => true], 202); } if (!preg_match('/^\d{6}$/', $totpCode) || !totp_verify($staff['totp_secret'], $totpCode)) { + login_guard_failed($staff, $email, 'app', 'Falscher 2FA-Code'); api_error(401, 'INVALID_TOTP', 'Der Authenticator-Code ist ungültig.'); } } @@ -37,6 +40,7 @@ rate_limit_reset('mobile_staff_login_email', strtolower($email)); rate_limit_reset('mobile_staff_login_ip', client_ip()); $issued = api_issue_token($pdo, (int) $staff['id'], isset($body['deviceName']) ? (string) $body['deviceName'] : null); audit_log((int) $staff['id'], 'Mobile App: Anmeldung'); +login_guard_success($staff, 'app', isset($body['deviceName']) ? (string) $body['deviceName'] : null); api_json([ 'token' => $issued['token'], 'expiresAt' => $issued['expires_at'], diff --git a/DeskHub-Webseite/plugin_login.php b/DeskHub-Webseite/plugin_login.php new file mode 100644 index 00000000..b81be5a7 --- /dev/null +++ b/DeskHub-Webseite/plugin_login.php @@ -0,0 +1,43 @@ +&e=. Das Plugin braucht die + * Berechtigung "auth.customer" und meldet die Adresse mit plugin_register_customer_login() an. Hier steht nur, was für alle gleich ist: + * Weiterleitung, Anmeldung (und, wenn das Plugin es erlaubt, Anlegen des Kundenkontos), Fehler und Begrenzung der Aufrufe. + * Die Rückkehr vom Dienst kann auch über staff/plugin_login.php kommen (gleiche Rückkehr-Adresse für alle). + */ +require_once __DIR__ . '/includes/bootstrap.php'; + +header('Cache-Control: no-store'); +header('Referrer-Policy: no-referrer'); + +function plugin_customer_login_back(string $message): never +{ + flash('error', $message); + redirect('/login.php'); +} + +if (current_user()) redirect('/home.php'); + +$slug = (string) ($_GET['p'] ?? ''); +$endpoint = (string) ($_GET['e'] ?? ''); +$handler = plugin_state()['customer_logins'][$slug][$endpoint] ?? null; +if (!is_callable($handler)) { + http_response_code(404); + echo 'Unbekannte Anmelde-Adresse.'; + exit; +} +if (!rate_limit_check('plugin_customer_login', client_ip(), 40, 15)) plugin_customer_login_back('Zu viele Anmeldeversuche. Bitte versuchen Sie es in einigen Minuten erneut.'); + +$result = plugin_guard($slug, fn() => $handler(['query' => $_GET, 'ip' => client_ip()]), null); +if (!is_array($result)) plugin_customer_login_back('Die Anmeldung ist gerade nicht möglich. Bitte versuchen Sie es später noch einmal oder melden Sie sich mit E-Mail und Passwort an.'); + +if (!empty($result['redirect'])) { + $url = (string) $result['redirect']; + if (!plugin_login_external_url_ok($url)) plugin_customer_login_back('Die Adresse des Anmeldedienstes ist ungültig.'); + header('Location: ' . $url, true, 302); + exit; +} +if (!empty($result['customer']) && is_array($result['customer'])) plugin_customer_login_finish($slug, $result['customer']); +plugin_customer_login_back(mb_substr((string) ($result['error'] ?? 'Die Anmeldung wurde abgelehnt.'), 0, 300)); diff --git a/DeskHub-Webseite/version.php b/DeskHub-Webseite/version.php index 20dba5a2..238ab7d4 100644 --- a/DeskHub-Webseite/version.php +++ b/DeskHub-Webseite/version.php @@ -1,7 +1,7 @@