web - refactor core modules, harden authorization, simplify endpoints
This commit is contained in:
@@ -1,106 +1,96 @@
|
||||
<?php /* Name: login.php
|
||||
Beschreibung: Login-Seite (Authorisierung) */
|
||||
|
||||
require_once $_SERVER['DOCUMENT_ROOT'].'/intranet_repo/general_files/ini/parameters.php';
|
||||
require_once $_SERVER['DOCUMENT_ROOT'].'/intranet_repo/general_files/ini/functions.php';
|
||||
|
||||
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
|
||||
$pdo = require_once($_SERVER['DOCUMENT_ROOT']."/intranet_repo/general_files/ini/database.php");
|
||||
|
||||
$sql = "SELECT
|
||||
u.*,
|
||||
l.password_hash AS password_hash
|
||||
FROM
|
||||
user_general_information AS u
|
||||
INNER JOIN
|
||||
user_login AS l ON u.user_id = l.user_id
|
||||
WHERE
|
||||
u.mail = ?";
|
||||
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute([$_POST["email"]]);
|
||||
$user = $stmt->fetch();
|
||||
|
||||
if ($user) {
|
||||
|
||||
if (isset($user['password_hash']) && password_verify($_POST["password"], $user["password_hash"])) {
|
||||
|
||||
session_start();
|
||||
session_regenerate_id();
|
||||
$_SESSION["user_id"] = $user["user_id"];
|
||||
|
||||
header("Location: {$g_httpHomePath}");
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
$is_invalid = true;
|
||||
}
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="de" dir="ltr">
|
||||
<head>
|
||||
<?php require_once $_SERVER['DOCUMENT_ROOT'].'/intranet_repo/projects/_central/php/html_head.php'; ?>
|
||||
|
||||
<script src="https://unpkg.com/just-validate@latest/dist/just-validate.production.min.js" defer></script>
|
||||
<script src="validation.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="login_panel">
|
||||
|
||||
<?php if (isset($is_invalid) && $is_invalid): ?>
|
||||
<div class="nak-toast nak-toast--error nak-toast--inline" id="nakToastLogin">
|
||||
<div class="nak-toast__icon">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="square" stroke-linejoin="miter"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
|
||||
</div>
|
||||
<span class="nak-toast__text">Ungültiger Login!</span>
|
||||
</div>
|
||||
<script>requestAnimationFrame(function(){document.getElementById('nakToastLogin').classList.add('nak-toast--visible');});</script>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($_GET["message"]) && $_GET["message"] == 1): ?>
|
||||
<div class="nak-toast nak-toast--success nak-toast--inline" id="nakToastPw">
|
||||
<div class="nak-toast__icon">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="square" stroke-linejoin="miter"><path d="M20 6L9 17l-5-5"/></svg>
|
||||
</div>
|
||||
<span class="nak-toast__text">Passwort erfolgreich geändert. Sie können sich jetzt anmelden.</span>
|
||||
</div>
|
||||
<script>requestAnimationFrame(function(){document.getElementById('nakToastPw').classList.add('nak-toast--visible');});</script>
|
||||
<?php endif; ?>
|
||||
<div class="login_content">
|
||||
<div class="nak-logo-link auth-logo">
|
||||
<img class="auth-logo__icon" src="/intranet_repo/projects/_central/img/nak-logo.svg" alt="NAK Logo">
|
||||
<div class="auth-logo__text">
|
||||
<div class="auth-logo__name">Neuapostolische Kirche</div>
|
||||
<div class="auth-logo__gemeinde">Berlin-Reinickendorf</div>
|
||||
</div>
|
||||
</div>
|
||||
<h2>Anmelden</h2>
|
||||
|
||||
<form method="post">
|
||||
<div class="flexcolumn_center">
|
||||
<input class="login_field" placeholder="E-Mailadresse" type="email" name="email" id="email"
|
||||
value="<?= htmlspecialchars($_POST["email"] ?? "") ?>">
|
||||
<input class="login_field" placeholder="Passwort" type="password" name="password" id="password">
|
||||
<input class="login_button" type="submit" name="submit" value="Anmelden">
|
||||
</div>
|
||||
</form>
|
||||
<div class="small_text" style="text-align:center;margin-top:24px;color:var(--gray-600);">
|
||||
Sie haben noch keinen Account für das Intranet?<br>
|
||||
<a href="signup.php">Registrieren</a><br>
|
||||
<br>
|
||||
Passwort vergessen?<br>
|
||||
<a href="reset-password.php">Passwort zurücksetzen</a><br>
|
||||
<br>
|
||||
Probleme bei der Anmeldung?<br>
|
||||
<a href="#" onclick="openContactModal(); return false;">Admin kontaktieren</a>
|
||||
</div>
|
||||
</div><!-- EOD: login_content -->
|
||||
</div><!-- EOD: login_panel -->
|
||||
|
||||
<?php require_once $_SERVER['DOCUMENT_ROOT'].'/intranet_repo/projects/_central/php/footer.php'; ?>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
<?php
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/intranet_repo/general_files/ini/parameters.php';
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/intranet_repo/general_files/ini/functions.php';
|
||||
require_once __DIR__ . '/util/authorization_helpers.php';
|
||||
|
||||
authorization_start_session();
|
||||
authorization_send_security_headers();
|
||||
|
||||
if (isset($_SESSION['user_id'])) {
|
||||
authorization_redirect($g_httpHomePath, 302);
|
||||
}
|
||||
|
||||
$isInvalid = false;
|
||||
$email = '';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$email = trim((string) ($_POST['email'] ?? ''));
|
||||
$loginPassword = (string) ($_POST['password'] ?? '');
|
||||
|
||||
if (!authorization_csrf_is_valid($_POST['csrf_token'] ?? null)) {
|
||||
$isInvalid = true;
|
||||
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL) || $loginPassword === '') {
|
||||
$isInvalid = true;
|
||||
} else {
|
||||
$pdo = require $_SERVER['DOCUMENT_ROOT'] . '/intranet_repo/general_files/ini/database.php';
|
||||
$userId = authorization_authenticate($pdo, $email, $loginPassword);
|
||||
if ($userId !== null) {
|
||||
session_regenerate_id(true);
|
||||
$_SESSION = ['user_id' => $userId];
|
||||
authorization_redirect($g_httpHomePath);
|
||||
}
|
||||
$isInvalid = true;
|
||||
}
|
||||
}
|
||||
|
||||
$csrfToken = authorization_csrf_token();
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="de" dir="ltr">
|
||||
<head>
|
||||
<?php require_once $_SERVER['DOCUMENT_ROOT'] . '/intranet_repo/projects/_central/php/html_head.php'; ?>
|
||||
<link rel="stylesheet" href="/intranet_repo/projects/_central/layouts/authorization.css">
|
||||
<script src="/intranet_repo/projects/_central/scripts/authorization.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="login_panel">
|
||||
<?php if ($isInvalid): ?>
|
||||
<div class="nak-toast nak-toast--error nak-toast--inline" data-auth-toast>
|
||||
<div class="nak-toast__icon">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="square" stroke-linejoin="miter"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
|
||||
</div>
|
||||
<span class="nak-toast__text">Ungültige E-Mail-Adresse oder ungültiges Passwort.</span>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (($_GET['message'] ?? '') === '1'): ?>
|
||||
<div class="nak-toast nak-toast--success nak-toast--inline" data-auth-toast>
|
||||
<div class="nak-toast__icon">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="square" stroke-linejoin="miter"><path d="M20 6L9 17l-5-5"/></svg>
|
||||
</div>
|
||||
<span class="nak-toast__text">Passwort erfolgreich geändert. Sie können sich jetzt anmelden.</span>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="login_content">
|
||||
<div class="nak-logo-link auth-logo">
|
||||
<img class="auth-logo__icon" src="/intranet_repo/projects/_central/img/nak-logo.svg" alt="NAK Logo">
|
||||
<div class="auth-logo__text">
|
||||
<div class="auth-logo__name">Neuapostolische Kirche</div>
|
||||
<div class="auth-logo__gemeinde">Berlin-Reinickendorf</div>
|
||||
</div>
|
||||
</div>
|
||||
<h2>Anmelden</h2>
|
||||
|
||||
<form method="post" autocomplete="on">
|
||||
<input type="hidden" name="csrf_token" value="<?= authorization_html($csrfToken) ?>">
|
||||
<div class="flexcolumn_center">
|
||||
<input class="login_field" placeholder="E-Mailadresse" type="email" name="email" id="email" value="<?= authorization_html($email) ?>" required autocomplete="username">
|
||||
<input class="login_field" placeholder="Passwort" type="password" name="password" id="password" required autocomplete="current-password">
|
||||
<input class="login_button" type="submit" value="Anmelden">
|
||||
</div>
|
||||
</form>
|
||||
<div class="small_text" style="text-align:center;margin-top:24px;color:var(--gray-600);">
|
||||
Sie haben noch keinen Account für das Intranet?<br>
|
||||
<a href="signup.php">Registrieren</a><br><br>
|
||||
Passwort vergessen?<br>
|
||||
<a href="reset-password.php">Passwort zurücksetzen</a><br><br>
|
||||
Probleme bei der Anmeldung?<br>
|
||||
<a href="#" onclick="openContactModal(); return false;">Admin kontaktieren</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php require_once $_SERVER['DOCUMENT_ROOT'] . '/intranet_repo/projects/_central/php/footer.php'; ?>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,13 +1,22 @@
|
||||
<?php /* Name: loginout.php
|
||||
Beschreibung: Logout-Seite */
|
||||
<?php
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/intranet_repo/general_files/ini/parameters.php';
|
||||
require_once __DIR__ . '/util/authorization_helpers.php';
|
||||
|
||||
if (session_status() == PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
require_once $_SERVER['DOCUMENT_ROOT'].'/intranet_repo/general_files/ini/parameters.php';
|
||||
require_once $_SERVER['DOCUMENT_ROOT'].'/intranet_repo/general_files/ini/functions.php';
|
||||
authorization_start_session();
|
||||
authorization_send_security_headers();
|
||||
|
||||
$_SESSION = [];
|
||||
if (ini_get('session.use_cookies')) {
|
||||
$params = session_get_cookie_params();
|
||||
setcookie(session_name(), '', [
|
||||
'expires' => time() - 42000,
|
||||
'path' => $params['path'],
|
||||
'domain' => $params['domain'],
|
||||
'secure' => $params['secure'],
|
||||
'httponly' => $params['httponly'],
|
||||
'samesite' => $params['samesite'] ?? 'Lax',
|
||||
]);
|
||||
}
|
||||
session_destroy();
|
||||
|
||||
header("Location: {$g_httpLoginPath}");
|
||||
exit();
|
||||
authorization_redirect($g_httpLoginPath, 302);
|
||||
|
||||
@@ -1,223 +0,0 @@
|
||||
<?php /* Name: reset-password-process.php
|
||||
Beschreibung: Verarbeitung des Passwort-Resets (3 Schritte)
|
||||
- Schritt 1: OTP generieren und per SMS + Mail senden
|
||||
- Schritt 2: OTP verifizieren → Reset-Token erzeugen
|
||||
- Schritt 3: Neues Passwort setzen mit Reset-Token */
|
||||
|
||||
$pdo = require_once($_SERVER['DOCUMENT_ROOT']."/intranet_repo/general_files/ini/database.php");
|
||||
require_once $_SERVER['DOCUMENT_ROOT'].'/intranet_repo/general_files/ini/send_mail.php';
|
||||
|
||||
// PostgreSQL-Zeitzone auf PHP-Zeitzone setzen
|
||||
$pdo->exec("SET timezone = 'Europe/Berlin'");
|
||||
|
||||
if ($_SERVER["REQUEST_METHOD"] !== "POST") {
|
||||
header("Location: reset-password.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
$action = $_POST["action"] ?? '';
|
||||
|
||||
// =============================================
|
||||
// SCHRITT 1: OTP anfordern
|
||||
// =============================================
|
||||
if ($action === "request_otp") {
|
||||
$email = trim($_POST["email"] ?? '');
|
||||
|
||||
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
header("Location: reset-password.php?error=1");
|
||||
exit;
|
||||
}
|
||||
|
||||
// Nutzer suchen (muss registriert sein = user_login existiert)
|
||||
$stmt = $pdo->prepare("SELECT ugi.user_id, ugi.phone, ugi.first_name FROM user_general_information ugi INNER JOIN user_login ul ON ugi.user_id = ul.user_id WHERE ugi.mail = ?");
|
||||
$stmt->execute([$email]);
|
||||
$user = $stmt->fetch();
|
||||
|
||||
if (!$user) {
|
||||
header("Location: reset-password.php?error=1");
|
||||
exit;
|
||||
}
|
||||
|
||||
if (empty($user['phone'])) {
|
||||
header("Location: reset-password.php?error=2");
|
||||
exit;
|
||||
}
|
||||
|
||||
// OTP generieren (6-stellig)
|
||||
$otp = str_pad(random_int(0, 999999), 6, '0', STR_PAD_LEFT);
|
||||
|
||||
// Ablaufzeit als UNIX-Timestamp speichern (vermeidet Timezone-Probleme)
|
||||
$expires_timestamp = time() + 600; // 10 Minuten
|
||||
$expires_at = date('Y-m-d H:i:s', $expires_timestamp);
|
||||
|
||||
// Alte OTPs für diesen Nutzer invalidieren
|
||||
$stmt = $pdo->prepare("UPDATE password_reset_otp SET used = 1 WHERE user_id = ? AND used = 0");
|
||||
$stmt->execute([$user['user_id']]);
|
||||
|
||||
// Neues OTP speichern
|
||||
$stmt = $pdo->prepare("INSERT INTO password_reset_otp (user_id, otp_code, expires_at) VALUES (?, ?, ?)");
|
||||
$stmt->execute([$user['user_id'], $otp, $expires_at]);
|
||||
|
||||
// SMS senden (wie im Seelsorgeplaner)
|
||||
$phone_full = "+49" . $user['phone'];
|
||||
$messageText = "Intranet NAK Reinickendorf\n\nIhr Bestaetigungscode: {$otp}\n\nDer Code ist 10 Minuten gueltig.";
|
||||
|
||||
$smsData = array(
|
||||
"phone" => $phone_full,
|
||||
"message" => $messageText
|
||||
);
|
||||
|
||||
$options = array(
|
||||
'http' => array(
|
||||
'header' => "Content-Type: application/json; charset=utf-8",
|
||||
'method' => 'POST',
|
||||
'content' => json_encode($smsData, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
|
||||
'timeout' => 10,
|
||||
),
|
||||
);
|
||||
|
||||
$context = stream_context_create($options);
|
||||
$smsResult = @file_get_contents(rtrim(intranet_env('SMS_GATEWAY_URL'), '/') . '/send_sms', false, $context);
|
||||
|
||||
if ($smsResult === FALSE) {
|
||||
header("Location: reset-password.php?error=4");
|
||||
exit;
|
||||
}
|
||||
|
||||
// OTP zusätzlich per Mail senden
|
||||
$greeting = !empty($user['first_name']) ? "Hallo {$user['first_name']}" : "Hallo";
|
||||
$mailBetreff = "Ihr Bestätigungscode";
|
||||
$mailNachricht = "{$greeting},\n\n";
|
||||
$mailNachricht .= "Sie haben das Zurücksetzen Ihres Passworts angefordert.\n\n";
|
||||
$mailNachricht .= "Ihr Bestätigungscode lautet: {$otp}\n\n";
|
||||
$mailNachricht .= "Der Code ist 10 Minuten gültig.\n\n";
|
||||
$mailNachricht .= "Falls Sie diese Anfrage nicht gestellt haben, können Sie diese Nachricht ignorieren.";
|
||||
|
||||
send_intranet_mail($email, $mailBetreff, $mailNachricht);
|
||||
|
||||
// Telefonnummer maskieren für Anzeige (nur letzte 3 Ziffern zeigen)
|
||||
$phone_masked = str_repeat('*', max(0, strlen($user['phone']) - 3)) . substr($user['phone'], -3);
|
||||
|
||||
header("Location: reset-password.php?step=verify&uid={$user['user_id']}&phone=" . urlencode($phone_masked));
|
||||
exit;
|
||||
}
|
||||
|
||||
// =============================================
|
||||
// SCHRITT 2: OTP verifizieren → Reset-Token erzeugen
|
||||
// =============================================
|
||||
if ($action === "verify_otp") {
|
||||
$user_id = (int)($_POST["user_id"] ?? 0);
|
||||
$otp_code = trim($_POST["otp_code"] ?? '');
|
||||
|
||||
if ($user_id === 0 || strlen($otp_code) !== 6) {
|
||||
header("Location: reset-password.php?error=3");
|
||||
exit;
|
||||
}
|
||||
|
||||
// OTP prüfen – Vergleich mit PHP-Zeit statt DB NOW()
|
||||
$now = date('Y-m-d H:i:s');
|
||||
$stmt = $pdo->prepare("SELECT id FROM password_reset_otp WHERE user_id = ? AND otp_code = ? AND used = 0 AND expires_at > ?");
|
||||
$stmt->execute([$user_id, $otp_code, $now]);
|
||||
$otpRecord = $stmt->fetch();
|
||||
|
||||
if (!$otpRecord) {
|
||||
header("Location: reset-password.php?step=verify&uid={$user_id}&error=3");
|
||||
exit;
|
||||
}
|
||||
|
||||
// OTP als verwendet markieren
|
||||
$stmt = $pdo->prepare("UPDATE password_reset_otp SET used = 1 WHERE id = ?");
|
||||
$stmt->execute([$otpRecord['id']]);
|
||||
|
||||
// Einmal-Token für Passwort-Änderung generieren (in Session speichern)
|
||||
$reset_token = bin2hex(random_bytes(32));
|
||||
if (session_status() == PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
$_SESSION['reset_token'] = $reset_token;
|
||||
$_SESSION['reset_user_id'] = $user_id;
|
||||
$_SESSION['reset_token_expires'] = time() + 600; // 10 Minuten
|
||||
|
||||
header("Location: reset-password.php?step=newpassword&token=" . urlencode($reset_token));
|
||||
exit;
|
||||
}
|
||||
|
||||
// =============================================
|
||||
// SCHRITT 3: Neues Passwort setzen (nach OTP-Verifizierung)
|
||||
// =============================================
|
||||
if ($action === "set_password") {
|
||||
if (session_status() == PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
|
||||
$reset_token = $_POST["reset_token"] ?? '';
|
||||
$new_password = $_POST["new_password"] ?? '';
|
||||
$new_password_confirm = $_POST["new_password_confirm"] ?? '';
|
||||
|
||||
// Token validieren
|
||||
if (empty($reset_token)
|
||||
|| !isset($_SESSION['reset_token'])
|
||||
|| $reset_token !== $_SESSION['reset_token']
|
||||
|| !isset($_SESSION['reset_token_expires'])
|
||||
|| $_SESSION['reset_token_expires'] < time()) {
|
||||
// Session-Daten aufräumen
|
||||
unset($_SESSION['reset_token'], $_SESSION['reset_user_id'], $_SESSION['reset_token_expires']);
|
||||
header("Location: reset-password.php?error=7");
|
||||
exit;
|
||||
}
|
||||
|
||||
$user_id = (int)$_SESSION['reset_user_id'];
|
||||
|
||||
// Passwort-Validierung
|
||||
if (strlen($new_password) < 8 || !preg_match("/[a-z]/i", $new_password) || !preg_match("/[0-9]/", $new_password)) {
|
||||
header("Location: reset-password.php?step=newpassword&token=" . urlencode($reset_token) . "&error=5");
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($new_password !== $new_password_confirm) {
|
||||
header("Location: reset-password.php?step=newpassword&token=" . urlencode($reset_token) . "&error=6");
|
||||
exit;
|
||||
}
|
||||
|
||||
// Neues Passwort setzen
|
||||
$password_hash = password_hash($new_password, PASSWORD_DEFAULT);
|
||||
$stmt = $pdo->prepare("UPDATE user_login SET password_hash = ? WHERE user_id = ?");
|
||||
$stmt->execute([$password_hash, $user_id]);
|
||||
|
||||
// User-Daten laden für Mail und Vermerk
|
||||
$stmt = $pdo->prepare("SELECT mail, first_name, last_name FROM user_general_information WHERE user_id = ?");
|
||||
$stmt->execute([$user_id]);
|
||||
$resetUserData = $stmt->fetch();
|
||||
|
||||
if ($resetUserData) {
|
||||
$loginUrl = "https://" . ($_SERVER['HTTP_HOST'] ?? intranet_env('INTRANET_FQDN')) . "/intranet_repo/projects/authorization/login.php";
|
||||
$greeting = !empty($resetUserData['first_name']) ? "Hallo {$resetUserData['first_name']}" : "Hallo";
|
||||
|
||||
$mailBetreff = "Passwort erfolgreich geändert";
|
||||
$mailNachricht = "{$greeting},\n\n";
|
||||
$mailNachricht .= "Ihr Passwort für das Intranet der NAK Reinickendorf wurde erfolgreich geändert.\n\n";
|
||||
$mailNachricht .= "Sie können sich ab sofort mit Ihrem neuen Passwort anmelden.";
|
||||
|
||||
send_intranet_mail($resetUserData['mail'], $mailBetreff, $mailNachricht, null, "intranet@nak-reinickendorf.de", "Jetzt anmelden", $loginUrl);
|
||||
|
||||
// Vermerk in erledigte Tasks
|
||||
$userName = trim(($resetUserData['first_name'] ?? '') . ' ' . ($resetUserData['last_name'] ?? ''));
|
||||
if (empty($userName)) $userName = $resetUserData['mail'];
|
||||
|
||||
$taskSubject = "Passwort selbst zurückgesetzt: {$userName}";
|
||||
$taskMessage = "{$userName} ({$resetUserData['mail']}) hat das eigene Passwort erfolgreich zurückgesetzt.";
|
||||
|
||||
$stmt = $pdo->prepare("INSERT INTO admin_messages (type, sender_name, sender_email, subject, message, status, processed_at, created_at) VALUES ('system', ?, ?, ?, ?, 'done', NOW(), NOW())");
|
||||
$stmt->execute([$userName, $resetUserData['mail'], $taskSubject, $taskMessage]);
|
||||
}
|
||||
|
||||
// Session-Daten aufräumen
|
||||
unset($_SESSION['reset_token'], $_SESSION['reset_user_id'], $_SESSION['reset_token_expires']);
|
||||
|
||||
header("Location: login.php?message=1");
|
||||
exit;
|
||||
}
|
||||
|
||||
header("Location: reset-password.php");
|
||||
exit;
|
||||
?>
|
||||
@@ -1,159 +1,324 @@
|
||||
<?php /* Name: reset-password.php
|
||||
Beschreibung: Passwort-Reset per SMS OTP (3 Schritte)
|
||||
- Schritt 1: E-Mail eingeben → OTP per SMS
|
||||
- Schritt 2: OTP-Code verifizieren
|
||||
- Schritt 3: Neues Passwort setzen */
|
||||
|
||||
if (session_status() == PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
require_once $_SERVER['DOCUMENT_ROOT'].'/intranet_repo/general_files/ini/parameters.php';
|
||||
require_once $_SERVER['DOCUMENT_ROOT'].'/intranet_repo/general_files/ini/functions.php';
|
||||
|
||||
$step = $_GET["step"] ?? "email";
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="de" dir="ltr">
|
||||
<head>
|
||||
<?php require_once $_SERVER['DOCUMENT_ROOT'].'/intranet_repo/projects/_central/php/html_head.php'; ?>
|
||||
<style>
|
||||
.login_button { position: relative; }
|
||||
.login_button.is-loading {
|
||||
color: transparent !important;
|
||||
pointer-events: none;
|
||||
}
|
||||
.login_button.is-loading::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
margin: -11px 0 0 -11px;
|
||||
border: 3px solid rgba(255,255,255,0.35);
|
||||
border-top-color: #FFFFFF;
|
||||
border-radius: 50%;
|
||||
animation: spin 0.7s linear infinite;
|
||||
}
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="login_panel">
|
||||
|
||||
<?php if (isset($_GET["error"])): ?>
|
||||
<?php
|
||||
$errorMessages = [
|
||||
1 => "E-Mail-Adresse nicht gefunden oder nicht registriert.",
|
||||
2 => "Keine Telefonnummer hinterlegt. Bitte wenden Sie sich an den Administrator.",
|
||||
3 => "Ungültiger oder abgelaufener Code.",
|
||||
4 => "SMS konnte nicht gesendet werden. Bitte versuchen Sie es erneut.",
|
||||
5 => "Passwort muss mindestens 8 Zeichen, einen Buchstaben und eine Zahl enthalten.",
|
||||
6 => "Passwörter stimmen nicht überein.",
|
||||
7 => "Sitzung abgelaufen. Bitte starten Sie den Vorgang erneut."
|
||||
];
|
||||
$errorNum = (int)$_GET["error"];
|
||||
$errorText = $errorMessages[$errorNum] ?? "Unbekannter Fehler.";
|
||||
?>
|
||||
<div class="nak-toast nak-toast--error nak-toast--inline" id="nakToastErr">
|
||||
<div class="nak-toast__icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="square" stroke-linejoin="miter"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg></div>
|
||||
<span class="nak-toast__text"><?= htmlspecialchars($errorText) ?></span>
|
||||
</div>
|
||||
<script>requestAnimationFrame(function(){document.getElementById('nakToastErr').classList.add('nak-toast--visible');});</script>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($_GET["success"]) && $_GET["success"] == 1): ?>
|
||||
<div class="nak-toast nak-toast--success nak-toast--inline" id="nakToastOk">
|
||||
<div class="nak-toast__icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="square" stroke-linejoin="miter"><path d="M20 6L9 17l-5-5"/></svg></div>
|
||||
<span class="nak-toast__text">Passwort erfolgreich geändert. Sie können sich jetzt anmelden.</span>
|
||||
</div>
|
||||
<script>requestAnimationFrame(function(){document.getElementById('nakToastOk').classList.add('nak-toast--visible');});</script>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="login_content">
|
||||
<div class="nak-logo-link auth-logo">
|
||||
<img class="auth-logo__icon" src="/intranet_repo/projects/_central/img/nak-logo.svg" alt="NAK Logo">
|
||||
<div class="auth-logo__text">
|
||||
<div class="auth-logo__name">Neuapostolische Kirche</div>
|
||||
<div class="auth-logo__gemeinde">Berlin-Reinickendorf</div>
|
||||
</div>
|
||||
</div>
|
||||
<h2>Passwort zurücksetzen</h2>
|
||||
|
||||
<?php if ($step === "email"): ?>
|
||||
<!-- SCHRITT 1: E-Mail eingeben -->
|
||||
<p style="text-align:center;color:var(--gray-600);font-size:var(--font-size-sm);margin-bottom:var(--space-sm);">Geben Sie Ihre E-Mail-Adresse ein. Ein Bestätigungscode wird an Ihre hinterlegte Telefonnummer und per Mail gesendet.</p>
|
||||
|
||||
<form action="reset-password-process.php" method="post" id="otpRequestForm">
|
||||
<input type="hidden" name="action" value="request_otp">
|
||||
<label for="email">E-Mailadresse</label>
|
||||
<div class="item">
|
||||
<input class="login_field" type="email" id="email" name="email" required>
|
||||
</div>
|
||||
<button class="login_button" type="submit" id="requestBtn">Code anfordern</button>
|
||||
</form>
|
||||
<script>
|
||||
document.getElementById('otpRequestForm').addEventListener('submit', function() {
|
||||
var btn = document.getElementById('requestBtn');
|
||||
btn.classList.add('is-loading');
|
||||
btn.disabled = true;
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php elseif ($step === "verify"): ?>
|
||||
<!-- SCHRITT 2: Nur OTP-Code eingeben -->
|
||||
<?php $phone_masked = htmlspecialchars($_GET["phone"] ?? "***"); ?>
|
||||
<p style="text-align:center;color:var(--gray-600);font-size:var(--font-size-sm);margin-bottom:var(--space-sm);">Ein 6-stelliger Code wurde an die Nummer +49 <?= $phone_masked ?> und an Ihre E-Mail-Adresse gesendet.</p>
|
||||
|
||||
<form action="reset-password-process.php" method="post">
|
||||
<input type="hidden" name="action" value="verify_otp">
|
||||
<input type="hidden" name="user_id" value="<?= (int)($_GET["uid"] ?? 0) ?>">
|
||||
|
||||
<label for="otp_code">Bestätigungscode</label>
|
||||
<div class="item">
|
||||
<input class="login_field" type="text" id="otp_code" name="otp_code" maxlength="6" inputmode="numeric" pattern="[0-9]{6}" placeholder="000000" required autocomplete="one-time-code" style="text-align:center;font-size:1.4em;letter-spacing:8px;">
|
||||
</div>
|
||||
|
||||
<input class="login_button" type="submit" value="Code bestätigen">
|
||||
</form>
|
||||
|
||||
<?php elseif ($step === "newpassword"): ?>
|
||||
<!-- SCHRITT 3: Neues Passwort setzen (nur nach verifiziertem OTP) -->
|
||||
<p style="text-align:center;color:var(--gray-600);font-size:var(--font-size-sm);margin-bottom:var(--space-sm);">Ihr Code wurde bestätigt. Geben Sie nun Ihr neues Passwort ein.</p>
|
||||
|
||||
<form action="reset-password-process.php" method="post">
|
||||
<input type="hidden" name="action" value="set_password">
|
||||
<input type="hidden" name="reset_token" value="<?= htmlspecialchars($_GET["token"] ?? '') ?>">
|
||||
|
||||
<label for="new_password">Neues Passwort</label>
|
||||
<div class="item">
|
||||
<input class="login_field" type="password" id="new_password" name="new_password" required>
|
||||
</div>
|
||||
|
||||
<label for="new_password_confirm">Neues Passwort wiederholen</label>
|
||||
<div class="item">
|
||||
<input class="login_field" type="password" id="new_password_confirm" name="new_password_confirm" required>
|
||||
</div>
|
||||
|
||||
<input class="login_button" type="submit" value="Passwort ändern">
|
||||
</form>
|
||||
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="small_text" align="center">
|
||||
<br>
|
||||
<a href="login.php">Zurück zur Anmeldung</a><br>
|
||||
<br>
|
||||
Probleme beim Zurücksetzen?<br>
|
||||
<a href="#" onclick="openContactModal(); return false;">Admin kontaktieren</a>
|
||||
</div>
|
||||
</div><!-- EOD: login_content -->
|
||||
</div><!-- EOD: login_panel -->
|
||||
|
||||
<?php require_once $_SERVER['DOCUMENT_ROOT'].'/intranet_repo/projects/_central/php/footer.php'; ?>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
<?php
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/intranet_repo/general_files/ini/parameters.php';
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/intranet_repo/general_files/ini/functions.php';
|
||||
require_once __DIR__ . '/util/authorization_helpers.php';
|
||||
|
||||
authorization_start_session();
|
||||
authorization_send_security_headers();
|
||||
|
||||
if (isset($_SESSION['user_id'])) {
|
||||
authorization_redirect($g_httpHomePath, 302);
|
||||
}
|
||||
|
||||
$allowedSteps = ['email', 'verify', 'newpassword'];
|
||||
$step = (string) ($_GET['step'] ?? 'email');
|
||||
if (!in_array($step, $allowedSteps, true)) {
|
||||
$step = 'email';
|
||||
}
|
||||
|
||||
$error = null;
|
||||
$errorCode = (int) ($_GET['error'] ?? 0);
|
||||
$errorMessages = [
|
||||
1 => 'Bitte geben Sie eine gültige E-Mail-Adresse ein.',
|
||||
3 => 'Ungültiger oder abgelaufener Bestätigungscode.',
|
||||
5 => 'Passwort muss mindestens 8 Zeichen, einen Buchstaben und eine Zahl enthalten.',
|
||||
6 => 'Passwörter stimmen nicht überein.',
|
||||
7 => 'Sitzung abgelaufen. Bitte starten Sie den Vorgang erneut.',
|
||||
];
|
||||
if (isset($errorMessages[$errorCode])) {
|
||||
$error = $errorMessages[$errorCode];
|
||||
}
|
||||
|
||||
if ($step === 'verify' && !authorization_reset_request_is_active()) {
|
||||
authorization_clear_reset_state();
|
||||
authorization_redirect('reset-password.php?error=7', 302);
|
||||
}
|
||||
if ($step === 'newpassword' && !authorization_reset_is_authorized()) {
|
||||
authorization_clear_reset_state();
|
||||
authorization_redirect('reset-password.php?error=7', 302);
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$action = (string) ($_POST['action'] ?? '');
|
||||
|
||||
if (!authorization_csrf_is_valid($_POST['csrf_token'] ?? null)) {
|
||||
authorization_clear_reset_state();
|
||||
authorization_redirect('reset-password.php?error=7');
|
||||
}
|
||||
|
||||
$pdo = require $_SERVER['DOCUMENT_ROOT'] . '/intranet_repo/general_files/ini/database.php';
|
||||
$pdo->exec("SET timezone = 'Europe/Berlin'");
|
||||
|
||||
if ($action === 'request_otp') {
|
||||
$email = trim((string) ($_POST['email'] ?? ''));
|
||||
if (strlen($email) > 254 || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
authorization_redirect('reset-password.php?error=1');
|
||||
}
|
||||
|
||||
authorization_clear_reset_state();
|
||||
$_SESSION['auth_reset_started_at'] = time();
|
||||
$_SESSION['auth_reset_attempts'] = 0;
|
||||
$_SESSION['auth_reset_user_id'] = 0;
|
||||
|
||||
$stmt = $pdo->prepare(
|
||||
'SELECT ugi.user_id, ugi.phone, ugi.first_name, ugi.mail '
|
||||
. 'FROM user_general_information ugi '
|
||||
. 'INNER JOIN user_login ul ON ul.user_id = ugi.user_id '
|
||||
. 'WHERE LOWER(ugi.mail) = LOWER(?) LIMIT 1'
|
||||
);
|
||||
$stmt->execute([$email]);
|
||||
$resetUser = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (is_array($resetUser)) {
|
||||
$userId = (int) $resetUser['user_id'];
|
||||
$otp = str_pad((string) random_int(0, 999999), 6, '0', STR_PAD_LEFT);
|
||||
$expiresAt = date('Y-m-d H:i:s', time() + AUTH_RESET_TTL_SECONDS);
|
||||
|
||||
$pdo->beginTransaction();
|
||||
try {
|
||||
$stmt = $pdo->prepare('UPDATE password_reset_otp SET used = 1 WHERE user_id = ? AND used = 0');
|
||||
$stmt->execute([$userId]);
|
||||
$stmt = $pdo->prepare('INSERT INTO password_reset_otp (user_id, otp_code, expires_at) VALUES (?, ?, ?)');
|
||||
$stmt->execute([$userId, $otp, $expiresAt]);
|
||||
$pdo->commit();
|
||||
} catch (Throwable $e) {
|
||||
if ($pdo->inTransaction()) {
|
||||
$pdo->rollBack();
|
||||
}
|
||||
error_log('authorization reset OTP persistence failed: ' . $e->getMessage());
|
||||
authorization_redirect('reset-password.php?step=verify&sent=1');
|
||||
}
|
||||
|
||||
$mailSent = false;
|
||||
try {
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/intranet_repo/general_files/ini/send_mail.php';
|
||||
$greeting = !empty($resetUser['first_name']) ? 'Hallo ' . $resetUser['first_name'] : 'Hallo';
|
||||
$mailMessage = $greeting . ",\n\n"
|
||||
. "Sie haben das Zurücksetzen Ihres Passworts angefordert.\n\n"
|
||||
. "Ihr Bestätigungscode lautet: {$otp}\n\n"
|
||||
. "Der Code ist 10 Minuten gültig.\n\n"
|
||||
. 'Falls Sie diese Anfrage nicht gestellt haben, können Sie diese Nachricht ignorieren.';
|
||||
$mailSent = send_intranet_mail((string) $resetUser['mail'], 'Ihr Bestätigungscode', $mailMessage);
|
||||
} catch (Throwable $e) {
|
||||
error_log('authorization reset mail delivery failed: ' . $e->getMessage());
|
||||
}
|
||||
$smsSent = authorization_send_reset_sms(authorization_normalize_phone((string) ($resetUser['phone'] ?? '')), $otp);
|
||||
|
||||
if ($mailSent || $smsSent) {
|
||||
$_SESSION['auth_reset_user_id'] = $userId;
|
||||
} else {
|
||||
error_log('authorization reset delivery failed for user_id=' . $userId);
|
||||
$stmt = $pdo->prepare('UPDATE password_reset_otp SET used = 1 WHERE user_id = ? AND used = 0');
|
||||
$stmt->execute([$userId]);
|
||||
}
|
||||
}
|
||||
|
||||
// Immer dieselbe Antwort, damit aus dem Reset-Flow kein Kontostatus abgeleitet werden kann.
|
||||
authorization_redirect('reset-password.php?step=verify&sent=1');
|
||||
}
|
||||
|
||||
if ($action === 'verify_otp') {
|
||||
if (!authorization_reset_request_is_active()) {
|
||||
authorization_clear_reset_state();
|
||||
authorization_redirect('reset-password.php?error=7');
|
||||
}
|
||||
|
||||
$otp = trim((string) ($_POST['otp_code'] ?? ''));
|
||||
$attempts = (int) ($_SESSION['auth_reset_attempts'] ?? 0) + 1;
|
||||
$_SESSION['auth_reset_attempts'] = $attempts;
|
||||
$userId = (int) ($_SESSION['auth_reset_user_id'] ?? 0);
|
||||
|
||||
if (!preg_match('/^[0-9]{6}$/', $otp) || $userId <= 0 || $attempts > AUTH_RESET_MAX_ATTEMPTS) {
|
||||
if ($attempts >= AUTH_RESET_MAX_ATTEMPTS && $userId > 0) {
|
||||
$stmt = $pdo->prepare('UPDATE password_reset_otp SET used = 1 WHERE user_id = ? AND used = 0');
|
||||
$stmt->execute([$userId]);
|
||||
authorization_clear_reset_state();
|
||||
authorization_redirect('reset-password.php?error=7');
|
||||
}
|
||||
authorization_redirect('reset-password.php?step=verify&error=3');
|
||||
}
|
||||
|
||||
$now = date('Y-m-d H:i:s');
|
||||
$pdo->beginTransaction();
|
||||
try {
|
||||
$stmt = $pdo->prepare(
|
||||
'SELECT id FROM password_reset_otp '
|
||||
. 'WHERE user_id = ? AND otp_code = ? AND used = 0 AND expires_at > ? '
|
||||
. 'ORDER BY id DESC LIMIT 1 FOR UPDATE'
|
||||
);
|
||||
$stmt->execute([$userId, $otp, $now]);
|
||||
$otpId = (int) ($stmt->fetchColumn() ?: 0);
|
||||
|
||||
if ($otpId <= 0) {
|
||||
$pdo->rollBack();
|
||||
authorization_redirect('reset-password.php?step=verify&error=3');
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare('UPDATE password_reset_otp SET used = 1 WHERE id = ?');
|
||||
$stmt->execute([$otpId]);
|
||||
$pdo->commit();
|
||||
|
||||
session_regenerate_id(true);
|
||||
$_SESSION['auth_reset_user_id'] = $userId;
|
||||
$_SESSION['auth_reset_authorized_until'] = time() + AUTH_RESET_TTL_SECONDS;
|
||||
unset($_SESSION['auth_reset_attempts']);
|
||||
authorization_redirect('reset-password.php?step=newpassword');
|
||||
} catch (Throwable $e) {
|
||||
if ($pdo->inTransaction()) {
|
||||
$pdo->rollBack();
|
||||
}
|
||||
error_log('authorization OTP verification failed: ' . $e->getMessage());
|
||||
authorization_redirect('reset-password.php?step=verify&error=3');
|
||||
}
|
||||
}
|
||||
|
||||
if ($action === 'set_password') {
|
||||
if (!authorization_reset_is_authorized()) {
|
||||
authorization_clear_reset_state();
|
||||
authorization_redirect('reset-password.php?error=7');
|
||||
}
|
||||
|
||||
$newPassword = (string) ($_POST['new_password'] ?? '');
|
||||
$confirmation = (string) ($_POST['new_password_confirm'] ?? '');
|
||||
$passwordError = authorization_password_error($newPassword, $confirmation);
|
||||
if ($passwordError !== null) {
|
||||
$code = str_contains($passwordError, 'stimmen') ? 6 : 5;
|
||||
authorization_redirect('reset-password.php?step=newpassword&error=' . $code);
|
||||
}
|
||||
|
||||
$userId = (int) $_SESSION['auth_reset_user_id'];
|
||||
$passwordHash = password_hash($newPassword, PASSWORD_DEFAULT);
|
||||
$resetUser = null;
|
||||
|
||||
$pdo->beginTransaction();
|
||||
try {
|
||||
$stmt = $pdo->prepare('UPDATE user_login SET password_hash = ? WHERE user_id = ?');
|
||||
$stmt->execute([$passwordHash, $userId]);
|
||||
if ($stmt->rowCount() !== 1) {
|
||||
throw new RuntimeException('Reset target login no longer exists.');
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare('UPDATE password_reset_otp SET used = 1 WHERE user_id = ? AND used = 0');
|
||||
$stmt->execute([$userId]);
|
||||
|
||||
$stmt = $pdo->prepare('SELECT mail, first_name, last_name FROM user_general_information WHERE user_id = ?');
|
||||
$stmt->execute([$userId]);
|
||||
$resetUser = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
if (!is_array($resetUser)) {
|
||||
throw new RuntimeException('Reset target user no longer exists.');
|
||||
}
|
||||
|
||||
$userName = trim(($resetUser['first_name'] ?? '') . ' ' . ($resetUser['last_name'] ?? ''));
|
||||
if ($userName === '') {
|
||||
$userName = (string) $resetUser['mail'];
|
||||
}
|
||||
$taskSubject = "Passwort selbst zurückgesetzt: {$userName}";
|
||||
$taskMessage = "{$userName} ({$resetUser['mail']}) hat das eigene Passwort erfolgreich zurückgesetzt.";
|
||||
$stmt = $pdo->prepare(
|
||||
"INSERT INTO admin_messages (type, sender_name, sender_email, subject, message, status, processed_at, created_at) "
|
||||
. "VALUES ('system', ?, ?, ?, ?, 'done', NOW(), NOW())"
|
||||
);
|
||||
$stmt->execute([$userName, $resetUser['mail'], $taskSubject, $taskMessage]);
|
||||
|
||||
$pdo->commit();
|
||||
} catch (Throwable $e) {
|
||||
if ($pdo->inTransaction()) {
|
||||
$pdo->rollBack();
|
||||
}
|
||||
error_log('authorization password reset failed: ' . $e->getMessage());
|
||||
authorization_clear_reset_state();
|
||||
authorization_redirect('reset-password.php?error=7');
|
||||
}
|
||||
|
||||
try {
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/intranet_repo/general_files/ini/send_mail.php';
|
||||
$loginUrl = authorization_public_url('/intranet_repo/projects/authorization/login.php');
|
||||
$greeting = !empty($resetUser['first_name']) ? 'Hallo ' . $resetUser['first_name'] : 'Hallo';
|
||||
$message = $greeting . ",\n\n"
|
||||
. "Ihr Passwort für das Intranet der NAK Reinickendorf wurde erfolgreich geändert.\n\n"
|
||||
. 'Sie können sich ab sofort mit Ihrem neuen Passwort anmelden.';
|
||||
send_intranet_mail((string) $resetUser['mail'], 'Passwort erfolgreich geändert', $message, null, 'intranet@nak-reinickendorf.de', 'Jetzt anmelden', $loginUrl);
|
||||
} catch (Throwable $e) {
|
||||
error_log('authorization password reset notification failed: ' . $e->getMessage());
|
||||
}
|
||||
|
||||
authorization_clear_reset_state();
|
||||
session_regenerate_id(true);
|
||||
authorization_redirect('login.php?message=1');
|
||||
}
|
||||
|
||||
authorization_redirect('reset-password.php');
|
||||
}
|
||||
|
||||
$csrfToken = authorization_csrf_token();
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="de" dir="ltr">
|
||||
<head>
|
||||
<?php require_once $_SERVER['DOCUMENT_ROOT'] . '/intranet_repo/projects/_central/php/html_head.php'; ?>
|
||||
<link rel="stylesheet" href="/intranet_repo/projects/_central/layouts/authorization.css">
|
||||
<script src="/intranet_repo/projects/_central/scripts/authorization.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="login_panel">
|
||||
<?php if ($error !== null): ?>
|
||||
<div class="nak-toast nak-toast--error nak-toast--inline" data-auth-toast>
|
||||
<div class="nak-toast__icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="square" stroke-linejoin="miter"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg></div>
|
||||
<span class="nak-toast__text"><?= authorization_html($error) ?></span>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="login_content">
|
||||
<div class="nak-logo-link auth-logo">
|
||||
<img class="auth-logo__icon" src="/intranet_repo/projects/_central/img/nak-logo.svg" alt="NAK Logo">
|
||||
<div class="auth-logo__text">
|
||||
<div class="auth-logo__name">Neuapostolische Kirche</div>
|
||||
<div class="auth-logo__gemeinde">Berlin-Reinickendorf</div>
|
||||
</div>
|
||||
</div>
|
||||
<h2>Passwort zurücksetzen</h2>
|
||||
|
||||
<?php if ($step === 'email'): ?>
|
||||
<p style="text-align:center;color:var(--gray-600);font-size:var(--font-size-sm);margin-bottom:var(--space-sm);">Geben Sie Ihre E-Mail-Adresse ein. Wenn ein passendes Konto vorhanden ist, senden wir einen Bestätigungscode an die hinterlegten Kontaktwege.</p>
|
||||
<form method="post" id="otpRequestForm">
|
||||
<input type="hidden" name="action" value="request_otp">
|
||||
<input type="hidden" name="csrf_token" value="<?= authorization_html($csrfToken) ?>">
|
||||
<label for="email">E-Mailadresse</label>
|
||||
<div class="item"><input class="login_field" type="email" id="email" name="email" maxlength="254" required autocomplete="email"></div>
|
||||
<button class="login_button" type="submit" id="requestBtn">Code anfordern</button>
|
||||
</form>
|
||||
|
||||
<?php elseif ($step === 'verify'): ?>
|
||||
<p style="text-align:center;color:var(--gray-600);font-size:var(--font-size-sm);margin-bottom:var(--space-sm);"><?= authorization_html(authorization_masked_reset_message()) ?></p>
|
||||
<form method="post">
|
||||
<input type="hidden" name="action" value="verify_otp">
|
||||
<input type="hidden" name="csrf_token" value="<?= authorization_html($csrfToken) ?>">
|
||||
<label for="otp_code">Bestätigungscode</label>
|
||||
<div class="item"><input class="login_field" type="text" id="otp_code" name="otp_code" maxlength="6" inputmode="numeric" pattern="[0-9]{6}" placeholder="000000" required autocomplete="one-time-code" style="text-align:center;font-size:1.4em;letter-spacing:8px;"></div>
|
||||
<input class="login_button" type="submit" value="Code bestätigen">
|
||||
</form>
|
||||
|
||||
<?php elseif ($step === 'newpassword'): ?>
|
||||
<p style="text-align:center;color:var(--gray-600);font-size:var(--font-size-sm);margin-bottom:var(--space-sm);">Ihr Code wurde bestätigt. Geben Sie nun Ihr neues Passwort ein.</p>
|
||||
<form method="post">
|
||||
<input type="hidden" name="action" value="set_password">
|
||||
<input type="hidden" name="csrf_token" value="<?= authorization_html($csrfToken) ?>">
|
||||
<label for="new_password">Neues Passwort</label>
|
||||
<div class="item"><input class="login_field" type="password" id="new_password" name="new_password" required autocomplete="new-password"></div>
|
||||
<label for="new_password_confirm">Neues Passwort wiederholen</label>
|
||||
<div class="item"><input class="login_field" type="password" id="new_password_confirm" name="new_password_confirm" required autocomplete="new-password"></div>
|
||||
<input class="login_button" type="submit" value="Passwort ändern">
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="small_text" align="center">
|
||||
<br><a href="login.php">Zurück zur Anmeldung</a><br><br>
|
||||
Probleme beim Zurücksetzen?<br>
|
||||
<a href="#" onclick="openContactModal(); return false;">Admin kontaktieren</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php require_once $_SERVER['DOCUMENT_ROOT'] . '/intranet_repo/projects/_central/php/footer.php'; ?>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
<?php /* Name: self-signup-process.php
|
||||
Beschreibung: Verarbeitung der Selbstregistrierung
|
||||
- Validierung der Eingaben
|
||||
- Speicherung in pending_registrations
|
||||
- Mail an Admin (samuel.bias@aol.de)
|
||||
- Eintrag in admin_messages */
|
||||
|
||||
$pdo = require_once($_SERVER['DOCUMENT_ROOT']."/intranet_repo/general_files/ini/database.php");
|
||||
require_once $_SERVER['DOCUMENT_ROOT'].'/intranet_repo/general_files/ini/send_mail.php';
|
||||
|
||||
// Validierung
|
||||
if (empty($_POST["first_name"])) {
|
||||
die("Vorname ist erforderlich.");
|
||||
}
|
||||
if (empty($_POST["last_name"])) {
|
||||
die("Nachname ist erforderlich.");
|
||||
}
|
||||
if (!filter_var($_POST["email"], FILTER_VALIDATE_EMAIL)) {
|
||||
die("Gültige E-Mail-Adresse erforderlich.");
|
||||
}
|
||||
if (strlen($_POST["password"]) < 8) {
|
||||
die("Passwort muss mindestens 8 Zeichen lang sein.");
|
||||
}
|
||||
if (!preg_match("/[a-z]/i", $_POST["password"])) {
|
||||
die("Passwort muss mindestens einen Buchstaben enthalten.");
|
||||
}
|
||||
if (!preg_match("/[0-9]/", $_POST["password"])) {
|
||||
die("Passwort muss mindestens eine Zahl enthalten.");
|
||||
}
|
||||
if ($_POST["password"] !== $_POST["password_confirmation"]) {
|
||||
die("Passwörter stimmen nicht überein.");
|
||||
}
|
||||
|
||||
$email = trim($_POST["email"]);
|
||||
$first_name = trim($_POST["first_name"]);
|
||||
$last_name = trim($_POST["last_name"]);
|
||||
|
||||
// Prüfen ob E-Mail bereits existiert (in user_general_information oder pending_registrations)
|
||||
$checkSql = "SELECT COUNT(*) FROM user_general_information WHERE mail = ?";
|
||||
$stmt = $pdo->prepare($checkSql);
|
||||
$stmt->execute([$email]);
|
||||
$count1 = $stmt->fetchColumn();
|
||||
|
||||
$checkSql2 = "SELECT COUNT(*) FROM pending_registrations WHERE email = ? AND status = 'pending'";
|
||||
$stmt2 = $pdo->prepare($checkSql2);
|
||||
$stmt2->execute([$email]);
|
||||
$count2 = $stmt2->fetchColumn();
|
||||
|
||||
if ($count1 > 0 || $count2 > 0) {
|
||||
header("Location: self-signup.php?error=2");
|
||||
exit;
|
||||
}
|
||||
|
||||
// Passwort hashen und Token generieren
|
||||
$password_hash = password_hash($_POST["password"], PASSWORD_DEFAULT);
|
||||
$token = bin2hex(random_bytes(32));
|
||||
|
||||
// In pending_registrations speichern
|
||||
$insertSql = "INSERT INTO pending_registrations (first_name, last_name, email, password_hash, token) VALUES (?, ?, ?, ?, ?)";
|
||||
$stmt = $pdo->prepare($insertSql);
|
||||
if (!$stmt) {
|
||||
header("Location: self-signup.php?error=1");
|
||||
exit;
|
||||
}
|
||||
|
||||
if (!$stmt->execute([$first_name, $last_name, $email, $password_hash, $token])) {
|
||||
header("Location: self-signup.php?error=1");
|
||||
exit;
|
||||
}
|
||||
$pending_id = $pdo->lastInsertId();
|
||||
|
||||
// Admin-Nachricht in Inbox speichern
|
||||
$subject = "Neue Registrierungsanfrage: {$first_name} {$last_name}";
|
||||
$message_text = "Neue Selbstregistrierung:\n\nName: {$first_name} {$last_name}\nE-Mail: {$email}\n\nBitte im Adminpanel freischalten.";
|
||||
$data_json = json_encode(['pending_id' => $pending_id, 'token' => $token]);
|
||||
|
||||
$inboxSql = "INSERT INTO admin_messages (type, sender_name, sender_email, subject, message, data_json) VALUES ('registration', ?, ?, ?, ?, ?)";
|
||||
$stmt = $pdo->prepare($inboxSql);
|
||||
$sender_name = "{$first_name} {$last_name}";
|
||||
$stmt->execute([$sender_name, $email, $subject, $message_text, $data_json]);
|
||||
|
||||
// Mail an Admin-Team (Admin + Technik)
|
||||
$admin_email = ADMIN_EMAIL;
|
||||
$mail_betreff = "Intranet: Neue Registrierungsanfrage von {$first_name} {$last_name}";
|
||||
$mail_nachricht = "Hallo Admin,\n\n";
|
||||
$mail_nachricht .= "Es liegt eine neue Registrierungsanfrage vor:\n\n";
|
||||
$mail_nachricht .= "Name: {$first_name} {$last_name}\n";
|
||||
$mail_nachricht .= "E-Mail: {$email}\n\n";
|
||||
$mail_nachricht .= "Bitte melden Sie sich im Adminpanel an, um die Anfrage zu bearbeiten.\n\n";
|
||||
$mail_nachricht .= "-- Intranet NAK Reinickendorf";
|
||||
|
||||
send_admin_team_mail($pdo, $mail_betreff, $mail_nachricht);
|
||||
|
||||
// Erfolg
|
||||
header("Location: self-signup.php?success=1");
|
||||
exit;
|
||||
?>
|
||||
@@ -1,101 +0,0 @@
|
||||
<?php /* Name: self-signup.php
|
||||
Beschreibung: Selbstregistrierung - Nutzer kann sich eigenständig registrieren.
|
||||
Admin erhält Mail + Inbox-Nachricht zur Freischaltung. */
|
||||
|
||||
if (session_status() == PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
require_once $_SERVER['DOCUMENT_ROOT'].'/intranet_repo/general_files/ini/parameters.php';
|
||||
require_once $_SERVER['DOCUMENT_ROOT'].'/intranet_repo/general_files/ini/functions.php';
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="de" dir="ltr">
|
||||
<head>
|
||||
<?php require_once $_SERVER['DOCUMENT_ROOT'].'/intranet_repo/projects/_central/php/html_head.php'; ?>
|
||||
|
||||
<script src="just-validate.js" defer></script>
|
||||
<script src="validation.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="login_panel">
|
||||
|
||||
<?php if (isset($_GET["error"])): ?>
|
||||
<?php if ($_GET["error"] == 1): ?>
|
||||
<div class="nak-toast nak-toast--error nak-toast--inline" id="nakToastErr">
|
||||
<div class="nak-toast__icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="square" stroke-linejoin="miter"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg></div>
|
||||
<span class="nak-toast__text">Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.</span>
|
||||
</div>
|
||||
<script>requestAnimationFrame(function(){document.getElementById('nakToastErr').classList.add('nak-toast--visible');});</script>
|
||||
<?php elseif ($_GET["error"] == 2): ?>
|
||||
<div class="nak-toast nak-toast--error nak-toast--inline" id="nakToastErr">
|
||||
<div class="nak-toast__icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="square" stroke-linejoin="miter"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg></div>
|
||||
<span class="nak-toast__text">Diese E-Mailadresse ist bereits registriert oder es liegt bereits eine Anfrage vor.</span>
|
||||
</div>
|
||||
<script>requestAnimationFrame(function(){document.getElementById('nakToastErr').classList.add('nak-toast--visible');});</script>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($_GET["success"]) && $_GET["success"] == 1): ?>
|
||||
<div class="nak-toast nak-toast--success nak-toast--inline" id="nakToastOk">
|
||||
<div class="nak-toast__icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="square" stroke-linejoin="miter"><path d="M20 6L9 17l-5-5"/></svg></div>
|
||||
<span class="nak-toast__text">Ihre Registrierungsanfrage wurde gesendet. Sie erhalten eine Benachrichtigung, sobald Ihr Konto freigeschaltet wurde.</span>
|
||||
</div>
|
||||
<script>requestAnimationFrame(function(){document.getElementById('nakToastOk').classList.add('nak-toast--visible');});</script>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="login_content">
|
||||
<div class="nak-logo-link" style="display:flex;align-items:flex-end;gap:12px;text-decoration:none;margin-bottom:24px;">
|
||||
<img src="/intranet_repo/projects/_central/img/nak-logo.svg" alt="NAK Logo" style="width:52px;height:52px;flex-shrink:0;">
|
||||
<div style="display:flex;flex-direction:column;justify-content:flex-end;line-height:1.2;">
|
||||
<div style="font-family:var(--font-primary);font-weight:500;font-size:0.875rem;color:var(--gray-900);white-space:nowrap;">Neuapostolische Kirche</div>
|
||||
<div style="font-family:var(--font-primary);font-weight:300;font-size:0.75rem;color:var(--gray-600);white-space:nowrap;">Berlin-Reinickendorf</div>
|
||||
</div>
|
||||
</div>
|
||||
<h2>Registrierung beantragen</h2>
|
||||
<h3 class="no_margins" align="center">Ihr Antrag wird von einem Administrator geprüft und freigeschaltet.</h3>
|
||||
|
||||
<form action="self-signup-process.php" method="post" id="signup" novalidate>
|
||||
<label for="first_name"><br>Vorname</label>
|
||||
<div class="item">
|
||||
<input class="login_field" type="text" id="first_name" name="first_name" required>
|
||||
</div>
|
||||
|
||||
<label for="last_name">Nachname</label>
|
||||
<div class="item">
|
||||
<input class="login_field" type="text" id="last_name" name="last_name" required>
|
||||
</div>
|
||||
|
||||
<label for="email">E-Mailadresse</label>
|
||||
<div class="item">
|
||||
<input class="login_field" type="email" id="email" name="email" required>
|
||||
</div>
|
||||
|
||||
<label for="password">Passwort</label>
|
||||
<div class="item">
|
||||
<input class="login_field" type="password" id="password" name="password" required>
|
||||
</div>
|
||||
|
||||
<label for="password_confirmation">Passwort wiederholen</label>
|
||||
<div class="item">
|
||||
<input class="login_field" type="password" id="password_confirmation" name="password_confirmation" required>
|
||||
</div>
|
||||
|
||||
<input class="login_button" type="submit" value="Registrierung beantragen">
|
||||
</form>
|
||||
<div class="small_text" align="center">
|
||||
Sie haben bereits einen Account?
|
||||
<br>
|
||||
<a href="login.php">Anmelden</a>
|
||||
<br><br>
|
||||
Sie wurden bereits vom Admin vorregistriert?
|
||||
<br>
|
||||
<a href="signup.php">Direkt registrieren</a>
|
||||
</div>
|
||||
</div><!-- EOD: login_content -->
|
||||
</div><!-- EOD: login_panel -->
|
||||
|
||||
<?php require_once $_SERVER['DOCUMENT_ROOT'].'/intranet_repo/projects/_central/php/footer.php'; ?>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,118 +0,0 @@
|
||||
<?php /* Name: signup-process.php
|
||||
Beschreibung: Vereinte Registrierungs-Verarbeitung
|
||||
- Prüft ob Nutzer vorregistriert ist → direkte Registrierung
|
||||
- Wenn nicht → automatisch Antrag in pending_registrations + Mail an Admin */
|
||||
|
||||
$pdo = require_once($_SERVER['DOCUMENT_ROOT']."/intranet_repo/general_files/ini/database.php");
|
||||
require_once $_SERVER['DOCUMENT_ROOT'].'/intranet_repo/general_files/ini/send_mail.php';
|
||||
|
||||
// === Validierung ===
|
||||
if (empty($_POST["first_name"])) {
|
||||
die("Vorname ist erforderlich.");
|
||||
}
|
||||
if (empty($_POST["last_name"])) {
|
||||
die("Nachname ist erforderlich.");
|
||||
}
|
||||
if (!filter_var($_POST["email"], FILTER_VALIDATE_EMAIL)) {
|
||||
die("Gültige E-Mail-Adresse erforderlich.");
|
||||
}
|
||||
if (empty($_POST["phone"])) {
|
||||
die("Telefonnummer ist erforderlich.");
|
||||
}
|
||||
if (strlen($_POST["password"]) < 8) {
|
||||
die("Passwort muss mindestens 8 Zeichen lang sein.");
|
||||
}
|
||||
if (!preg_match("/[a-z]/i", $_POST["password"])) {
|
||||
die("Passwort muss mindestens einen Buchstaben enthalten.");
|
||||
}
|
||||
if (!preg_match("/[0-9]/", $_POST["password"])) {
|
||||
die("Passwort muss mindestens eine Zahl enthalten.");
|
||||
}
|
||||
if ($_POST["password"] !== $_POST["password_confirmation"]) {
|
||||
die("Passwörter stimmen nicht überein.");
|
||||
}
|
||||
|
||||
$email = trim($_POST["email"]);
|
||||
$first_name = trim($_POST["first_name"]);
|
||||
$last_name = trim($_POST["last_name"]);
|
||||
$phone = preg_replace('/[^0-9]/', '', trim($_POST["phone"]));
|
||||
$password_hash = password_hash($_POST["password"], PASSWORD_DEFAULT);
|
||||
|
||||
// === Prüfung 1: Ist der Nutzer vom Admin vorregistriert? ===
|
||||
$stmt = $pdo->prepare("SELECT user_id FROM user_general_information WHERE mail = ?");
|
||||
$stmt->execute([$email]);
|
||||
$user_id = $stmt->fetchColumn();
|
||||
|
||||
if ($user_id) {
|
||||
// Nutzer ist vorregistriert – prüfen ob bereits Login existiert
|
||||
$stmt = $pdo->prepare("SELECT COUNT(*) FROM user_login WHERE user_id = ?");
|
||||
$stmt->execute([$user_id]);
|
||||
$loginExists = $stmt->fetchColumn();
|
||||
|
||||
if ($loginExists > 0) {
|
||||
// Bereits vollständig registriert
|
||||
header("Location: signup.php?error=2");
|
||||
exit;
|
||||
}
|
||||
|
||||
// Direkte Registrierung durchführen
|
||||
$stmt = $pdo->prepare("INSERT INTO user_login (user_id, password_hash) VALUES (?, ?)");
|
||||
|
||||
if ($stmt->execute([$user_id, $password_hash])) {
|
||||
|
||||
// Namen und Telefonnummer aktualisieren
|
||||
$stmt = $pdo->prepare("UPDATE user_general_information SET first_name=?, last_name=?, phone=? WHERE user_id=?");
|
||||
$stmt->execute([$first_name, $last_name, $phone, $user_id]);
|
||||
|
||||
header("Location: login.php");
|
||||
exit;
|
||||
} else {
|
||||
header("Location: signup.php?error=1");
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
// === Prüfung 2: Liegt bereits ein Antrag vor? ===
|
||||
$stmt = $pdo->prepare("SELECT COUNT(*) FROM pending_registrations WHERE email = ? AND status = 'pending'");
|
||||
$stmt->execute([$email]);
|
||||
$pendingCount = $stmt->fetchColumn();
|
||||
|
||||
if ($pendingCount > 0) {
|
||||
header("Location: signup.php?error=2");
|
||||
exit;
|
||||
}
|
||||
|
||||
// === Antrag stellen (Nutzer nicht vorregistriert) ===
|
||||
$token = bin2hex(random_bytes(32));
|
||||
|
||||
$stmt = $pdo->prepare("INSERT INTO pending_registrations (first_name, last_name, email, phone, password_hash, token) VALUES (?, ?, ?, ?, ?, ?)");
|
||||
|
||||
if (!$stmt->execute([$first_name, $last_name, $email, $phone, $password_hash, $token])) {
|
||||
header("Location: signup.php?error=1");
|
||||
exit;
|
||||
}
|
||||
$pending_id = $pdo->lastInsertId();
|
||||
|
||||
// Admin-Nachricht in Inbox speichern
|
||||
$subject = "Neue Registrierungsanfrage: {$first_name} {$last_name}";
|
||||
$message_text = "Neue Registrierungsanfrage:\n\nName: {$first_name} {$last_name}\nE-Mail: {$email}\n\nBitte im Adminpanel freischalten.";
|
||||
$data_json = json_encode(['pending_id' => $pending_id, 'token' => $token]);
|
||||
|
||||
$stmt = $pdo->prepare("INSERT INTO admin_messages (type, sender_name, sender_email, subject, message, data_json) VALUES ('registration', ?, ?, ?, ?, ?)");
|
||||
$sender_name = "{$first_name} {$last_name}";
|
||||
$stmt->execute([$sender_name, $email, $subject, $message_text, $data_json]);
|
||||
|
||||
// Mail an Admin-Team (Admin + Technik)
|
||||
$mail_betreff = "Neue Registrierungsanfrage von {$first_name} {$last_name}";
|
||||
$mail_nachricht = "Es liegt eine neue Registrierungsanfrage vor:\n\n";
|
||||
$mail_nachricht .= "Name: {$first_name} {$last_name}\n";
|
||||
$mail_nachricht .= "E-Mail: {$email}\n";
|
||||
$mail_nachricht .= "Telefon: {$phone}\n\n";
|
||||
$mail_nachricht .= "Bitte melden Sie sich im Adminpanel an, um die Anfrage zu bearbeiten.";
|
||||
|
||||
send_admin_team_mail($pdo, $mail_betreff, $mail_nachricht, "Im Adminpanel bearbeiten", "https://" . ($_SERVER['HTTP_HOST'] ?? intranet_env('INTRANET_FQDN')) . "/intranet_repo/projects/adminpanel/admin_inbox.php");
|
||||
|
||||
// Erfolg – gleiche Meldung wie bei direkter Registrierung (Nutzer merkt keinen Unterschied)
|
||||
header("Location: signup.php?success=1");
|
||||
exit;
|
||||
?>
|
||||
@@ -1,111 +1,136 @@
|
||||
<?php /* Name: signup.php
|
||||
Beschreibung: Registrierungsformular (vereint)
|
||||
- Wenn Nutzer vorregistriert: direkte Registrierung
|
||||
- Wenn nicht: Antrag wird automatisch gestellt
|
||||
- Verwendet signup-process.php */
|
||||
<?php
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/intranet_repo/general_files/ini/parameters.php';
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/intranet_repo/general_files/ini/functions.php';
|
||||
require_once __DIR__ . '/util/authorization_helpers.php';
|
||||
|
||||
if (session_status() == PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
require_once $_SERVER['DOCUMENT_ROOT'].'/intranet_repo/general_files/ini/parameters.php';
|
||||
require_once $_SERVER['DOCUMENT_ROOT'].'/intranet_repo/general_files/ini/functions.php';
|
||||
authorization_start_session();
|
||||
authorization_send_security_headers();
|
||||
|
||||
if (isset($_SESSION['user_id'])) {
|
||||
authorization_redirect($g_httpHomePath, 302);
|
||||
}
|
||||
|
||||
$form = authorization_registration_input([]);
|
||||
$error = null;
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$form = authorization_registration_input($_POST);
|
||||
|
||||
if (!authorization_csrf_is_valid($_POST['csrf_token'] ?? null)) {
|
||||
$error = 'Die Anfrage konnte nicht bestätigt werden. Bitte laden Sie die Seite neu und versuchen Sie es erneut.';
|
||||
} else {
|
||||
$error = authorization_registration_error($form);
|
||||
}
|
||||
|
||||
if ($error === null) {
|
||||
try {
|
||||
$pdo = require $_SERVER['DOCUMENT_ROOT'] . '/intranet_repo/general_files/ini/database.php';
|
||||
$result = authorization_register($pdo, $form);
|
||||
|
||||
if ($result['status'] === 'pending') {
|
||||
try {
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/intranet_repo/general_files/ini/send_mail.php';
|
||||
$mailSubject = "Neue Registrierungsanfrage von {$form['first_name']} {$form['last_name']}";
|
||||
$mailMessage = "Es liegt eine neue Registrierungsanfrage vor:\n\n"
|
||||
. "Name: {$form['first_name']} {$form['last_name']}\n"
|
||||
. "E-Mail: {$form['email']}\n"
|
||||
. "Telefon: {$form['phone']}\n\n"
|
||||
. 'Bitte melden Sie sich im Adminpanel an, um die Anfrage zu bearbeiten.';
|
||||
send_admin_team_mail(
|
||||
$pdo,
|
||||
$mailSubject,
|
||||
$mailMessage,
|
||||
'Im Adminpanel bearbeiten',
|
||||
authorization_public_url('/intranet_repo/projects/adminpanel/admin_inbox.php')
|
||||
);
|
||||
} catch (Throwable $e) {
|
||||
// Die Anfrage ist bereits atomar gespeichert; Mailfehler dürfen keine Doppelregistrierung provozieren.
|
||||
error_log('authorization signup notification failed: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
authorization_redirect('signup.php?success=1');
|
||||
} catch (Throwable $e) {
|
||||
error_log('authorization signup failed: ' . $e->getMessage());
|
||||
$error = 'Die Registrierung konnte nicht verarbeitet werden. Bitte versuchen Sie es erneut.';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$csrfToken = authorization_csrf_token();
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="de" dir="ltr">
|
||||
<head>
|
||||
<?php require_once $_SERVER['DOCUMENT_ROOT'].'/intranet_repo/projects/_central/php/html_head.php'; ?>
|
||||
|
||||
<!-- Quelle für just-validate.js: https://unpkg.com/just-validate@latest/dist/just-validate.production.min.js -->
|
||||
<script src="just-validate.js" defer></script>
|
||||
<script src="validation.js" defer></script>
|
||||
<?php require_once $_SERVER['DOCUMENT_ROOT'] . '/intranet_repo/projects/_central/php/html_head.php'; ?>
|
||||
<link rel="stylesheet" href="/intranet_repo/projects/_central/layouts/authorization.css">
|
||||
<script src="just-validate.js" defer></script>
|
||||
<script src="/intranet_repo/projects/_central/scripts/authorization.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="login_panel">
|
||||
<div class="login_panel">
|
||||
<?php if ($error !== null): ?>
|
||||
<div class="nak-toast nak-toast--error nak-toast--inline" data-auth-toast>
|
||||
<div class="nak-toast__icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="square" stroke-linejoin="miter"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg></div>
|
||||
<span class="nak-toast__text"><?= authorization_html($error) ?></span>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($_GET["error"])): ?>
|
||||
<?php if ($_GET["error"] == 1): ?>
|
||||
<div class="nak-toast nak-toast--error nak-toast--inline" id="nakToastErr">
|
||||
<div class="nak-toast__icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="square" stroke-linejoin="miter"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg></div>
|
||||
<span class="nak-toast__text">Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.</span>
|
||||
</div>
|
||||
<script>requestAnimationFrame(function(){document.getElementById('nakToastErr').classList.add('nak-toast--visible');});</script>
|
||||
<?php elseif ($_GET["error"] == 2): ?>
|
||||
<div class="nak-toast nak-toast--error nak-toast--inline" id="nakToastErr">
|
||||
<div class="nak-toast__icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="square" stroke-linejoin="miter"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg></div>
|
||||
<span class="nak-toast__text">Diese E-Mailadresse ist bereits registriert oder es liegt bereits eine Anfrage vor.</span>
|
||||
</div>
|
||||
<script>requestAnimationFrame(function(){document.getElementById('nakToastErr').classList.add('nak-toast--visible');});</script>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
<?php if (($_GET['success'] ?? '') === '1'): ?>
|
||||
<div class="nak-toast nak-toast--success nak-toast--inline" data-auth-toast>
|
||||
<div class="nak-toast__icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="square" stroke-linejoin="miter"><path d="M20 6L9 17l-5-5"/></svg></div>
|
||||
<span class="nak-toast__text">Ihre Angaben wurden verarbeitet. Falls Ihr Konto direkt aktiviert werden konnte, können Sie sich anmelden. Andernfalls erhalten Sie nach der Prüfung eine Benachrichtigung.</span>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($_GET["success"]) && $_GET["success"] == 1): ?>
|
||||
<div class="nak-toast nak-toast--success nak-toast--inline" id="nakToastOk">
|
||||
<div class="nak-toast__icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="square" stroke-linejoin="miter"><path d="M20 6L9 17l-5-5"/></svg></div>
|
||||
<span class="nak-toast__text">Ihre Registrierung wurde beantragt. Sie erhalten eine Benachrichtigung, sobald Ihr Konto freigeschaltet wurde.</span>
|
||||
</div>
|
||||
<script>requestAnimationFrame(function(){document.getElementById('nakToastOk').classList.add('nak-toast--visible');});</script>
|
||||
<?php endif; ?>
|
||||
<div class="login_content">
|
||||
<div class="nak-logo-link auth-logo">
|
||||
<img class="auth-logo__icon" src="/intranet_repo/projects/_central/img/nak-logo.svg" alt="NAK Logo">
|
||||
<div class="auth-logo__text">
|
||||
<div class="auth-logo__name">Neuapostolische Kirche</div>
|
||||
<div class="auth-logo__gemeinde">Berlin-Reinickendorf</div>
|
||||
</div>
|
||||
</div>
|
||||
<h2>Registrieren</h2>
|
||||
<p style="text-align:center;color:var(--gray-600);font-size:var(--font-size-sm);margin-bottom:var(--space-sm);">Erstellen Sie Ihren persönlichen Zugang zum Intranet.</p>
|
||||
|
||||
<div class="login_content">
|
||||
<div class="nak-logo-link auth-logo">
|
||||
<img class="auth-logo__icon" src="/intranet_repo/projects/_central/img/nak-logo.svg" alt="NAK Logo">
|
||||
<div class="auth-logo__text">
|
||||
<div class="auth-logo__name">Neuapostolische Kirche</div>
|
||||
<div class="auth-logo__gemeinde">Berlin-Reinickendorf</div>
|
||||
</div>
|
||||
</div>
|
||||
<h2>Registrieren</h2>
|
||||
<p style="text-align:center;color:var(--gray-600);font-size:var(--font-size-sm);margin-bottom:var(--space-sm);">Erstellen Sie Ihren persönlichen Zugang zum Intranet.</p>
|
||||
|
||||
<form action="signup-process.php" method="post" id="signup" novalidate>
|
||||
<label for="first_name">Vorname</label>
|
||||
<div class="item">
|
||||
<input class="login_field" type="text" id="first_name" name="first_name">
|
||||
</div>
|
||||
|
||||
<label for="last_name">Nachname</label>
|
||||
<div class="item">
|
||||
<input class="login_field" type="text" id="last_name" name="last_name">
|
||||
</div>
|
||||
|
||||
<label for="email">E-Mailadresse</label>
|
||||
<div class="item">
|
||||
<input class="login_field" type="email" id="email" name="email">
|
||||
</div>
|
||||
<form method="post" id="signup" novalidate autocomplete="on">
|
||||
<input type="hidden" name="csrf_token" value="<?= authorization_html($csrfToken) ?>">
|
||||
|
||||
<label for="phone">Telefonnummer</label>
|
||||
<div class="item">
|
||||
<div style="display:flex;align-items:stretch;width:100%;max-width:100%;margin-bottom:10px;box-sizing:border-box;">
|
||||
<div class="input-prefix" style="border-radius:0;border:1px solid var(--gray-300);border-right:none;margin:0;padding:14px 12px;font-weight:500;box-shadow:none;width:auto;background:var(--gray-200);color:var(--gray-700);display:flex;align-items:center;flex-shrink:0;">+49</div>
|
||||
<input class="login_field" type="tel" id="phone" name="phone" style="border-radius:0;border-left:0;margin:0;flex:1;width:auto;min-width:0;">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label for="password">Passwort</label>
|
||||
<div class="item">
|
||||
<input class="login_field" type="password" id="password" name="password">
|
||||
</div>
|
||||
|
||||
<label for="password_confirmation">Passwort wiederholen</label>
|
||||
<div class="item">
|
||||
<input class="login_field" type="password" id="password_confirmation" name="password_confirmation">
|
||||
</div>
|
||||
|
||||
<input class="login_button" type="submit" value="Registrieren">
|
||||
</form>
|
||||
<div class="small_text" align="center">
|
||||
Sie haben bereits einen Account?
|
||||
<br>
|
||||
<a href="login.php">Anmelden</a><br>
|
||||
<br>
|
||||
Probleme bei der Registrierung?<br>
|
||||
<a href="#" onclick="openContactModal(); return false;">Admin kontaktieren</a>
|
||||
</div>
|
||||
</div><!-- EOD: login_content -->
|
||||
</div><!-- EOD: login_panel -->
|
||||
<label for="first_name">Vorname</label>
|
||||
<div class="item"><input class="login_field" type="text" id="first_name" name="first_name" value="<?= authorization_html($form['first_name']) ?>" maxlength="100" required autocomplete="given-name"></div>
|
||||
|
||||
<?php require_once $_SERVER['DOCUMENT_ROOT'].'/intranet_repo/projects/_central/php/footer.php'; ?>
|
||||
<label for="last_name">Nachname</label>
|
||||
<div class="item"><input class="login_field" type="text" id="last_name" name="last_name" value="<?= authorization_html($form['last_name']) ?>" maxlength="100" required autocomplete="family-name"></div>
|
||||
|
||||
<label for="email">E-Mailadresse</label>
|
||||
<div class="item"><input class="login_field" type="email" id="email" name="email" value="<?= authorization_html($form['email']) ?>" maxlength="254" required autocomplete="email"></div>
|
||||
|
||||
<label for="phone">Telefonnummer</label>
|
||||
<div class="item">
|
||||
<div style="display:flex;align-items:stretch;width:100%;max-width:100%;margin-bottom:10px;box-sizing:border-box;">
|
||||
<div class="input-prefix" style="border-radius:0;border:1px solid var(--gray-300);border-right:none;margin:0;padding:14px 12px;font-weight:500;box-shadow:none;width:auto;background:var(--gray-200);color:var(--gray-700);display:flex;align-items:center;flex-shrink:0;">+49</div>
|
||||
<input class="login_field" type="tel" id="phone" name="phone" value="<?= authorization_html($form['phone']) ?>" maxlength="20" style="border-radius:0;border-left:0;margin:0;flex:1;width:auto;min-width:0;" required autocomplete="tel-national">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label for="password">Passwort</label>
|
||||
<div class="item"><input class="login_field" type="password" id="password" name="password" required autocomplete="new-password"></div>
|
||||
|
||||
<label for="password_confirmation">Passwort wiederholen</label>
|
||||
<div class="item"><input class="login_field" type="password" id="password_confirmation" name="password_confirmation" required autocomplete="new-password"></div>
|
||||
|
||||
<input class="login_button" type="submit" value="Registrieren">
|
||||
</form>
|
||||
<div class="small_text" align="center">
|
||||
Sie haben bereits einen Account?<br>
|
||||
<a href="login.php">Anmelden</a><br><br>
|
||||
Probleme bei der Registrierung?<br>
|
||||
<a href="#" onclick="openContactModal(); return false;">Admin kontaktieren</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php require_once $_SERVER['DOCUMENT_ROOT'] . '/intranet_repo/projects/_central/php/footer.php'; ?>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,313 @@
|
||||
<?php
|
||||
|
||||
const AUTH_RESET_TTL_SECONDS = 600;
|
||||
const AUTH_RESET_MAX_ATTEMPTS = 5;
|
||||
const AUTH_DUMMY_PASSWORD_HASH = '$2y$12$7v2tTTu3R6UkMcX7N9FvJeetK1LEkkkFe/qbj./2EdWVKKT0LS7RS';
|
||||
|
||||
function authorization_start_session(): void
|
||||
{
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
}
|
||||
|
||||
function authorization_send_security_headers(): void
|
||||
{
|
||||
header('Cache-Control: no-store, max-age=0');
|
||||
header('Pragma: no-cache');
|
||||
header('Referrer-Policy: no-referrer');
|
||||
header('X-Content-Type-Options: nosniff');
|
||||
header('X-Frame-Options: DENY');
|
||||
}
|
||||
|
||||
function authorization_html($value): string
|
||||
{
|
||||
return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
|
||||
function authorization_csrf_token(): string
|
||||
{
|
||||
authorization_start_session();
|
||||
if (empty($_SESSION['authorization_csrf'])) {
|
||||
$_SESSION['authorization_csrf'] = bin2hex(random_bytes(32));
|
||||
}
|
||||
|
||||
return (string) $_SESSION['authorization_csrf'];
|
||||
}
|
||||
|
||||
function authorization_csrf_is_valid($postedToken): bool
|
||||
{
|
||||
authorization_start_session();
|
||||
$sessionToken = $_SESSION['authorization_csrf'] ?? '';
|
||||
|
||||
return is_string($postedToken)
|
||||
&& is_string($sessionToken)
|
||||
&& $sessionToken !== ''
|
||||
&& hash_equals($sessionToken, $postedToken);
|
||||
}
|
||||
|
||||
function authorization_redirect(string $location, int $status = 303): never
|
||||
{
|
||||
header('Location: ' . $location, true, $status);
|
||||
exit;
|
||||
}
|
||||
|
||||
function authorization_password_error(string $password, ?string $confirmation = null): ?string
|
||||
{
|
||||
if (strlen($password) < 8 || !preg_match('/[a-z]/i', $password) || !preg_match('/[0-9]/', $password)) {
|
||||
return 'Passwort muss mindestens 8 Zeichen, einen Buchstaben und eine Zahl enthalten.';
|
||||
}
|
||||
|
||||
if ($confirmation !== null && !hash_equals($password, $confirmation)) {
|
||||
return 'Passwörter stimmen nicht überein.';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function authorization_normalize_phone(string $phone): string
|
||||
{
|
||||
return preg_replace('/\D+/', '', trim($phone)) ?? '';
|
||||
}
|
||||
|
||||
function authorization_registration_input(array $input): array
|
||||
{
|
||||
return [
|
||||
'first_name' => trim((string) ($input['first_name'] ?? '')),
|
||||
'last_name' => trim((string) ($input['last_name'] ?? '')),
|
||||
'email' => trim((string) ($input['email'] ?? '')),
|
||||
'phone' => authorization_normalize_phone((string) ($input['phone'] ?? '')),
|
||||
'password' => (string) ($input['password'] ?? ''),
|
||||
'password_confirmation' => (string) ($input['password_confirmation'] ?? ''),
|
||||
];
|
||||
}
|
||||
|
||||
function authorization_text_length(string $value): int
|
||||
{
|
||||
return function_exists('mb_strlen') ? mb_strlen($value) : strlen($value);
|
||||
}
|
||||
|
||||
function authorization_registration_error(array $input): ?string
|
||||
{
|
||||
if ($input['first_name'] === '' || authorization_text_length($input['first_name']) > 100) {
|
||||
return 'Bitte geben Sie einen gültigen Vornamen ein.';
|
||||
}
|
||||
if ($input['last_name'] === '' || authorization_text_length($input['last_name']) > 100) {
|
||||
return 'Bitte geben Sie einen gültigen Nachnamen ein.';
|
||||
}
|
||||
if (strlen($input['email']) > 254 || !filter_var($input['email'], FILTER_VALIDATE_EMAIL)) {
|
||||
return 'Bitte geben Sie eine gültige E-Mail-Adresse ein.';
|
||||
}
|
||||
if (!preg_match('/^[1-9][0-9]{5,14}$/', $input['phone'])) {
|
||||
return 'Bitte geben Sie eine gültige Telefonnummer ohne führende 0 ein.';
|
||||
}
|
||||
|
||||
return authorization_password_error($input['password'], $input['password_confirmation']);
|
||||
}
|
||||
|
||||
function authorization_authenticate(PDO $pdo, string $email, string $password): ?int
|
||||
{
|
||||
$stmt = $pdo->prepare(
|
||||
'SELECT u.user_id, l.password_hash '
|
||||
. 'FROM user_general_information u '
|
||||
. 'INNER JOIN user_login l ON l.user_id = u.user_id '
|
||||
. 'WHERE LOWER(u.mail) = LOWER(?) '
|
||||
. 'LIMIT 1'
|
||||
);
|
||||
$stmt->execute([trim($email)]);
|
||||
$user = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
$storedHash = is_array($user) && is_string($user['password_hash'] ?? null)
|
||||
? $user['password_hash']
|
||||
: AUTH_DUMMY_PASSWORD_HASH;
|
||||
$valid = password_verify($password, $storedHash);
|
||||
|
||||
if (!$valid || !is_array($user)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$userId = (int) $user['user_id'];
|
||||
if ($userId <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (password_needs_rehash($storedHash, PASSWORD_DEFAULT)) {
|
||||
$newHash = password_hash($password, PASSWORD_DEFAULT);
|
||||
$update = $pdo->prepare('UPDATE user_login SET password_hash = ? WHERE user_id = ?');
|
||||
$update->execute([$newHash, $userId]);
|
||||
}
|
||||
|
||||
return $userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{status:string,pending_id?:int,token?:string}
|
||||
*/
|
||||
function authorization_register(PDO $pdo, array $input): array
|
||||
{
|
||||
$passwordHash = password_hash($input['password'], PASSWORD_DEFAULT);
|
||||
$email = $input['email'];
|
||||
|
||||
$pdo->beginTransaction();
|
||||
try {
|
||||
// Serialisiert parallele Registrierungsversuche derselben E-Mail ohne neues Schema.
|
||||
$lock = $pdo->prepare('SELECT pg_advisory_xact_lock(hashtext(LOWER(?)))');
|
||||
$lock->execute([$email]);
|
||||
|
||||
$stmt = $pdo->prepare(
|
||||
'SELECT user_id FROM user_general_information '
|
||||
. 'WHERE LOWER(mail) = LOWER(?) LIMIT 1 FOR UPDATE'
|
||||
);
|
||||
$stmt->execute([$email]);
|
||||
$userId = (int) ($stmt->fetchColumn() ?: 0);
|
||||
|
||||
if ($userId > 0) {
|
||||
$stmt = $pdo->prepare('SELECT 1 FROM user_login WHERE user_id = ? LIMIT 1');
|
||||
$stmt->execute([$userId]);
|
||||
if ($stmt->fetchColumn()) {
|
||||
$pdo->commit();
|
||||
return ['status' => 'existing'];
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare('INSERT INTO user_login (user_id, password_hash) VALUES (?, ?)');
|
||||
$stmt->execute([$userId, $passwordHash]);
|
||||
|
||||
$stmt = $pdo->prepare(
|
||||
'UPDATE user_general_information '
|
||||
. 'SET first_name = ?, last_name = ?, phone = ? WHERE user_id = ?'
|
||||
);
|
||||
$stmt->execute([$input['first_name'], $input['last_name'], $input['phone'], $userId]);
|
||||
|
||||
$pdo->commit();
|
||||
return ['status' => 'activated'];
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare(
|
||||
"SELECT id FROM pending_registrations WHERE LOWER(email) = LOWER(?) AND status = 'pending' LIMIT 1 FOR UPDATE"
|
||||
);
|
||||
$stmt->execute([$email]);
|
||||
if ($stmt->fetchColumn()) {
|
||||
$pdo->commit();
|
||||
return ['status' => 'existing'];
|
||||
}
|
||||
|
||||
// Token bleibt für Schema-/Admin-Kompatibilität erhalten, wird aber nicht mehr in admin_messages dupliziert.
|
||||
$token = bin2hex(random_bytes(32));
|
||||
$stmt = $pdo->prepare(
|
||||
'INSERT INTO pending_registrations '
|
||||
. '(first_name, last_name, email, phone, password_hash, token) '
|
||||
. 'VALUES (?, ?, ?, ?, ?, ?)'
|
||||
);
|
||||
$stmt->execute([
|
||||
$input['first_name'],
|
||||
$input['last_name'],
|
||||
$email,
|
||||
$input['phone'],
|
||||
$passwordHash,
|
||||
$token,
|
||||
]);
|
||||
$pendingId = (int) $pdo->lastInsertId();
|
||||
|
||||
$subject = "Neue Registrierungsanfrage: {$input['first_name']} {$input['last_name']}";
|
||||
$message = "Neue Registrierungsanfrage:\n\n"
|
||||
. "Name: {$input['first_name']} {$input['last_name']}\n"
|
||||
. "E-Mail: {$email}\n\n"
|
||||
. 'Bitte im Adminpanel freischalten.';
|
||||
$dataJson = json_encode(['pending_id' => $pendingId], JSON_THROW_ON_ERROR);
|
||||
$senderName = trim($input['first_name'] . ' ' . $input['last_name']);
|
||||
|
||||
$stmt = $pdo->prepare(
|
||||
"INSERT INTO admin_messages (type, sender_name, sender_email, subject, message, data_json) "
|
||||
. "VALUES ('registration', ?, ?, ?, ?, ?)"
|
||||
);
|
||||
$stmt->execute([$senderName, $email, $subject, $message, $dataJson]);
|
||||
|
||||
$pdo->commit();
|
||||
return ['status' => 'pending', 'pending_id' => $pendingId];
|
||||
} catch (Throwable $e) {
|
||||
if ($pdo->inTransaction()) {
|
||||
$pdo->rollBack();
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
function authorization_public_url(string $path): string
|
||||
{
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/intranet_repo/general_files/ini/runtime_env.php';
|
||||
$host = intranet_env('INTRANET_FQDN');
|
||||
return 'https://' . $host . '/' . ltrim($path, '/');
|
||||
}
|
||||
|
||||
function authorization_clear_reset_state(): void
|
||||
{
|
||||
authorization_start_session();
|
||||
unset(
|
||||
$_SESSION['auth_reset_user_id'],
|
||||
$_SESSION['auth_reset_started_at'],
|
||||
$_SESSION['auth_reset_attempts'],
|
||||
$_SESSION['auth_reset_authorized_until']
|
||||
);
|
||||
}
|
||||
|
||||
function authorization_reset_request_is_active(): bool
|
||||
{
|
||||
authorization_start_session();
|
||||
$startedAt = (int) ($_SESSION['auth_reset_started_at'] ?? 0);
|
||||
return $startedAt > 0 && ($startedAt + AUTH_RESET_TTL_SECONDS) >= time();
|
||||
}
|
||||
|
||||
function authorization_reset_is_authorized(): bool
|
||||
{
|
||||
authorization_start_session();
|
||||
$userId = (int) ($_SESSION['auth_reset_user_id'] ?? 0);
|
||||
$authorizedUntil = (int) ($_SESSION['auth_reset_authorized_until'] ?? 0);
|
||||
return $userId > 0 && $authorizedUntil >= time();
|
||||
}
|
||||
|
||||
function authorization_masked_reset_message(): string
|
||||
{
|
||||
return 'Wenn ein passendes Konto vorhanden ist, wurde ein 6-stelliger Bestätigungscode an die hinterlegten Kontaktwege gesendet.';
|
||||
}
|
||||
|
||||
function authorization_send_reset_sms(string $phone, string $otp): bool
|
||||
{
|
||||
if ($phone === '') {
|
||||
return false;
|
||||
}
|
||||
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/intranet_repo/general_files/ini/runtime_env.php';
|
||||
$payload = json_encode([
|
||||
'phone' => '+49' . $phone,
|
||||
'message' => "Intranet NAK Reinickendorf\n\nIhr Bestaetigungscode: {$otp}\n\nDer Code ist 10 Minuten gueltig.",
|
||||
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
|
||||
if ($payload === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$context = stream_context_create([
|
||||
'http' => [
|
||||
'header' => "Content-Type: application/json; charset=utf-8\r\n",
|
||||
'method' => 'POST',
|
||||
'content' => $payload,
|
||||
'timeout' => 10,
|
||||
'ignore_errors' => true,
|
||||
],
|
||||
]);
|
||||
|
||||
try {
|
||||
$gatewayUrl = intranet_env('SMS_GATEWAY_URL');
|
||||
} catch (RuntimeException $e) {
|
||||
error_log('SMS gateway unavailable: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = @file_get_contents(rtrim($gatewayUrl, '/') . '/send_sms', false, $context);
|
||||
if ($result === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$statusLine = $http_response_header[0] ?? '';
|
||||
return preg_match('/\s2\d\d\s/', $statusLine) === 1;
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
<?php /* Name: validate-email.php
|
||||
Beschreibung: Validierung der E-Mailadresse
|
||||
- Wird innerhalb von validation.js aufgerufen
|
||||
- Prüft ob E-Mail bereits vollständig registriert oder
|
||||
ein offener Antrag vorliegt */
|
||||
|
||||
$pdo = require_once($_SERVER['DOCUMENT_ROOT']."/intranet_repo/general_files/ini/database.php");
|
||||
|
||||
header("Content-Type: application/json");
|
||||
|
||||
if (!isset($_GET["email"]) || empty(trim($_GET["email"]))) {
|
||||
echo json_encode(["available" => false]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$email = trim($_GET["email"]);
|
||||
$is_available = true;
|
||||
|
||||
// Prüfung 1: Gibt es einen vollständigen Login für diese E-Mail?
|
||||
$stmt = $pdo->prepare("SELECT u.user_id FROM user_general_information u INNER JOIN user_login l ON u.user_id = l.user_id WHERE u.mail = ?");
|
||||
$stmt->execute([$email]);
|
||||
|
||||
if ($stmt->fetch()) {
|
||||
$is_available = false;
|
||||
}
|
||||
|
||||
// Prüfung 2: Liegt bereits ein offener Antrag vor?
|
||||
if ($is_available) {
|
||||
$stmt = $pdo->prepare("SELECT COUNT(*) FROM pending_registrations WHERE email = ? AND status = 'pending'");
|
||||
$stmt->execute([$email]);
|
||||
$pendingCount = $stmt->fetchColumn();
|
||||
|
||||
if ($pendingCount > 0) {
|
||||
$is_available = false;
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode(["available" => $is_available]);
|
||||
exit;
|
||||
?>
|
||||
@@ -1,74 +0,0 @@
|
||||
const validation = new JustValidate("#signup");
|
||||
validation
|
||||
.addField("#first_name", [
|
||||
{
|
||||
rule: "required",
|
||||
errorMessage: "Pflichtfeld"
|
||||
}
|
||||
])
|
||||
.addField("#last_name", [
|
||||
{
|
||||
rule: "required",
|
||||
errorMessage: "Pflichtfeld"
|
||||
}
|
||||
])
|
||||
.addField("#email", [
|
||||
{
|
||||
rule: "required",
|
||||
errorMessage: "Pflichtfeld"
|
||||
},
|
||||
{
|
||||
rule: "email",
|
||||
errorMessage: "Ungültig"
|
||||
},
|
||||
{
|
||||
validator: (value) => () => {
|
||||
return fetch("validate-email.php?email=" + encodeURIComponent(value))
|
||||
.then(function(response) {
|
||||
return response.json();
|
||||
})
|
||||
.then(function(json) {
|
||||
return json.available;
|
||||
});
|
||||
},
|
||||
errorMessage: "Bereits registriert"
|
||||
}
|
||||
])
|
||||
.addField("#phone", [
|
||||
{
|
||||
rule: "required",
|
||||
errorMessage: "Pflichtfeld"
|
||||
},
|
||||
{
|
||||
rule: "minLength",
|
||||
value: 6,
|
||||
errorMessage: "Ungültige Telefonnummer"
|
||||
},
|
||||
{
|
||||
validator: (value) => {
|
||||
return /^[1-9][0-9\s\-\/()]{4,}$/.test(value);
|
||||
},
|
||||
errorMessage: "Ohne führende 0, nur Ziffern"
|
||||
}
|
||||
])
|
||||
.addField("#password", [
|
||||
{
|
||||
rule: "required",
|
||||
errorMessage: "Pflichtfeld"
|
||||
},
|
||||
{
|
||||
rule: "password",
|
||||
errorMessage: "Zu leicht"
|
||||
}
|
||||
])
|
||||
.addField("#password_confirmation", [
|
||||
{
|
||||
validator: (value, fields) => {
|
||||
return value === fields["#password"].elem.value;
|
||||
},
|
||||
errorMessage: "Passwörter müssen übereinstimmen"
|
||||
}
|
||||
])
|
||||
.onSuccess((event) => {
|
||||
document.getElementById("signup").submit();
|
||||
});
|
||||
Reference in New Issue
Block a user