diff options
| author | Simon Rettberg | 2025-07-23 16:02:41 +0200 |
|---|---|---|
| committer | Simon Rettberg | 2025-07-23 16:02:41 +0200 |
| commit | db2c123ef4c3b9e044f4a7c38a0091ac7f443a52 (patch) | |
| tree | 2ef33ea19850571fe88039037cd0c942d6d556a3 | |
| parent | [baseconfig_bwlp/syslog] Update translations (diff) | |
| download | slx-admin-db2c123ef4c3b9e044f4a7c38a0091ac7f443a52.tar.gz slx-admin-db2c123ef4c3b9e044f4a7c38a0091ac7f443a52.tar.xz slx-admin-db2c123ef4c3b9e044f4a7c38a0091ac7f443a52.zip | |
[session] Add audit log view
Basic 1st version :)
| -rw-r--r-- | inc/audit.inc.php | 7 | ||||
| -rw-r--r-- | modules-available/adduser/permissions/permissions.json | 3 | ||||
| -rw-r--r-- | modules-available/main/install.inc.php | 3 | ||||
| -rw-r--r-- | modules-available/session/lang/de/messages.json | 1 | ||||
| -rw-r--r-- | modules-available/session/lang/de/module.json | 1 | ||||
| -rw-r--r-- | modules-available/session/lang/de/template-tags.json | 12 | ||||
| -rw-r--r-- | modules-available/session/lang/en/messages.json | 3 | ||||
| -rw-r--r-- | modules-available/session/lang/en/module.json | 1 | ||||
| -rw-r--r-- | modules-available/session/lang/en/template-tags.json | 12 | ||||
| -rw-r--r-- | modules-available/session/page.inc.php | 83 | ||||
| -rw-r--r-- | modules-available/session/templates/audit-list.html | 65 | ||||
| -rw-r--r-- | modules-available/session/templates/change-password.html | 6 |
12 files changed, 175 insertions, 22 deletions
diff --git a/inc/audit.inc.php b/inc/audit.inc.php index 443b5ca6..5a1c7691 100644 --- a/inc/audit.inc.php +++ b/inc/audit.inc.php @@ -64,9 +64,10 @@ class Audit if ($key === 'prevent_autofill' || $key === 'password_fake' || $key === 'do' || $key === 'action' || $key === 'token') continue; // These don't matter - if (strpos($key, 'pass') !== false || strpos($key, 'token') !== false - || substr($key, -3) === 'key' // privatekey, hmac-key, ... - || substr($key, 0, 2) === 'pw') { + $lkey = strtolower($key); + if (strpos($lkey, 'pass') !== false || strpos($lkey, 'token') !== false + || substr($lkey, -3) === 'key' // privatekey, hmac-key, ... + || substr($lkey, 0, 2) === 'pw') { $value = '*****'; // Censor } elseif (is_array($value)) { $value = self::processPostArray($value, $maxlen, $hadLongString); diff --git a/modules-available/adduser/permissions/permissions.json b/modules-available/adduser/permissions/permissions.json index e8fd0a5e..b06ce7f0 100644 --- a/modules-available/adduser/permissions/permissions.json +++ b/modules-available/adduser/permissions/permissions.json @@ -10,5 +10,8 @@ }, "user.view-list": { "location-aware": false + }, + "audit.show": { + "location-aware": false } }
\ No newline at end of file diff --git a/modules-available/main/install.inc.php b/modules-available/main/install.inc.php index d5ce1992..ef8a9f91 100644 --- a/modules-available/main/install.inc.php +++ b/modules-available/main/install.inc.php @@ -96,7 +96,8 @@ $res[] = tableCreate('audit', " `action` varchar(100) NOT NULL, `data` mediumblob NOT NULL, `response` smallint(3) unsigned DEFAULT NULL, - PRIMARY KEY (`id`) + PRIMARY KEY (`id`), + KEY `dateline` (`dateline`) "); // Update path diff --git a/modules-available/session/lang/de/messages.json b/modules-available/session/lang/de/messages.json index c75973f0..f0f15734 100644 --- a/modules-available/session/lang/de/messages.json +++ b/modules-available/session/lang/de/messages.json @@ -3,5 +3,6 @@ "pass-too-short": "Passwort zu kurz", "password-changed": "Passwort erfolgreich ge\u00e4ndert", "password-unchanged": "Passwort wurde nicht ge\u00e4ndert", + "user-not-found": "Unbekannte Nutzer-ID", "wrong-password": "Falsches Passwort" }
\ No newline at end of file diff --git a/modules-available/session/lang/de/module.json b/modules-available/session/lang/de/module.json index afd14498..9333859e 100644 --- a/modules-available/session/lang/de/module.json +++ b/modules-available/session/lang/de/module.json @@ -1,5 +1,6 @@ { "module_name": "Sitzungsverwaltung", + "page-title-audit-list": "Audit log", "page-title-session-list": "Aktive Sitzungen", "page_title": "Anmelden" }
\ No newline at end of file diff --git a/modules-available/session/lang/de/template-tags.json b/modules-available/session/lang/de/template-tags.json index 5c32788d..1c9a0480 100644 --- a/modules-available/session/lang/de/template-tags.json +++ b/modules-available/session/lang/de/template-tags.json @@ -1,14 +1,24 @@ { + "lang_action": "Aktion", "lang_activeSessions": "Bekannte Sitzungen", + "lang_auditLogHeading": "Audit-Log", + "lang_auditLogIntro": "Hier sehen Sie das Protokoll der in dieser Administrationsoberfl\u00e4che ausgef\u00fchrten Aktionen.", "lang_changePassword": "Passwort \u00e4ndern", + "lang_clearFilter": "L\u00f6schen", "lang_currentPassword": "Aktuelles Passwort", + "lang_currentlyFilteredByUser": "Aktuell gefiltert Nach Nutzer", "lang_enter": "Anmeldung", "lang_expires": "L\u00e4uft bei Inaktivit\u00e4t ab", "lang_fixedIpSession": "Sitzung an IP-Adresse binden", "lang_killOtherSessions": "Alle meine anderen Sitzungen ausloggen", "lang_lastAddress": "Letzter Zugriff von", "lang_login": "Anmelden", + "lang_module": "Modul", "lang_newPassword": "Neues Passwort", + "lang_params": "Parameter", "lang_register": "Registrieren", - "lang_repeatPassword": "Passwort wiederholen" + "lang_repeatPassword": "Passwort wiederholen", + "lang_responseCode": "HTTP-Code", + "lang_showAuditLog": "Audit-Log der Adminoberfl\u00e4che", + "lang_when": "Wann" }
\ No newline at end of file diff --git a/modules-available/session/lang/en/messages.json b/modules-available/session/lang/en/messages.json index a7b14d61..9c81729c 100644 --- a/modules-available/session/lang/en/messages.json +++ b/modules-available/session/lang/en/messages.json @@ -3,5 +3,6 @@ "pass-too-short": "Password too short", "password-changed": "Password successfully changed", "password-unchanged": "Password unchanged", + "user-not-found": "Unknown user id", "wrong-password": "Wrong password" -} +}
\ No newline at end of file diff --git a/modules-available/session/lang/en/module.json b/modules-available/session/lang/en/module.json index 2227d2ed..66b54fcd 100644 --- a/modules-available/session/lang/en/module.json +++ b/modules-available/session/lang/en/module.json @@ -1,5 +1,6 @@ { "module_name": "Session management", + "page-title-audit-list": "Audit log", "page-title-session-list": "Active sessions", "page_title": "Log in" }
\ No newline at end of file diff --git a/modules-available/session/lang/en/template-tags.json b/modules-available/session/lang/en/template-tags.json index d7f8d98b..eb2c10bf 100644 --- a/modules-available/session/lang/en/template-tags.json +++ b/modules-available/session/lang/en/template-tags.json @@ -1,14 +1,24 @@ { + "lang_action": "Action", "lang_activeSessions": "Known sessions", + "lang_auditLogHeading": "Audit log", + "lang_auditLogIntro": "This is the log of all actions performed on this admin interface.", "lang_changePassword": "Change password", + "lang_clearFilter": "Clear", "lang_currentPassword": "Current password", + "lang_currentlyFilteredByUser": "Currently filtered by user", "lang_enter": "Enter", "lang_expires": "Expires on no activity", "lang_fixedIpSession": "Bind session to IP address", "lang_killOtherSessions": "Log out all my other sessions", "lang_lastAddress": "Last access from", "lang_login": "Login", + "lang_module": "Module", "lang_newPassword": "New password", + "lang_params": "Parameters", "lang_register": "Register", - "lang_repeatPassword": "Repeat password" + "lang_repeatPassword": "Repeat password", + "lang_responseCode": "HTTP code", + "lang_showAuditLog": "Show audit log of admin interface", + "lang_when": "When" }
\ No newline at end of file diff --git a/modules-available/session/page.inc.php b/modules-available/session/page.inc.php index c59af63a..d113f523 100644 --- a/modules-available/session/page.inc.php +++ b/modules-available/session/page.inc.php @@ -54,31 +54,86 @@ class Page_Session extends Page } Util::redirect('?do=session', 200); } else { - // No action, change title to session list - Render::setTitle(Dictionary::translate('page-title-session-list')); + // No action, change title + switch (Request::get('show', false, 'string')) { + case 'audit': + Render::setTitle(Dictionary::translate('page-title-audit-list'));; + break; + default: + Render::setTitle(Dictionary::translate('page-title-session-list')); + } } } protected function doRender() { if (User::isLoggedIn()) { - $res = Database::simpleQuery("SELECT u.login, s.userid, s.dateline, s.lastip, s.fixedip + $show = Request::get('show', false, 'string'); + if ($show === 'audit') { + self::showAudit(); + } else { + self::showSessions(); + } + } else { + Render::addTemplate('page-login'); + } + } + + private static function showSessions() + { + $res = Database::simpleQuery("SELECT u.login, s.userid, s.dateline, s.lastip, s.fixedip FROM session s INNER JOIN user u USING (userid) ORDER BY dateline DESC"); - $sessions = []; - $perm = User::hasPermission('.adduser.user.*'); - foreach ($res as $row) { - if ($perm || $row['userid'] == User::getId()) { - $row['dateline_s'] = Util::prettyTime($row['dateline']); - $sessions[] = $row; - } + $sessions = []; + $perm = User::hasPermission('.adduser.user.*'); + foreach ($res as $row) { + if ($perm || $row['userid'] == User::getId()) { + $row['dateline_s'] = Util::prettyTime($row['dateline']); + $sessions[] = $row; + } + } + Render::addTemplate('change-password', [ + 'sessions' => $sessions, + 'link' => User::hasPermission('.adduser.user.edit'), + 'audit' => User::hasPermission('.adduser.audit.show'), + ]); + } + + private static function showAudit() + { + User::assertPermission('.adduser.audit.show'); + $user = Request::get('user', 0, 'int'); + $args = []; + $extra = ''; + $username = null; + if ($user > 0) { + $args['userid'] = $user; + $extra .= ' WHERE a.userid = :userid'; + $row = Database::queryFirst("SELECT login FROM user WHERE userid = :userid", ['userid' => $user]); + if ($row === false) { + Message::addError('user-not-found', $user); + Util::redirect('?do=session&show=audit', 404); + } + $username = $row['login']; + } + $res = Database::queryAll("SELECT u.userid, u.login, + a.action, a.dateline, a.ipaddr, a.data, a.module, a.response + FROM audit a + LEFT JOIN user u USING (userid) + $extra + ORDER BY dateline DESC + LIMIT 500", $args); + foreach ($res as &$row) { + $row['dateline_s'] = Util::prettyTime($row['dateline']); + if ($row['response'] < 200 || $row['response'] >= 400) { + $row['class'] = 'text-danger slx-bold'; + } + if ($username !== null) { + unset($row['login']); } - Render::addTemplate('change-password', ['sessions' => $sessions, - 'link' => User::hasPermission('.adduser.user.edit')]); - } else { - Render::addTemplate('page-login'); } + Render::addTemplate('audit-list', ['list' => $res, 'user' => $username]); } } diff --git a/modules-available/session/templates/audit-list.html b/modules-available/session/templates/audit-list.html new file mode 100644 index 00000000..0c79c52d --- /dev/null +++ b/modules-available/session/templates/audit-list.html @@ -0,0 +1,65 @@ +<div class="page-header"> + <h1>{{lang_auditLogHeading}}</h1> + <p>{{lang_auditLogIntro}}</p> +</div> + +{{#user}} + <p> + {{lang_currentlyFilteredByUser}}: <b>{{user}}</b> + <a class="btn btn-xs btn-danger" href="?do=session&show=audit"> + <span class="glyphicon glyphicon-remove"></span> + {{lang_clearFilter}} + </a> + </p> +{{/user}} + +<table class="table"> + <tr> + <th>{{lang_when}}</th> + <th>{{lang_responseCode}}</th> + <th>{{lang_user}}</th> + <th>{{lang_module}}</th> + <th>{{lang_action}}</th> + <th>{{lang_params}}</th> + </tr> + {{#list}} + <tr> + <td class="text-nowrap">{{dateline_s}}</td> + <td class="{{class}}">{{response}}</td> + <td> + {{^login}}{{ipaddr}}{{/login}} + {{#login}} + <a href="?do=session&show=audit&user={{userid}}">{{login}}</a> + {{/login}} + </td> + <td class="text-nowrap">{{module}}</td> + <td class="text-nowrap">{{action}}</td> + <td><table class="slx-ellipsis"><tr><td class="json-stuff" data-toggle="modal" data-target="#myModal">{{data}}</td></tr></table></td> + </tr> + {{/list}} +</table> + +<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> + <div class="modal-dialog modal-lg"> + <div class="modal-content"> + <div class="modal-header"> + <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span><span + class="sr-only">{{lang_close}}</span></button> + <h4 class="modal-title" id="myModalLabel">{{lang_params}}</h4> + </div> + <div class="modal-body"> + <pre id="details-body"></pre> + </div> + </div> + </div> +</div> + +<script> + document.addEventListener('DOMContentLoaded', function() { + $('.json-stuff').click(function() { + var txt = $(this).text(); + txt = JSON.stringify(JSON.parse(txt), null, 2); + $('#details-body').text(txt); + }); + }); +</script>
\ No newline at end of file diff --git a/modules-available/session/templates/change-password.html b/modules-available/session/templates/change-password.html index 9f19c695..10b0f43d 100644 --- a/modules-available/session/templates/change-password.html +++ b/modules-available/session/templates/change-password.html @@ -49,4 +49,8 @@ </tr> {{/sessions}} </tbody> -</table>
\ No newline at end of file +</table> + +{{#audit}} + <a href="?do=session&show=audit">{{lang_showAuditLog}} »</a> +{{/audit}}
\ No newline at end of file |
