Upload folder via GUI - includes

This commit is contained in:
Git Manager GUI
2026-06-05 22:23:21 +02:00
parent 54170fa514
commit 5d6c2d73be
4 changed files with 534 additions and 0 deletions

108
includes/login.php Normal file
View File

@@ -0,0 +1,108 @@
<?php
if ( ! defined( 'ABSPATH' ) ) exit;
// Login-Button auf der WP-Login-Seite
add_action( 'login_form', 'authentik_login_button' );
function authentik_login_button() {
$s = authentik_get_settings();
if ( empty( $s['client_id'] ) || ! authentik_get_authorize_url() ) return;
$url = authentik_build_login_url();
?>
<div style="text-align:center; margin-bottom: 16px;">
<a href="<?php echo esc_url( $url ); ?>"
style="display:inline-block; background:#fd4b2d; color:#fff; padding:10px 20px;
border-radius:4px; text-decoration:none; font-weight:bold; width:100%; box-sizing:border-box; text-align:center;">
🔐 Login mit Authentik
</a>
</div>
<div style="text-align:center; margin-bottom:12px; color:#999; font-size:12px;"> oder mit Benutzername/Passwort </div>
<?php
}
// Callback-Handler
add_action( 'wp_ajax_nopriv_authentik_callback', 'authentik_handle_callback' );
add_action( 'wp_ajax_authentik_callback', 'authentik_handle_callback' );
function authentik_handle_callback() {
$code = $_GET['code'] ?? '';
$state = $_GET['state'] ?? '';
$error = $_GET['error'] ?? '';
if ( $error ) {
authentik_login_error( 'Authentik-Fehler: ' . esc_html( $error ) );
}
// State validieren
if ( ! $state || ! get_transient( 'authentik_state_' . $state ) ) {
authentik_login_error( 'Ungültiger State-Parameter.' );
}
delete_transient( 'authentik_state_' . $state );
if ( ! $code ) {
authentik_login_error( 'Kein Autorisierungscode erhalten.' );
}
// Token holen
$tokens = authentik_exchange_code( $code );
if ( is_wp_error( $tokens ) ) {
authentik_login_error( $tokens->get_error_message() );
}
// Userinfo holen
$userinfo = authentik_get_userinfo( $tokens['access_token'] );
if ( is_wp_error( $userinfo ) || empty( $userinfo ) ) {
authentik_login_error( 'Benutzerinformationen konnten nicht abgerufen werden.' );
}
// User finden oder erstellen
$user = authentik_find_or_create_user( $userinfo );
if ( is_wp_error( $user ) ) {
authentik_login_error( $user->get_error_message() );
}
// Einloggen
wp_set_auth_cookie( $user->ID, true );
wp_set_current_user( $user->ID );
do_action( 'wp_login', $user->user_login, $user );
// Weiterleitung
$redirect = admin_url();
if ( ! user_can( $user->ID, 'manage_options' ) ) {
$redirect = home_url();
}
wp_redirect( $redirect );
exit;
}
function authentik_login_error( $message ) {
wp_redirect( wp_login_url() . '?authentik_error=' . urlencode( $message ) );
exit;
}
// Fehlermeldung anzeigen
add_filter( 'login_message', 'authentik_show_login_message' );
function authentik_show_login_message( $message ) {
if ( ! empty( $_GET['authentik_error'] ) ) {
$message .= '<div id="login_error">' . esc_html( urldecode( $_GET['authentik_error'] ) ) . '</div>';
}
return $message;
}
// Frontend: Verknüpfungs-Button für eingeloggte User (Shortcode)
add_shortcode( 'authentik_link_account', 'authentik_link_account_shortcode' );
function authentik_link_account_shortcode() {
if ( ! is_user_logged_in() ) return '';
$user = wp_get_current_user();
$subject = get_user_meta( $user->ID, 'authentik_subject', true );
ob_start();
if ( $subject ) {
echo '<p>✓ Dein Konto ist mit Authentik verknüpft.</p>';
} else {
$url = authentik_build_login_url();
echo '<a href="' . esc_url( $url ) . '" class="button">Konto mit Authentik verknüpfen</a>';
}
return ob_get_clean();
}

