misuzu/src/AuditLog/AuditLog.php
flash 383e2ed0e0 Rewrote the user information class.
This one took multiple days and it pretty invasive into the core of Misuzu so issue might (will) arise, there's also some features that have gone temporarily missing in the mean time and some inefficiencies introduced that will be fixed again at a later time.
The old class isn't gone entirely because I still have to figure out what I'm gonna do about validation, but for the most part this knocks out one of the "layers of backwards compatibility", as I've been referring to it, and is moving us closer to a future where Flashii actually gets real updates.
If you run into anything that's broken and you're inhibited from reporting it through the forum, do it through chat or mail me at flashii-issues@flash.moe.
2023-08-02 22:12:47 +00:00

149 lines
4.6 KiB
PHP

<?php
namespace Misuzu\AuditLog;
use InvalidArgumentException;
use Index\Data\DbStatementCache;
use Index\Data\IDbConnection;
use Index\Data\IDbResult;
use Index\Net\IPAddress;
use Misuzu\Pagination;
use Misuzu\Users\UserInfo;
class AuditLog {
private DbStatementCache $cache;
public function __construct(IDbConnection $dbConn) {
$this->cache = new DbStatementCache($dbConn);
}
public function countLogs(
UserInfo|string|null $userInfo = null,
IPAddress|string|null $remoteAddr = null
): int {
if($userInfo instanceof UserInfo)
$userInfo = $userInfo->getId();
if($remoteAddr instanceof IPAddress)
$remoteAddr = (string)$remoteAddr;
$hasUserInfo = $userInfo !== null;
$hasRemoteAddr = $remoteAddr !== null;
$args = 0;
$query = 'SELECT COUNT(*) FROM msz_audit_log';
if($hasUserInfo) {
++$args;
$query .= ' WHERE user_id = ?';
}
if($hasRemoteAddr) {
$query .= (++$args > 1 ? ' AND' : ' WHERE');
$query .= ' log_ip = INET6_ATON(?)';
}
$stmt = $this->cache->get($query);
$args = 0;
if($hasUserInfo)
$stmt->addParameter(++$args, $userInfo);
if($hasRemoteAddr)
$stmt->addParameter(++$args, $remoteAddr);
$stmt->execute();
$result = $stmt->getResult();
$count = 0;
if($result->next())
$count = $result->getInteger(0);
return $count;
}
public function getLogs(
UserInfo|string|null $userInfo = null,
IPAddress|string|null $remoteAddr = null,
?Pagination $pagination = null
): array {
if($userInfo instanceof UserInfo)
$userInfo = $userInfo->getId();
if($remoteAddr instanceof IPAddress)
$remoteAddr = (string)$remoteAddr;
$hasUserInfo = $userInfo !== null;
$hasRemoteAddr = $remoteAddr !== null;
$hasPagination = $pagination !== null;
$args = 0;
$query = 'SELECT user_id, log_action, log_params, UNIX_TIMESTAMP(log_created), INET6_NTOA(log_ip), log_country FROM msz_audit_log';
if($hasUserInfo) {
++$args;
$query .= ' WHERE user_id = ?';
}
if($hasRemoteAddr) {
$query .= (++$args > 1 ? ' AND' : ' WHERE');
$query .= ' log_ip = INET6_ATON(?)';
}
$query .= ' ORDER BY log_created DESC';
if($hasPagination)
$query .= ' LIMIT ? OFFSET ?';
$stmt = $this->cache->get($query);
$args = 0;
if($hasUserInfo)
$stmt->addParameter(++$args, $userInfo);
if($hasRemoteAddr)
$stmt->addParameter(++$args, $remoteAddr);
if($hasPagination) {
$stmt->addParameter(++$args, $pagination->getRange());
$stmt->addParameter(++$args, $pagination->getOffset());
}
$stmt->execute();
$result = $stmt->getResult();
$logs = [];
while($result->next())
$logs[] = new AuditLogInfo($result);
return $logs;
}
public function createLog(
UserInfo|string|null $userInfo,
string $action,
array $params = [],
IPAddress|string $remoteAddr = '::1',
string $countryCode = 'XX'
): void {
if($userInfo instanceof UserInfo)
$userInfo = $userInfo->getId();
if($remoteAddr instanceof IPAddress)
$remoteAddr = (string)$remoteAddr;
// action names should have stricter validation,
// i do want to switch to a lowercase colon separated format later but i'll save that for the unified log in Hanyuu
$actionTrim = trim($action);
if($actionTrim !== $action || empty($actionTrim))
throw new InvalidArgumentException('$action may not be empty.');
if(strlen($countryCode) !== 2 || !ctype_alpha($countryCode))
throw new InvalidArgumentException('$countryCode must be two alpha characters.');
foreach($params as &$param) {
if(is_array($param))
$param = implode(', ', $param);
elseif(is_object($param))
$param = (string)$param;
}
$params = json_encode($params);
$stmt = $this->cache->get('INSERT INTO msz_audit_log (user_id, log_action, log_params, log_ip, log_country) VALUES (?, ?, ?, INET6_ATON(?), UPPER(?))');
$stmt->addParameter(1, $userInfo);
$stmt->addParameter(2, $action);
$stmt->addParameter(3, $params);
$stmt->addParameter(4, $remoteAddr);
$stmt->addParameter(5, $countryCode);
$stmt->execute();
}
}