Rewrote audit log to be OOP.

This commit is contained in:
flash 2020-05-21 15:05:30 +00:00
parent fe58673890
commit bfe69276b8
22 changed files with 316 additions and 275 deletions

View file

@ -0,0 +1,21 @@
<?php
namespace Misuzu\DatabaseMigrations\AuditLogTableFixes;
use PDO;
function migrate_up(PDO $conn): void {
$conn->exec('
ALTER TABLE `msz_audit_log`
DROP COLUMN `log_id`,
ADD INDEX `audit_log_created_index` (`log_created`);
');
}
function migrate_down(PDO $conn): void {
$conn->exec("
ALTER TABLE `msz_audit_log`
ADD COLUMN `log_id` INT UNSIGNED NOT NULL AUTO_INCREMENT FIRST,
DROP INDEX `audit_log_created_index`,
ADD PRIMARY KEY (`log_id`);
");
}

View file

@ -65,7 +65,6 @@ class_alias(\Misuzu\Http\HttpRequestMessage::class, '\HttpRequest');
require_once 'utility.php';
require_once 'src/perms.php';
require_once 'src/audit_log.php';
require_once 'src/manage.php';
require_once 'src/url.php';
require_once 'src/Forum/perms.php';

View file

@ -2,7 +2,9 @@
namespace Misuzu;
use UnexpectedValueException;
use Misuzu\AuditLog;
use Misuzu\Net\IPAddress;
use Misuzu\Users\User;
require_once '../../misuzu.php';
@ -59,7 +61,7 @@ while($canResetPassword) {
}
if(user_password_set($userId, $passwordNew)) {
audit_log(MSZ_AUDIT_PASSWORD_RESET, $userId);
AuditLog::(AuditLog::PASSWORD_RESET, [], User::byId($userId));
} else {
throw new UnexpectedValueException('Password reset failed.');
}

View file

@ -1,6 +1,7 @@
<?php
namespace Misuzu;
use Misuzu\AuditLog;
use Misuzu\Comments\CommentsCategory;
use Misuzu\Comments\CommentsCategoryNotFoundException;
use Misuzu\Comments\CommentsPost;
@ -154,13 +155,13 @@ switch($commentMode) {
$commentInfo2->save();
if($isModAction) {
audit_log(MSZ_AUDIT_COMMENT_ENTRY_DELETE_MOD, $currentUserInfo->getId(), [
AuditLog::create(AuditLog::COMMENT_ENTRY_DELETE_MOD, [
$commentInfo2->getId(),
$commentUserId = $commentInfo2->getUserId(),
($commentUserId < 1 ? '(Deleted User)' : $commentInfo2->getUser()->getUsername()),
]);
} else {
audit_log(MSZ_AUDIT_COMMENT_ENTRY_DELETE, $currentUserInfo->getId(), [$commentInfo2->getId()]);
AuditLog::create(AuditLog::COMMENT_ENTRY_DELETE, [$commentInfo2->getId()]);
}
if($redirect) {
@ -187,7 +188,7 @@ switch($commentMode) {
$commentInfo2->setDeleted(false);
$commentInfo2->save();
audit_log(MSZ_AUDIT_COMMENT_ENTRY_RESTORE, $currentUserInfo->getId(), [
AuditLog::create(AuditLog::COMMENT_ENTRY_RESTORE, [
$commentInfo2->getId(),
$commentUserId = $commentInfo2->getUserId(),
($commentUserId < 1 ? '(Deleted User)' : $commentInfo2->getUser()->getUsername()),

View file

@ -1,6 +1,8 @@
<?php
namespace Misuzu;
use Misuzu\AuditLog;
require_once '../../misuzu.php';
$postId = !empty($_GET['p']) && is_string($_GET['p']) ? (int)$_GET['p'] : 0;
@ -137,7 +139,7 @@ switch($postMode) {
$deletePost = forum_post_delete($postInfo['post_id']);
if($deletePost) {
audit_log(MSZ_AUDIT_FORUM_POST_DELETE, $currentUserId, [$postInfo['post_id']]);
AuditLog::create(AuditLog::FORUM_POST_DELETE, [$postInfo['post_id']]);
}
if($isXHR) {
@ -191,7 +193,7 @@ switch($postMode) {
break;
}
audit_log(MSZ_AUDIT_FORUM_POST_NUKE, $currentUserId, [$postInfo['post_id']]);
AuditLog::create(AuditLog::FORUM_POST_NUKE, [$postInfo['post_id']]);
http_response_code(204);
if(!$isXHR) {
@ -233,7 +235,7 @@ switch($postMode) {
break;
}
audit_log(MSZ_AUDIT_FORUM_POST_RESTORE, $currentUserId, [$postInfo['post_id']]);
AuditLog::create(AuditLog::FORUM_POST_RESTORE, [$postInfo['post_id']]);
http_response_code(204);
if(!$isXHR) {

View file

@ -1,6 +1,8 @@
<?php
namespace Misuzu;
use Misuzu\AuditLog;
require_once '../../misuzu.php';
$postId = !empty($_GET['p']) && is_string($_GET['p']) ? (int)$_GET['p'] : 0;
@ -185,7 +187,7 @@ if(in_array($moderationMode, $validModerationModes, true)) {
$deleteTopic = forum_topic_delete($topic['topic_id']);
if($deleteTopic) {
audit_log(MSZ_AUDIT_FORUM_TOPIC_DELETE, $topicUserId, [$topic['topic_id']]);
AuditLog::create(AuditLog::FORUM_TOPIC_DELETE, [$topic['topic_id']]);
}
if($isXHR) {
@ -240,7 +242,7 @@ if(in_array($moderationMode, $validModerationModes, true)) {
break;
}
audit_log(MSZ_AUDIT_FORUM_TOPIC_RESTORE, $topicUserId, [$topic['topic_id']]);
AuditLog::create(AuditLog::FORUM_TOPIC_RESTORE, [$topic['topic_id']]);
http_response_code(204);
if(!$isXHR) {
@ -283,7 +285,7 @@ if(in_array($moderationMode, $validModerationModes, true)) {
break;
}
audit_log(MSZ_AUDIT_FORUM_TOPIC_NUKE, $topicUserId, [$topic['topic_id']]);
AuditLog::create(AuditLog::FORUM_TOPIC_NUKE, [$topic['topic_id']]);
http_response_code(204);
if(!$isXHR) {
@ -295,7 +297,7 @@ if(in_array($moderationMode, $validModerationModes, true)) {
case 'bump':
if($canBumpTopic && forum_topic_bump($topic['topic_id'])) {
audit_log(MSZ_AUDIT_FORUM_TOPIC_BUMP, $topicUserId, [$topic['topic_id']]);
AuditLog::create(AuditLog::FORUM_TOPIC_BUMP, [$topic['topic_id']]);
}
url_redirect('forum-topic', [
@ -305,7 +307,7 @@ if(in_array($moderationMode, $validModerationModes, true)) {
case 'lock':
if($canLockTopic && !$topicIsLocked && forum_topic_lock($topic['topic_id'])) {
audit_log(MSZ_AUDIT_FORUM_TOPIC_LOCK, $topicUserId, [$topic['topic_id']]);
AuditLog::create(AuditLog::FORUM_TOPIC_LOCK, [$topic['topic_id']]);
}
url_redirect('forum-topic', [
@ -315,7 +317,7 @@ if(in_array($moderationMode, $validModerationModes, true)) {
case 'unlock':
if($canLockTopic && $topicIsLocked && forum_topic_unlock($topic['topic_id'])) {
audit_log(MSZ_AUDIT_FORUM_TOPIC_UNLOCK, $topicUserId, [$topic['topic_id']]);
AuditLog::create(AuditLog::FORUM_TOPIC_UNLOCK, [$topic['topic_id']]);
}
url_redirect('forum-topic', [

View file

@ -1,6 +1,7 @@
<?php
namespace Misuzu;
use Misuzu\AuditLog;
use Misuzu\Changelog\ChangelogChange;
use Misuzu\Changelog\ChangelogChangeNotFoundException;
use Misuzu\Changelog\ChangelogTag;
@ -58,11 +59,10 @@ if($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) {
->setUser($changeUser)
->save();
audit_log(
AuditLog::create(
empty($isNew)
? MSZ_AUDIT_CHANGELOG_ENTRY_EDIT
: MSZ_AUDIT_CHANGELOG_ENTRY_CREATE,
User::getCurrent()->getId(),
? AuditLog::CHANGELOG_ENTRY_EDIT
: AuditLog::CHANGELOG_ENTRY_CREATE,
[$change->getId()]
);
}

View file

@ -1,6 +1,7 @@
<?php
namespace Misuzu;
use Misuzu\AuditLog;
use Misuzu\Changelog\ChangelogTag;
use Misuzu\Changelog\ChangelogTagNotFoundException;
use Misuzu\Users\User;
@ -33,11 +34,10 @@ if(!empty($_POST['tag']) && is_array($_POST['tag']) && CSRF::validateRequest())
->setArchived(!empty($_POST['tag']['archived']))
->save();
audit_log(
AuditLog::create(
empty($isNew)
? MSZ_AUDIT_CHANGELOG_TAG_EDIT
: MSZ_AUDIT_CHANGELOG_TAG_CREATE,
User::getCurrent()->getId(),
? AuditLog::CHANGELOG_TAG_EDIT
: AuditLog::CHANGELOG_TAG_CREATE,
[$tagInfo->getId()]
);

View file

@ -21,7 +21,7 @@ $statistics = DB::query('
AND `user_deleted` IS NULL
) AS `stat_users_active`,
(
SELECT COUNT(`log_id`)
SELECT COUNT(*)
FROM `msz_audit_log`
) AS `stat_audit_logs`,
(

View file

@ -1,6 +1,9 @@
<?php
namespace Misuzu;
use Misuzu\AuditLog;
use Misuzu\Pagination;
require_once '../../../misuzu.php';
if(!perms_check_user(MSZ_PERMS_GENERAL, user_session_current('user_id'), MSZ_PERM_GENERAL_VIEW_LOGS)) {
@ -8,20 +11,16 @@ if(!perms_check_user(MSZ_PERMS_GENERAL, user_session_current('user_id'), MSZ_PER
return;
}
$logsPagination = new Pagination(audit_log_count(), 50);
$pagination = new Pagination(AuditLog::countAll(), 50);
if(!$logsPagination->hasValidOffset()) {
if(!$pagination->hasValidOffset()) {
echo render_error(404);
return;
}
$logs = audit_log_list(
$logsPagination->getOffset(),
$logsPagination->getRange()
);
$logs = AuditLog::all($pagination);
Template::render('manage.general.logs', [
'global_logs' => $logs,
'global_logs_pagination' => $logsPagination,
'global_logs_strings' => MSZ_AUDIT_LOG_STRINGS,
'global_logs_pagination' => $pagination,
]);

View file

@ -1,6 +1,7 @@
<?php
namespace Misuzu;
use Misuzu\AuditLog;
use Misuzu\News\NewsCategory;
use Misuzu\News\NewsCategoryNotFoundException;
@ -33,11 +34,10 @@ if(!empty($_POST['category']) && CSRF::validateRequest()) {
->setHidden(!empty($_POST['category']['hidden']))
->save();
audit_log(
AuditLog::create(
empty($isNew)
? MSZ_AUDIT_NEWS_CATEGORY_EDIT
: MSZ_AUDIT_NEWS_CATEGORY_CREATE,
user_session_current('user_id'),
? AuditLog::NEWS_CATEGORY_EDIT
: AuditLog::NEWS_CATEGORY_CREATE,
[$categoryInfo->getId()]
);

View file

@ -1,6 +1,7 @@
<?php
namespace Misuzu;
use Misuzu\AuditLog;
use Misuzu\News\NewsCategory;
use Misuzu\News\NewsPost;
use Misuzu\News\NewsPostNotFoundException;
@ -41,11 +42,10 @@ if(!empty($_POST['post']) && CSRF::validateRequest()) {
$postInfo->save();
audit_log(
AuditLog::create(
empty($isNew)
? MSZ_AUDIT_NEWS_POST_EDIT
: MSZ_AUDIT_NEWS_POST_CREATE,
$currentUserId,
? AuditLog::NEWS_POST_EDIT
: AuditLog::NEWS_POST_CREATE,
[$postInfo->getId()]
);

View file

@ -1,6 +1,7 @@
<?php
namespace Misuzu;
use Misuzu\AuditLog;
use Misuzu\Users\User;
use chillerlan\QRCode\QRCode;
use chillerlan\QRCode\QROptions;
@ -99,7 +100,7 @@ if($isVerifiedRequest && !empty($_POST['current_password'])) {
}
} else {
user_email_set($currentUserId, $_POST['email']['new']);
audit_log(MSZ_AUDIT_PERSONAL_EMAIL_CHANGE, $currentUserId, [
AuditLog::create(AuditLog::PERSONAL_EMAIL_CHANGE, [
$_POST['email']['new'],
]);
}
@ -117,7 +118,7 @@ if($isVerifiedRequest && !empty($_POST['current_password'])) {
$errors[] = 'The given passwords was too weak.';
} else {
user_password_set($currentUserId, $_POST['password']['new']);
audit_log(MSZ_AUDIT_PERSONAL_PASSWORD_CHANGE, $currentUserId);
AuditLog::create(AuditLog::PERSONAL_PASSWORD_CHANGE);
}
}
}

View file

@ -2,6 +2,7 @@
namespace Misuzu;
use ZipArchive;
use Misuzu\AuditLog;
use Misuzu\Users\User;
require_once '../../misuzu.php';
@ -34,7 +35,7 @@ if(isset($_POST['action']) && is_string($_POST['action'])) {
&& $currentUser->checkPassword($_POST['password'] ?? '')) {
switch($_POST['action']) {
case 'data':
audit_log(MSZ_AUDIT_PERSONAL_DATA_DOWNLOAD, $currentUserId);
AuditLog::create(AuditLog::PERSONAL_DATA_DOWNLOAD);
$timeStamp = floor(time() / 3600) * 3600;
$fileName = sprintf('msz-user-data-%d-%d.zip', $currentUserId, $timeStamp);

View file

@ -1,6 +1,10 @@
<?php
namespace Misuzu;
use Misuzu\AuditLog;
use Misuzu\Pagination;
use Misuzu\Users\User;
require_once '../../misuzu.php';
if(!user_session_active()) {
@ -8,26 +12,19 @@ if(!user_session_active()) {
return;
}
$currentUserId = user_session_current('user_id');
$loginHistoryPagination = new Pagination(user_login_attempts_count($currentUserId), 15);
$accountLogPagination = new Pagination(audit_log_count($currentUserId), 15);
$currentUser = User::getCurrent();
$loginHistoryPagination = new Pagination(user_login_attempts_count($currentUser->getId()), 15, 'hp');
$accountLogPagination = new Pagination(AuditLog::countAll($currentUser), 15, 'ap');
$loginHistoryList = user_login_attempts_list(
$loginHistoryPagination->getOffset(),
$loginHistoryPagination->getRange(),
$currentUserId
);
$accountLogList = audit_log_list(
$accountLogPagination->getOffset(),
$accountLogPagination->getRange(),
$currentUserId
$currentUser->getId()
);
Template::render('settings.logs', [
'login_history_list' => $loginHistoryList,
'login_history_pagination' => $loginHistoryPagination,
'account_log_list' => $accountLogList,
'account_log_list' => AuditLog::all($accountLogPagination, $currentUser),
'account_log_pagination' => $accountLogPagination,
'account_log_strings' => MSZ_AUDIT_LOG_STRINGS,
]);

View file

@ -1,6 +1,8 @@
<?php
namespace Misuzu;
use Misuzu\AuditLog;
require_once '../../misuzu.php';
if(!user_session_active()) {
@ -28,14 +30,14 @@ if(!empty($_POST['session']) && CSRF::validateRequest()) {
}
user_session_delete($session['session_id']);
audit_log(MSZ_AUDIT_PERSONAL_SESSION_DESTROY, $currentUserId, [
AuditLog::create(AuditLog::PERSONAL_SESSION_DESTROY, [
$session['session_id'],
]);
}
} elseif($_POST['session'] === 'all') {
$currentSessionKilled = true;
user_session_purge_all($currentUserId);
audit_log(MSZ_AUDIT_PERSONAL_SESSION_DESTROY_ALL, $currentUserId);
AuditLog::create(AuditLog::PERSONAL_SESSION_DESTROY_ALL);
}
if($currentSessionKilled) {

199
src/AuditLog.php Normal file
View file

@ -0,0 +1,199 @@
<?php
namespace Misuzu;
use Misuzu\DB;
use Misuzu\Pagination;
use Misuzu\Net\IPAddress;
use Misuzu\Users\User;
use Misuzu\Users\UserNotFoundException;
class AuditLog {
public const PERSONAL_EMAIL_CHANGE = 'PERSONAL_EMAIL_CHANGE';
public const PERSONAL_PASSWORD_CHANGE = 'PERSONAL_PASSWORD_CHANGE';
public const PERSONAL_SESSION_DESTROY = 'PERSONAL_SESSION_DESTROY';
public const PERSONAL_SESSION_DESTROY_ALL = 'PERSONAL_SESSION_DESTROY_ALL';
public const PERSONAL_DATA_DOWNLOAD = 'PERSONAL_DATA_DOWNLOAD';
public const PASSWORD_RESET = 'PASSWORD_RESET';
public const CHANGELOG_ENTRY_CREATE = 'CHANGELOG_ENTRY_CREATE';
public const CHANGELOG_ENTRY_EDIT = 'CHANGELOG_ENTRY_EDIT';
public const CHANGELOG_TAG_ADD = 'CHANGELOG_TAG_ADD';
public const CHANGELOG_TAG_REMOVE = 'CHANGELOG_TAG_REMOVE';
public const CHANGELOG_TAG_CREATE = 'CHANGELOG_TAG_CREATE';
public const CHANGELOG_TAG_EDIT = 'CHANGELOG_TAG_EDIT';
public const CHANGELOG_ACTION_CREATE = 'CHANGELOG_ACTION_CREATE';
public const CHANGELOG_ACTION_EDIT = 'CHANGELOG_ACTION_EDIT';
public const COMMENT_ENTRY_DELETE = 'COMMENT_ENTRY_DELETE';
public const COMMENT_ENTRY_DELETE_MOD = 'COMMENT_ENTRY_DELETE_MOD';
public const COMMENT_ENTRY_RESTORE = 'COMMENT_ENTRY_RESTORE';
public const NEWS_POST_CREATE = 'NEWS_POST_CREATE';
public const NEWS_POST_EDIT = 'NEWS_POST_EDIT';
public const NEWS_CATEGORY_CREATE = 'NEWS_CATEGORY_CREATE';
public const NEWS_CATEGORY_EDIT = 'NEWS_CATEGORY_EDIT';
public const FORUM_TOPIC_DELETE = 'FORUM_TOPIC_DELETE';
public const FORUM_TOPIC_RESTORE = 'FORUM_TOPIC_RESTORE';
public const FORUM_TOPIC_NUKE = 'FORUM_TOPIC_NUKE';
public const FORUM_TOPIC_BUMP = 'FORUM_TOPIC_BUMP';
public const FORUM_TOPIC_LOCK = 'FORUM_TOPIC_LOCK';
public const FORUM_TOPIC_UNLOCK = 'FORUM_TOPIC_UNLOCK';
public const FORUM_POST_EDIT = 'FORUM_POST_EDIT';
public const FORUM_POST_DELETE = 'FORUM_POST_DELETE';
public const FORUM_POST_RESTORE = 'FORUM_POST_RESTORE';
public const FORUM_POST_NUKE = 'FORUM_POST_NUKE';
public const FORMATS = [
self::PERSONAL_EMAIL_CHANGE => 'Changed e-mail address to %s.',
self::PERSONAL_PASSWORD_CHANGE => 'Changed account password.',
self::PERSONAL_SESSION_DESTROY => 'Ended session #%d.',
self::PERSONAL_SESSION_DESTROY_ALL => 'Ended all personal sessions.',
self::PERSONAL_DATA_DOWNLOAD => 'Downloaded archive of account data.',
self::PASSWORD_RESET => 'Successfully used the password reset form to change password.',
self::CHANGELOG_ENTRY_CREATE => 'Created a new changelog entry #%d.',
self::CHANGELOG_ENTRY_EDIT => 'Edited changelog entry #%d.',
self::CHANGELOG_TAG_ADD => 'Added tag #%2$d to changelog entry #%1$d.',
self::CHANGELOG_TAG_REMOVE => 'Removed tag #%2$d from changelog entry #%1$d.',
self::CHANGELOG_TAG_CREATE => 'Created new changelog tag #%d.',
self::CHANGELOG_TAG_EDIT => 'Edited changelog tag #%d.',
self::CHANGELOG_ACTION_CREATE => 'Created new changelog action #%d.',
self::CHANGELOG_ACTION_EDIT => 'Edited changelog action #%d.',
self::COMMENT_ENTRY_DELETE => 'Deleted comment #%d.',
self::COMMENT_ENTRY_DELETE_MOD => 'Deleted comment #%d by user #%d %s.',
self::COMMENT_ENTRY_RESTORE => 'Restored comment #%d by user #%d %s.',
self::NEWS_POST_CREATE => 'Created news post #%d.',
self::NEWS_POST_EDIT => 'Edited news post #%d.',
self::NEWS_CATEGORY_CREATE => 'Created news category #%d.',
self::NEWS_CATEGORY_EDIT => 'Edited news category #%d.',
self::FORUM_POST_EDIT => 'Edited forum post #%d.',
self::FORUM_POST_DELETE => 'Deleted forum post #%d.',
self::FORUM_POST_RESTORE => 'Restored forum post #%d.',
self::FORUM_POST_NUKE => 'Nuked forum post #%d.',
self::FORUM_TOPIC_DELETE => 'Deleted forum topic #%d.',
self::FORUM_TOPIC_RESTORE => 'Restored forum topic #%d.',
self::FORUM_TOPIC_NUKE => 'Nuked forum topic #%d.',
self::FORUM_TOPIC_BUMP => 'Manually bumped forum topic #%d.',
self::FORUM_TOPIC_LOCK => 'Locked forum topic #%d.',
self::FORUM_TOPIC_UNLOCK => 'Unlocked forum topic #%d.',
];
// Database fields
private $user_id = null;
private $log_action = '';
private $log_params = [];
private $log_created = null;
private $log_ip = '::1';
private $log_country = 'XX';
private $user = null;
private $userLookedUp = false;
public const TABLE = 'audit_log';
private const QUERY_SELECT = 'SELECT %1$s FROM `' . DB::PREFIX . self::TABLE . '` AS '. self::TABLE;
private const SELECT = '%1$s.`user_id`, %1$s.`log_action`, %1$s.`log_params`, %1$s.`log_country`'
. ', INET6_NTOA(%1$s.`log_ip`) AS `log_ip`'
. ', UNIX_TIMESTAMP(%1$s.`log_created`) AS `log_created`';
public function getUserId(): int {
return $this->user_id < 1 ? -1 : $this->user_id;
}
public function getUser(): ?User {
if(!$this->userLookedUp && ($userId = $this->getUserId()) > 0) {
$this->userLookedUp = true;
try {
$this->user = User::byId($userId);
} catch(UserNotFoundException $ex) {}
}
return $this->user;
}
public function getAction(): string {
return $this->log_action;
}
public function getParams(): array {
if(is_string($this->log_params))
$this->log_params = json_decode($this->log_params) ?? [];
return $this->log_params;
}
public function getCreatedTime(): int {
return $this->log_created === null ? -1 : $this->log_created;
}
public function getRemoteAddress(): string {
return $this->log_ip;
}
public function getCountry(): string {
return $this->log_country;
}
public function getCountryName(): string {
return get_country_name($this->getCountry());
}
public function getString(): string {
if(!array_key_exists($this->getAction(), self::FORMATS))
return sprintf('%s(%s)', $this->getAction(), json_encode($this->getParams()));
return vsprintf(self::FORMATS[$this->getAction()], $this->getParams());
}
public static function create(string $action, array $params = [], ?User $user = null, ?string $remoteAddr = null): void {
$user = $user ?? User::getCurrent();
$remoteAddr = $ipAddress ?? IPAddress::remote();
$createLog = DB::prepare(
'INSERT INTO `' . DB::PREFIX . self::TABLE . '` (`log_action`, `user_id`, `log_params`, `log_ip`, `log_country`)'
. ' VALUES (:action, :user, :params, INET6_ATON(:ip), :country)'
) ->bind('action', $action)
->bind('user', $user === null ? null : $user->getId()) // this null situation should never ever happen but better safe than sorry !
->bind('params', json_encode($params))
->bind('ip', $remoteAddr)
->bind('country', IPAddress::country($remoteAddr))
->execute();
}
private static function countQueryBase(): string {
return sprintf(self::QUERY_SELECT, sprintf('COUNT(*)', self::TABLE));
}
public static function countAll(?User $user = null): int {
$getCount = DB::prepare(
self::countQueryBase()
. ($user === null ? '' : ' WHERE `user_id` = :user')
);
if($user !== null)
$getCount->bind('user', $user->getId());
return (int)$getCount->fetchColumn();
}
private static function byQueryBase(): string {
return sprintf(self::QUERY_SELECT, sprintf(self::SELECT, self::TABLE));
}
public static function all(?Pagination $pagination = null, ?User $user = null): array {
$logsQuery = self::byQueryBase()
. ($user === null ? '' : ' WHERE `user_id` = :user')
. ' ORDER BY `log_created` DESC';
if($pagination !== null)
$logsQuery .= ' LIMIT :range OFFSET :offset';
$getLogs = DB::prepare($logsQuery);
if($user !== null)
$getLogs->bind('user', $user->getId());
if($pagination !== null)
$getLogs->bind('range', $pagination->getRange())
->bind('offset', $pagination->getOffset());
return $getLogs->fetchObjects(self::class);
}
}

View file

@ -1,146 +0,0 @@
<?php
audit_log_define('PERSONAL_EMAIL_CHANGE');
audit_log_define('PERSONAL_PASSWORD_CHANGE');
audit_log_define('PERSONAL_SESSION_DESTROY');
audit_log_define('PERSONAL_SESSION_DESTROY_ALL');
audit_log_define('PERSONAL_DATA_DOWNLOAD');
audit_log_define('PASSWORD_RESET');
audit_log_define('CHANGELOG_ENTRY_CREATE');
audit_log_define('CHANGELOG_ENTRY_EDIT');
audit_log_define('CHANGELOG_TAG_ADD');
audit_log_define('CHANGELOG_TAG_REMOVE');
audit_log_define('CHANGELOG_TAG_CREATE');
audit_log_define('CHANGELOG_TAG_EDIT');
audit_log_define('CHANGELOG_ACTION_CREATE');
audit_log_define('CHANGELOG_ACTION_EDIT');
audit_log_define('COMMENT_ENTRY_DELETE');
audit_log_define('COMMENT_ENTRY_DELETE_MOD');
audit_log_define('COMMENT_ENTRY_RESTORE');
audit_log_define('NEWS_POST_CREATE');
audit_log_define('NEWS_POST_EDIT');
audit_log_define('NEWS_CATEGORY_CREATE');
audit_log_define('NEWS_CATEGORY_EDIT');
audit_log_define('FORUM_POST_EDIT');
audit_log_define('FORUM_POST_DELETE');
audit_log_define('FORUM_POST_RESTORE');
audit_log_define('FORUM_POST_NUKE');
audit_log_define('FORUM_TOPIC_DELETE');
audit_log_define('FORUM_TOPIC_RESTORE');
audit_log_define('FORUM_TOPIC_NUKE');
audit_log_define('FORUM_TOPIC_BUMP');
audit_log_define('FORUM_TOPIC_LOCK');
audit_log_define('FORUM_TOPIC_UNLOCK');
// replace this with a localisation system
define('MSZ_AUDIT_LOG_STRINGS', [
MSZ_AUDIT_PERSONAL_EMAIL_CHANGE => 'Changed e-mail address to %s.',
MSZ_AUDIT_PERSONAL_PASSWORD_CHANGE => 'Changed account password.',
MSZ_AUDIT_PERSONAL_SESSION_DESTROY => 'Ended session #%d.',
MSZ_AUDIT_PERSONAL_SESSION_DESTROY_ALL => 'Ended all personal sessions.',
MSZ_AUDIT_PASSWORD_RESET => 'Successfully used the password reset form to change password.',
MSZ_AUDIT_CHANGELOG_ENTRY_CREATE => 'Created a new changelog entry #%d.',
MSZ_AUDIT_CHANGELOG_ENTRY_EDIT => 'Edited changelog entry #%d.',
MSZ_AUDIT_CHANGELOG_TAG_ADD => 'Added tag #%2$d to changelog entry #%1$d.',
MSZ_AUDIT_CHANGELOG_TAG_REMOVE => 'Removed tag #%2$d from changelog entry #%1$d.',
MSZ_AUDIT_CHANGELOG_TAG_CREATE => 'Created new changelog tag #%d.',
MSZ_AUDIT_CHANGELOG_TAG_EDIT => 'Edited changelog tag #%d.',
MSZ_AUDIT_CHANGELOG_ACTION_CREATE => 'Created new changelog action #%d.',
MSZ_AUDIT_CHANGELOG_ACTION_EDIT => 'Edited changelog action #%d.',
MSZ_AUDIT_COMMENT_ENTRY_DELETE => 'Deleted comment #%d.',
MSZ_AUDIT_COMMENT_ENTRY_DELETE_MOD => 'Deleted comment #%d by user #%d %s.',
MSZ_AUDIT_COMMENT_ENTRY_RESTORE => 'Restored comment #%d by user #%d %s.',
MSZ_AUDIT_NEWS_POST_CREATE => 'Created news post #%d.',
MSZ_AUDIT_NEWS_POST_EDIT => 'Edited news post #%d.',
MSZ_AUDIT_NEWS_CATEGORY_CREATE => 'Created news category #%d.',
MSZ_AUDIT_NEWS_CATEGORY_EDIT => 'Edited news category #%d.',
MSZ_AUDIT_FORUM_POST_EDIT => 'Edited forum post #%d.',
MSZ_AUDIT_FORUM_POST_DELETE => 'Deleted forum post #%d.',
MSZ_AUDIT_FORUM_POST_RESTORE => 'Restored forum post #%d.',
MSZ_AUDIT_FORUM_POST_NUKE => 'Nuked forum post #%d.',
MSZ_AUDIT_FORUM_TOPIC_DELETE => 'Deleted forum topic #%d.',
MSZ_AUDIT_FORUM_TOPIC_RESTORE => 'Restored forum topic #%d.',
MSZ_AUDIT_FORUM_TOPIC_NUKE => 'Nuked forum topic #%d.',
MSZ_AUDIT_FORUM_TOPIC_BUMP => 'Manually bumped forum topic #%d.',
MSZ_AUDIT_FORUM_TOPIC_LOCK => 'Locked forum topic #%d.',
MSZ_AUDIT_FORUM_TOPIC_UNLOCK => 'Unlocked forum topic #%d.',
MSZ_AUDIT_PERSONAL_DATA_DOWNLOAD => 'Downloaded archive of account data.',
]);
function audit_log_define(string $name): void {
define("MSZ_AUDIT_{$name}", $name);
}
function audit_log(
string $action,
int $userId = 0,
array $params = [],
?string $ipAddress = null
): void {
$ipAddress = $ipAddress ?? \Misuzu\Net\IPAddress::remote();
for($i = 0; $i < count($params); $i++) {
if(preg_match('#^(-?[0-9]+)$#', $params[$i])) {
$params[$i] = (int)$params[$i];
}
}
$addLog = \Misuzu\DB::prepare('
INSERT INTO `msz_audit_log`
(`log_action`, `user_id`, `log_params`, `log_ip`, `log_country`)
VALUES
(:action, :user, :params, INET6_ATON(:ip), :country)
');
$addLog->bind('action', $action);
$addLog->bind('user', $userId < 1 ? null : $userId);
$addLog->bind('params', json_encode($params));
$addLog->bind('ip', $ipAddress);
$addLog->bind('country', \Misuzu\Net\IPAddress::country($ipAddress));
$addLog->execute();
}
function audit_log_count($userId = 0): int {
$getCount = \Misuzu\DB::prepare(sprintf('
SELECT COUNT(`log_id`)
FROM `msz_audit_log`
%s
', $userId < 1 ? '' : 'WHERE `user_id` = :user_id'));
if($userId >= 1) {
$getCount->bind('user_id', $userId);
}
return (int)$getCount->fetchColumn();
}
function audit_log_list(int $offset, int $take, int $userId = 0): array {
$offset = max(0, $offset);
$take = max(1, $take);
$isGlobal = $userId < 1;
$getLogs = \Misuzu\DB::prepare(sprintf(
'
SELECT
l.`log_id`, l.`log_action`, l.`log_params`, l.`log_created`, l.`log_country`,
INET6_NTOA(l.`log_ip`) as `log_ip`
%2$s
FROM `msz_audit_log` as l
%1$s
ORDER BY l.`log_id` DESC
LIMIT :offset, :take
',
$isGlobal
? 'LEFT JOIN `msz_users` as u ON u.`user_id` = l.`user_id` LEFT JOIN `msz_roles` as r ON r.`role_id` = u.`display_role`'
: 'WHERE l.`user_id` = :user_id',
$isGlobal
? ', u.`user_id`, u.`username`, COALESCE(u.`user_colour`, r.`role_colour`) as `user_colour`'
: ''
));
if(!$isGlobal) {
$getLogs->bind('user_id', $userId);
}
$getLogs->bind('offset', $offset);
$getLogs->bind('take', $take);
return $getLogs->fetchAll();
}

View file

@ -1,8 +1,7 @@
<?php
function manage_get_menu(int $userId): array {
if(!perms_check_user(MSZ_PERMS_GENERAL, $userId, MSZ_PERM_GENERAL_CAN_MANAGE)) {
if(!perms_check_user(MSZ_PERMS_GENERAL, $userId, MSZ_PERM_GENERAL_CAN_MANAGE))
return [];
}
$menu = [
'General' => [
@ -10,57 +9,36 @@ function manage_get_menu(int $userId): array {
],
];
if(perms_check_user(MSZ_PERMS_GENERAL, $userId, MSZ_PERM_GENERAL_VIEW_LOGS)) {
if(perms_check_user(MSZ_PERMS_GENERAL, $userId, MSZ_PERM_GENERAL_VIEW_LOGS))
$menu['General']['Logs'] = url('manage-general-logs');
}
if(perms_check_user(MSZ_PERMS_GENERAL, $userId, MSZ_PERM_GENERAL_MANAGE_EMOTES)) {
if(perms_check_user(MSZ_PERMS_GENERAL, $userId, MSZ_PERM_GENERAL_MANAGE_EMOTES))
$menu['General']['Emoticons'] = url('manage-general-emoticons');
}
if(perms_check_user(MSZ_PERMS_GENERAL, $userId, MSZ_PERM_GENERAL_MANAGE_CONFIG)) {
if(perms_check_user(MSZ_PERMS_GENERAL, $userId, MSZ_PERM_GENERAL_MANAGE_CONFIG))
$menu['General']['Settings'] = url('manage-general-settings');
}
if(perms_check_user(MSZ_PERMS_GENERAL, $userId, MSZ_PERM_GENERAL_MANAGE_BLACKLIST)) {
if(perms_check_user(MSZ_PERMS_GENERAL, $userId, MSZ_PERM_GENERAL_MANAGE_BLACKLIST))
$menu['General']['IP Blacklist'] = url('manage-general-blacklist');
}
if(perms_check_user(MSZ_PERMS_USER, $userId, MSZ_PERM_USER_MANAGE_USERS)) {
if(perms_check_user(MSZ_PERMS_USER, $userId, MSZ_PERM_USER_MANAGE_USERS))
$menu['Users & Roles']['Users'] = url('manage-users');
}
if(perms_check_user(MSZ_PERMS_USER, $userId, MSZ_PERM_USER_MANAGE_ROLES)) {
if(perms_check_user(MSZ_PERMS_USER, $userId, MSZ_PERM_USER_MANAGE_ROLES))
$menu['Users & Roles']['Roles'] = url('manage-roles');
}
if(perms_check_user(MSZ_PERMS_USER, $userId, MSZ_PERM_USER_MANAGE_REPORTS)) {
if(perms_check_user(MSZ_PERMS_USER, $userId, MSZ_PERM_USER_MANAGE_REPORTS))
$menu['Users & Roles']['Reports'] = url('manage-users-reports');
}
if(perms_check_user(MSZ_PERMS_USER, $userId, MSZ_PERM_USER_MANAGE_WARNINGS)) {
if(perms_check_user(MSZ_PERMS_USER, $userId, MSZ_PERM_USER_MANAGE_WARNINGS))
$menu['Users & Roles']['Warnings'] = url('manage-users-warnings');
}
if(perms_check_user(MSZ_PERMS_NEWS, $userId, MSZ_PERM_NEWS_MANAGE_POSTS)) {
if(perms_check_user(MSZ_PERMS_NEWS, $userId, MSZ_PERM_NEWS_MANAGE_POSTS))
$menu['News']['Posts'] = url('manage-news-posts');
}
if(perms_check_user(MSZ_PERMS_NEWS, $userId, MSZ_PERM_NEWS_MANAGE_CATEGORIES)) {
if(perms_check_user(MSZ_PERMS_NEWS, $userId, MSZ_PERM_NEWS_MANAGE_CATEGORIES))
$menu['News']['Categories'] = url('manage-news-categories');
}
if(perms_check_user(MSZ_PERMS_FORUM, $userId, MSZ_PERM_FORUM_MANAGE_FORUMS)) {
if(perms_check_user(MSZ_PERMS_FORUM, $userId, MSZ_PERM_FORUM_MANAGE_FORUMS))
$menu['Forum']['Categories'] = url('manage-forum-categories');
}
if(perms_check_user(MSZ_PERMS_CHANGELOG, $userId, MSZ_PERM_CHANGELOG_MANAGE_CHANGES)) {
if(perms_check_user(MSZ_PERMS_CHANGELOG, $userId, MSZ_PERM_CHANGELOG_MANAGE_CHANGES))
$menu['Changelog']['Changes'] = url('manage-changelog-changes');
}
if(perms_check_user(MSZ_PERMS_CHANGELOG, $userId, MSZ_PERM_CHANGELOG_MANAGE_TAGS)) {
if(perms_check_user(MSZ_PERMS_CHANGELOG, $userId, MSZ_PERM_CHANGELOG_MANAGE_TAGS))
$menu['Changelog']['Tags'] = url('manage-changelog-tags');
}
return $menu;
}
@ -70,14 +48,10 @@ define('MSZ_MANAGE_PERM_NO', 'no');
define('MSZ_MANAGE_PERM_NEVER', 'never');
function manage_perms_value(int $perm, int $allow, int $deny): string {
if(perms_check($deny, $perm)) {
if(perms_check($deny, $perm))
return MSZ_MANAGE_PERM_NEVER;
}
if(perms_check($allow, $perm)) {
if(perms_check($allow, $perm))
return MSZ_MANAGE_PERM_YES;
}
return MSZ_MANAGE_PERM_NO;
}
@ -85,18 +59,15 @@ function manage_perms_apply(array $list, array $post, ?array $raw = null): ?arra
$perms = $raw !== null ? $raw : perms_create();
foreach($list as $section) {
if(empty($post[$section['section']])
|| !is_array($post[$section['section']])) {
if(empty($post[$section['section']]) || !is_array($post[$section['section']]))
continue;
}
$allowKey = perms_get_key($section['section'], MSZ_PERMS_ALLOW);
$denyKey = perms_get_key($section['section'], MSZ_PERMS_DENY);
foreach($section['perms'] as $perm) {
if(empty($post[$section['section']][$perm['section']]['value'])) {
if(empty($post[$section['section']][$perm['section']]['value']))
continue;
}
switch($post[$section['section']][$perm['section']]['value']) {
case MSZ_MANAGE_PERM_YES:
@ -119,16 +90,10 @@ function manage_perms_apply(array $list, array $post, ?array $raw = null): ?arra
}
$returnNothing = 0;
foreach($perms as $perm) {
foreach($perms as $perm)
$returnNothing |= $perm;
}
if($returnNothing === 0) {
return null;
}
return $perms;
return $returnNothing === 0 ? null : $perms;
}
function manage_perms_calculate(array $rawPerms, array $perms): array {

View file

@ -13,7 +13,7 @@
</div>
{% for log in global_logs %}
{{ user_account_log(log, global_logs_strings) }}
{{ user_account_log(log, true) }}
{% endfor %}
<div class="settings__account-logs__pagination">

View file

@ -52,7 +52,7 @@
</div>
{% for log in account_log_list %}
{{ user_account_log(log, account_log_strings) }}
{{ user_account_log(log) }}
{% endfor %}
<div class="settings__account-logs__pagination">

View file

@ -264,27 +264,23 @@
</div>
{% endmacro %}
{% macro user_account_log(data, strings) %}
{% macro user_account_log(data, is_manage) %}
{% from 'macros.twig' import avatar %}
<div class="settings__account-log" id="account-log-{{ data.log_id }}">
{% if data.user_id is defined %}
<a href="{{ url('user-profile', {'user': data.user_id}) }}" class="settings__account-log__user" style="{{ data.user_colour|html_colour }}">
<div class="settings__account-log__user__avatar">{{ avatar(data.user_id, 20, data.username) }}</div>
<div class="settings__account-log__user__name">{{ data.username }}</div>
<div class="settings__account-log">
{% if is_manage %}
<a href="{{ url('user-profile', {'user': data.user.id}) }}" class="settings__account-log__user" style="--user-colour: {{ data.user.colour }}">
<div class="settings__account-log__user__avatar">{{ avatar(data.user.id, 20, data.user.username) }}</div>
<div class="settings__account-log__user__name">{{ data.user.username }}</div>
</a>
{% endif %}
<div class="settings__account-log__container">
<div class="settings__account-log__important">
<div class="flag flag--{{ data.log_country|lower }} settings__login-attempt__flag" title="{{ data.log_country|country_name }}">{{ data.log_country }}</div>
<div class="flag flag--{{ data.country|lower }} settings__login-attempt__flag" title="{{ data.countryName }}">{{ data.country }}</div>
<div class="settings__login-attempt__description">
{% if data.log_action in strings|keys %}
{{ strings[data.log_action]|log_format(data.log_params) }}
{% else %}
{{ data.log_action }}({{ data.log_params }})
{% endif %}
{{ data.string }}
</div>
</div>
@ -294,16 +290,16 @@
IP Address
</div>
<div class="settings__account-log__detail__value">
{{ data.log_ip }}
{{ data.remoteAddress }}
</div>
</div>
<div class="settings__account-log__detail" title="{{ data.log_created|date('r') }}">
<div class="settings__account-log__detail" title="{{ data.createdTime|date('r') }}">
<div class="settings__account-log__detail__title">
Date
</div>
<time class="settings__account-log__detail__value" datetime="{{ data.log_created|date('c') }}">
{{ data.log_created|time_diff }}
<time class="settings__account-log__detail__value" datetime="{{ data.createdTime|date('c') }}">
{{ data.createdTime|time_diff }}
</time>
</div>
</div>