Display audit log in settings.

This commit is contained in:
flash 2018-07-23 15:29:57 +02:00
parent 0aaa16e78f
commit 105492154e
7 changed files with 181 additions and 6 deletions

View file

@ -0,0 +1,49 @@
.settings__log {
&__country {
vertical-align: middle;
}
&__entry {
display: flex;
border: 1px solid #9475b2;
justify-content: space-between;
padding: 1px;
flex-wrap: wrap;
&:not(:last-child) {
margin-bottom: 1px;
}
}
&__column {
flex-grow: 1;
margin-left: 5px;
margin-right: 1px;
&--ip,
&--date {
flex-shrink: 0;
&:not(:last-child) {
flex-grow: 0;
}
}
&--action {
flex-shrink: 1;
flex-grow: 1;
}
&--ip {
min-width: 200px;
}
&--date {
min-width: 120px;
}
&__name {
font-weight: 700;
}
}
}

View file

@ -69,6 +69,7 @@ body {
@import "classes/settings/account";
@import "classes/settings/images";
@import "classes/settings/avatar";
@import "classes/settings/log";
@import "classes/settings/login-history";
@import "classes/settings/sessions";

View file

@ -0,0 +1,20 @@
<?php
namespace Misuzu\DatabaseMigrations\AddCountryToAuditLog;
use PDO;
function migrate_up(PDO $conn): void
{
$conn->exec('
ALTER TABLE `msz_audit_log`
ADD COLUMN `log_country` CHAR(2) NOT NULL DEFAULT \'XX\' AFTER `log_ip`;
');
}
function migrate_down(PDO $conn): void
{
$conn->exec('
ALTER TABLE `msz_audit_log`
DROP COLUMN `log_country`;
');
}

View file

@ -28,6 +28,10 @@ $settingsModes = [
'title' => 'Login History',
'allow' => true,
],
'log' => [
'title' => 'Account Log',
'allow' => true,
],
];
$settingsMode = $_GET['m'] ?? null;
@ -426,6 +430,36 @@ switch ($settingsMode) {
'login_attempts_count' => $loginAttemptsCount,
]);
break;
case 'log':
$auditLogCount = audit_log_count($app->getUserId());
$auditLog = audit_log_list(
$queryOffset,
max(20, $queryTake),
$app->getUserId()
);
$tpl->vars([
'audit_logs' => $auditLog,
'audit_log_count' => $auditLogCount,
'audit_log_take' => $queryTake,
'audit_log_offset' => $queryOffset,
'log_strings' => [
'PERSONAL_EMAIL_CHANGE' => 'Changed e-mail address to %s.',
'PERSONAL_PASSWORD_CHANGE' => 'Changed account password.',
'PERSONAL_SESSION_DESTROY' => 'Ended session #%d.',
'PASSWORD_RESET' => 'Successfully used the password reset form to change password.',
'CHANGELOG_ENTRY_CREATE' => 'Created a new changelog entry #%d.',
'CHANGELOG_ENTRY_EDIT' => 'Edited changelog entry #%d.',
'CHANGELOG_TAG_ADD' => 'Added tag #%2$d to changelog entry #%1$d.',
'CHANGELOG_TAG_REMOVE' => 'Removed tag #%2$d from changelog entry #%1$d.',
'CHANGELOG_TAG_CREATE' => 'Created new changelog tag #%d.',
'CHANGELOG_TAG_EDIT' => 'Edited changelog tag #%d.',
'CHANGELOG_ACTION_CREATE' => 'Created new changelog action #%d.',
'CHANGELOG_ACTION_EDITl' => 'Edited changelog action #%d.',
],
]);
break;
}
echo $tpl->render("settings.{$settingsMode}");

View file

@ -296,6 +296,7 @@ class Application extends ApplicationBase
$this->templatingInstance->addFilter('parse_line');
$this->templatingInstance->addFilter('parse_text');
$this->templatingInstance->addFilter('asset_url');
$this->templatingInstance->addFilter('vsprintf');
$this->templatingInstance->addFunction('git_commit_hash');
$this->templatingInstance->addFunction('git_branch');

View file

