Beendet alle Anmeldungen in Browsern und in den Apps (Android, Windows-Programm, Chrome-Erweiterung) und stoppt die Push-Benachrichtigungen auf diesen Geräten. Sinnvoll, wenn ein Gerät verloren ging oder Sie sich irgendwo angemeldet gelassen haben. Ihr Passwort bleibt unverändert.
Von diesen Geräten haben Sie sich schon angemeldet. Meldet sich jemand von einem neuen Gerät oder Ort an, bekommen Sie eine Mail. Ein Gerät, das Sie nicht kennen, können Sie hier entfernen; ändern Sie dann auch Ihr Passwort.
diff --git a/DeskHub-Webseite/staff/recurring_tickets.php b/DeskHub-Webseite/staff/recurring_tickets.php
index ebbfcfeb..e2a23ba1 100644
--- a/DeskHub-Webseite/staff/recurring_tickets.php
+++ b/DeskHub-Webseite/staff/recurring_tickets.php
@@ -1,6 +1,6 @@
query('SELECT department_id FROM recurring_tickets')->fetchAll(PDO::FETCH_COLUMN)));
diff --git a/DeskHub-Webseite/staff/report_builder.php b/DeskHub-Webseite/staff/report_builder.php
index 31c9834a..7ef4ab3b 100644
--- a/DeskHub-Webseite/staff/report_builder.php
+++ b/DeskHub-Webseite/staff/report_builder.php
@@ -25,7 +25,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$cfg = report_clean_config($pdo, $cfgIn);
$now = db_now();
$pdo->prepare('INSERT INTO saved_reports (staff_id, name, config, is_shared, created_at, updated_at) VALUES (?,?,?,?,?,?)')
- ->execute([$staff['id'], mb_substr($name, 0, 120), json_encode($cfg, JSON_UNESCAPED_UNICODE), !empty($_POST['shared']) ? 1 : 0, $now, $now]);
+ ->execute([$staff['id'], mb_substr($name, 0, 120), json_encode($cfg, JSON_UNESCAPED_UNICODE), (!empty($_POST['shared']) && has_permission($staff, 'share_views')) ? 1 : 0, $now, $now]);
flash('success', 'Bericht gespeichert.');
}
redirect('/staff/report_builder.php?' . ltrim((string) ($_POST['return_query'] ?? ''), '?'));
@@ -46,7 +46,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$own->execute([$sid]);
$sch = $own->fetch();
$mine = $sch && ((int) $sch['staff_id'] === (int) $staff['id'] || $staff['role'] === 'admin');
- if ($action === 'schedule_add') {
+ if (in_array($action, ['schedule_add'], true) && !has_permission($staff, 'schedule_reports')) {
+ flash('error', 'Für Berichte per E-Mail fehlt die erforderliche Berechtigung.');
+ } elseif ($action === 'schedule_add') {
$err = report_schedule_create($pdo, $staff, $_POST);
$err ? flash('error', $err) : flash('success', 'Zeitplan angelegt. Der Bericht wird zum nächsten Termin per E-Mail gesendet.');
} elseif (!$mine) {
@@ -208,7 +210,7 @@ require __DIR__ . '/_header.php';
-
+
diff --git a/DeskHub-Webseite/staff/reset_password.php b/DeskHub-Webseite/staff/reset_password.php
index 693012f2..dee67f88 100644
--- a/DeskHub-Webseite/staff/reset_password.php
+++ b/DeskHub-Webseite/staff/reset_password.php
@@ -25,7 +25,10 @@ if (!$token || !$email) {
consume_password_reset('staff', $token);
$staffRow = $pdo->prepare('SELECT id FROM staff WHERE LOWER(email) = LOWER(?)');
$staffRow->execute([$email]);
- audit_log((int) $staffRow->fetchColumn(), 'Passwort per Reset-Link zurückgesetzt');
+ $resetStaffId = (int) $staffRow->fetchColumn();
+ audit_log($resetStaffId, 'Passwort per Reset-Link zurückgesetzt');
+ // Wer den Link aus der Mail benutzt, hat sich als Besitzer des Postfachs ausgewiesen: Die Anmeldesperre endet damit
+ if ($resetStaffId > 0) login_guard_unlock($pdo, $resetStaffId, null, 'Sperre aufgehoben: Passwort per Reset-Link zurückgesetzt');
$done = true;
}
}
diff --git a/DeskHub-Webseite/staff/roles.php b/DeskHub-Webseite/staff/roles.php
index dd47320a..14435798 100644
--- a/DeskHub-Webseite/staff/roles.php
+++ b/DeskHub-Webseite/staff/roles.php
@@ -4,23 +4,10 @@ require_once __DIR__ . '/_ui.php';
$staff = require_admin_or_permission('manage_roles');
$pdo = get_db();
-$ticketPermLabels = [
- 'reply_ticket' => 'Tickets beantworten',
- 'close_ticket' => 'Tickets schließen/lösen',
- 'delete_ticket' => 'Tickets löschen',
- 'transfer_ticket' => 'Tickets übertragen (Abteilung ändern)',
- 'post_note' => 'Interne Notizen schreiben',
- 'assign_ticket' => 'Tickets zuweisen (Mitarbeiter/Team)',
- 'manage_canned' => 'Textbausteine verwalten',
- 'view_reports' => 'Berichte einsehen',
- 'manage_kb' => 'Wissensdatenbank verwalten',
- 'create_ticket' => 'Tickets im Namen von Kunden erstellen',
-];
-// Feinkörnige Adminportal-Rechte ("Co-Admin") – siehe admin_section_permissions() in functions.php.
-// manage_kb steckt bewusst nur oben bei den Ticket-Rechten, nicht doppelt hier.
-$adminPermLabels = admin_section_permissions();
-unset($adminPermLabels['manage_kb']);
-$permLabels = array_merge($ticketPermLabels, $adminPermLabels);
+// Alle Rechte, Gruppen, Beschreibungen und Standardwerte: includes/permissions.php
+$catalog = permission_catalog();
+$permLabels = permission_labels();
+$agentDefaults = default_permissions('agent');
$errors = [];
@@ -39,8 +26,16 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if ($dup->fetchColumn() > 0) $errors[] = 'Eine Rolle mit diesem Namen existiert bereits.';
if (!$errors) {
$perms = [];
+ // Wer kein echter Administrator ist, kann keine Rechte vergeben, die er selbst nicht hat (sonst könnte sich ein Co-Admin zum Administrator hochstufen)
+ $oldPerms = [];
+ if ($action === 'update') {
+ $op = $pdo->prepare('SELECT permissions FROM roles WHERE id = ?'); $op->execute([$id]);
+ $oldPerms = json_decode((string) $op->fetchColumn(), true) ?: [];
+ }
foreach (array_keys($permLabels) as $p) {
- $perms[$p] = !empty($_POST['perm_' . $p]);
+ $want = !empty($_POST['perm_' . $p]);
+ if ($staff['role'] !== 'admin' && $want && !has_permission($staff, $p)) $want = !empty($oldPerms[$p]);
+ $perms[$p] = $want;
}
$json = json_encode($perms);
if ($action === 'create') {
@@ -58,6 +53,17 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
}
}
+ if ($action === 'seed_defaults') {
+ if ($staff['role'] !== 'admin') {
+ flash('error', 'Vordefinierte Rollen anlegen dürfen nur Administratoren.');
+ } else {
+ $made = permission_seed_default_roles($pdo);
+ if ($made) { audit_log($staff['id'], 'Vordefinierte Rollen angelegt: ' . implode(', ', $made)); flash('success', count($made) . ' Rolle(n) angelegt: ' . implode(', ', $made) . '.'); }
+ else flash('info', 'Alle vordefinierten Rollen sind schon vorhanden.');
+ }
+ redirect('/staff/roles.php');
+ }
+
if ($action === 'delete') {
$id = (int) $_POST['id'];
// Mitarbeiter und Abteilungszugriffe auf die Hauptrolle zurücksetzen
@@ -74,17 +80,19 @@ $isNew = isset($_GET['new']);
$editId = (int) ($_GET['edit'] ?? 0);
$showForm = $isNew || $editId || ($errors && in_array($_POST['action'] ?? '', ['create', 'update'], true));
+$presetNames = [];
+foreach (permission_presets() as [$__pn, , , $__seed]) if ($__seed) $presetNames[] = mb_strtolower($__pn);
$roles = $pdo->query(
'SELECT r.*, (SELECT COUNT(*) FROM staff s WHERE s.role_id = r.id) + (SELECT COUNT(*) FROM staff_department sd WHERE sd.role_id = r.id) AS staff_count
FROM roles r ORDER BY r.name'
)->fetchAll();
$editRole = null;
-$editPerms = array_fill_keys(array_keys($permLabels), false);
+$editPerms = $agentDefaults; // neue Rolle: wie ein Standard-Mitarbeiter; fehlende Rechte älterer Rollen gelten mit dem Standardwert
if ($editId) {
$es = $pdo->prepare('SELECT * FROM roles WHERE id = ?'); $es->execute([$editId]);
$editRole = $es->fetch() ?: null;
- if ($editRole) $editPerms = array_merge($editPerms, json_decode($editRole['permissions'] ?? '{}', true) ?? []);
+ if ($editRole) $editPerms = permission_effective(json_decode($editRole['permissions'] ?? '{}', true) ?? []);
}
if ($_POST && $errors) foreach (array_keys($permLabels) as $p) $editPerms[$p] = !empty($_POST['perm_' . $p]);
@@ -123,24 +131,52 @@ require __DIR__ . '/_header.php';
-
Administratoren haben immer alle Rechte. Die Rechte einer Rolle gelten für Mitarbeiter mit dieser Rolle – je Abteilung kann ein Mitarbeiter eine andere Rolle haben (Mitarbeiter → Zugang).
-
Tickets
-
- $label): ?>
-
-
-
-
Adminportal-Bereiche („Co-Admin“)
-
- $label): ?>
-
+
Administratoren haben immer alle Rechte. Die Rechte einer Rolle gelten für Mitarbeiter mit dieser Rolle – je Abteilung kann ein Mitarbeiter eine andere Rolle haben (Mitarbeiter → Zugang). Ein Recht für einen Adminportal-Bereich macht die Person zum „Co-Admin“ für genau diesen Bereich. Immer nur echten Administratoren vorbehalten: Einstellungen, Backup, System-Update, Plugins, API-Schlüssel, Webhooks, osTicket-Import, Sicherheitsprüfung, Passkeys ein-/ausschalten und die Datenschutz-Löschfunktionen.
Benutzerdefinierte Berechtigungssets für Mitarbeiter. Ohne Rolle gelten die Standard-Mitarbeiterrechte.
+
Berechtigungssets für Mitarbeiter. Ohne Rolle gelten die Standard-Mitarbeiterrechte.
+
Rollen mit dem Kennzeichen „vordefiniert“ (Co-Administrator, Support-Manager, IT / Technik, Teamleitung, Support 1st und 2nd Level, Redaktion, Auswertung, Teamverwaltung, Aushilfe, Nur lesen) wurden mit dem Update angelegt und lassen sich wie jede andere Rolle ändern oder löschen.
Der Code ist neuer als die Datenbank. Vor dem Einspielen wird automatisch ein Backup erstellt.
- Details= htmlspecialchars(implode(', ', $pendingMigrations)) ?>
-
- ✓ Die Datenbank ist aktuell
-
Alle bekannten Datenbank-Änderungen sind eingespielt.
-
-
-
- = csrf_field() ?>
-
-
-
-
-
-
-
-
-
-
Installierte Version
-
= htmlspecialchars($curVer) ?>
-
DeskHub Ticketsystem
-
-
-
-
-
-
-
Manuelles Update
-
ZIP-Datei von Gitea hochladen und installieren.
-
- = csrf_field() ?>
-
-
-
-
-
-
-
-
- 1): ?>
-
-
Bestimmte Version installieren
-
- = csrf_field() ?>
-
-
-
-
-
-
-
-
-
-
-
-
Zugang zum Update-Server
-
- Nur nötig, wenn das Repository mit den Releases privat ist. Gitea meldet dann beim Laden der Versionen „404“. Ein Token mit Leserecht genügt
- (in Gitea: Einstellungen → Anwendungen → Zugriffstoken erzeugen, Berechtigung „repository: Lesen“). Es wird verschlüsselt gespeichert.
-
-
-
✓ Token aus config/config.php wird verwendet. Es hat Vorrang vor einem hier gespeicherten.
-
-
✓ Token gespeichert. Zum Ersetzen unten ein neues eintragen.
-
-
Kein Token eingetragen (öffentlicher Zugriff).
-
-
- = csrf_field() ?>
-
-
-
-
-
-
-
-
-
- = csrf_field() ?>
-
-
-
- Alternative: in config/config.php eintragen
-
In der Datei config/config.php (Hauptordner von DeskHub, im Ordner config) als eigener Eintrag im großen return [ … ];, zum Beispiel direkt unter 'app_key':
-
'update' => ['token' => 'HIER_DAS_TOKEN'],
-
-
-
-
-
Hinweise
-
-
config/config.php und database/deskhub.sqlite werden niemals überschrieben
-
Nur signierte Releases: Updates werden nur installiert, wenn sie mit dem Schlüssel des Herausgebers signiert sind. Ein manipuliertes Paket wird abgelehnt. (Ein manuell hochgeladenes ZIP wird nicht geprüft: Laden Sie nur Pakete aus vertrauenswürdiger Quelle hoch.)
Der Code ist neuer als die Datenbank. Vor dem Einspielen wird automatisch ein Backup erstellt.
+ Details= htmlspecialchars(implode(', ', $pendingMigrations)) ?>
+
+ ✓ Die Datenbank ist aktuell
+
Alle bekannten Datenbank-Änderungen sind eingespielt.
+
+
+
+ = csrf_field() ?>
+
+
+
+
+
+
+
+
+
+
Installierte Version
+
= htmlspecialchars($curVer) ?>
+
DeskHub Ticketsystem
+
Updates kommen von: = htmlspecialchars($relSource) ?>= $relSource === 'Gitea' && update_publisher_endpoint(['list' => 1]) !== '' ? ' (Plugin-Webseite nicht erreichbar oder ohne Update)' : '' ?>
+
+
+
+
+
+
+
Manuelles Update
+
Eine ZIP-Datei des Herausgebers hochladen und installieren.
+
+ = csrf_field() ?>
+
+
+
+
+
+
+
+
+ 1): ?>
+
+
Bestimmte Version installieren
+
+ = csrf_field() ?>
+
+
+
+
+
+
+
+
+
+
+
+
Zugang zum Update-Server
+
+ Nur für die Ausweichquelle Gitea (wenn die Plugin-Webseite keine Updates liefert). Nötig, wenn das Repository mit den Releases privat ist. Gitea meldet dann beim Laden der Versionen „404“. Ein Token mit Leserecht genügt
+ (in Gitea: Einstellungen → Anwendungen → Zugriffstoken erzeugen, Berechtigung „repository: Lesen“). Es wird verschlüsselt gespeichert.
+
+
+
✓ Token aus config/config.php wird verwendet. Es hat Vorrang vor einem hier gespeicherten.
+
+
✓ Token gespeichert. Zum Ersetzen unten ein neues eintragen.
+
+
Kein Token eingetragen (öffentlicher Zugriff).
+
+
+ = csrf_field() ?>
+
+
+
+
+
+
+
+
+
+ = csrf_field() ?>
+
+
+
+ Alternative: in config/config.php eintragen
+
In der Datei config/config.php (Hauptordner von DeskHub, im Ordner config) als eigener Eintrag im großen return [ … ];, zum Beispiel direkt unter 'app_key':
+
'update' => ['token' => 'HIER_DAS_TOKEN'],
+
+
+
+
+
Hinweise
+
+
config/config.php und database/deskhub.sqlite werden niemals überschrieben
+
Nur signierte Releases: Updates werden nur installiert, wenn sie mit dem Schlüssel des Herausgebers signiert sind. Ein manipuliertes Paket wird abgelehnt. (Ein manuell hochgeladenes ZIP wird nicht geprüft: Laden Sie nur Pakete aus vertrauenswürdiger Quelle hoch.)