177 lines
15 KiB
PHP
177 lines
15 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
if (($staff['role'] ?? '') !== 'admin') {
|
|
http_response_code(403);
|
|
echo '<p>Keine Berechtigung.</p>';
|
|
return;
|
|
}
|
|
|
|
$message = '';
|
|
$messageType = 'success';
|
|
if (($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'POST') {
|
|
verify_csrf();
|
|
$action = (string) ($_POST['action'] ?? '');
|
|
if ($action === 'save') {
|
|
$issuer = rtrim(trim((string) ($_POST['issuer'] ?? '')), '/');
|
|
$clientId = trim((string) ($_POST['client_id'] ?? ''));
|
|
$secretIn = (string) ($_POST['client_secret'] ?? '');
|
|
$scopes = trim(preg_replace('/\s+/', ' ', (string) ($_POST['scopes'] ?? '')));
|
|
if ($issuer !== '' && !authentik_url_ok($issuer)) {
|
|
$message = 'Bitte die Issuer-Adresse mit https:// angeben, ohne Parameter (zum Beispiel https://auth.example.de/application/o/deskhub).';
|
|
$messageType = 'error';
|
|
} elseif (strlen($clientId) > 200 || preg_match('/[\x00-\x20]/', $clientId)) {
|
|
$message = 'Die Client-ID ist ungültig.';
|
|
$messageType = 'error';
|
|
} elseif ($scopes !== '' && !preg_match('/^[A-Za-z0-9_.:\- ]{1,200}$/', $scopes)) {
|
|
$message = 'Die Bereiche (Scopes) enthalten ungültige Zeichen.';
|
|
$messageType = 'error';
|
|
} else {
|
|
plugin_set_setting(AUTHENTIK, 'issuer', $issuer);
|
|
plugin_set_setting(AUTHENTIK, 'client_id', $clientId);
|
|
if ($secretIn !== '') plugin_set_setting(AUTHENTIK, 'client_secret_enc', encrypt_secret($secretIn));
|
|
plugin_set_setting(AUTHENTIK, 'scopes', $scopes !== '' ? $scopes : 'openid email profile');
|
|
plugin_set_setting(AUTHENTIK, 'group', mb_substr(trim((string) ($_POST['group'] ?? '')), 0, 100));
|
|
plugin_set_setting(AUTHENTIK, 'label', mb_substr(trim((string) ($_POST['label'] ?? '')), 0, 60));
|
|
plugin_set_setting(AUTHENTIK, 'enabled', !empty($_POST['enabled']) ? '1' : '0');
|
|
plugin_set_setting(AUTHENTIK, 'only_linked', !empty($_POST['only_linked']) ? '1' : '0');
|
|
plugin_set_setting(AUTHENTIK, 'require_verified', !empty($_POST['require_verified']) ? '1' : '0');
|
|
plugin_set_setting(AUTHENTIK, 'customers', !empty($_POST['customers']) ? '1' : '0');
|
|
plugin_set_setting(AUTHENTIK, 'customer_create', !empty($_POST['customer_create']) ? '1' : '0');
|
|
plugin_set_setting(AUTHENTIK, 'customer_group', mb_substr(trim((string) ($_POST['customer_group'] ?? '')), 0, 100));
|
|
plugin_set_setting(AUTHENTIK, 'discovery', ''); // neu laden
|
|
audit_log((int) $staff['id'], 'authentik-Anmeldung: Einstellungen geändert' . (!empty($_POST['enabled']) ? ' (eingeschaltet)' : ' (ausgeschaltet)'));
|
|
$message = 'Gespeichert.';
|
|
}
|
|
} elseif ($action === 'unlink_staff') {
|
|
$sid = (int) ($_POST['staff_id'] ?? 0);
|
|
if ($sid > 0 && authentik_link_of($sid) !== null) {
|
|
authentik_link_remove($sid);
|
|
audit_log((int) $staff['id'], 'authentik: Verknüpfung von Mitarbeiter #' . $sid . ' gelöst');
|
|
$message = 'Die Verknüpfung ist gelöst.';
|
|
}
|
|
} elseif ($action === 'test') {
|
|
$cfg = authentik_config();
|
|
if (!authentik_url_ok($cfg['issuer'])) {
|
|
$message = 'Bitte zuerst eine gültige Issuer-Adresse speichern.';
|
|
$messageType = 'error';
|
|
} else {
|
|
$d = authentik_discovery($cfg['issuer'], true);
|
|
if ($d['ok']) {
|
|
$message = 'Verbindung in Ordnung: authentik meldet den Aussteller „' . $d['doc']['issuer'] . '“ und die Anmelde-Adresse ' . $d['doc']['authorization_endpoint'] . '.';
|
|
} else {
|
|
$message = $d['error'];
|
|
$messageType = 'error';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$cfg = authentik_config();
|
|
$hasSecret = $cfg['client_secret'] !== '';
|
|
$ready = authentik_ready();
|
|
$redirect = authentik_redirect_uri();
|
|
?>
|
|
<h1>authentik-Anmeldung</h1>
|
|
<?php if ($message !== ''): ?><div class="flash <?= $messageType === 'error' ? 'error' : 'success' ?>" role="status"><?= e($message) ?></div><?php endif; ?>
|
|
|
|
<section class="card" style="max-width:900px">
|
|
<h3>Zustand</h3>
|
|
<p>
|
|
<?php if ($ready): ?><strong style="color:#157a45">✓ Eingeschaltet.</strong> Auf der Anmeldeseite des Mitarbeiterbereichs erscheint der Knopf „<?= e($cfg['label']) ?>“.
|
|
<?php elseif ($cfg['enabled']): ?><strong style="color:#b45309">Eingeschaltet, aber unvollständig.</strong> Issuer-Adresse, Client-ID und Geheimnis müssen eingetragen sein.
|
|
<?php else: ?><strong style="color:#b45309">Ausgeschaltet.</strong> Der Knopf erscheint erst nach dem Einschalten.<?php endif; ?>
|
|
</p>
|
|
<p class="muted" style="font-size:.88rem">Angemeldet wird nur, wer in authentik bestätigt ist und in DeskHub ein <strong>aktives Mitarbeiterkonto</strong> hat, das mit seinem authentik-Konto <strong>verknüpft</strong> ist (oder dieselbe E-Mail-Adresse hat). Konten werden nicht automatisch angelegt. E-Mail und Passwort funktionieren weiter. Die zweite Stufe (Authenticator-Code) von DeskHub entfällt bei dieser Anmeldung: Dafür sorgt authentik.</p>
|
|
<?php
|
|
// Warum erscheint (oder fehlt) der Knopf auf der Anmeldeseite des Kundenportals?
|
|
$serverOk = function_exists('plugin_register_customer_login');
|
|
$permOk = in_array('auth.customer', plugin_state()['perms'][AUTHENTIK] ?? [], true);
|
|
$custRegistered = isset(plugin_state()['customer_logins'][AUTHENTIK]['start']);
|
|
$checks = [
|
|
[$serverOk, 'Der DeskHub-Server kennt die Kunden-Anmeldung (Version 1.0.52 oder neuer).', 'Der Server ist zu alt: Bitte DeskHub 1.0.52 oder neuer einspielen (auch login.php, plugin_login.php und includes/plugins.php).'],
|
|
[$permOk, 'Die Berechtigung „auth.customer“ ist freigegeben.', 'Die Berechtigung „auth.customer“ fehlt: Unter Plugins das Plugin deaktivieren und wieder aktivieren und die Berechtigungen bestätigen.'],
|
|
[$cfg['customers'], 'Kunden-Anmeldung ist eingeschaltet („Auch Kunden mit authentik anmelden“).', 'Kunden-Anmeldung ist ausgeschaltet: Unten unter „Kunden“ den Haken setzen und speichern.'],
|
|
[$ready, 'Die Verbindung zu authentik ist eingerichtet und eingeschaltet.', 'Die Verbindung ist nicht vollständig (Issuer, Client-ID, Geheimnis, eingeschaltet).'],
|
|
];
|
|
// Zeichnet die Kunden-Anmeldeseite (login.php im DeskHub-Hauptordner) die Knöpfe der Plugins überhaupt? (Datei fehlt oder ist alt: Knopf bleibt weg)
|
|
$loginFile = dirname(plugins_dir()) . '/login.php';
|
|
$loginPhp = is_file($loginFile) ? (string) @file_get_contents($loginFile) : null;
|
|
$loginOk = $loginPhp === null ? true : str_contains($loginPhp, 'customer.login.buttons');
|
|
$checks[] = [$loginOk, 'Die Anmeldeseite des Kundenportals (login.php) ist auf dem neuesten Stand.', 'Die Datei login.php im DeskHub-Hauptordner ist alt und zeigt keine Plugin-Knöpfe: Bitte die aktuelle login.php hochladen.'];
|
|
$allOk = $serverOk && $permOk && $cfg['customers'] && $ready && $custRegistered && $loginOk;
|
|
?>
|
|
<h4 style="margin:14px 0 6px">Kunden-Anmeldung</h4>
|
|
<p style="margin:0 0 6px"><?php if ($allOk): ?><strong style="color:#157a45">✓ Der Knopf erscheint auf der Anmeldeseite des Kundenportals.</strong><?php else: ?><strong style="color:#b45309">Der Knopf erscheint dort noch nicht.</strong><?php endif; ?></p>
|
|
<ul style="list-style:none;padding:0;margin:0;font-size:.88rem">
|
|
<?php foreach ($checks as [$ok, $yes, $no]): ?><li><?= $ok ? '<span style="color:#157a45">✓</span> ' . e($yes) : '<span style="color:#b91c1c">✗</span> ' . e($no) ?></li><?php endforeach; ?>
|
|
</ul>
|
|
</section>
|
|
|
|
<form method="post" class="card" style="max-width:900px;margin-top:16px">
|
|
<?= csrf_field() ?>
|
|
<input type="hidden" name="action" value="save">
|
|
<h3>Verbindung zu authentik</h3>
|
|
<label style="display:flex;gap:8px;align-items:center;font-weight:400"><input type="checkbox" name="enabled" value="1" style="width:auto" <?= $cfg['enabled'] ? 'checked' : '' ?>> Anmeldung mit authentik einschalten</label>
|
|
<label for="issuer">Issuer-Adresse (OpenID-Konfiguration)</label>
|
|
<input type="text" inputmode="url" id="issuer" name="issuer" value="<?= e($cfg['issuer']) ?>" placeholder="https://auth.example.de/application/o/deskhub" autocomplete="off">
|
|
<label for="client_id">Client-ID</label>
|
|
<input type="text" id="client_id" name="client_id" value="<?= e($cfg['client_id']) ?>" autocomplete="off">
|
|
<label for="client_secret">Client-Geheimnis (Client Secret)</label>
|
|
<input type="password" id="client_secret" name="client_secret" value="" placeholder="<?= $hasSecret ? '(unverändert)' : '' ?>" autocomplete="new-password">
|
|
<label for="scopes">Bereiche (Scopes)</label>
|
|
<input type="text" id="scopes" name="scopes" value="<?= e($cfg['scopes']) ?>">
|
|
<label style="display:flex;gap:8px;align-items:flex-start;font-weight:400;margin-top:10px"><input type="checkbox" name="only_linked" value="1" style="width:auto;margin-top:3px" <?= plugin_setting(AUTHENTIK, 'only_linked', '0') === '1' ? 'checked' : '' ?>> <span><strong>Nur verknüpfte Konten anmelden.</strong> Ohne Haken klappt die Anmeldung auch über die E-Mail-Adresse (das Konto wird dabei gleich verknüpft). Mit Haken muss jeder Mitarbeiter sein Konto erst unter „Mein authentik-Konto“ verknüpfen.</span></label>
|
|
<label style="display:flex;gap:8px;align-items:flex-start;font-weight:400;margin-top:10px"><input type="checkbox" name="require_verified" value="1" style="width:auto;margin-top:3px" <?= $cfg['require_verified'] ? 'checked' : '' ?>> <span><strong>Bestätigte E-Mail-Adresse verlangen (empfohlen).</strong> Gilt, wo die Adresse über das Konto entscheidet: bei Kunden und bei Mitarbeitern, die noch nicht verknüpft sind. Meldet authentik die Adresse als „nicht bestätigt“, kommen sie sonst nicht hinein. Wer schon verknüpft ist, meldet sich immer an. Nur ausschalten, wenn nur du in authentik Benutzer anlegst und Adressen ändern darfst.</span></label>
|
|
<label for="group">Nur Mitglieder dieser authentik-Gruppe (optional)</label>
|
|
<input type="text" id="group" name="group" value="<?= e($cfg['group']) ?>" placeholder="zum Beispiel deskhub-support">
|
|
<h3 style="margin-top:22px">Kunden (Ticketersteller)</h3>
|
|
<label style="display:flex;gap:8px;align-items:flex-start;font-weight:400"><input type="checkbox" name="customers" value="1" style="width:auto;margin-top:3px" <?= $cfg['customers'] ? 'checked' : '' ?>> <span><strong>Auch Kunden mit authentik anmelden.</strong> Der Knopf erscheint auf der Anmeldeseite des Kundenportals. Es zählt nur die in authentik bestätigte E-Mail-Adresse: Hat ein Kundenkonto dieselbe Adresse, meldet DeskHub es an.</span></label>
|
|
<label style="display:flex;gap:8px;align-items:flex-start;font-weight:400;margin-top:10px"><input type="checkbox" name="customer_create" value="1" style="width:auto;margin-top:3px" <?= $cfg['customer_create'] ? 'checked' : '' ?>> <span><strong>Kundenkonto automatisch anlegen,</strong> wenn es zu der E-Mail-Adresse noch keines gibt (ohne Passwort; wer eines möchte, nutzt „Passwort vergessen“). Ohne Haken kommen nur Kunden hinein, die schon ein Konto haben.</span></label>
|
|
<label for="customer_group">Nur Kunden aus dieser authentik-Gruppe (optional)</label>
|
|
<input type="text" id="customer_group" name="customer_group" value="<?= e($cfg['customer_group']) ?>" placeholder="zum Beispiel deskhub-kunden">
|
|
<h3 style="margin-top:22px">Beschriftung</h3>
|
|
<label for="label">Beschriftung des Knopfes</label>
|
|
<input type="text" id="label" name="label" value="<?= e($cfg['label']) ?>">
|
|
<div class="btn-row"><button type="submit" class="btn">Speichern</button></div>
|
|
</form>
|
|
|
|
<form method="post" class="card" style="max-width:900px;margin-top:16px">
|
|
<?= csrf_field() ?>
|
|
<input type="hidden" name="action" value="test">
|
|
<h3>Verbindung testen</h3>
|
|
<p class="muted" style="font-size:.88rem">Ruft die Konfiguration von authentik ab (ohne Anmeldung). Vorher speichern.</p>
|
|
<button type="submit" class="btn secondary">Verbindung testen</button>
|
|
</form>
|
|
|
|
<?php $links = authentik_links_all(); ?>
|
|
<section class="card" style="max-width:900px;margin-top:16px">
|
|
<h3>Verknüpfte Konten (<?= count($links) ?>)</h3>
|
|
<?php if (!$links): ?>
|
|
<p class="muted">Noch kein Mitarbeiter hat sein Konto verknüpft. Mitarbeiter tun das selbst unter „Mein authentik-Konto“; bei der ersten Anmeldung über die E-Mail-Adresse geschieht es automatisch.</p>
|
|
<?php else: ?>
|
|
<div style="overflow-x:auto"><table style="width:100%">
|
|
<thead><tr><th>Mitarbeiter</th><th>E-Mail in DeskHub</th><th>E-Mail in authentik</th><th>Seit</th><th></th></tr></thead>
|
|
<tbody>
|
|
<?php foreach ($links as $l): ?>
|
|
<tr><td><?= e($l['name']) ?></td><td><?= e($l['staff_email']) ?></td><td><?= e($l['email']) ?></td><td><?= e(format_dt($l['at'])) ?></td>
|
|
<td><form method="post" data-confirm="Verknüpfung dieses Mitarbeiters lösen?" onsubmit="return confirm(this.dataset.confirm)"><?= csrf_field() ?><input type="hidden" name="action" value="unlink_staff"><input type="hidden" name="staff_id" value="<?= (int) $l['staff_id'] ?>"><button class="btn secondary" style="padding:3px 10px;font-size:.8rem" type="submit">Lösen</button></form></td></tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table></div>
|
|
<?php endif; ?>
|
|
</section>
|
|
|
|
<section class="card" style="max-width:900px;margin-top:16px">
|
|
<h3>Einrichtung in authentik</h3>
|
|
<ol style="line-height:1.7;padding-left:20px;font-size:.92rem">
|
|
<li>In authentik unter <strong>Anwendungen → Provider</strong> einen Provider vom Typ <strong>OAuth2/OpenID</strong> anlegen. <strong>Client-Typ:</strong> vertraulich (Confidential).</li>
|
|
<li><strong>Umleitungs-URI (Redirect URI)</strong> genau so eintragen (Modus „Strict“): <code><?= e($redirect) ?></code></li>
|
|
<li><strong>Bereiche:</strong> mindestens <code>openid</code>, <code>email</code> und <code>profile</code>. Für die Gruppenprüfung zusätzlich den Bereich, der <code>groups</code> liefert (in authentik: Bereichs-Zuordnung „groups“).</li>
|
|
<li>Eine <strong>Anwendung</strong> mit diesem Provider anlegen und bestimmen, wer sie nutzen darf (authentik prüft das selbst).</li>
|
|
<li>Client-ID und Client-Geheimnis hier eintragen. Die <strong>Issuer-Adresse</strong> steht beim Provider unter „OpenID-Konfigurations-Aussteller“, sie sieht so aus: <code>https://auth.example.de/application/o/<Anwendungs-Kürzel>/</code>.</li>
|
|
<li>Jeder Mitarbeiter hat sein DeskHub-Konto und verknüpft es einmal selbst unter <strong>Mein authentik-Konto</strong>. Alternativ klappt die erste Anmeldung auch, wenn die E-Mail-Adresse in authentik und DeskHub gleich ist (dann wird automatisch verknüpft).</li>
|
|
</ol>
|
|
<p class="muted" style="font-size:.85rem"><strong>Sicherheit:</strong> Der Ablauf nutzt PKCE, einen Zustandswert und eine Nonce; Aussteller, Zielgruppe und Ablauf des ID-Tokens werden geprüft. Das Token kommt direkt von authentik über eine geprüfte HTTPS-Verbindung. Das Geheimnis liegt verschlüsselt in der Datenbank. Fällt authentik aus, melden sich Mitarbeiter weiter mit E-Mail und Passwort an. Jede Anmeldung steht im Admin-Protokoll.</p>
|
|
</section>
|