@ -11,24 +11,40 @@ function audit_log(
$ipAddress = $ipAddress ?? IPAddress::remote();
for ($i = 0; $i < count($params); $i++) {
if (preg_match('#(-?[0-9]+)#', $params[$i])) {
if (preg_match('#^(-?[0-9]+)$#', $params[$i])) {
$params[$i] = (int)$params[$i];
}
}
$addLog = Database::prepare('
INSERT INTO `msz_audit_log`
(`log_action`, `user_id`, `log_params`, `log_ip`)
(`log_action`, `user_id`, `log_params`, `log_ip`, `log_country`)
VALUES
(:action, :user, :params, :log_ip)
(:action, :user, :params, :ip, :country)
');
$addLog->bindValue('action', $action);
$addLog->bindValue('user', $userId < 1 ? null : $userId);
$addLog->bindValue('params', json_encode($params));
$addLog->bindValue('log_ip', $ipAddress->getRaw());
$addLog->bindValue('ip', $ipAddress->getRaw());
$addLog->bindValue('country', $ipAddress->getCountryCode());
$addLog->execute();
}
function audit_log_count($userId = 0): int
{
$getCount = Database::prepare(sprintf('
SELECT COUNT(`log_id`)
FROM `msz_audit_log`
WHERE %s
', $userId < 1 ? '1' : '`user_id` = :user_id'));
if ($userId >= 1) {
$getCount->bindValue('user_id', $userId);
}
return $getCount->execute() ? (int)$getCount->fetchColumn() : 0;
}
function audit_log_list(int $offset, int $take, int $userId = 0): array
{
$offset = max(0, $offset);
@ -36,7 +52,7 @@ function audit_log_list(int $offset, int $take, int $userId = 0): array
$getLogs = Database::prepare(sprintf('
SELECT
l.`log_id`, l.`log_action`, l.`log_params`, l.`log_created`,
l.`log_id`, l.`log_action`, l.`log_params`, l.`log_created`, l.`log_country`,
u.`user_id`, u.`username`,
INET6_NTOA(l.`log_ip`) as `log_ip`,
COALESCE(u.`user_colour`, r.`role_colour`) as `user_colour`
@ -51,7 +67,7 @@ function audit_log_list(int $offset, int $take, int $userId = 0): array
', $userId < 1 ? '1' : 'l.`user_id` = :user_id'));
if ($userId >= 1) {
$getLogs->bindValue('user_id');
$getLogs->bindValue('user_id', $userId);
}
$getLogs->bindValue('offset', $offset);

View file

@ -0,0 +1,54 @@
{% extends '@mio/settings/master.twig' %}
{% from '@mio/macros.twig' import pagination %}
{% set alpagination = pagination(audit_log_count, audit_log_take, audit_log_offset, '?m=log', 'settings__') %}
{% block settings_content %}
<div class="settings__description">
<p>This is a log of all "important" actions that have been done using your account for your review. If you notice anything strange, please alert the staff.</p>
</div>
<div class="settings__log">
{{ alpagination }}
{% for log in audit_logs %}
<div class="settings__log__entry" id="log-{{ log.log_id }}">
<div class="settings__log__column settings__login-history__column--ip">
<div class="settings__log__column__name">
IP
</div>
<div class="settings__log__column__value">
{{ log.log_ip }}
{% if log.log_country|default('XX') != 'XX' %}
<img class="settings__log__country" src="https://static.flash.moe/flags/fff/{{ log.log_country|lower }}.png" alt="{{ log.log_country }}" title="{{ log.log_country|country_name }}">
{% endif %}
</div>
</div>
<div class="settings__log__column settings__log__column--date" title="{{ log.log_created|date('r') }}">
<div class="settings__log__column__name">
Date
</div>
<time class="settings__log__column__value" datetime="{{ log.log_created|date('c') }}">
{{ log.log_created|time_diff }}
</time>
</div>
<div class="settings__log__column settings__log__column--action">
<div class="settings__log__column__name">
Action
</div>
<div class="settings__log__column__value">
{% if log.log_action in log_strings|keys %}
{{ log_strings[log.log_action]|vsprintf(log.log_params|json_decode) }}
{% else %}
{{ log.log_action }}({{ log.log_params }})
{% endif %}
</div>
</div>
</div>
{% endfor %}
{{ alpagination }}
</div>
{% endblock %}