2023-07-17 17:43:17 +00:00
|
|
|
<?php
|
|
|
|
namespace Misuzu\AuditLog;
|
|
|
|
|
|
|
|
use InvalidArgumentException;
|
2023-07-22 14:00:51 +00:00
|
|
|
use Index\Data\DbStatementCache;
|
2023-07-17 17:43:17 +00:00
|
|
|
use Index\Data\IDbConnection;
|
|
|
|
use Index\Data\IDbResult;
|
|
|
|
use Index\Net\IPAddress;
|
|
|
|
use Misuzu\Pagination;
|
2023-08-02 22:12:47 +00:00
|
|
|
use Misuzu\Users\UserInfo;
|
2023-07-17 17:43:17 +00:00
|
|
|
|
|
|
|
class AuditLog {
|
|
|
|
private DbStatementCache $cache;
|
|
|
|
|
|
|
|
public function __construct(IDbConnection $dbConn) {
|
|
|
|
$this->cache = new DbStatementCache($dbConn);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function countLogs(
|
2023-08-02 22:12:47 +00:00
|
|
|
UserInfo|string|null $userInfo = null,
|
2023-07-17 17:43:17 +00:00
|
|
|
IPAddress|string|null $remoteAddr = null
|
|
|
|
): int {
|
2023-08-02 22:12:47 +00:00
|
|
|
if($userInfo instanceof UserInfo)
|
|
|
|
$userInfo = $userInfo->getId();
|
2023-07-17 17:43:17 +00:00
|
|
|
if($remoteAddr instanceof IPAddress)
|
|
|
|
$remoteAddr = (string)$remoteAddr;
|
|
|
|
|
|
|
|
$hasUserInfo = $userInfo !== null;
|
|
|
|
$hasRemoteAddr = $remoteAddr !== null;
|
|
|
|
|
|
|
|
$args = 0;
|
|
|
|
$query = 'SELECT COUNT(*) FROM msz_audit_log';
|
|
|
|
if($hasUserInfo) {
|
2023-07-18 22:24:23 +00:00
|
|
|
++$args;
|
|
|
|
$query .= ' WHERE user_id = ?';
|
2023-07-17 17:43:17 +00:00
|
|
|
}
|
|
|
|
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(
|
2023-08-02 22:12:47 +00:00
|
|
|
UserInfo|string|null $userInfo = null,
|
2023-07-17 17:43:17 +00:00
|
|
|
IPAddress|string|null $remoteAddr = null,
|
|
|
|
?Pagination $pagination = null
|
|
|
|
): array {
|
2023-08-02 22:12:47 +00:00
|
|
|
if($userInfo instanceof UserInfo)
|
|
|
|
$userInfo = $userInfo->getId();
|
2023-07-17 17:43:17 +00:00
|
|
|
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) {
|
2023-07-18 22:24:23 +00:00
|
|
|
++$args;
|
|
|
|
$query .= ' WHERE user_id = ?';
|
2023-07-17 17:43:17 +00:00
|
|
|
}
|
|
|
|
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(
|
2023-08-02 22:12:47 +00:00
|
|
|
UserInfo|string|null $userInfo,
|
2023-07-17 17:43:17 +00:00
|
|
|
string $action,
|
|
|
|
array $params = [],
|
|
|
|
IPAddress|string $remoteAddr = '::1',
|
|
|
|
string $countryCode = 'XX'
|
|
|
|
): void {
|
2023-08-02 22:12:47 +00:00
|
|
|
if($userInfo instanceof UserInfo)
|
|
|
|
$userInfo = $userInfo->getId();
|
2023-07-17 17:43:17 +00:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|