73
includes/oauth.php Normal file
View File

@@ -0,0 +1,73 @@
<?php
if ( ! defined( 'ABSPATH' ) ) exit;
function authentik_get_authorize_url() {
return get_option( 'authentik_oidc_authorize_url', '' );
}
function authentik_get_token_url() {
return get_option( 'authentik_oidc_token_url', '' );
}
function authentik_get_userinfo_url() {
return get_option( 'authentik_oidc_userinfo_url', '' );
}
function authentik_get_logout_url() {
return get_option( 'authentik_oidc_logout_url', '' );
}
function authentik_build_login_url() {
$s = authentik_get_settings();
$state = wp_generate_password( 16, false );
set_transient( 'authentik_state_' . $state, 1, 300 );
$params = [
'response_type' => 'code',
'client_id' => $s['client_id'],
'redirect_uri' => $s['redirect_uri'],
'scope' => 'openid email profile',
'state' => $state,
];
return authentik_get_authorize_url() . '?' . http_build_query( $params );
}
function authentik_exchange_code( $code ) {
$s = authentik_get_settings();
$res = wp_remote_post( authentik_get_token_url(), [
'timeout' => (int) $s['timeout'],
'body' => [
'grant_type' => 'authorization_code',
'code' => $code,
'redirect_uri' => $s['redirect_uri'],
'client_id' => $s['client_id'],
'client_secret' => $s['client_secret'],
],
] );
if ( is_wp_error( $res ) ) {
return new WP_Error( 'token_request_failed', $res->get_error_message() );
}
$body = json_decode( wp_remote_retrieve_body( $res ), true );
if ( empty( $body['access_token'] ) ) {
return new WP_Error( 'token_missing', 'Kein Access-Token erhalten.' );
}
return $body;
}
function authentik_get_userinfo( $access_token ) {
$s = authentik_get_settings();
$res = wp_remote_get( authentik_get_userinfo_url(), [
'timeout' => (int) $s['timeout'],
'headers' => [ 'Authorization' => 'Bearer ' . $access_token ],
] );
if ( is_wp_error( $res ) ) {
return new WP_Error( 'userinfo_failed', $res->get_error_message() );
}
return json_decode( wp_remote_retrieve_body( $res ), true );
}

204
includes/settings.php Normal file
View File

