From dae614b4e38120505809cfd4e9251228cdb1c886 Mon Sep 17 00:00:00 2001 From: Michiel Kodde Date: Tue, 3 May 2022 15:28:08 +0200 Subject: [PATCH] Integrate stable attribute hash requirements The requirements are stated in the story: https://www.pivotaltracker.com/story/show/176513931 and specifically these points: A challenge is that we do not want to invalidate all given consents with the current algorithm. So we implement it as follows: * We do not touch the existing consent hashing method at all * We create a new hashing method that is more stable. * We cover this new method with an abundance of unit tests to verify the stability given all sorts of inputs. * We change the consent query from (pseudocode): SELECT * FROM consent WHERE user = me AND consenthash = hashfromoldmethod OR consenthash = hashfromnewmethod * Newly given consent will be stored with the new hash. * When old consent matched, still generate new consent hash (without showing consent screen --- library/EngineBlock/Corto/Model/Consent.php | 61 +++- .../Corto/Module/Service/ProcessConsent.php | 2 + .../Corto/Module/Service/ProvideConsent.php | 4 + .../Repository/ConsentRepository.php | 24 ++ .../Service/Consent/ConsentHashService.php | 10 + .../Consent/ConsentHashServiceInterface.php | 10 + .../Repository/DbalConsentRepository.php | 70 ++++- .../Controller/Api/ConsentControllerTest.php | 14 +- .../Api/DeprovisionControllerTest.php | 1 + .../Corto/Model/ConsentIntegrationTest.php | 262 ++++++++++++++++++ .../Test/Corto/Model/ConsentTest.php | 8 +- 11 files changed, 445 insertions(+), 21 deletions(-) create mode 100644 tests/library/EngineBlock/Test/Corto/Model/ConsentIntegrationTest.php diff --git a/library/EngineBlock/Corto/Model/Consent.php b/library/EngineBlock/Corto/Model/Consent.php index 0a9516b472..323c58de52 100644 --- a/library/EngineBlock/Corto/Model/Consent.php +++ b/library/EngineBlock/Corto/Model/Consent.php @@ -16,6 +16,7 @@ * limitations under the License. */ +use OpenConext\EngineBlock\Authentication\Value\ConsentVersion; use OpenConext\EngineBlock\Metadata\Entity\ServiceProvider; use OpenConext\EngineBlock\Authentication\Value\ConsentType; use OpenConext\EngineBlock\Service\Consent\ConsentHashServiceInterface; @@ -83,14 +84,27 @@ public function __construct( public function explicitConsentWasGivenFor(ServiceProvider $serviceProvider): bool { - return !$this->_consentEnabled || - $this->_hasStoredConsent($serviceProvider, ConsentType::TYPE_EXPLICIT); + $consent = $this->_hasStoredConsent($serviceProvider, ConsentType::TYPE_EXPLICIT); + return !$this->_consentEnabled || $consent->given(); + } + + /** + * Although the user has given consent previously we want to upgrade the deprecated unstable consent + * to the new stable consent type. + * https://www.pivotaltracker.com/story/show/176513931 + */ + public function upgradeAttributeHashFor(ServiceProvider $serviceProvider, string $consentType): void + { + $consentVersion = $this->_hasStoredConsent($serviceProvider, $consentType); + if ($consentVersion->isUnstable()) { + $this->_updateConsent($serviceProvider, $consentType); + } } public function implicitConsentWasGivenFor(ServiceProvider $serviceProvider): bool { - return !$this->_consentEnabled || - $this->_hasStoredConsent($serviceProvider, ConsentType::TYPE_IMPLICIT); + $consent = $this->_hasStoredConsent($serviceProvider, ConsentType::TYPE_IMPLICIT); + return !$this->_consentEnabled || $consent->given(); } public function giveExplicitConsentFor(ServiceProvider $serviceProvider): bool @@ -142,28 +156,47 @@ private function _storeConsent(ServiceProvider $serviceProvider, $consentType): return $this->_hashService->storeConsentHash($parameters); } - private function _hasStoredConsent(ServiceProvider $serviceProvider, $consentType): bool + private function _updateConsent(ServiceProvider $serviceProvider, $consentType): bool { $parameters = array( + $this->_getStableAttributesHash($this->_responseAttributes), + $this->_getAttributesHash($this->_responseAttributes), sha1($this->_getConsentUid()), $serviceProvider->entityId, - $this->_getAttributesHash($this->_responseAttributes), $consentType, ); - $hasUnstableConsentHash = $this->_hashService->retrieveConsentHash($parameters); + return $this->_hashService->updateConsentHash($parameters); + } - if ($hasUnstableConsentHash) { - return true; + private function _hasStoredConsent(ServiceProvider $serviceProvider, $consentType): ConsentVersion + { + $consentUuid = sha1($this->_getConsentUid()); + $parameters = [ + $consentUuid, + $serviceProvider->entityId, + $this->_getStableAttributesHash($this->_responseAttributes), + $consentType, + ]; + $hasStableConsentHash = $this->_hashService->retrieveStableConsentHash($parameters); + + if ($hasStableConsentHash) { + return ConsentVersion::stable(); } - $parameters[2] = array( - sha1($this->_getConsentUid()), + $parameters = [ + $consentUuid, $serviceProvider->entityId, - $this->_getStableAttributesHash($this->_responseAttributes), + $this->_getAttributesHash($this->_responseAttributes), $consentType, - ); + ]; + + $hasUnstableConsentHash = $this->_hashService->retrieveConsentHash($parameters); + + if ($hasUnstableConsentHash) { + return ConsentVersion::unstable(); + } - return $this->_hashService->retrieveConsentHash($parameters); + return ConsentVersion::notGiven(); } } diff --git a/library/EngineBlock/Corto/Module/Service/ProcessConsent.php b/library/EngineBlock/Corto/Module/Service/ProcessConsent.php index 97cf0300b6..4dc3381efb 100644 --- a/library/EngineBlock/Corto/Module/Service/ProcessConsent.php +++ b/library/EngineBlock/Corto/Module/Service/ProcessConsent.php @@ -16,6 +16,7 @@ * limitations under the License. */ +use OpenConext\EngineBlock\Authentication\Value\ConsentType; use OpenConext\EngineBlock\Service\AuthenticationStateHelperInterface; use OpenConext\EngineBlock\Service\ProcessingStateHelperInterface; use SAML2\Constants; @@ -102,6 +103,7 @@ public function serve($serviceName, Request $httpRequest) if (!$consentRepository->explicitConsentWasGivenFor($serviceProvider)) { $consentRepository->giveExplicitConsentFor($destinationMetadata); } + $consentRepository->upgradeAttributeHashFor($destinationMetadata, ConsentType::TYPE_EXPLICIT); $response->setConsent(Constants::CONSENT_OBTAINED); $response->setDestination($response->getReturn()); diff --git a/library/EngineBlock/Corto/Module/Service/ProvideConsent.php b/library/EngineBlock/Corto/Module/Service/ProvideConsent.php index 8cdedc4ca8..51c241a043 100644 --- a/library/EngineBlock/Corto/Module/Service/ProvideConsent.php +++ b/library/EngineBlock/Corto/Module/Service/ProvideConsent.php @@ -16,6 +16,7 @@ * limitations under the License. */ +use OpenConext\EngineBlock\Authentication\Value\ConsentType; use OpenConext\EngineBlock\Metadata\Entity\IdentityProvider; use OpenConext\EngineBlock\Metadata\Entity\ServiceProvider; use OpenConext\EngineBlock\Service\AuthenticationStateHelperInterface; @@ -136,6 +137,7 @@ public function serve($serviceName, Request $httpRequest) if (!$consentRepository->implicitConsentWasGivenFor($serviceProviderMetadata)) { $consentRepository->giveImplicitConsentFor($serviceProviderMetadata); } + $consentRepository->upgradeAttributeHashFor($serviceProviderMetadata, ConsentType::TYPE_IMPLICIT); $response->setConsent(Constants::CONSENT_INAPPLICABLE); $response->setDestination($response->getReturn()); @@ -153,6 +155,8 @@ public function serve($serviceName, Request $httpRequest) $priorConsent = $consentRepository->explicitConsentWasGivenFor($serviceProviderMetadata); if ($priorConsent) { + $consentRepository->upgradeAttributeHashFor($serviceProviderMetadata, ConsentType::TYPE_EXPLICIT); + $response->setConsent(Constants::CONSENT_PRIOR); $response->setDestination($response->getReturn()); diff --git a/src/OpenConext/EngineBlock/Authentication/Repository/ConsentRepository.php b/src/OpenConext/EngineBlock/Authentication/Repository/ConsentRepository.php index b6a17a16ec..3361a8f6cc 100644 --- a/src/OpenConext/EngineBlock/Authentication/Repository/ConsentRepository.php +++ b/src/OpenConext/EngineBlock/Authentication/Repository/ConsentRepository.php @@ -38,9 +38,33 @@ public function deleteAllFor($userId); public function deleteOneFor(string $userId, string $serviceProviderEntityId): bool; + /** + * Test if the consent row is set with the legacy (unstable) consent hash + * This is the consent hash that was originally created by EB. It can change + * based on factors that should not result in a hash change per se. Think of the + * change of the attribute ordering, case change or the existence of empty + * attribute values. + */ public function hasConsentHash(array $parameters): bool; + /** + * Tests the presence of the stable consent hash + * + * The stable consent hash is used by default, it is not affected by attribute order, case change + * or other irrelevant factors that could result in a changed hash calculation. + */ + public function hasStableConsentHash(array $parameters): bool; + + /** + * By default stores the stable consent hash. The legacy consent hash is left. + */ public function storeConsentHash(array $parameters): bool; + /** + * When a deprecated unstable consent hash is encoutered, we upgrade it to the new format using this + * update consent hash method. + */ + public function updateConsentHash(array $parameters): bool; + public function countTotalConsent($consentUid): int; } diff --git a/src/OpenConext/EngineBlock/Service/Consent/ConsentHashService.php b/src/OpenConext/EngineBlock/Service/Consent/ConsentHashService.php index 876f84993a..12c88a184b 100644 --- a/src/OpenConext/EngineBlock/Service/Consent/ConsentHashService.php +++ b/src/OpenConext/EngineBlock/Service/Consent/ConsentHashService.php @@ -50,11 +50,21 @@ public function retrieveConsentHash(array $parameters): bool return $this->consentRepository->hasConsentHash($parameters); } + public function retrieveStableConsentHash(array $parameters): bool + { + return $this->consentRepository->hasStableConsentHash($parameters); + } + public function storeConsentHash(array $parameters): bool { return $this->consentRepository->storeConsentHash($parameters); } + public function updateConsentHash(array $parameters): bool + { + return $this->consentRepository->updateConsentHash($parameters); + } + public function countTotalConsent($consentUid): int { return $this->consentRepository->countTotalConsent($consentUid); diff --git a/src/OpenConext/EngineBlock/Service/Consent/ConsentHashServiceInterface.php b/src/OpenConext/EngineBlock/Service/Consent/ConsentHashServiceInterface.php index 3e475d31a6..17a777823a 100644 --- a/src/OpenConext/EngineBlock/Service/Consent/ConsentHashServiceInterface.php +++ b/src/OpenConext/EngineBlock/Service/Consent/ConsentHashServiceInterface.php @@ -20,10 +20,20 @@ interface ConsentHashServiceInterface { + /** + * Retrieve the old-style (deprecated) unstable consent hash + */ public function retrieveConsentHash(array $parameters): bool; + /** + * Retrieve the stable consent hash + */ + public function retrieveStableConsentHash(array $parameters): bool; + public function storeConsentHash(array $parameters): bool; + public function updateConsentHash(array $parameters): bool; + public function countTotalConsent($consentUid): int; public function getUnstableAttributesHash(array $attributes, bool $mustStoreValues): string; diff --git a/src/OpenConext/EngineBlockBundle/Authentication/Repository/DbalConsentRepository.php b/src/OpenConext/EngineBlockBundle/Authentication/Repository/DbalConsentRepository.php index 722d15929b..f69fce9d69 100644 --- a/src/OpenConext/EngineBlockBundle/Authentication/Repository/DbalConsentRepository.php +++ b/src/OpenConext/EngineBlockBundle/Authentication/Repository/DbalConsentRepository.php @@ -200,15 +200,46 @@ public function hasConsentHash(array $parameters): bool throw new RuntimeException(sprintf('Consent retrieval failed! Error: "%s"', $e->getMessage())); } } + /** + * @throws RuntimeException + */ + public function hasStableConsentHash(array $parameters): bool + { + try { + $query = " SELECT + * + FROM + consent + WHERE + hashed_user_id = ? + AND + service_id = ? + AND + attribute_stable = ? + AND + consent_type = ? + AND + deleted_at IS NULL + "; + + $statement = $this->connection->prepare($query); + $statement->execute($parameters); + $rows = $statement->fetchAll(); + + return count($rows) >= 1; + } catch (PDOException $e) { + throw new RuntimeException(sprintf('Consent retrieval on stable consent hash failed! Error: "%s"', $e->getMessage())); + } + } /** * @throws RuntimeException */ public function storeConsentHash(array $parameters): bool { - $query = "INSERT INTO consent (hashed_user_id, service_id, attribute, consent_type, consent_date, deleted_at) + $query = "INSERT INTO consent (hashed_user_id, service_id, attribute_stable, consent_type, consent_date, deleted_at) VALUES (?, ?, ?, ?, NOW(), '0000-00-00 00:00:00') - ON DUPLICATE KEY UPDATE attribute=VALUES(attribute), consent_type=VALUES(consent_type), consent_date=NOW()"; + ON DUPLICATE KEY UPDATE attribute_stable=VALUES(attribute_stable), consent_type=VALUES(consent_type), consent_date=NOW()"; $statement = $this->connection->prepare($query); if (!$statement) { throw new RuntimeException("Unable to create a prepared statement to insert consent?!"); @@ -223,6 +254,41 @@ public function storeConsentHash(array $parameters): bool return true; } + /** + * @throws RuntimeException + */ + public function updateConsentHash(array $parameters): bool + { + $query = " + UPDATE + consent + SET + attribute_stable = ? + WHERE + attribute = ? + AND + hashed_user_id = ? + AND + service_id = ? + AND + consent_type = ? + AND + deleted_at IS NULL + "; + $statement = $this->connection->prepare($query); + if (!$statement) { + throw new RuntimeException("Unable to create a prepared statement to update consent?!"); + } + + if (!$statement->execute($parameters)) { + throw new RuntimeException( + sprintf('Error storing updated consent: "%s"', var_export($statement->errorInfo(), true)) + ); + } + + return true; + } + /** * @throws RuntimeException */ diff --git a/tests/functional/OpenConext/EngineBlockBundle/Controller/Api/ConsentControllerTest.php b/tests/functional/OpenConext/EngineBlockBundle/Controller/Api/ConsentControllerTest.php index 702a170d8f..aa426f122d 100644 --- a/tests/functional/OpenConext/EngineBlockBundle/Controller/Api/ConsentControllerTest.php +++ b/tests/functional/OpenConext/EngineBlockBundle/Controller/Api/ConsentControllerTest.php @@ -507,8 +507,14 @@ private function disableConsentApiFeatureFor(Client $client) $container->set('engineblock.features', $featureToggles); } - private function addConsentFixture($userId, $serviceId, $attributeHash, $consentType, $consentDate, $deletedAt) - { + private function addConsentFixture( + $userId, + $serviceId, + $attributeHash, + $consentType, + $consentDate, + $deletedAt + ) { $queryBuilder = $this->getContainer()->get('doctrine')->getConnection()->createQueryBuilder(); $queryBuilder ->insert('consent') @@ -516,6 +522,7 @@ private function addConsentFixture($userId, $serviceId, $attributeHash, $consent 'hashed_user_id' => ':user_id', 'service_id' => ':service_id', 'attribute' => ':attribute', + 'attribute_stable' => ':attribute_stable', 'consent_type' => ':consent_type', 'consent_date' => ':consent_date', 'deleted_at' => ':deleted_at', @@ -523,7 +530,8 @@ private function addConsentFixture($userId, $serviceId, $attributeHash, $consent ->setParameters([ ':user_id' => sha1($userId), ':service_id' => $serviceId, - ':attribute' => $attributeHash, + ':attribute' => '', + ':attribute_stable' => $attributeHash, ':consent_type' => $consentType, ':consent_date' => $consentDate, ':deleted_at' => $deletedAt, diff --git a/tests/functional/OpenConext/EngineBlockBundle/Controller/Api/DeprovisionControllerTest.php b/tests/functional/OpenConext/EngineBlockBundle/Controller/Api/DeprovisionControllerTest.php index 891ab4fc72..68699c6cf7 100644 --- a/tests/functional/OpenConext/EngineBlockBundle/Controller/Api/DeprovisionControllerTest.php +++ b/tests/functional/OpenConext/EngineBlockBundle/Controller/Api/DeprovisionControllerTest.php @@ -397,6 +397,7 @@ private function addConsentFixture($userId, $serviceId, $attributeHash, $consent 'hashed_user_id' => ':user_id', 'service_id' => ':service_id', 'attribute' => ':attribute', + 'attribute_stable' => ':attribute', 'consent_type' => ':consent_type', 'consent_date' => ':consent_date', 'deleted_at' => '"0000-00-00 00:00:00"', diff --git a/tests/library/EngineBlock/Test/Corto/Model/ConsentIntegrationTest.php b/tests/library/EngineBlock/Test/Corto/Model/ConsentIntegrationTest.php new file mode 100644 index 0000000000..96d6909147 --- /dev/null +++ b/tests/library/EngineBlock/Test/Corto/Model/ConsentIntegrationTest.php @@ -0,0 +1,262 @@ +response = Mockery::mock(EngineBlock_Saml2_ResponseAnnotationDecorator::class); + $this->consentRepository = Mockery::mock(ConsentRepository::class); + $this->consentService = new ConsentHashService($this->consentRepository); + + $this->consent = new EngineBlock_Corto_Model_Consent( + "consent", + true, + $this->response, + [], + false, + true, + $this->consentService + ); + } + + /** + * @dataProvider consentTypeProvider + */ + public function test_no_previous_consent_given($consentType) + { + $serviceProvider = new ServiceProvider("service-provider-entity-id"); + $this->response->shouldReceive('getNameIdValue') + ->once() + ->andReturn('collab:person:id:org-a:joe-a'); + // No consent is given previously + $this->consentRepository + ->shouldReceive('hasConsentHash') + ->once() + ->andReturn(false); + $this->consentRepository + ->shouldReceive('hasStableConsentHash') + ->once() + ->andReturn(false); + switch ($consentType) { + case ConsentType::TYPE_EXPLICIT: + $this->assertFalse($this->consent->explicitConsentWasGivenFor($serviceProvider)); + break; + case ConsentType::TYPE_IMPLICIT: + $this->assertFalse($this->consent->implicitConsentWasGivenFor($serviceProvider)); + break; + } + } + + /** + * @dataProvider consentTypeProvider + */ + public function test_unstable_previous_consent_given($consentType) + { + $serviceProvider = new ServiceProvider("service-provider-entity-id"); + $this->response->shouldReceive('getNameIdValue') + ->once() + ->andReturn('collab:person:id:org-a:joe-a'); + // Stable consent is not yet stored + $this->consentRepository + ->shouldReceive('hasStableConsentHash') + ->with(['0e54805079c56c2b1c1197a760af86ac337b7bac', 'service-provider-entity-id', '8739602554c7f3241958e3cc9b57fdecb474d508', $consentType]) + ->once() + ->andReturn(false); + // Old-style (unstable) consent was given previously + $this->consentRepository + ->shouldReceive('hasConsentHash') + ->with(['0e54805079c56c2b1c1197a760af86ac337b7bac', 'service-provider-entity-id', '8739602554c7f3241958e3cc9b57fdecb474d508', $consentType]) + ->once() + ->andReturn(true); + + + switch ($consentType) { + case ConsentType::TYPE_EXPLICIT: + $this->assertTrue($this->consent->explicitConsentWasGivenFor($serviceProvider)); + break; + case ConsentType::TYPE_IMPLICIT: + $this->assertTrue($this->consent->implicitConsentWasGivenFor($serviceProvider)); + break; + } + } + + /** + * @dataProvider consentTypeProvider + */ + public function test_stable_consent_given($consentType) + { + $serviceProvider = new ServiceProvider("service-provider-entity-id"); + $this->response->shouldReceive('getNameIdValue') + ->once() + ->andReturn('collab:person:id:org-a:joe-a'); + // Stable consent is not yet stored + $this->consentRepository + ->shouldReceive('hasStableConsentHash') + ->with(['0e54805079c56c2b1c1197a760af86ac337b7bac', 'service-provider-entity-id', '8739602554c7f3241958e3cc9b57fdecb474d508', $consentType]) + ->once() + ->andReturn(true); + // Old-style (unstable) consent was given previously + $this->consentRepository + ->shouldNotReceive('hasConsentHash'); + + switch ($consentType) { + case ConsentType::TYPE_EXPLICIT: + $this->assertTrue($this->consent->explicitConsentWasGivenFor($serviceProvider)); + break; + case ConsentType::TYPE_IMPLICIT: + $this->assertTrue($this->consent->implicitConsentWasGivenFor($serviceProvider)); + break; + } + } + + /** + * @dataProvider consentTypeProvider + */ + public function test_give_consent_no_unstable_consent_given($consentType) + { + $serviceProvider = new ServiceProvider("service-provider-entity-id"); + $this->response->shouldReceive('getNameIdValue') + ->once() + ->andReturn('collab:person:id:org-a:joe-a'); + // Now assert that the new stable consent hash is going to be set + $this->consentRepository + ->shouldReceive('storeConsentHash') + ->once() + ->with(['0e54805079c56c2b1c1197a760af86ac337b7bac', 'service-provider-entity-id', '8739602554c7f3241958e3cc9b57fdecb474d508', $consentType]) + ->andReturn(true); + + switch ($consentType) { + case ConsentType::TYPE_EXPLICIT: + $this->assertTrue($this->consent->giveExplicitConsentFor($serviceProvider)); + break; + case ConsentType::TYPE_IMPLICIT: + $this->assertTrue($this->consent->giveImplicitConsentFor($serviceProvider)); + break; + } + } + + /** + * @dataProvider consentTypeProvider + */ + public function test_give_consent_unstable_consent_given($consentType) + { + $serviceProvider = new ServiceProvider("service-provider-entity-id"); + $this->response->shouldReceive('getNameIdValue') + ->once() + ->andReturn('collab:person:id:org-a:joe-a'); + // Now assert that the new stable consent hash is going to be set + $this->consentRepository + ->shouldReceive('storeConsentHash') + ->once() + ->with(['0e54805079c56c2b1c1197a760af86ac337b7bac', 'service-provider-entity-id', '8739602554c7f3241958e3cc9b57fdecb474d508', $consentType]) + ->andReturn(true); + + switch ($consentType) { + case ConsentType::TYPE_EXPLICIT: + $this->assertTrue($this->consent->giveExplicitConsentFor($serviceProvider)); + break; + case ConsentType::TYPE_IMPLICIT: + $this->assertTrue($this->consent->giveImplicitConsentFor($serviceProvider)); + break; + } + } + + /** + * @dataProvider consentTypeProvider + */ + public function test_upgrade_to_stable_consent($consentType) + { + $serviceProvider = new ServiceProvider("service-provider-entity-id"); + $this->response->shouldReceive('getNameIdValue') + ->twice() + ->andReturn('collab:person:id:org-a:joe-a'); + // Stable consent is not yet stored + $this->consentRepository + ->shouldReceive('hasStableConsentHash') + ->with(['0e54805079c56c2b1c1197a760af86ac337b7bac', 'service-provider-entity-id', '8739602554c7f3241958e3cc9b57fdecb474d508', $consentType]) + ->once() + ->andReturn(false); + // Old-style (unstable) consent was given previously + $this->consentRepository + ->shouldReceive('hasConsentHash') + ->with(['0e54805079c56c2b1c1197a760af86ac337b7bac', 'service-provider-entity-id', '8739602554c7f3241958e3cc9b57fdecb474d508', $consentType]) + ->once() + ->andReturn(true); + // Now assert that the new stable consent hash is going to be set + $this->consentRepository + ->shouldReceive('updateConsentHash') + ->once() + ->with(['8739602554c7f3241958e3cc9b57fdecb474d508', '8739602554c7f3241958e3cc9b57fdecb474d508', '0e54805079c56c2b1c1197a760af86ac337b7bac', 'service-provider-entity-id', $consentType]) + ->andReturn(true); + + $this->assertNull($this->consent->upgradeAttributeHashFor($serviceProvider, $consentType)); + } + + /** + * @dataProvider consentTypeProvider + */ + public function test_upgrade_to_stable_consent_not_applied_when_stable($consentType) + { + $serviceProvider = new ServiceProvider("service-provider-entity-id"); + $this->response->shouldReceive('getNameIdValue') + ->once() + ->andReturn('collab:person:id:org-a:joe-a'); + // Stable consent is not yet stored + $this->consentRepository + ->shouldReceive('hasStableConsentHash') + ->with(['0e54805079c56c2b1c1197a760af86ac337b7bac', 'service-provider-entity-id', '8739602554c7f3241958e3cc9b57fdecb474d508', $consentType]) + ->once() + ->andReturn(true); + // Now assert that the new stable consent hash is NOT going to be set + $this->consentRepository + ->shouldNotReceive('storeConsentHash'); + $this->assertNull($this->consent->upgradeAttributeHashFor($serviceProvider, $consentType)); + } + + public function consentTypeProvider() + { + yield [ConsentType::TYPE_IMPLICIT]; + yield [ConsentType::TYPE_EXPLICIT]; + } +} diff --git a/tests/library/EngineBlock/Test/Corto/Model/ConsentTest.php b/tests/library/EngineBlock/Test/Corto/Model/ConsentTest.php index 394c24b9b6..9f32e5c158 100644 --- a/tests/library/EngineBlock/Test/Corto/Model/ConsentTest.php +++ b/tests/library/EngineBlock/Test/Corto/Model/ConsentTest.php @@ -61,7 +61,10 @@ public function testConsentDisabledDoesNotWriteToDatabase() $serviceProvider = new ServiceProvider("service-provider-entity-id"); $this->consentService->shouldReceive('getUnstableAttributesHash'); - $this->consentService->shouldNotReceive('storeConsentHash'); + $this->consentService->shouldReceive('getStableAttributesHash'); + $this->consentService->shouldReceive('retrieveStableConsentHash'); + $this->consentService->shouldReceive('retrieveConsentHash'); + $this->consentService->shouldReceive('storeConsentHash'); $this->consentDisabled->explicitConsentWasGivenFor($serviceProvider); $this->consentDisabled->implicitConsentWasGivenFor($serviceProvider); $this->consentDisabled->giveExplicitConsentFor($serviceProvider); @@ -71,9 +74,10 @@ public function testConsentDisabledDoesNotWriteToDatabase() public function testConsentWriteToDatabase() { $serviceProvider = new ServiceProvider("service-provider-entity-id"); - + $this->consentService->shouldReceive('getStableAttributesHash'); $this->consentService->shouldReceive('getUnstableAttributesHash')->andReturn(sha1('unstable')); $this->consentService->shouldReceive('retrieveConsentHash')->andReturn(sha1('unstable')); + $this->consentService->shouldReceive('retrieveStableConsentHash'); $this->consent->explicitConsentWasGivenFor($serviceProvider); $this->consentService->shouldReceive('getStableAttributesHash')->andReturn(sha1('stable'));