Files
2026-11-05 21:21:44 +00:00

185 lines
15 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
/**
* Rechte-Katalog: Alle Berechtigungen an einer Stelle, gruppiert, mit Beschreibung und Standardwert.
*
* - 'agent' : Standardwert für Mitarbeiter ohne eigene Rolle (und für Rechte, die in einer älteren Rolle noch fehlen). Rechte, die bis zur
* Einführung des Katalogs jeder Mitarbeiter hatte, stehen auf true; neue Adminportal-Bereiche auf false.
* - 'area' : Bereich des Adminportals („Co-Admin“): Wer so ein Recht hat, sieht das Adminportal im Menü.
* - Echte Administratoren haben immer alle Rechte (has_permission()).
* Bewusst nicht delegierbar (immer nur echte Administratoren): Einstellungen, Backup, System-Update, Plugins, API-Schlüssel, Webhooks, osTicket-Import,
* Sicherheitsprüfung, Passkeys ein-/ausschalten und die Datenschutz-Löschfunktionen.
*/
function permission_catalog(): array
{
$p = fn(string $label, string $hint, bool $agent, bool $area = false) => ['label' => $label, 'hint' => $hint, 'agent' => $agent, 'area' => $area];
return [
'tickets' => ['title' => 'Tickets bearbeiten', 'items' => [
'reply_ticket' => $p('Tickets beantworten', 'Antworten, Antworten einplanen, Anhänge aus E-Mails nachladen', true),
'post_note' => $p('Interne Notizen schreiben', 'Notizen, die nur das Team sieht', true),
'close_ticket' => $p('Tickets schließen/lösen', 'Status auf Gelöst oder Geschlossen setzen, als Spam markieren', true),
'assign_ticket' => $p('Tickets zuweisen (Mitarbeiter/Team)', 'Bearbeiter oder Team ändern', true),
'transfer_ticket' => $p('Tickets übertragen (Abteilung ändern)', 'Ticket in eine andere Abteilung verschieben', true),
'create_ticket' => $p('Tickets im Namen von Kunden erstellen', 'Neues Ticket anlegen', true),
'merge_split_ticket' => $p('Tickets zusammenführen und teilen', 'Doppelte Tickets vereinen, Nachrichten abspalten', true),
'link_tickets' => $p('Tickets verknüpfen', 'Verwandt, doppelt oder blockiert', true),
'delete_ticket' => $p('Tickets löschen', 'Endgültig löschen, auch per Sammelaktion', false),
]],
'customers' => ['title' => 'Kunden und Sperren', 'items' => [
'view_customers' => $p('Kundenprofile ansehen', 'Profil eines Kunden mit allen seinen Tickets', true),
'manage_customers' => $p('Kundenverzeichnis verwalten', 'Alle Kunden-Konten ansehen und Organisationen zuordnen', false, true),
'manage_organizations' => $p('Organisationen verwalten', 'Mehrere Kunden zu einer Firma bündeln', false, true),
'manage_banlist' => $p('Sperrliste und Absender sperren', 'Adressen und Domains blockieren', true),
]],
'reports' => ['title' => 'Auswertung und Export', 'items' => [
'view_reports' => $p('Berichte einsehen', 'Übersicht, Berichte, Bericht-Baukasten (nur eigene Abteilungen)', true),
'schedule_reports' => $p('Berichte per E-Mail versenden', 'Zeitpläne im Bericht-Baukasten (externe Empfänger nur Administratoren)', true),
'export_tickets' => $p('Tickets als CSV exportieren', 'Ticketliste für Excel herunterladen', false),
'view_time_entries' => $p('Zeiterfassung einsehen', 'Auswertung der erfassten Zeiten', false, true),
'view_audit_log' => $p('Admin-Protokoll einsehen', 'Wer hat was geändert', false, true),
]],
'mail' => ['title' => 'E-Mail', 'items' => [
'fetch_mail' => $p('E-Mails jetzt abrufen', 'Den Abruf per Knopf auslösen', true),
'manage_email_addresses' => $p('E-Mail-Adressen und Postfächer verwalten', 'Absender, Eingang, SMTP und IMAP je Adresse', false, true),
'manage_email_templates' => $p('E-Mail-Vorlagen verwalten', 'Texte der automatischen Mails', false, true),
'view_mail_queue' => $p('Mail-Warteschlange verwalten', 'Nicht zugestellte Mails ansehen, erneut senden, löschen', false, true),
]],
'content' => ['title' => 'Inhalte und Vorlagen', 'items' => [
'manage_canned' => $p('Textbausteine verwalten', 'Vorlagen für Antworten', false, true),
'manage_ticket_templates' => $p('Ticket-Vorlagen verwalten', 'Vorgefüllte Tickets für Mitarbeiter und Kunden', false, true),
'manage_kb' => $p('Wissensdatenbank verwalten', 'Artikel und Kategorien', false, true),
'manage_assistant' => $p('Digitalen Assistenten verwalten', 'Antworten, Fragen-Log, Einstellungen', false, true),
'manage_site_pages' => $p('Seiten im Kundenportal verwalten', 'Impressum, AGB und weitere Seiten', false, true),
'manage_languages' => $p('Sprachen verwalten', 'Sprachpakete und Standardsprache', false, true),
'manage_status_page' => $p('Statusseite verwalten', 'Dienste und Störungsmeldungen', false, true),
'share_views' => $p('Ansichten mit dem Team teilen', 'Gespeicherte Filter für alle sichtbar machen', true),
]],
'setup' => ['title' => 'Einrichtung', 'items' => [
'manage_departments' => $p('Abteilungen verwalten', 'Abteilungen, Formulare, automatische Zuweisung', false, true),
'manage_priorities' => $p('Prioritäten / SLA verwalten', 'Prioritäten und SLA-Pläne', false, true),
'manage_help_topics' => $p('Hilfethemen verwalten', 'Auswahl für Kunden beim Ticket-Erstellen', false, true),
'manage_custom_fields' => $p('Ticket-Felder und Listen verwalten', 'Zusatzfelder und Auswahllisten', false, true),
'manage_automation' => $p('Ticket-Regeln und wiederkehrende Tickets verwalten', 'Automatische Aktionen bei neuen Tickets, regelmäßige Tickets', false, true),
'manage_escalations' => $p('Eskalationsregeln verwalten', 'Aktionen bei überfälligen oder liegengebliebenen Tickets', false, true),
'manage_business_hours' => $p('Geschäftszeiten verwalten', 'Zeitfenster und Feiertage für die SLA', false, true),
'manage_problems' => $p('Problemmanagement', 'Probleme erfassen und mit Tickets verknüpfen', false, true),
]],
'team' => ['title' => 'Team und Sicherheit', 'items' => [
'manage_staff' => $p('Mitarbeiter verwalten', 'Konten, Sperre aufheben, Überall abmelden, Passkeys entfernen', false, true),
'manage_teams' => $p('Teams verwalten', 'Gruppen von Mitarbeitern', false, true),
'manage_roles' => $p('Rollen verwalten', 'Rechte vergeben', false, true),
'manage_availability' => $p('Status und Schichten der Kollegen ändern', 'Status anderer Mitarbeiter setzen, automatisches Offline einstellen', false, true),
'view_login_guard' => $p('Anmelde-Wächter ansehen', 'Anmeldeprotokoll und gesperrte Konten; Einstellungen bleiben Administratoren', false, true),
'view_help' => $p('Hilfe und Handbuch', 'Hilfe-Seite mit dem Handbuch (PDF)', false, true),
]],
];
}
/** Alle Rechte als schlüssel => Beschriftung (Reihenfolge wie im Katalog). */
function permission_labels(): array
{
$out = [];
foreach (permission_catalog() as $g) foreach ($g['items'] as $k => $i) $out[$k] = $i['label'];
return $out;
}
/** Rechte für Adminportal-Bereiche („Co-Admin“): schlüssel => Beschriftung. */
function admin_section_permissions(): array
{
$out = [];
foreach (permission_catalog() as $g) foreach ($g['items'] as $k => $i) if ($i['area']) $out[$k] = $i['label'];
return $out;
}
/** Standardrechte: Administrator alles, Mitarbeiter die Standardwerte des Katalogs. */
function default_permissions(string $role): array
{
$out = [];
foreach (permission_catalog() as $g) foreach ($g['items'] as $k => $i) $out[$k] = $role === 'admin' ? true : (bool) $i['agent'];
return $out;
}
/**
* Vorlagen für Rollen: Schlüssel => [Name, Beschreibung, Rechte, als Rolle anlegen?].
* Die Vorlagen mit „true“ werden bei der Installation und beim Update als echte Rollen angelegt (siehe permission_seed_default_roles()); sie lassen sich danach
* wie jede andere Rolle ändern oder löschen. „Mitarbeiter“ ist nur eine Vorlage im Formular, weil das die Standardrechte ohne Rolle sind.
*/
function permission_presets(): array
{
$all = array_keys(permission_labels());
$on = fn(array $keys) => array_fill_keys($keys, true) + array_fill_keys($all, false);
$agent = array_keys(array_filter(default_permissions('agent')));
$lead = array_merge($agent, ['delete_ticket', 'export_tickets', 'view_time_entries', 'manage_canned', 'manage_kb', 'manage_teams', 'manage_availability']);
return [
'mitarbeiter' => ['Mitarbeiter', 'Die Standardrechte ohne Rolle: Tickets bearbeiten, Berichte der eigenen Abteilungen.', $on($agent), false],
'co_admin' => ['Co-Administrator', 'Alle Rechte, die sich vergeben lassen. Nur Einstellungen, Backup, System-Update, Plugins, API-Schlüssel, Webhooks, Import und Sicherheitsprüfung bleiben echten Administratoren.', $on($all), true],
'support_manager' => ['Support-Manager', 'Leitet den Support: Tickets, Einrichtung (Abteilungen, Prioritäten, Themen, Regeln, Felder), Mitarbeiter, Teams, Protokolle und Mail-Warteschlange.', $on(array_merge($lead, [
'manage_departments', 'manage_priorities', 'manage_help_topics', 'manage_custom_fields', 'manage_automation', 'manage_escalations', 'manage_business_hours', 'manage_problems',
'manage_ticket_templates', 'manage_staff', 'view_audit_log', 'view_login_guard', 'view_mail_queue', 'manage_customers', 'manage_organizations', 'manage_email_templates', 'view_help'])), true],
'it' => ['IT / Technik', 'Betrieb und Technik: Tickets bearbeiten, dazu Postfächer und E-Mail-Adressen, Mail-Warteschlange, Anmelde-Wächter, Statusseite, Regeln, Eskalationen, Protokoll und Hilfe.', $on(array_merge($agent, [
'manage_email_addresses', 'view_mail_queue', 'view_login_guard', 'manage_status_page', 'manage_automation', 'manage_escalations', 'view_audit_log', 'manage_problems', 'manage_custom_fields', 'view_help'])), true],
'teamleitung' => ['Teamleitung', 'Führt ein Team: wie Mitarbeiter, dazu Löschen, Export, Zeiterfassung, Textbausteine, Wissensdatenbank, Teams und Schichten.', $on($lead), true],
'support_2' => ['Support 2nd Level', 'Erfahrene Mitarbeiter: Tickets bearbeiten, Probleme verwalten, Export und Zeiterfassung.', $on(array_merge($agent, ['manage_problems', 'export_tickets', 'view_time_entries'])), true],
'support_1' => ['Support 1st Level', 'Einstieg: antworten, Notizen, zuweisen, Tickets anlegen und verknüpfen, E-Mails abrufen. Kein Schließen, Verschieben, Zusammenführen oder Sperren.', $on(['reply_ticket', 'post_note', 'assign_ticket', 'create_ticket', 'link_tickets', 'view_customers', 'fetch_mail']), true],
'redaktion' => ['Redaktion (Wissen und Inhalte)', 'Pflegt Inhalte: Wissensdatenbank, digitaler Assistent, Textbausteine, Ticket- und E-Mail-Vorlagen, Seiten und Sprachen.', $on(['reply_ticket', 'post_note', 'view_customers', 'manage_kb', 'manage_assistant', 'manage_canned', 'manage_ticket_templates', 'manage_email_templates', 'manage_site_pages', 'manage_languages']), true],
'auswertung' => ['Auswertung / Controlling', 'Zahlen und Berichte: Berichte (auch per E-Mail), Export, Zeiterfassung, Protokoll, Ansichten teilen. Kann keine Tickets bearbeiten.', $on(['view_reports', 'schedule_reports', 'export_tickets', 'view_time_entries', 'view_audit_log', 'share_views', 'view_customers']), true],
'teamverwaltung' => ['Teamverwaltung / Schichtplanung', 'Verwaltet Personal: Mitarbeiter, Teams, Status und Schichten, Geschäftszeiten, Anmelde-Wächter ansehen.', $on(['view_reports', 'manage_staff', 'manage_teams', 'manage_availability', 'manage_business_hours', 'view_login_guard', 'view_audit_log']), true],
'aushilfe' => ['Aushilfe / Praktikant', 'Nur das Nötigste: antworten und Notizen schreiben, Kundenprofile ansehen.', $on(['reply_ticket', 'post_note', 'view_customers']), true],
'nur_lesen' => ['Nur lesen', 'Tickets, Kundenprofile und Berichte ansehen, nichts ändern.', $on(['view_customers', 'view_reports']), true],
];
}
/**
* Legt die vordefinierten Rollen an, die es noch nicht gibt (Vergleich über den Namen, Groß-/Kleinschreibung egal). Vorhandene Rollen werden nie verändert.
* @return list<string> Namen der neu angelegten Rollen
*/
function permission_seed_default_roles(PDO $pdo): array
{
$created = [];
$exists = $pdo->prepare('SELECT COUNT(*) FROM roles WHERE LOWER(name) = LOWER(?)');
$now = date('Y-m-d H:i:s');
foreach (permission_presets() as [$name, $desc, $perms, $seed]) {
if (!$seed) continue;
$exists->execute([$name]);
if ((int) $exists->fetchColumn() > 0) continue;
$pdo->prepare('INSERT INTO roles (name, permissions, is_active, notes, created_at, updated_at) VALUES (?,?,1,?,?,?)')
->execute([$name, json_encode($perms), 'Vordefinierte Rolle – frei änderbar. ' . $desc, $now, $now]);
$created[] = $name;
}
return $created;
}
/** Namen der vordefinierten Rollen, die auf dieser Installation fehlen. @return list<string> */
function permission_missing_default_roles(PDO $pdo): array
{
$have = array_map('mb_strtolower', $pdo->query('SELECT name FROM roles')->fetchAll(PDO::FETCH_COLUMN));
$missing = [];
foreach (permission_presets() as [$name, , , $seed]) if ($seed && !in_array(mb_strtolower($name), $have, true)) $missing[] = $name;
return $missing;
}
/**
* Rechte, die es schon vor dem Katalog gab. Fehlt eines davon in einer gespeicherten Rolle, gilt wie bisher „nein“; bei allen neueren Rechten gilt
* der Standardwert des Katalogs (so behalten bestehende Rollen, was bisher für alle offen war, und neue Adminbereiche bleiben aus).
*/
function permission_legacy_keys(): array
{
return ['reply_ticket', 'close_ticket', 'delete_ticket', 'transfer_ticket', 'post_note', 'assign_ticket', 'manage_canned', 'view_reports', 'manage_kb', 'create_ticket',
'manage_departments', 'manage_priorities', 'manage_help_topics', 'manage_email_addresses', 'manage_email_templates', 'manage_organizations', 'manage_teams', 'manage_roles',
'manage_staff', 'view_audit_log', 'manage_escalations', 'view_time_entries', 'manage_business_hours', 'manage_assistant'];
}
/** Wirksame Rechte einer gespeicherten Rolle (so, wie has_permission() sie auswertet): fehlende alte Rechte = nein, fehlende neuere = Standardwert. */
function permission_effective(array $rolePerms): array
{
$defaults = default_permissions('agent');
$legacy = permission_legacy_keys();
$out = [];
foreach (array_keys($defaults) as $k) {
$out[$k] = array_key_exists($k, $rolePerms) ? (bool) $rolePerms[$k] : (!in_array($k, $legacy, true) && $defaults[$k]);
}
return $out;
}