Files
WP-Business-Forum/includes/class-forum-auth.php
2026-03-30 20:41:51 +02:00

155 lines
7.2 KiB
PHP

<?php
if ( ! defined( 'ABSPATH' ) ) exit;
class WBF_Auth {
const SESSION_KEY = 'wbf_forum_user';
public static function init() {
// PHP 8.3: session_start() nach gesendeten Headers erzeugt E_WARNING,
// der direkt in den HTML-Output fließt und das Layout zerstört.
// Lösung: headers_sent() prüfen + session_start() mit Cookie-Optionen aufrufen.
if ( ! session_id() ) {
if ( headers_sent() ) {
return;
}
$session_opts = [
'cookie_httponly' => true,
'cookie_samesite' => 'Lax',
'use_strict_mode' => true,
];
if ( is_ssl() || ( ! empty( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] !== 'off' ) ) {
$session_opts['cookie_secure'] = true;
}
session_start( $session_opts );
}
// Auto-login via Remember-Me cookie if not already logged in
if ( empty( $_SESSION[ self::SESSION_KEY ] ) && isset( $_COOKIE['wbf_remember'] ) ) {
$row = WBF_DB::verify_remember_token( $_COOKIE['wbf_remember'] );
if ( $row ) {
$user = WBF_DB::get_user( (int)$row->user_id );
if ( $user && WBF_Roles::level($user->role) >= 0 ) {
$_SESSION[ self::SESSION_KEY ] = $user->id;
WBF_DB::touch_last_active( $user->id );
}
}
}
}
public static function is_forum_logged_in() {
self::init();
return ! empty( $_SESSION[ self::SESSION_KEY ] );
}
public static function get_current_user() {
self::init();
if ( empty( $_SESSION[ self::SESSION_KEY ] ) ) return null;
return WBF_DB::get_user( (int) $_SESSION[ self::SESSION_KEY ] );
}
public static function login( $username_or_email, $password, $remember = false ) {
self::init();
$user = WBF_DB::get_user_by( 'username', $username_or_email );
if ( ! $user ) {
$user = WBF_DB::get_user_by( 'email', $username_or_email );
}
if ( ! $user ) return array( 'success' => false, 'message' => 'Benutzer nicht gefunden.' );
if ( ! password_verify( $password, $user->password ) ) {
return array( 'success' => false, 'message' => 'Falsches Passwort.' );
}
// ── 2FA-Check ─────────────────────────────────────────────────────────
// Wenn 2FA aktiv: Login pausieren und TOTP-Code anfordern.
// remember-Flag in Session merken, damit es nach 2FA-Verifikation gesetzt wird.
if ( class_exists('WBF_TOTP') && WBF_TOTP::is_enabled_for( $user->id ) ) {
$_SESSION[ WBF_TOTP::SESSION_PENDING ] = $user->id;
if ( $remember ) {
$_SESSION['wbf_2fa_remember'] = true;
}
return array( 'success' => false, '2fa_required' => true );
}
// ── Ende 2FA-Check ────────────────────────────────────────────────────
if ( WBF_Roles::level($user->role) < 0 ) {
// Zeitlich begrenzte Sperre prüfen — automatisch aufheben wenn abgelaufen
if ( ! empty($user->ban_until) && strtotime($user->ban_until) <= time() ) {
$restore = ! empty($user->pre_ban_role) ? $user->pre_ban_role : 'member';
WBF_DB::update_user( $user->id, [
'role' => $restore,
'ban_reason' => '',
'ban_until' => null,
'pre_ban_role' => '',
]);
$user = WBF_DB::get_user( $user->id );
if ( session_id() ) session_regenerate_id( true );
$_SESSION[ self::SESSION_KEY ] = $user->id;
WBF_DB::touch_last_active( $user->id );
return array( 'success' => true, 'user' => $user );
}
$reason = !empty($user->ban_reason) ? $user->ban_reason : 'Dein Konto wurde gesperrt.';
if ( ! empty($user->ban_until) ) {
$until_fmt = date_i18n( 'd.m.Y \u\m H:i \U\h\r', strtotime($user->ban_until) );
$reason .= ' (Gesperrt bis: ' . $until_fmt . ')';
}
return array( 'success' => false, 'banned' => true, 'message' => $reason );
}
if ( session_id() ) session_regenerate_id( true );
$_SESSION[ self::SESSION_KEY ] = $user->id;
WBF_DB::touch_last_active( $user->id );
return array( 'success' => true, 'user' => $user );
}
public static function register( $username, $email, $password, $display_name ) {
self::init();
$username = sanitize_user( $username );
$email = sanitize_email( $email );
$display_name = sanitize_text_field( $display_name );
if ( strlen($username) < 3 ) return array('success'=>false,'message'=>'Benutzername mindestens 3 Zeichen.');
if ( ! is_email($email) ) return array('success'=>false,'message'=>'Ungültige E-Mail-Adresse.');
if ( strlen($password) < 6 ) return array('success'=>false,'message'=>'Passwort mindestens 6 Zeichen.');
if ( empty($display_name) ) return array('success'=>false,'message'=>'Anzeigename darf nicht leer sein.');
if ( WBF_DB::get_user_by('username', $username) ) return array('success'=>false,'message'=>'Benutzername bereits vergeben.');
if ( WBF_DB::get_user_by('email', $email) ) return array('success'=>false,'message'=>'E-Mail bereits registriert.');
$avatar = 'https://www.gravatar.com/avatar/' . md5( strtolower($email) ) . '?d=identicon&s=80';
$id = WBF_DB::create_user( array(
'username' => $username,
'email' => $email,
'password' => password_hash( $password, PASSWORD_DEFAULT ),
'display_name' => $display_name,
'avatar_url' => $avatar,
));
if ( session_id() ) session_regenerate_id( true );
$_SESSION[ self::SESSION_KEY ] = $id;
return array('success'=>true,'user'=>WBF_DB::get_user($id));
}
public static function logout() {
self::init();
$user_id = $_SESSION[ self::SESSION_KEY ] ?? 0;
unset( $_SESSION[ self::SESSION_KEY ] );
// 2FA-Pending-State ebenfalls löschen
if ( class_exists('WBF_TOTP') ) {
unset( $_SESSION[ WBF_TOTP::SESSION_PENDING ] );
unset( $_SESSION['wbf_2fa_remember'] );
}
if ( $user_id ) {
WBF_DB::delete_remember_token( (int)$user_id );
}
if ( isset($_COOKIE['wbf_remember']) ) {
setcookie( 'wbf_remember', '', time() - 3600, COOKIEPATH, COOKIE_DOMAIN, is_ssl(), true );
}
}
/** Remember-Me Token setzen und Cookie senden */
public static function set_remember_cookie( $user_id ) {
$token = WBF_DB::create_remember_token( (int)$user_id );
setcookie( 'wbf_remember', $token, time() + 30 * DAY_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN, is_ssl(), true );
}
}
add_action('init', array('WBF_Auth','init'), 1);