*/ function web_handoff_pages(array $staff): array { $isAdmin = ($staff['role'] ?? '') === 'admin'; $perm = fn(string $p): bool => $isAdmin || has_permission($staff, $p); $all = [ // Alle Mitarbeiter ['mentions', 'Erwähnungen', '/staff/mentions.php', 'Allgemein', true], ['agent_directory', 'Mitarbeiterverzeichnis', '/staff/agent_directory.php', 'Allgemein', true], // Mit Recht ['assistant', 'Digitaler Assistent (Antworten, Fragen-Log)', '/staff/assistant.php', 'Verwaltung', $perm('manage_assistant')], ['imap_accounts', 'Postfächer (E-Mail-Abruf)', '/staff/imap_accounts.php', 'Verwaltung', $perm('manage_email_addresses')], // Nur Administratoren ['settings', 'Einstellungen', '/staff/settings.php', 'Verwaltung', $isAdmin], ['recurring_tickets', 'Wiederkehrende Tickets', '/staff/recurring_tickets.php', 'Verwaltung', $isAdmin], ['canned_responses', 'Textbausteine', '/staff/canned_responses.php', 'Verwaltung', $isAdmin], ['custom_fields', 'Ticket-Felder', '/staff/custom_fields.php', 'Verwaltung', $isAdmin], ['custom_lists', 'Benutzerdefinierte Listen', '/staff/custom_lists.php', 'Verwaltung', $isAdmin], ['site_pages', 'Seiten (Impressum, Datenschutz …)', '/staff/site_pages.php', 'Verwaltung', $isAdmin], ['ticket_templates', 'Ticket-Vorlagen', '/staff/ticket_templates.php', 'Verwaltung', $isAdmin], ['status_page', 'Statusseite (Dienste, Meldungen)', '/staff/status_page.php', 'Verwaltung', $isAdmin], ['webhooks', 'Webhooks', '/staff/webhooks.php', 'Verwaltung', $isAdmin], ['api_keys', 'API-Schlüssel', '/staff/api_keys.php', 'Verwaltung', $isAdmin], ['plugins', 'Plugins und Themes', '/staff/plugins.php', 'System', $isAdmin], ['backup', 'Backup', '/staff/backup.php', 'System', $isAdmin], ['update', 'System-Update', '/staff/update.php', 'System', $isAdmin], ['security_check', 'Sicherheitsprüfung', '/staff/security_check.php', 'System', $isAdmin], ['import_osticket', 'Import aus osTicket', '/staff/import_osticket.php', 'System', $isAdmin], ]; $out = []; foreach ($all as [$key, $label, $path, $group, $allowed]) { if ($allowed) $out[] = ['key' => $key, 'label' => $label, 'path' => $path, 'group' => $group]; } return $out; } /** Darf diese Adresse aus der App geöffnet werden? (eine Seite der Liste oder ein einzelnes Ticket) */ function web_handoff_path_allowed(array $staff, string $path): bool { if (preg_match('#^/staff/(ticket_view|ticket_print|ticket_split)\.php\?n=[A-Za-z0-9._-]{1,40}$#', $path)) return true; foreach (web_handoff_pages($staff) as $p) if ($p['path'] === $path) return true; return false; } /** Erzeugt die einmalige Anmelde-Adresse für die Seite $path. */ function web_handoff_create(PDO $pdo, array $staff, string $path): ?string { if (!web_handoff_path_allowed($staff, $path)) return null; $token = bin2hex(random_bytes(32)); $now = db_now(); $pdo->prepare('DELETE FROM web_login_tokens WHERE expires_at < ?')->execute([date('Y-m-d H:i:s', time() - 3600)]); $pdo->prepare('INSERT INTO web_login_tokens (staff_id, token_hash, path, created_at, expires_at) VALUES (?,?,?,?,?)') ->execute([(int) $staff['id'], hash('sha256', $token), $path, $now, date('Y-m-d H:i:s', time() + 60)]); return rtrim(base_url(), '/') . '/staff/app_login.php?t=' . $token; } /** Löst die Anmelde-Adresse ein (einmalig). @return array{staff:array,path:string}|null */ function web_handoff_consume(PDO $pdo, string $token): ?array { if (!preg_match('/^[a-f0-9]{64}$/', $token)) return null; $hash = hash('sha256', $token); $upd = $pdo->prepare('UPDATE web_login_tokens SET used_at = ? WHERE token_hash = ? AND used_at IS NULL AND expires_at >= ?'); $upd->execute([db_now(), $hash, db_now()]); if ($upd->rowCount() !== 1) return null; // schon benutzt, abgelaufen oder unbekannt $row = $pdo->prepare('SELECT t.path AS handoff_path, s.* FROM web_login_tokens t JOIN staff s ON s.id = t.staff_id WHERE t.token_hash = ? AND s.is_active = 1'); $row->execute([$hash]); $r = $row->fetch(); if (!$r) return null; $path = (string) $r['handoff_path']; unset($r['handoff_path']); return ['staff' => $r, 'path' => $path]; }