@@ -0,0 +1,204 @@
<?php
if ( ! defined( 'ABSPATH' ) ) exit;
add_action( 'admin_menu', 'authentik_admin_menu' );
function authentik_admin_menu() {
add_options_page(
'Authentik Login',
'Authentik Login',
'manage_options',
'authentik-login',
'authentik_settings_page'
);
}
add_action( 'admin_init', 'authentik_register_settings' );
function authentik_register_settings() {
register_setting( 'authentik_settings_group', 'authentik_settings', 'authentik_sanitize_settings' );
}
function authentik_sanitize_settings( $input ) {
$clean = [];
$fields = [
'client_id', 'client_secret', 'discovery_url',
'redirect_uri', 'default_role', 'admin_group',
'timeout'
];
foreach ( $fields as $f ) {
$clean[ $f ] = isset( $input[ $f ] ) ? sanitize_text_field( $input[ $f ] ) : '';
}
$clean['create_users'] = ! empty( $input['create_users'] ) ? 1 : 0;
$clean['link_existing'] = ! empty( $input['link_existing'] ) ? 1 : 0;
$clean['sync_roles'] = ! empty( $input['sync_roles'] ) ? 1 : 0;
return $clean;
}
function authentik_get_settings() {
$defaults = [
'client_id' => '',
'client_secret' => '',
'discovery_url' => '',
'redirect_uri' => admin_url( 'admin-ajax.php?action=authentik_callback' ),
'default_role' => 'subscriber',
'admin_group' => 'wordpress_admin',
'timeout' => 30,
'create_users' => 1,
'link_existing' => 1,
'sync_roles' => 1,
];
$saved = get_option( 'authentik_settings', [] );
return wp_parse_args( $saved, $defaults );
}
function authentik_settings_page() {
$s = authentik_get_settings();
?>
<div class="wrap">
<h1>Authentik Login Einstellungen</h1>
<?php
// Discovery import
if ( isset( $_POST['authentik_import_discovery'] ) && check_admin_referer( 'authentik_import' ) ) {
$url = esc_url_raw( $_POST['discovery_url_import'] ?? '' );
$res = wp_remote_get( $url, [ 'timeout' => 15 ] );
if ( ! is_wp_error( $res ) ) {
$data = json_decode( wp_remote_retrieve_body( $res ), true );
if ( $data ) {
$map = [
'authorization_endpoint' => 'authorize_url',
'token_endpoint' => 'token_url',
'userinfo_endpoint' => 'userinfo_url',
'jwks_uri' => 'jwks_url',
'issuer' => 'issuer',
'end_session_endpoint' => 'logout_url',
];
foreach ( $map as $key => $opt ) {
if ( isset( $data[ $key ] ) ) {
update_option( 'authentik_oidc_' . $opt, $data[ $key ] );
}
}
echo '<div class="notice notice-success"><p>Discovery-Dokument erfolgreich importiert!</p></div>';
}
} else {
echo '<div class="notice notice-error"><p>Fehler: ' . esc_html( $res->get_error_message() ) . '</p></div>';
}
}
?>
<form method="post" action="">
<?php wp_nonce_field( 'authentik_import' ); ?>
<h2>Discovery-Dokument importieren</h2>
<table class="form-table">
<tr>
<th>Discovery URL</th>
<td>
<input type="url" name="discovery_url_import" class="regular-text"
placeholder="https://auth.example.com/application/o/app/.well-known/openid-configuration"
value="<?php echo esc_attr( $s['discovery_url'] ); ?>">
<input type="submit" name="authentik_import_discovery" class="button button-secondary" value="Importieren">
<p class="description">Trägt alle Endpunkt-URLs automatisch ein.</p>
</td>
</tr>
</table>
</form>
<form method="post" action="options.php">
<?php settings_fields( 'authentik_settings_group' ); ?>
<h2>Client-Einstellungen</h2>
<table class="form-table">
<tr>
<th>Client ID</th>
<td><input type="text" name="authentik_settings[client_id]" class="regular-text" value="<?php echo esc_attr( $s['client_id'] ); ?>"></td>
</tr>
<tr>
<th>Client Secret</th>
<td><input type="password" name="authentik_settings[client_secret]" class="regular-text" value="<?php echo esc_attr( $s['client_secret'] ); ?>"></td>
</tr>
<tr>
<th>Discovery URL</th>
<td>
<input type="url" name="authentik_settings[discovery_url]" class="regular-text" value="<?php echo esc_attr( $s['discovery_url'] ); ?>">
<p class="description">Wird gespeichert aber nicht direkt verwendet nutze den Import-Button oben.</p>
</td>
</tr>
<tr>
<th>Redirect URI</th>
<td>
<input type="url" name="authentik_settings[redirect_uri]" class="regular-text" value="<?php echo esc_attr( $s['redirect_uri'] ); ?>">
<p class="description">Diese URI muss exakt in Authentik eingetragen sein.</p>
</td>
</tr>
<tr>
<th>HTTP-Timeout (Sekunden)</th>
<td><input type="number" name="authentik_settings[timeout]" value="<?php echo esc_attr( $s['timeout'] ); ?>" min="5" max="60"></td>
</tr>
</table>
<h2>Benutzer-Einstellungen</h2>
<table class="form-table">
<tr>
<th>Neue Benutzer erstellen</th>
<td><input type="checkbox" name="authentik_settings[create_users]" value="1" <?php checked( $s['create_users'], 1 ); ?>>
<p class="description">Erstellt automatisch einen WordPress-Account wenn kein passender User gefunden wird.</p>
</td>
</tr>
<tr>
<th>Bestehende User verknüpfen</th>
<td><input type="checkbox" name="authentik_settings[link_existing]" value="1" <?php checked( $s['link_existing'], 1 ); ?>>
<p class="description">Verknüpft Authentik-Login mit bestehendem WordPress-Account (per E-Mail oder Benutzername).</p>
</td>
</tr>
<tr>
<th>Rollen synchronisieren</th>
<td><input type="checkbox" name="authentik_settings[sync_roles]" value="1" <?php checked( $s['sync_roles'], 1 ); ?>>
<p class="description">Überträgt Authentik-Gruppen als WordPress-Rollen.</p>
</td>
</tr>
<tr>
<th>Standard-Rolle</th>
<td>
<?php wp_dropdown_roles( $s['default_role'] ); ?>
<input type="hidden" name="authentik_settings[default_role]" value="">
<select name="authentik_settings[default_role]">
<?php
foreach ( wp_roles()->roles as $role => $data ) {
echo '<option value="' . esc_attr( $role ) . '" ' . selected( $s['default_role'], $role, false ) . '>' . esc_html( $data['name'] ) . '</option>';
}
?>
</select>
<p class="description">Rolle für neue Benutzer ohne passende Authentik-Gruppe.</p>
</td>
</tr>
<tr>
<th>Admin-Gruppe in Authentik</th>
<td>
<input type="text" name="authentik_settings[admin_group]" class="regular-text" value="<?php echo esc_attr( $s['admin_group'] ); ?>">
<p class="description">Authentik-Gruppenname der WordPress-Administratoren werden soll (z.B. <code>wordpress_admin</code>).</p>
</td>
</tr>
</table>
<h2>Erkannte Endpunkte</h2>
<table class="form-table">
<?php
$endpoints = [
'authentik_oidc_authorize_url' => 'Authorize URL',
'authentik_oidc_token_url' => 'Token URL',
'authentik_oidc_userinfo_url' => 'Userinfo URL',
'authentik_oidc_jwks_url' => 'JWKS URL',
'authentik_oidc_issuer' => 'Issuer',
'authentik_oidc_logout_url' => 'Logout URL',
];
foreach ( $endpoints as $opt => $label ) {
$val = get_option( $opt, '' );
echo '<tr><th>' . esc_html( $label ) . '</th><td><code>' . esc_html( $val ) . '</code></td></tr>';
}
?>
</table>
<?php submit_button( 'Einstellungen speichern' ); ?>
</form>
</div>
<?php
}

