Upload via GUI (360 Dateien)
This commit is contained in:
@@ -34,13 +34,15 @@ function plugin_known_permissions(): array
|
||||
'api.endpoints' => 'Eigene Endpunkte in der Mitarbeiter-API',
|
||||
'api.public' => 'Öffentliche Webhook-Adresse (zum Beispiel für Discord oder Telegram); das Plugin prüft die Echtheit der Anfragen selbst',
|
||||
'db.migrations' => 'Eigene Datenbank-Tabellen anlegen',
|
||||
'auth.customer' => 'Kunden über einen externen Dienst anmelden und Kundenkonten anlegen (zum Beispiel authentik): Knopf auf der Anmeldeseite des Kundenportals. ACHTUNG: Das Plugin entscheidet selbst, wer sich anmelden darf und ob Konten entstehen.',
|
||||
'auth.login' => 'Mitarbeiter über einen externen Dienst anmelden (zum Beispiel authentik): Knopf auf der Anmeldeseite und eigene Anmelde-Adresse. ACHTUNG: Das Plugin entscheidet selbst, wer sich anmelden darf.',
|
||||
];
|
||||
}
|
||||
|
||||
/** Gemeinsamer Zustand (Hooks, Menüs, aktuell ausgeführtes Plugin). */
|
||||
function &plugin_state(): array
|
||||
{
|
||||
static $state = ['hooks' => [], 'filters' => [], 'menus' => [], 'api' => [], 'webhooks' => [], 'perms' => [], 'loaded' => [], 'current' => null, 'booted' => false, 'shutdown' => false];
|
||||
static $state = ['hooks' => [], 'filters' => [], 'menus' => [], 'api' => [], 'webhooks' => [], 'logins' => [], 'login_loggedin' => [], 'customer_logins' => [], 'perms' => [], 'loaded' => [], 'current' => null, 'booted' => false, 'shutdown' => false];
|
||||
return $state;
|
||||
}
|
||||
|
||||
@@ -128,6 +130,8 @@ function plugin_filter_permission(string $hook): ?string
|
||||
{
|
||||
if ($hook === 'ticket.view.sidebar' || $hook === 'staff.page.top') return 'ui.sidebar';
|
||||
if (str_starts_with($hook, 'ticket.')) return 'hooks.tickets';
|
||||
if ($hook === 'staff.login.buttons') return 'auth.login'; // Knöpfe auf der Mitarbeiter-Anmeldeseite
|
||||
if ($hook === 'customer.login.buttons') return 'auth.customer'; // Knöpfe auf der Kunden-Anmeldeseite
|
||||
if ($hook === 'backup.list.rows') return 'ui.menu'; // wer einen Reiter unter „Backup“ anbieten darf, darf auch die Liste der Backups ergänzen
|
||||
return null;
|
||||
}
|
||||
@@ -182,6 +186,37 @@ function plugin_register_webhook(string $endpoint, callable $handler): void
|
||||
$state['webhooks'][$slug][$endpoint] = $handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registriert eine Anmelde-Adresse für Mitarbeiter: GET /staff/plugin_login.php?p=<slug>&e=<name>. Gedacht für externe Anmeldedienste (OpenID Connect, SAML …).
|
||||
* Der Handler erhält ['query' => $_GET, 'ip' => …] (die Sitzung $_SESSION steht zur Verfügung) und gibt eines davon zurück:
|
||||
* ['redirect' => 'https://…'] weiter zum Anmeldedienst (nur https, oder http zu localhost)
|
||||
* ['staff_id' => 12] dieser Mitarbeiter ist geprüft: DeskHub meldet ihn an (nur aktive Konten, mit Eintrag im Admin-Protokoll)
|
||||
* ['error' => 'Text'] zurück zur Anmeldeseite mit dieser Meldung
|
||||
* Den Link zum Starten baut plugin_login_url(). Wer anmelden darf, entscheidet allein das Plugin (Berechtigung auth.login).
|
||||
*
|
||||
* Mit $whenLoggedIn = true läuft die Adresse auch für bereits angemeldete Mitarbeiter (zum Beispiel, um ein Konto beim Anmeldedienst zu verknüpfen).
|
||||
* Der Handler bekommt dann zusätzlich ['staff' => <angemeldeter Mitarbeiter>] und darf nur zurückgeben:
|
||||
* ['redirect' => 'https://…'] weiter zum Anmeldedienst
|
||||
* ['local' => '/staff/…', 'notice' => 'Text'] zurück in den Mitarbeiterbereich, mit Hinweis
|
||||
* ['error' => 'Text', 'back' => '/staff/…'] zurück mit Fehlermeldung
|
||||
* Ein anderer Mitarbeiter kann so nie angemeldet werden (staff_id wird dann abgelehnt).
|
||||
*/
|
||||
function plugin_register_login(string $endpoint, callable $handler, bool $whenLoggedIn = false): void
|
||||
{
|
||||
$state = &plugin_state();
|
||||
$slug = $state['current'];
|
||||
if ($slug === null || !preg_match('/^[a-z0-9_-]{1,40}$/', $endpoint)) return;
|
||||
if (!plugin_permission_allowed('auth.login')) return;
|
||||
$state['logins'][$slug][$endpoint] = $handler;
|
||||
$state['login_loggedin'][$slug][$endpoint] = $whenLoggedIn;
|
||||
}
|
||||
|
||||
/** Adresse (ohne Domain) einer mit plugin_register_login() angemeldeten Anmelde-Adresse. */
|
||||
function plugin_login_url(string $slug, string $endpoint): string
|
||||
{
|
||||
return '/staff/plugin_login.php?p=' . rawurlencode($slug) . '&e=' . rawurlencode($endpoint);
|
||||
}
|
||||
|
||||
function plugin_register_api(string $endpoint, callable $handler, ?string $permission = null): void
|
||||
{
|
||||
if (!plugin_permission_allowed('api.endpoints')) return;
|
||||
@@ -983,3 +1018,79 @@ function catalog_thumb(string $kind, array $c): string
|
||||
}
|
||||
return '<div aria-hidden="true" style="' . $box . 'display:flex;align-items:center;justify-content:center;font-weight:700;color:var(--muted,#64748b);font-size:1.3rem">' . e(mb_strtoupper(mb_substr((string) $c['name'], 0, 1))) . '</div>';
|
||||
}
|
||||
|
||||
// ── Anmeldung von Kunden über Plugins (Berechtigung auth.customer) ──────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Registriert eine Anmelde-Adresse für Kunden: GET /plugin_login.php?p=<slug>&e=<name> (öffentlich, Kundenportal).
|
||||
* Der Handler erhält ['query' => $_GET, 'ip' => …] und gibt zurück:
|
||||
* ['redirect' => 'https://…'] weiter zum Anmeldedienst (nur https, oder http zu localhost)
|
||||
* ['customer' => ['email' => …, 'name' => …, 'create' => true|false, 'via' => 'Dienst']]
|
||||
* diese Person ist vom Dienst bestätigt: DeskHub meldet das Kundenkonto mit dieser
|
||||
* E-Mail-Adresse an; gibt es keines und 'create' ist true, wird es angelegt
|
||||
* ['error' => 'Text'] zurück zur Kunden-Anmeldeseite mit dieser Meldung
|
||||
* Die Rückkehr vom Dienst darf auch über eine mit plugin_register_login() angemeldete Adresse kommen, wenn das Plugin beide Berechtigungen hat.
|
||||
* Das Plugin trägt die Verantwortung, dass die E-Mail-Adresse wirklich bestätigt ist.
|
||||
*/
|
||||
function plugin_register_customer_login(string $endpoint, callable $handler): void
|
||||
{
|
||||
$state = &plugin_state();
|
||||
$slug = $state['current'];
|
||||
if ($slug === null || !preg_match('/^[a-z0-9_-]{1,40}$/', $endpoint)) return;
|
||||
if (!plugin_permission_allowed('auth.customer')) return;
|
||||
$state['customer_logins'][$slug][$endpoint] = $handler;
|
||||
}
|
||||
|
||||
/** Adresse (ohne Domain) einer mit plugin_register_customer_login() angemeldeten Kunden-Anmelde-Adresse. */
|
||||
function plugin_customer_login_url(string $slug, string $endpoint): string
|
||||
{
|
||||
return '/plugin_login.php?p=' . rawurlencode($slug) . '&e=' . rawurlencode($endpoint);
|
||||
}
|
||||
|
||||
/** Gilt die Weiterleitung zu einem Anmeldedienst? Nur https (oder http zu localhost zum Testen), keine Zeichen < Leerzeichen. */
|
||||
function plugin_login_external_url_ok(string $url): bool
|
||||
{
|
||||
if ($url === '' || strlen($url) > 4000 || preg_match('/[\x00-\x20]/', $url)) return false;
|
||||
$p = parse_url($url);
|
||||
if (!$p || empty($p['host'])) return false;
|
||||
$scheme = $p['scheme'] ?? '';
|
||||
return $scheme === 'https' || ($scheme === 'http' && in_array($p['host'], ['localhost', '127.0.0.1', '[::1]'], true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Meldet einen vom Plugin bestätigten Kunden an (und legt das Konto an, wenn erlaubt). Endet immer mit einer Weiterleitung.
|
||||
* Ein Plugin ohne Berechtigung auth.customer kommt hier nicht durch.
|
||||
* @param array{email?:string,name?:string,create?:bool,via?:string} $c
|
||||
*/
|
||||
function plugin_customer_login_finish(string $slug, array $c): never
|
||||
{
|
||||
$fail = function (string $message): never {
|
||||
flash('error', $message);
|
||||
redirect('/login.php');
|
||||
};
|
||||
if (!in_array('auth.customer', plugin_state()['perms'][$slug] ?? [], true)) $fail('Diese Anmeldung ist nicht erlaubt.');
|
||||
$email = strtolower(trim((string) ($c['email'] ?? '')));
|
||||
if (!filter_var($email, FILTER_VALIDATE_EMAIL) || strlen($email) > 150) $fail('Die E-Mail-Adresse ist ungültig.');
|
||||
if (is_banned($email)) $fail('Mit dieser E-Mail-Adresse ist keine Anmeldung möglich.');
|
||||
$pdo = get_db();
|
||||
$st = $pdo->prepare('SELECT * FROM users WHERE LOWER(email) = ?');
|
||||
$st->execute([$email]);
|
||||
$user = $st->fetch();
|
||||
$created = false;
|
||||
$via = mb_substr((string) ($c['via'] ?? $slug), 0, 60);
|
||||
if (!$user) {
|
||||
if (empty($c['create'])) $fail('Für diese E-Mail-Adresse gibt es kein Kundenkonto.');
|
||||
$name = trim((string) preg_replace('/\s+/u', ' ', strip_tags((string) ($c['name'] ?? ''))));
|
||||
if ($name === '') $name = ucfirst(explode('@', $email)[0]);
|
||||
$name = mb_substr($name, 0, 120);
|
||||
// Ein Passwort gibt es nicht: Wer eines möchte, nutzt „Passwort vergessen“
|
||||
$pdo->prepare('INSERT INTO users (name, email, password_hash, created_at) VALUES (?,?,?,?)')
|
||||
->execute([$name, $email, password_hash(bin2hex(random_bytes(24)), PASSWORD_DEFAULT), db_now()]);
|
||||
$user = ['id' => (int) $pdo->lastInsertId(), 'name' => $name, 'email' => $email];
|
||||
audit_log(null, 'Kundenkonto über ' . $via . ' angelegt: ' . mb_substr($email, 0, 120));
|
||||
$created = true;
|
||||
}
|
||||
customer_login($user);
|
||||
if ($created) flash('success', 'Willkommen! Ihr Konto wurde erstellt.');
|
||||
redirect('/home.php');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user