From 63454f8c63f679e81563676f78501fd181e9ef7f Mon Sep 17 00:00:00 2001 From: snipe Date: Mon, 18 May 2026 14:18:58 +0100 Subject: [PATCH] Respect the view encrypted custom fields permission --- .../Transformers/ActionlogsTransformer.php | 4 +- database/factories/UserFactory.php | 5 ++ tests/Feature/Assets/Api/AssetHistoryTest.php | 75 ++++++++++--------- 3 files changed, 45 insertions(+), 39 deletions(-) diff --git a/app/Http/Transformers/ActionlogsTransformer.php b/app/Http/Transformers/ActionlogsTransformer.php index 8190156056..de9522f1fd 100644 --- a/app/Http/Transformers/ActionlogsTransformer.php +++ b/app/Http/Transformers/ActionlogsTransformer.php @@ -116,8 +116,8 @@ class ActionlogsTransformer $clean_meta[$fieldname]['old'] = '************'; $clean_meta[$fieldname]['new'] = '************'; - // Display the changes if the user is an admin or superadmin - if (Gate::allows('admin')) { + // Display the changes if the user has permission to view encrypted custom fields + if (Gate::allows('assets.view.encrypted_custom_fields')) { $clean_meta[$fieldname]['old'] = ($enc_old) ? e(unserialize($enc_old, ['allowed_classes' => false])) : ''; $clean_meta[$fieldname]['new'] = ($enc_new) ? e(unserialize($enc_new, ['allowed_classes' => false])) : ''; } diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index 5e9dd8cf9c..47ebdc1c09 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -180,6 +180,11 @@ class UserFactory extends Factory return $this->appendPermission(['assets.view.requestable' => '1']); } + public function viewEncryptedCustomFields() + { + return $this->appendPermission(['assets.view.encrypted_custom_fields' => '1']); + } + public function deleteAssetModels() { return $this->appendPermission(['models.delete' => '1']); diff --git a/tests/Feature/Assets/Api/AssetHistoryTest.php b/tests/Feature/Assets/Api/AssetHistoryTest.php index f2b8c56c5f..32a473fd99 100644 --- a/tests/Feature/Assets/Api/AssetHistoryTest.php +++ b/tests/Feature/Assets/Api/AssetHistoryTest.php @@ -10,7 +10,36 @@ use Tests\TestCase; class AssetHistoryTest extends TestCase { - public function test_encrypted_custom_field_values_are_html_encoded_in_history_for_admins() + private function findFieldEntry(array $rows, string $dbColumn): ?array + { + foreach ($rows as $row) { + $entry = ($row['log_meta'] ?? [])[$dbColumn] ?? null; + if ($entry !== null) { + return $entry; + } + } + + return null; + } + + private function updateAssetEncryptedField(Asset $asset, CustomField $field, string $value, User $actor): void + { + $this->actingAsForApi($actor) + ->patchJson(route('api.assets.update', $asset->id), [ + $field->db_column_name() => $value, + ]) + ->assertOk(); + } + + private function getUpdateRows(Asset $asset, User $viewer): array + { + return $this->actingAsForApi($viewer) + ->getJson(route('api.assets.history', ['asset' => $asset->id, 'action_type' => 'update'])) + ->assertOk() + ->json('rows'); + } + + public function test_encrypted_custom_field_values_are_visible_to_users_with_encrypted_field_permission() { $this->markIncompleteIfMySQL('Custom Fields tests do not work on MySQL'); @@ -20,32 +49,19 @@ class AssetHistoryTest extends TestCase ]); $superuser = User::factory()->superuser()->create(); - $this->actingAsForApi($superuser) - ->patchJson(route('api.assets.update', $asset->id), [ - $field->db_column_name() => '', - ]) - ->assertOk(); + $this->updateAssetEncryptedField($asset, $field, '', $superuser); - $rows = $this->actingAsForApi($superuser) - ->getJson(route('api.assets.history', ['asset' => $asset->id, 'action_type' => 'update'])) - ->assertOk() - ->json('rows'); + $viewer = User::factory()->viewAssets()->viewAssetHistory()->viewEncryptedCustomFields()->create(); - $fieldEntry = null; - foreach ($rows as $row) { - $fieldEntry = ($row['log_meta'] ?? [])[$field->db_column] ?? null; - if ($fieldEntry !== null) { - break; - } - } - $this->assertNotNull($fieldEntry, 'Encrypted field change should appear in an update log_meta entry'); + $fieldEntry = $this->findFieldEntry($this->getUpdateRows($asset, $viewer), $field->db_column); + $this->assertNotNull($fieldEntry, 'Encrypted field change should be visible to users with the encrypted fields permission'); $newValue = $fieldEntry['new']; $this->assertStringNotContainsString('assertStringContainsString('<', $newValue, 'Value should be HTML-encoded in history log_meta'); } - public function test_encrypted_custom_field_values_are_masked_for_non_admins() + public function test_encrypted_custom_field_values_are_masked_for_users_without_encrypted_field_permission() { $this->markIncompleteIfMySQL('Custom Fields tests do not work on MySQL'); @@ -55,29 +71,14 @@ class AssetHistoryTest extends TestCase ]); $superuser = User::factory()->superuser()->create(); - $this->actingAsForApi($superuser) - ->patchJson(route('api.assets.update', $asset->id), [ - $field->db_column_name() => '', - ]) - ->assertOk(); + $this->updateAssetEncryptedField($asset, $field, '', $superuser); $viewer = User::factory()->viewAssets()->viewAssetHistory()->create(); - $rows = $this->actingAsForApi($viewer) - ->getJson(route('api.assets.history', ['asset' => $asset->id, 'action_type' => 'update'])) - ->assertOk() - ->json('rows'); - - $fieldEntry = null; - foreach ($rows as $row) { - $fieldEntry = ($row['log_meta'] ?? [])[$field->db_column] ?? null; - if ($fieldEntry !== null) { - break; - } - } + $fieldEntry = $this->findFieldEntry($this->getUpdateRows($asset, $viewer), $field->db_column); if ($fieldEntry !== null) { - $this->assertEquals('************', $fieldEntry['new'], 'Non-admin should see masked value for encrypted field changes'); + $this->assertEquals('************', $fieldEntry['new'], 'Users without encrypted field permission should see masked value'); $this->assertStringNotContainsString('