From 82ada04b7f09b4f292a05ede9bb8fce13c4c47db Mon Sep 17 00:00:00 2001 From: Nicos Panayides Date: Thu, 29 Aug 2024 15:34:45 +0300 Subject: [PATCH 1/5] Add support for sensitive fields --- README.md | 11 ++++++ src/Model/Behavior/AuditLogBehavior.php | 25 ++++++++++++++ .../Model/Behavior/AuditLogBehaviorTest.php | 34 +++++++++++++++++++ 3 files changed, 70 insertions(+) diff --git a/README.md b/README.md index f013d51..0187818 100644 --- a/README.md +++ b/README.md @@ -138,6 +138,17 @@ public function initialize(array $config = []): void } ``` +If you have fields that contain sensitive information but still want to track their changes you can use the `sensitive` configuration: + +```php +public function initialize(array $config = []): void +{ + ... + $this->addBehavior('AuditStash.AuditLog', [ + 'sensitive' => ['body'] + ]); +} + ### Storing The Logged In User It is often useful to store the identifier of the user that is triggering the changes in a certain table. For this purpose, `AuditStash` diff --git a/src/Model/Behavior/AuditLogBehavior.php b/src/Model/Behavior/AuditLogBehavior.php index 1c0b08c..327cf20 100644 --- a/src/Model/Behavior/AuditLogBehavior.php +++ b/src/Model/Behavior/AuditLogBehavior.php @@ -34,6 +34,7 @@ class AuditLogBehavior extends Behavior 'type' => null, 'blacklist' => ['created', 'modified'], 'whitelist' => [], + 'sensitive' => [], ]; /** @@ -88,6 +89,26 @@ public function injectTracking( } } + /** + * Redacts sensitive fields from the array + * + * @param array $fields Field + * @return void + */ + private function redactArray(array &$fields): void + { + $sensitive = $this->_config['sensitive'] ?? []; + if ($sensitive === []) { + return; + } + + foreach ($fields as $field => &$value) { + if (in_array($field, $sensitive)) { + $value = '****'; + } + } + } + /** * Calculates the changes done to the entity and stores the audit log event object into the * log queue inside the `_auditQueue` key in $options. @@ -122,7 +143,11 @@ public function afterSave( return; } + $this->redactArray($changed); + $original = $entity->extractOriginal(array_keys($changed)); + $this->redactArray($original); + $properties = $this->getAssociationProperties(array_keys($options['associated'])); foreach ($properties as $property) { unset($changed[$property], $original[$property]); diff --git a/tests/TestCase/Model/Behavior/AuditLogBehaviorTest.php b/tests/TestCase/Model/Behavior/AuditLogBehaviorTest.php index 5b93712..f99560d 100644 --- a/tests/TestCase/Model/Behavior/AuditLogBehaviorTest.php +++ b/tests/TestCase/Model/Behavior/AuditLogBehaviorTest.php @@ -192,4 +192,38 @@ public static function dataProviderForSaveType(): array [null], ]; } + + public function testSensitiveFields(): void + { + $behavior = new AuditLogBehavior($this->table, [ + 'whitelist' => ['id', 'title', 'body', 'author_id'], + 'sensitive' => ['body'], + ]); + + $data = [ + 'id' => 13, + 'title' => 'The Title', + 'body' => 'The Body', + 'author_id' => 1, + ]; + $entity = new Entity($data, ['markNew' => false, 'markClean' => true]); + $entity->author_id = 50; + $entity->body = 'The changed body'; + + $event = new Event('Model.afterSave'); + $queue = new SplObjectStorage(); + $behavior->afterSave($event, $entity, new ArrayObject([ + '_auditQueue' => $queue, + '_auditTransaction' => '1', + 'associated' => [], + ])); + + $event = $queue[$entity]; + + $this->assertInstanceOf(AuditUpdateEvent::class, $event); + + $changed = $event->getChanged(); + $this->assertArrayHasKey('body', $changed); + $this->assertEquals('****', $changed['body']); + } } From 5c3833ac9d0502598671a4cb9aa2750a687c135b Mon Sep 17 00:00:00 2001 From: Nicos Panayides Date: Thu, 29 Aug 2024 15:37:10 +0300 Subject: [PATCH 2/5] phpcs fixes --- tests/TestCase/Model/Behavior/AuditLogBehaviorTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/TestCase/Model/Behavior/AuditLogBehaviorTest.php b/tests/TestCase/Model/Behavior/AuditLogBehaviorTest.php index f99560d..4b5f117 100644 --- a/tests/TestCase/Model/Behavior/AuditLogBehaviorTest.php +++ b/tests/TestCase/Model/Behavior/AuditLogBehaviorTest.php @@ -195,7 +195,7 @@ public static function dataProviderForSaveType(): array public function testSensitiveFields(): void { - $behavior = new AuditLogBehavior($this->table, [ + $behavior = new AuditLogBehavior($this->table, [ 'whitelist' => ['id', 'title', 'body', 'author_id'], 'sensitive' => ['body'], ]); @@ -221,7 +221,7 @@ public function testSensitiveFields(): void $event = $queue[$entity]; $this->assertInstanceOf(AuditUpdateEvent::class, $event); - + $changed = $event->getChanged(); $this->assertArrayHasKey('body', $changed); $this->assertEquals('****', $changed['body']); From 1bb48c0be111ab7bd9f500eef2bd42ffa0a8eaf3 Mon Sep 17 00:00:00 2001 From: Nicos Panayides Date: Thu, 29 Aug 2024 15:39:39 +0300 Subject: [PATCH 3/5] Apply suggested phpdoc change and use strict mathing for fields --- src/Model/Behavior/AuditLogBehavior.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Model/Behavior/AuditLogBehavior.php b/src/Model/Behavior/AuditLogBehavior.php index 327cf20..25db57e 100644 --- a/src/Model/Behavior/AuditLogBehavior.php +++ b/src/Model/Behavior/AuditLogBehavior.php @@ -92,7 +92,7 @@ public function injectTracking( /** * Redacts sensitive fields from the array * - * @param array $fields Field + * @param array $fields Field * @return void */ private function redactArray(array &$fields): void @@ -103,7 +103,7 @@ private function redactArray(array &$fields): void } foreach ($fields as $field => &$value) { - if (in_array($field, $sensitive)) { + if (in_array($field, $sensitive, true)) { $value = '****'; } } From 1ceebd58f421378de28d371fe94e7882ddd1976b Mon Sep 17 00:00:00 2001 From: Nicos Panayides Date: Thu, 29 Aug 2024 15:45:16 +0300 Subject: [PATCH 4/5] Ensure we do not miss any updates if only sensitive fields have changed --- src/Model/Behavior/AuditLogBehavior.php | 6 +++--- tests/TestCase/Model/Behavior/AuditLogBehaviorTest.php | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Model/Behavior/AuditLogBehavior.php b/src/Model/Behavior/AuditLogBehavior.php index 25db57e..b9bd3b4 100644 --- a/src/Model/Behavior/AuditLogBehavior.php +++ b/src/Model/Behavior/AuditLogBehavior.php @@ -143,10 +143,7 @@ public function afterSave( return; } - $this->redactArray($changed); - $original = $entity->extractOriginal(array_keys($changed)); - $this->redactArray($original); $properties = $this->getAssociationProperties(array_keys($options['associated'])); foreach ($properties as $property) { @@ -157,6 +154,9 @@ public function afterSave( return; } + $this->redactArray($changed); + $this->redactArray($original); + $primary = $entity->extract((array)$this->_table->getPrimaryKey()); $auditEvent = $entity->isNew() ? AuditCreateEvent::class : AuditUpdateEvent::class; diff --git a/tests/TestCase/Model/Behavior/AuditLogBehaviorTest.php b/tests/TestCase/Model/Behavior/AuditLogBehaviorTest.php index 4b5f117..0cda13b 100644 --- a/tests/TestCase/Model/Behavior/AuditLogBehaviorTest.php +++ b/tests/TestCase/Model/Behavior/AuditLogBehaviorTest.php @@ -207,7 +207,6 @@ public function testSensitiveFields(): void 'author_id' => 1, ]; $entity = new Entity($data, ['markNew' => false, 'markClean' => true]); - $entity->author_id = 50; $entity->body = 'The changed body'; $event = new Event('Model.afterSave'); From a8d9a6db017d5bbab6fd699013b990dbeac2e6f5 Mon Sep 17 00:00:00 2001 From: Nicos Panayides Date: Thu, 29 Aug 2024 15:55:36 +0300 Subject: [PATCH 5/5] Fix README unclosed code block --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 0187818..f74858a 100644 --- a/README.md +++ b/README.md @@ -148,6 +148,7 @@ public function initialize(array $config = []): void 'sensitive' => ['body'] ]); } +``` ### Storing The Logged In User