diff --git a/CHANGELOG.md b/CHANGELOG.md index 46d8669..db74888 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### Added - Reset input when escape key is pressed +- Implement audit logging ## [0.26.6] - 2024-06-10 diff --git a/lib/AuditTrait.php b/lib/AuditTrait.php new file mode 100644 index 0000000..d48a8ad --- /dev/null +++ b/lib/AuditTrait.php @@ -0,0 +1,83 @@ +emitAuditEvent('user \'%s\' created a %s: %s', [ + $userId, + $entityName, + json_encode($entity) + ]); + } + + private function emitUpdateAuditEvents( + string $userId, + string $entityName, + Entity $newEntity, + Entity $oldEntity + ) { + $lines = $this->getLogLinesForEntityChanges( + $newEntity->jsonSerialize(), + $oldEntity->jsonSerialize() + ); + + foreach($lines as $line) { + $this->emitAuditEvent('user \'%s\' ' . $line . ' of %s with id %s', [ + $userId, + $entityName, + $newEntity->id + ]); + } + } + + private function emitDeleteAuditEvent(string $userId, string $entityName, Entity $entity) { + $this->emitAuditEvent('user \'%s\' deleted %s: %s', [ + $userId, + $entityName, + json_encode($entity) + ]); + } + + private function emitAuditEvent(string $message, array $params) { + $event = new CriticalActionPerformedEvent( + $message, + $params + ); + + $this->getDispatcher()->dispatchTyped($event); + } + + private function getLogLinesForEntityChanges(array $newEntity, array $oldEntity) { + $changes = array_diff( + $newEntity, + $oldEntity + ); + + $lines = []; + + foreach($changes as $key => $value) { + array_push($lines, 'changed ' . $key . ' from ' . $oldEntity[$key] . ' to ' . $value); + } + + return $lines; + } + + private function getDispatcher(): IEventDispatcher { + if(!isset($this->dispatcher)) { + $this->dispatcher = \OCP\Server::get(IEventDispatcher::class); + } + + return $this->dispatcher; + } + +} diff --git a/lib/Service/AccountService.php b/lib/Service/AccountService.php index 4f4e3b1..9fa92e1 100755 --- a/lib/Service/AccountService.php +++ b/lib/Service/AccountService.php @@ -10,8 +10,12 @@ use OCA\Money\Db\Account; use OCA\Money\Db\AccountMapper; +use OCA\Money\AuditTrait; + class AccountService { + use AuditTrait; + private $mapper; public function __construct(AccountMapper $mapper) { @@ -58,7 +62,11 @@ public function create( $account->setUserId($userId); $account->setBookId($bookId); - return $this->mapper->insert($account); + $insertedAccount = $this->mapper->insert($account); + + $this->emitCreateAuditEvent($userId, 'account', $insertedAccount); + + return $insertedAccount; } public function update( @@ -73,6 +81,7 @@ public function update( ) { try { $account = $this->mapper->find($id, $userId); + $originalAccount = clone $account; $account->setBookId($bookId); @@ -82,7 +91,11 @@ public function update( $account->setDescription($description); $account->setExtraData($extraData); - return $this->mapper->update($account); + $updatedAccount = $this->mapper->update($account); + + $this->emitUpdateAuditEvents($userId, 'account', $updatedAccount, $originalAccount); + + return $updatedAccount; } catch(Exception $e) { $this->handleException($e); } @@ -92,6 +105,9 @@ public function delete($id, $userId) { try { $account = $this->mapper->find($id, $userId); $this->mapper->delete($account); + + $this->emitDeleteAuditEvent($userId, 'account', $account); + return $account; } catch(Exception $e) { $this->handleException($e); diff --git a/lib/Service/SplitService.php b/lib/Service/SplitService.php index f3b8ee0..a6cb7b0 100755 --- a/lib/Service/SplitService.php +++ b/lib/Service/SplitService.php @@ -10,8 +10,12 @@ use OCA\Money\Db\Split; use OCA\Money\Db\SplitMapper; +use OCA\Money\AuditTrait; + class SplitService { + use AuditTrait; + private $splitMapper; public function __construct(SplitMapper $splitMapper) { @@ -57,12 +61,17 @@ public function create($transactionId, $destAccountId, $value, $convertRate, $de $split->setConvertRate($convertRate); $split->setValue($value); - return $this->splitMapper->insert($split); + $insertedSplit = $this->splitMapper->insert($split); + + $this->emitCreateAuditEvent($userId, 'split', $insertedSplit); + + return $insertedSplit; } public function update($id, $transactionId, $destAccountId, $value, $convertRate, $description, $userId) { try { $split = $this->splitMapper->find($id, $userId); + $originalSplit = clone $split; $split->setDescription($description); $split->setTransactionId($transactionId); @@ -70,7 +79,11 @@ public function update($id, $transactionId, $destAccountId, $value, $convertRate $split->setConvertRate($convertRate); $split->setValue($value); - return $this->splitMapper->update($split); + $updatedSplit = $this->splitMapper->update($split); + + $this->emitUpdateAuditEvents($userId, 'split', $updatedSplit, $originalSplit); + + return $updatedSplit; } catch(Exception $e) { $this->handleException($e); } @@ -80,6 +93,9 @@ public function delete($id, $userId) { try { $split = $this->splitMapper->find($id, $userId); $this->splitMapper->delete($split); + + $this->emitDeleteAuditEvent($userId, 'split', $split); + return $split; } catch(Exception $e) { $this->handleException($e); diff --git a/lib/Service/TransactionService.php b/lib/Service/TransactionService.php index 3a6be20..59fc32a 100644 --- a/lib/Service/TransactionService.php +++ b/lib/Service/TransactionService.php @@ -10,8 +10,12 @@ use OCA\Money\Db\Transaction; use OCA\Money\Db\TransactionMapper; +use OCA\Money\AuditTrait; + class TransactionService { + use AuditTrait; + private $transactionMapper; public function __construct(TransactionMapper $transactionMapper) { @@ -63,17 +67,27 @@ public function create($description, $date, $userId) { $transaction->setDate($date); $transaction->setTimestampAdded(time()); - return $this->transactionMapper->insert($transaction); + + $insertedTransaction = $this->transactionMapper->insert($transaction); + + $this->emitCreateAuditEvent($userId, 'transaction', $insertedTransaction); + + return $insertedTransaction; } public function update($id, $description, $date, $userId) { try { $transaction = $this->transactionMapper->find($id, $userId); + $originalTransaction = clone $transaction; $transaction->setDescription($description); $transaction->setDate($date); - return $this->transactionMapper->update($transaction); + $updatedTransaction = $this->transactionMapper->update($transaction); + + $this->emitUpdateAuditEvents($userId, 'transaction', $updatedTransaction, $originalTransaction); + + return $updatedTransaction; } catch(Exception $e) { $this->handleException($e); }