149
includes/user.php Normal file
View File

@@ -0,0 +1,149 @@
<?php
if ( ! defined( 'ABSPATH' ) ) exit;
/**
* Findet oder erstellt einen WordPress-User anhand der Authentik-Userinfo.
*/
function authentik_find_or_create_user( $userinfo ) {
$s = authentik_get_settings();
$subject = $userinfo['sub'] ?? '';
$email = $userinfo['email'] ?? '';
$username = $userinfo['preferred_username'] ?? $userinfo['name'] ?? '';
$groups = $userinfo['groups'] ?? [];
if ( empty( $subject ) ) {
return new WP_Error( 'no_sub', 'Kein Subject im Token.' );
}
// 1. Suche per gespeicherter Subject-ID (zuverlässigste Methode)
$user = authentik_find_user_by_subject( $subject );
// 2. Verknüpfe bestehenden User per E-Mail
if ( ! $user && $s['link_existing'] && $email ) {
$user = get_user_by( 'email', $email );
if ( $user ) {
update_user_meta( $user->ID, 'authentik_subject', $subject );
}
}
// 3. Verknüpfe bestehenden User per Benutzername
if ( ! $user && $s['link_existing'] && $username ) {
$user = get_user_by( 'login', $username );
if ( $user ) {
update_user_meta( $user->ID, 'authentik_subject', $subject );
}
}
// 4. Neuen User erstellen
if ( ! $user ) {
if ( ! $s['create_users'] ) {
return new WP_Error( 'user_not_found', 'Kein passender WordPress-Account gefunden.' );
}
$user_login = sanitize_user( $username ?: explode( '@', $email )[0] );
// Sicherstellen dass Benutzername einzigartig ist
if ( username_exists( $user_login ) ) {
$user_login = $user_login . '_' . substr( $subject, 0, 6 );
}
$user_id = wp_insert_user( [
'user_login' => $user_login,
'user_email' => $email,
'user_pass' => wp_generate_password( 32 ),
'display_name' => $userinfo['name'] ?? $user_login,
'role' => $s['default_role'],
] );
if ( is_wp_error( $user_id ) ) {
return $user_id;
}
update_user_meta( $user_id, 'authentik_subject', $subject );
$user = get_user_by( 'ID', $user_id );
}
// 5. Rollen synchronisieren
if ( $s['sync_roles'] && $user ) {
authentik_sync_roles( $user, $groups, $s );
}
return $user;
}
function authentik_find_user_by_subject( $subject ) {
$users = get_users( [
'meta_key' => 'authentik_subject',
'meta_value' => $subject,
'number' => 1,
] );
return ! empty( $users ) ? $users[0] : null;
}
function authentik_sync_roles( $user, $groups, $s ) {
$admin_group = $s['admin_group'];
if ( $admin_group && in_array( $admin_group, $groups, true ) ) {
$user->set_role( 'administrator' );
} else {
// Mappe Authentik-Gruppen auf WordPress-Rollen
$role_map = [
'wordpress_editor' => 'editor',
'wordpress_author' => 'author',
'wordpress_contributor' => 'contributor',
'wordpress_subscriber' => 'subscriber',
];
$assigned = false;
foreach ( $role_map as $group => $role ) {
if ( in_array( $group, $groups, true ) ) {
$user->set_role( $role );
$assigned = true;
break;
}
}
if ( ! $assigned ) {
// Behalte aktuelle Rolle wenn keine Gruppe passt
}
}
}
// Profilseite: Verknüpfungsstatus anzeigen
add_action( 'show_user_profile', 'authentik_show_link_status' );
add_action( 'edit_user_profile', 'authentik_show_link_status' );
function authentik_show_link_status( $user ) {
$subject = get_user_meta( $user->ID, 'authentik_subject', true );
?>
<h3>Authentik-Verknüpfung</h3>
<table class="form-table">
<tr>
<th>Status</th>
<td>
<?php if ( $subject ) : ?>
<span style="color:green;">✓ Verknüpft</span>
<p class="description">Subject: <code><?php echo esc_html( $subject ); ?></code></p>
<a href="<?php echo esc_url( wp_nonce_url( admin_url( 'admin-post.php?action=authentik_unlink&user_id=' . $user->ID ), 'authentik_unlink_' . $user->ID ) ); ?>" class="button button-small">Verknüpfung aufheben</a>
<?php else : ?>
<span style="color:orange;">✗ Nicht verknüpft</span>
<p class="description">Beim nächsten Authentik-Login wird dieser Account automatisch verknüpft (wenn E-Mail oder Benutzername übereinstimmt).</p>
<?php endif; ?>
</td>
</tr>
</table>
<?php
}
// Verknüpfung aufheben
add_action( 'admin_post_authentik_unlink', 'authentik_handle_unlink' );
function authentik_handle_unlink() {
$user_id = (int) $_GET['user_id'];
check_admin_referer( 'authentik_unlink_' . $user_id );
if ( ! current_user_can( 'edit_user', $user_id ) ) {
wp_die( 'Keine Berechtigung.' );
}
delete_user_meta( $user_id, 'authentik_subject' );
wp_redirect( admin_url( 'user-edit.php?user_id=' . $user_id . '&authentik_unlinked=1' ) );
exit;
}