🔴 Neue Updates auf Gitea';
$links[] = $update_link;
}
return $links;
}
// === 2. Admin-Dashboard-Hinweis bei neuer Version ===
add_action('admin_init', 'wp_multi_2fa_check_update_notice');
function wp_multi_2fa_check_update_notice() {
if (!current_user_can('update_plugins')) return;
$transient = get_transient('wp_multi_2fa_gitea_version');
if ($transient === false) {
$response = wp_remote_get('https://git.viper.ipv64.net/api/v1/repos/M_Viper/WP-Multi-2FA/releases/latest');
if (is_wp_error($response)) return;
$data = json_decode(wp_remote_retrieve_body($response));
if (isset($data->tag_name)) {
$latest = ltrim($data->tag_name, 'v'); // Entfernt evtl. "v" vor der Versionsnummer
set_transient('wp_multi_2fa_gitea_version', $latest, 12 * HOUR_IN_SECONDS);
} else {
return;
}
} else {
$latest = $transient;
}
if (!function_exists('get_plugin_data')) {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
}
$plugin_data = get_plugin_data(__FILE__);
$current_version = $plugin_data['Version'];
if (version_compare($latest, $current_version, '>')) {
add_action('admin_notices', function () use ($latest) {
?>
Allgemein
add_action('admin_init', [$this, 'register_settings_field']);
// Session für 2FA-Code speichern
if (!session_id()) {
session_start();
}
}
private function get_user_2fa_data($user_id) {
$all = get_option($this->option_name, []);
return $all[$user_id] ?? null;
}
private function set_user_2fa_data($user_id, $data) {
$all = get_option($this->option_name, []);
$all[$user_id] = $data;
update_option($this->option_name, $all);
}
public function show_2fa_settings($user) {
if (!current_user_can('edit_user', $user->ID)) return;
$data = $this->get_user_2fa_data($user->ID);
$enabled = $data['enabled'] ?? false;
$secret = $data['secret'] ?? null;
$backup_codes = $data['backup_codes'] ?? [];
if (!$secret && $enabled) {
$secret = $this->generate_secret();
$data['secret'] = $secret;
$backup_codes = $this->generate_backup_codes();
$data['backup_codes'] = $backup_codes;
$this->set_user_2fa_data($user->ID, $data);
}
?>
2-Faktor-Authentifizierung
get_user_2fa_data($user_id) ?? [];
$enabled = isset($_POST['wp2fa_enabled']) && $_POST['wp2fa_enabled'] == '1';
if ($enabled && empty($data['secret'])) {
$data['secret'] = $this->generate_secret();
$data['backup_codes'] = $this->generate_backup_codes();
}
if (!$enabled) {
// Deaktivieren: löschen
$data = [];
} else {
$data['enabled'] = true;
}
$this->set_user_2fa_data($user_id, $data);
}
private function generate_secret() {
$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
$secret = '';
for ($i=0; $i < 16; $i++) {
$secret .= $chars[random_int(0, 31)];
}
return $secret;
}
private function generate_backup_codes() {
$codes = [];
for ($i = 0; $i < 10; $i++) {
$codes[] = strtoupper(bin2hex(random_bytes(4)));
}
return $codes;
}
private function get_qr_code_url($email, $secret) {
$issuer = rawurlencode(get_bloginfo('name'));
$label = rawurlencode(get_bloginfo('name') . ':' . $email);
$otpauth = "otpauth://totp/{$label}?secret={$secret}&issuer={$issuer}";
$chl = urlencode($otpauth);
return "https://api.qrserver.com/v1/create-qr-code/?size=200x200&data={$chl}";
}
// Nach Login prüfen, ob 2FA nötig
public function after_login($user_login, $user) {
if (!$this->needs_2fa($user->ID)) return;
$_SESSION['wp2fa_user'] = $user->ID;
wp_logout();
wp_redirect(site_url('/wp-login.php?action=wp2fa'));
exit;
}
private function needs_2fa($user_id) {
$data = $this->get_user_2fa_data($user_id);
$admin_forced = get_option('wp2fa_admin_forced', false);
return ($data['enabled'] ?? false) || $admin_forced;
}
// 2FA Handler: Formular verarbeiten und ggf. anzeigen (Popup)
public function handle_2fa() {
if (!isset($_GET['action']) || $_GET['action'] !== 'wp2fa') return;
$user_id = $_SESSION['wp2fa_user'] ?? 0;
if (!$user_id) {
wp_redirect(wp_login_url());
exit;
}
$error = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$code = strtoupper(trim($_POST['wp2fa_code'] ?? ''));
$data = $this->get_user_2fa_data($user_id);
if (!$data) {
wp_redirect(wp_login_url());
exit;
}
$secret = $data['secret'] ?? '';
$backup_codes = $data['backup_codes'] ?? [];
if ($this->verify_code($secret, $code)) {
// Erfolgreicher Authenticator-Code
wp_set_auth_cookie($user_id);
unset($_SESSION['wp2fa_user']);
wp_redirect(admin_url());
exit;
} elseif (in_array($code, $backup_codes, true)) {
// Backup-Code erfolgreich, diesen Code löschen
$backup_codes = array_diff($backup_codes, [$code]);
$data['backup_codes'] = $backup_codes;
$this->set_user_2fa_data($user_id, $data);
wp_set_auth_cookie($user_id);
unset($_SESSION['wp2fa_user']);
wp_redirect(admin_url());
exit;
} else {
$error = 'Falscher Code!';
}
}
$this->show_2fa_form($error);
exit;
}
private function show_2fa_form($error = '') {
?>
2-Faktor-Authentifizierung
2-Faktor-Authentifizierung
get_totp_code($secret, $timeSlice + $i);
if ($calc === $code) return true;
}
return false;
}
private function get_totp_code($secret, $timeSlice) {
$secretkey = $this->base32_decode($secret);
$time = pack('N*', 0) . pack('N*', $timeSlice);
$hash = hash_hmac('sha1', $time, $secretkey, true);
$offset = ord(substr($hash, -1)) & 0x0F;
$truncatedHash = substr($hash, $offset, 4);
$code = unpack('N', $truncatedHash)[1] & 0x7FFFFFFF;
$code = $code % 1000000;
return str_pad($code, 6, '0', STR_PAD_LEFT);
}
private function base32_decode($b32) {
$alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
$b32 = strtoupper($b32);
$l = strlen($b32);
$n = 0;
$j = 0;
$binary = '';
for ($i = 0; $i < $l; $i++) {
$n = $n << 5; // 5 bits per char
$n = $n + strpos($alphabet, $b32[$i]);
$j += 5;
if ($j >= 8) {
$j -= 8;
$binary .= chr(($n & (0xFF << $j)) >> $j);
}
}
return $binary;
}
// Registrierung des Admin-Feldes in Einstellungen > Allgemein
public function register_settings_field() {
register_setting('general', 'wp2fa_admin_forced', [
'type' => 'boolean',
'description' => '2FA für alle Benutzer erzwingen',
'sanitize_callback' => [$this, 'sanitize_bool'],
'default' => false,
]);
add_settings_field(
'wp2fa_admin_forced',
'2FA für alle Benutzer erzwingen',
[$this, 'render_admin_forced_field'],
'general'
);
}
public function render_admin_forced_field() {
$forced = get_option('wp2fa_admin_forced', false);
?>
/>