Upload via GUI (452 Dateien)
This commit is contained in:
@@ -9,10 +9,7 @@ $pdo = get_db();
|
||||
|
||||
// Ticket-Rechte + alle Adminportal-Bereichsrechte (admin_section_permissions(), includes/functions.php) –
|
||||
// zusammen ergeben sie das vollständige Set, das über eine benutzerdefinierte Rolle vergeben werden kann.
|
||||
$rolePermissionKeys = array_merge([
|
||||
'reply_ticket', 'close_ticket', 'delete_ticket', 'transfer_ticket', 'post_note',
|
||||
'assign_ticket', 'manage_canned', 'view_reports', 'manage_kb', 'create_ticket',
|
||||
], array_keys(admin_section_permissions()));
|
||||
$rolePermissionKeys = array_keys(permission_labels());
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||
$roles = $pdo->query(
|
||||
@@ -21,11 +18,14 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||
|
||||
api_json([
|
||||
'permissionKeys' => $rolePermissionKeys,
|
||||
'permissionLabels' => permission_labels(),
|
||||
'permissionGroups' => array_values(array_map(fn($g) => ['title' => $g['title'], 'keys' => array_keys($g['items'])], permission_catalog())),
|
||||
'defaultAgentPermissions' => default_permissions('agent'),
|
||||
'items' => array_map(fn(array $r) => [
|
||||
'id' => (int) $r['id'],
|
||||
'name' => $r['name'],
|
||||
'permissions' => json_decode($r['permissions'] ?? '{}', true) ?? [],
|
||||
'effectivePermissions' => permission_effective(json_decode($r['permissions'] ?? '{}', true) ?? []),
|
||||
'staffCount' => (int) $r['staff_count'],
|
||||
'isActive' => (bool) ($r['is_active'] ?? 1),
|
||||
'notes' => $r['notes'] ?? null,
|
||||
@@ -44,9 +44,19 @@ if ($action === 'save') {
|
||||
if ($name === '') api_error(422, 'NAME_REQUIRED', 'Bitte einen Namen angeben.');
|
||||
|
||||
$incoming = (array) ($body['permissions'] ?? []);
|
||||
// Rechte, die eine ältere App-Version nicht kennt (und deshalb nicht mitschickt), bleiben unverändert bzw. bekommen den Standardwert
|
||||
$existingPerms = default_permissions('agent');
|
||||
if ($id) {
|
||||
$ep = $pdo->prepare('SELECT permissions FROM roles WHERE id = ?');
|
||||
$ep->execute([$id]);
|
||||
$existingPerms = array_merge($existingPerms, json_decode((string) $ep->fetchColumn(), true) ?: []);
|
||||
}
|
||||
$perms = [];
|
||||
foreach ($rolePermissionKeys as $p) {
|
||||
$perms[$p] = !empty($incoming[$p]);
|
||||
$want = array_key_exists($p, $incoming) ? !empty($incoming[$p]) : !empty($existingPerms[$p]);
|
||||
// Wer kein echter Administrator ist, kann keine Rechte vergeben, die er selbst nicht hat
|
||||
if ($staff['role'] !== 'admin' && $want && !has_permission($staff, $p)) $want = !empty($existingPerms[$p]) && $id > 0;
|
||||
$perms[$p] = $want;
|
||||
}
|
||||
$json = json_encode($perms);
|
||||
$dup = $pdo->prepare('SELECT COUNT(*) FROM roles WHERE LOWER(name) = LOWER(?) AND id <> ?');
|
||||
|
||||
@@ -17,7 +17,7 @@ $staff = api_require_staff();
|
||||
$pdo = get_db();
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
api_require_admin($staff);
|
||||
api_require_admin_or_permission($staff, 'manage_status_page');
|
||||
$b = api_request_body();
|
||||
$action = (string) ($b['action'] ?? '');
|
||||
if ($action === 'set_component') {
|
||||
@@ -39,4 +39,4 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
api_error(422, 'UNKNOWN_ACTION', 'Unbekannte Aktion.');
|
||||
}
|
||||
}
|
||||
api_json(['status' => status_summary($pdo)] + ['canManage' => $staff['role'] === 'admin']);
|
||||
api_json(['status' => status_summary($pdo)] + ['canManage' => has_permission($staff, 'manage_status_page')]);
|
||||
|
||||
@@ -12,8 +12,8 @@ require_once __DIR__ . '/common.php';
|
||||
|
||||
api_require_method('GET', 'POST');
|
||||
$staff = api_require_staff();
|
||||
if ($staff['role'] !== 'admin') {
|
||||
api_error(403, 'ADMIN_REQUIRED', 'Nur Administratoren können Ticket-Regeln verwalten.');
|
||||
if (!has_permission($staff, 'manage_automation')) {
|
||||
api_error(403, 'FORBIDDEN', 'Für Ticket-Regeln fehlt die erforderliche Berechtigung.');
|
||||
}
|
||||
$pdo = get_db();
|
||||
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/* Passkeys im Browser: Anmeldung (staff/login.php) und Registrierung (Profil → Sicherheit). Spricht mit /staff/passkey.php. */
|
||||
(function () {
|
||||
'use strict';
|
||||
function b64uToBuf(s) {
|
||||
s = s.replace(/-/g, '+').replace(/_/g, '/');
|
||||
while (s.length % 4) s += '=';
|
||||
var bin = atob(s), buf = new Uint8Array(bin.length);
|
||||
for (var i = 0; i < bin.length; i++) buf[i] = bin.charCodeAt(i);
|
||||
return buf.buffer;
|
||||
}
|
||||
function bufToB64u(buf) {
|
||||
var b = new Uint8Array(buf), s = '';
|
||||
for (var i = 0; i < b.length; i++) s += String.fromCharCode(b[i]);
|
||||
return btoa(s).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
||||
}
|
||||
function post(csrf, fields) {
|
||||
var fd = new FormData();
|
||||
fd.append('csrf_token', csrf);
|
||||
Object.keys(fields).forEach(function (k) { fd.append(k, fields[k]); });
|
||||
return fetch('/staff/passkey.php', { method: 'POST', body: fd, credentials: 'same-origin' }).then(function (r) {
|
||||
return r.json().catch(function () { return { ok: false, error: 'Unerwartete Antwort des Servers.' }; });
|
||||
});
|
||||
}
|
||||
function supported() {
|
||||
return !!(window.PublicKeyCredential && navigator.credentials && window.isSecureContext);
|
||||
}
|
||||
function friendly(e) {
|
||||
if (e && (e.name === 'NotAllowedError' || e.name === 'AbortError')) return 'Abgebrochen oder abgelaufen. Bitte noch einmal versuchen.';
|
||||
if (e && e.name === 'InvalidStateError') return 'Dieses Gerät ist schon als Passkey registriert.';
|
||||
return (e && e.message) ? e.message : 'Der Passkey konnte nicht verwendet werden.';
|
||||
}
|
||||
|
||||
window.DeskHubPasskey = {
|
||||
supported: supported,
|
||||
login: function (csrf) {
|
||||
return post(csrf, { action: 'login_options' }).then(function (r) {
|
||||
if (!r.ok) throw new Error(r.error);
|
||||
var o = r.options;
|
||||
return navigator.credentials.get({ publicKey: { challenge: b64uToBuf(o.challenge), rpId: o.rpId, timeout: o.timeout, userVerification: o.userVerification, allowCredentials: [] } });
|
||||
}).then(function (c) {
|
||||
var rs = c.response;
|
||||
return post(csrf, { action: 'login_verify', credential: JSON.stringify({ id: c.id, response: {
|
||||
clientDataJSON: bufToB64u(rs.clientDataJSON), authenticatorData: bufToB64u(rs.authenticatorData), signature: bufToB64u(rs.signature),
|
||||
userHandle: rs.userHandle ? bufToB64u(rs.userHandle) : ''
|
||||
} }) });
|
||||
}).then(function (r) {
|
||||
if (!r.ok) throw new Error(r.error);
|
||||
return r;
|
||||
}).catch(function (e) { throw new Error(friendly(e)); });
|
||||
},
|
||||
register: function (csrf, name, password) {
|
||||
return post(csrf, { action: 'register_options', password: password }).then(function (r) {
|
||||
if (!r.ok) throw new Error(r.error);
|
||||
var o = r.options;
|
||||
o.challenge = b64uToBuf(o.challenge);
|
||||
o.user.id = b64uToBuf(o.user.id);
|
||||
o.excludeCredentials = (o.excludeCredentials || []).map(function (c) { return { type: c.type, id: b64uToBuf(c.id) }; });
|
||||
return navigator.credentials.create({ publicKey: o });
|
||||
}).then(function (c) {
|
||||
var rs = c.response;
|
||||
return post(csrf, { action: 'register_verify', name: name, credential: JSON.stringify({ id: c.id, response: {
|
||||
clientDataJSON: bufToB64u(rs.clientDataJSON), attestationObject: bufToB64u(rs.attestationObject)
|
||||
} }) });
|
||||
}).then(function (r) {
|
||||
if (!r.ok) throw new Error(r.error);
|
||||
return r;
|
||||
}).catch(function (e) { throw new Error(friendly(e)); });
|
||||
}
|
||||
};
|
||||
})();
|
||||
Reference in New Issue
Block a user