Lines
100.00%
62 / 62
Methods
100.00%
6 / 6
Classes
100.00%
1 / 1
| Name | Lines | Methods | CRAP | ||||
|---|---|---|---|---|---|---|---|
| __construct | 100.00% | 1 / 1 | 100.00% | 1 / 1 | 1 | ||
| getNumberOfEntries | 100.00% | 2 / 2 | 100.00% | 1 / 1 | 1 | ||
| getAll | 100.00% | 23 / 23 | 100.00% | 1 / 1 | 5 | ||
| add | 100.00% | 23 / 23 | 100.00% | 1 / 1 | 2 | ||
| deleteOlderThan | 100.00% | 6 / 6 | 100.00% | 1 / 1 | 1 | ||
| getLastHash | 100.00% | 7 / 7 | 100.00% | 1 / 1 | 3 | ||
| 28 | readonly class AdminLogRepository | |
| 29 | { | |
| 30 | public function __construct( | |
| 31 | private Configuration $configuration, | |
| 32 | ) { | |
| 33 | } | |
| 34 | ||
| 35 | public function getNumberOfEntries(): int | |
| 36 | { | |
| 37 | $query = sprintf('SELECT id FROM %sfaqadminlog', Database::getTablePrefix()); | |
| 38 | ||
| 39 | return $this->configuration->getDb()->numRows($this->configuration->getDb()->query($query)); | |
| 40 | } | |
| 41 | ||
| 42 | /** | |
| 43 | * @return array<int, AdminLogEntity> | |
| 44 | */ | |
| 45 | public function getAll(): array | |
| 46 | { | |
| 47 | $data = []; | |
| 48 | ||
| 49 | $query = sprintf( | |
| 50 | 'SELECT id, time, usr AS user, text, ip, hash, previous_hash FROM %sfaqadminlog ORDER BY id ASC', | |
| 51 | Database::getTablePrefix(), | |
| 52 | ); | |
| 53 | ||
| 54 | $result = $this->configuration->getDb()->query($query); | |
| 55 | ||
| 56 | while (true) { | |
| 57 | $row = $this->configuration->getDb()->fetchObject($result); | |
| 58 | if (!is_object($row)) { | |
| 59 | break; | |
| 60 | } | |
| 61 | ||
| 62 | $hash = $row->hash ?? null; | |
| 63 | $previousHash = $row->previous_hash ?? null; | |
| 64 | ||
| 65 | $adminLog = new AdminLogEntity(); | |
| 66 | $adminLog | |
| 67 | ->setId((int) $row->id) | |
| 68 | ->setTime((int) $row->time) | |
| 69 | ->setUserId((int) $row->user) | |
| 70 | ->setText((string) $row->text) | |
| 71 | ->setIp((string) $row->ip) | |
| 72 | ->setHash($hash === null ? null : (string) $hash) | |
| 73 | ->setPreviousHash($previousHash === null ? null : (string) $previousHash); | |
| 74 | $data[(int) $row->id] = $adminLog; | |
| 75 | } | |
| 76 | ||
| 77 | return $data; | |
| 78 | } | |
| 79 | ||
| 80 | /** | |
| 81 | * Adds a new logging entry with hash chain integrity. | |
| 82 | * | |
| 83 | * @param User $user User object | |
| 84 | * @param string $logText Logged string | |
| 85 | * @param Request $request Request object | |
| 86 | * @param string|null $previousHash Hash of the previous entry | |
| 87 | */ | |
| 88 | public function add(User $user, string $logText, Request $request, ?string $previousHash = null): bool | |
| 89 | { | |
| 90 | $time = (int) $request->server->get('REQUEST_TIME', time()); | |
| 91 | $userId = $user->getUserId(); | |
| 92 | $ip = $request->getClientIp() ?? ''; | |
| 93 | ||
| 94 | // Create a temporary entity to calculate hash | |
| 95 | $entity = new AdminLogEntity(); | |
| 96 | $entity->setTime($time); | |
| 97 | $entity->setUserId($userId); | |
| 98 | $entity->setIp($ip); | |
| 99 | $entity->setText($logText); | |
| 100 | $entity->setPreviousHash($previousHash); | |
| 101 | ||
| 102 | // Calculate hash for this entry | |
| 103 | $hash = $entity->calculateHash(); | |
| 104 | ||
| 105 | $insert = sprintf( | |
| 106 | 'INSERT INTO %sfaqadminlog (id, time, usr, ip, text, hash, previous_hash) ' | |
| 107 | . "VALUES (%d, %d, %d, '%s', '%s', '%s', %s)", | |
| 108 | Database::getTablePrefix(), | |
| 109 | $this->configuration->getDb()->nextId(Database::getTablePrefix() . 'faqadminlog', 'id'), | |
| 110 | $time, | |
| 111 | $userId, | |
| 112 | $this->configuration->getDb()->escape($ip), | |
| 113 | $this->configuration->getDb()->escape($logText), | |
| 114 | $hash, | |
| 115 | $previousHash !== null ? "'" . $this->configuration->getDb()->escape($previousHash) . "'" : 'NULL', | |
| 116 | ); | |
| 117 | ||
| 118 | return (bool) $this->configuration->getDb()->query($insert); | |
| 119 | } | |
| 120 | ||
| 121 | public function deleteOlderThan(int $timestamp): bool | |
| 122 | { | |
| 123 | $table = Database::getTablePrefix() . 'faqadminlog'; | |
| 124 | $query = strtr('DELETE FROM table: WHERE time < ts:', [ | |
| 125 | 'table:' => $table, | |
| 126 | 'ts:' => (string) $timestamp, | |
| 127 | ]); | |
| 128 | ||
| 129 | return (bool) $this->configuration->getDb()->query($query); | |
| 130 | } | |
| 131 | ||
| 132 | /** | |
| 133 | * Returns the hash of the most recent log entry for chain linking. | |
| 134 | * | |
| 135 | * @return string|null Hash of the last entry or null if no entries exist | |
| 136 | */ | |
| 137 | public function getLastHash(): ?string | |
| 138 | { | |
| 139 | $query = sprintf('SELECT hash FROM %sfaqadminlog ORDER BY id DESC LIMIT 1', Database::getTablePrefix()); | |
| 140 | ||
| 141 | $result = $this->configuration->getDb()->query($query); | |
| 142 | ||
| 143 | if ($result !== false) { | |
| 144 | $row = $this->configuration->getDb()->fetchObject($result); | |
| 145 | if ($row instanceof \stdClass) { | |
| 146 | return (string) $row->hash; | |
| 147 | } | |
| 148 | } | |
| 149 | ||
| 150 | return null; | |
| 151 | } | |
| 152 | } |