@@ -47,7 +47,7 @@ public function getAll(): array
4747 $ data = [];
4848
4949 $ query = sprintf (
50- 'SELECT id, time, usr AS user, text, ip FROM %sfaqadminlog ORDER BY id DESC ' ,
50+ 'SELECT id, time, usr AS user, text, ip, hash, previous_hash FROM %sfaqadminlog ORDER BY id DESC ' ,
5151 Database::getTablePrefix (),
5252 );
5353
@@ -60,32 +60,52 @@ public function getAll(): array
6060 ->setTime ((int ) $ row ->time )
6161 ->setUserId ((int ) $ row ->user )
6262 ->setText ($ row ->text )
63- ->setIp ($ row ->ip );
63+ ->setIp ($ row ->ip )
64+ ->setHash ($ row ->hash ?? null )
65+ ->setPreviousHash ($ row ->previous_hash ?? null );
6466 $ data [$ row ->id ] = $ adminLog ;
6567 }
6668
6769 return $ data ;
6870 }
6971
70- public function add (User $ user , string $ logText , Request $ request ): bool
72+ /**
73+ * Adds a new logging entry with hash chain integrity.
74+ *
75+ * @param User $user User object
76+ * @param string $logText Logged string
77+ * @param Request $request Request object
78+ * @param string|null $previousHash Hash of the previous entry
79+ */
80+ public function add (User $ user , string $ logText , Request $ request , ?string $ previousHash = null ): bool
7181 {
72- $ table = Database::getTablePrefix () . 'faqadminlog ' ;
73- $ id = $ this ->configuration ->getDb ()->nextId ($ table , 'id ' );
74- $ time = (int ) $ request ->server ->get ('REQUEST_TIME ' );
82+ $ time = (int ) $ request ->server ->get ('REQUEST_TIME ' , time ());
7583 $ userId = $ user ->getUserId ();
76- $ text = $ this ->configuration ->getDb ()->escape (nl2br ($ logText ));
77- $ ip = $ this ->configuration ->getDb ()->escape ((string ) $ request ->getClientIp ());
84+ $ ip = $ request ->getClientIp () ?? '' ;
7885
79- $ query = strtr ("INSERT INTO table: (id, time, usr, text, ip) VALUES (id:, time:, userId:, 'text:', 'ip:') " , [
80- 'table: ' => $ table ,
81- 'id: ' => (string ) $ id ,
82- 'time: ' => (string ) $ time ,
83- 'userId: ' => (string ) $ userId ,
84- 'text: ' => $ text ,
85- 'ip: ' => $ ip ,
86- ]);
86+ // Create a temporary entity to calculate hash
87+ $ entity = new AdminLogEntity ();
88+ $ entity ->setTime ($ time );
89+ $ entity ->setUserId ($ userId );
90+ $ entity ->setIp ($ ip );
91+ $ entity ->setText ($ logText );
92+ $ entity ->setPreviousHash ($ previousHash );
8793
88- return (bool ) $ this ->configuration ->getDb ()->query ($ query );
94+ // Calculate hash for this entry
95+ $ hash = $ entity ->calculateHash ();
96+
97+ $ insert = sprintf (
98+ "INSERT INTO %sfaqadminlog (time, usr, ip, text, hash, previous_hash) VALUES (%d, %d, '%s', '%s', '%s', %s) " ,
99+ Database::getTablePrefix (),
100+ $ time ,
101+ $ userId ,
102+ $ this ->configuration ->getDb ()->escape ($ ip ),
103+ $ this ->configuration ->getDb ()->escape ($ logText ),
104+ $ hash ,
105+ $ previousHash !== null ? "' " . $ this ->configuration ->getDb ()->escape ($ previousHash ) . "' " : 'NULL ' ,
106+ );
107+
108+ return (bool ) $ this ->configuration ->getDb ()->query ($ insert );
89109 }
90110
91111 public function deleteOlderThan (int $ timestamp ): bool
@@ -98,4 +118,22 @@ public function deleteOlderThan(int $timestamp): bool
98118
99119 return (bool ) $ this ->configuration ->getDb ()->query ($ query );
100120 }
121+
122+ /**
123+ * Returns the hash of the most recent log entry for chain linking.
124+ *
125+ * @return string|null Hash of the last entry or null if no entries exist
126+ */
127+ public function getLastHash (): ?string
128+ {
129+ $ query = sprintf ('SELECT hash FROM %sfaqadminlog ORDER BY id DESC LIMIT 1 ' , Database::getTablePrefix ());
130+
131+ $ result = $ this ->configuration ->getDb ()->query ($ query );
132+
133+ if ($ result && ($ row = $ this ->configuration ->getDb ()->fetchObject ($ result ))) {
134+ return $ row ->hash ;
135+ }
136+
137+ return null ;
138+ }
101139}
0 commit comments