diff --git a/app/code/Magento/EncryptionKey/Model/ResourceModel/Key/Change.php b/app/code/Magento/EncryptionKey/Model/ResourceModel/Key/Change.php index 0d916389af994..32739df016c4a 100644 --- a/app/code/Magento/EncryptionKey/Model/ResourceModel/Key/Change.php +++ b/app/code/Magento/EncryptionKey/Model/ResourceModel/Key/Change.php @@ -14,6 +14,7 @@ use Magento\Framework\Config\Data\ConfigData; use Magento\Framework\Config\File\ConfigFilePool; use Magento\Framework\Encryption\EncryptorInterface; +use Magento\Framework\Encryption\Encryptor; use Magento\Framework\Exception\FileSystemException; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Filesystem; @@ -21,6 +22,9 @@ use Magento\Framework\Math\Random; use Magento\Framework\Model\ResourceModel\Db\AbstractDb; use Magento\Framework\Model\ResourceModel\Db\Context; +use Magento\Framework\Indexer\ConfigInterface; +use Magento\Framework\Json\EncoderInterface; +use Magento\Indexer\Model\ResourceModel\Indexer\State\CollectionFactory; /** * Encryption key changer resource model @@ -29,9 +33,9 @@ * @api * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - * @since 100.0.2 + * @since 100.0.2 * @deprecated - * @see Extensible Reencryption Mechanism + * @see Extensible Reencryption Mechanism */ class Change extends AbstractDb { @@ -66,19 +70,45 @@ class Change extends AbstractDb /** * Random string generator * - * @var Random + * @var Random * @since 100.0.4 */ protected $random; /** - * @param Context $context - * @param Filesystem $filesystem - * @param Structure $structure - * @param EncryptorInterface $encryptor - * @param Writer $writer - * @param Random $random - * @param string $connectionName + * Indexer Configuration + * + * @var IndexerConfig + */ + protected $indexerConfig; + + /** + * Json Encoder + * + * @var Encoder + */ + protected $encoder; + + /** + * Indexer State Collection Factory + * + * @var IndexerStateCollection + */ + protected $indexerStateCollection; + + /** + * @param Context $context + * @param Filesystem $filesystem + * @param Structure $structure + * @param EncryptorInterface $encryptor + * @param Writer $writer + * @param Random $random + * @param ConfigInterface $indexerConfig + * @param EncoderInterface $encoder + * @param CollectionFactory $indexerStateCollection + * @param string $connectionName + * + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( Context $context, @@ -87,6 +117,9 @@ public function __construct( EncryptorInterface $encryptor, Writer $writer, Random $random, + ConfigInterface $indexerConfig, + EncoderInterface $encoder, + CollectionFactory $indexerStateCollection, $connectionName = null ) { $this->encryptor = clone $encryptor; @@ -95,6 +128,9 @@ public function __construct( $this->structure = $structure; $this->writer = $writer; $this->random = $random; + $this->indexerConfig = $indexerConfig; + $this->encoder = $encoder; + $this->indexerStateCollection = $indexerStateCollection; } /** @@ -110,11 +146,11 @@ protected function _construct() /** * Change encryption key * - * @param string|null $key - * @return null|string - * @throws FileSystemException|LocalizedException|Exception + * @param string|null $key + * @return null|string + * @throws FileSystemException|LocalizedException|Exception * @deprecated - * @see Extensible Reencryption Mechanism + * @see Extensible Reencryption Mechanism */ public function changeEncryptionKey($key = null) { @@ -139,6 +175,7 @@ public function changeEncryptionKey($key = null) try { $this->_reEncryptSystemConfigurationValues(); $this->_reEncryptCreditCardNumbers(); + $this->updateIndexersHash(); $this->writer->saveConfig($configData); $this->commit(); return $key; @@ -151,14 +188,16 @@ public function changeEncryptionKey($key = null) /** * Gather all encrypted system config values and re-encrypt them * - * @return void + * @return void * @deprecated - * @see Extensible Reencryption Mechanism + * @see Extensible Reencryption Mechanism */ protected function _reEncryptSystemConfigurationValues() { // look for encrypted node entries in all system.xml files - /** @var Structure $configStructure */ + /** + * @var Structure $configStructure + */ $configStructure = $this->structure; $paths = $configStructure->getFieldPathsByAttribute( 'backend_model', @@ -188,9 +227,9 @@ protected function _reEncryptSystemConfigurationValues() /** * Gather saved credit card numbers from sales order payments and re-encrypt them * - * @return void + * @return void * @deprecated - * @see Extensible Reencryption Mechanism + * @see Extensible Reencryption Mechanism */ protected function _reEncryptCreditCardNumbers() { @@ -207,4 +246,34 @@ protected function _reEncryptCreditCardNumbers() ); } } + + /** + * Refresh the indexer hash to avoid grid data regeneration + * + * @return void + */ + protected function updateIndexersHash() + { + + $stateIndexers = []; + $stateCollection = $this->indexerStateCollection->create(); + foreach ($stateCollection->getItems() as $state) { + /** + * @var \Magento\Indexer\Model\Indexer\State $state + */ + $stateIndexers[$state->getIndexerId()] = $state; + } + + foreach ($this->indexerConfig->getIndexers() as $indexerId => $indexerConfig) { + $newHashConfig = $this->encryptor->hash( + $this->encoder->encode($indexerConfig), + Encryptor::HASH_VERSION_MD5 + ); + + if (isset($stateIndexers[$indexerId])) { + $stateIndexers[$indexerId]->setHashConfig($newHashConfig); + $stateIndexers[$indexerId]->save(); + } + } + } } diff --git a/app/code/Magento/EncryptionKey/Test/Unit/Model/ResourceModel/Key/ChangeTest.php b/app/code/Magento/EncryptionKey/Test/Unit/Model/ResourceModel/Key/ChangeTest.php index b0c5ecdc8ce19..e3e7c1f9b371c 100644 --- a/app/code/Magento/EncryptionKey/Test/Unit/Model/ResourceModel/Key/ChangeTest.php +++ b/app/code/Magento/EncryptionKey/Test/Unit/Model/ResourceModel/Key/ChangeTest.php @@ -1,8 +1,10 @@ encryptMock = $this->getMockBuilder(EncryptorInterface::class) @@ -98,6 +114,17 @@ protected function setUp(): void ->disableOriginalConstructor() ->getMock(); $this->randomMock = $this->createMock(Random::class); + $this->indexerConfigMock = $this->getMockBuilder(ConfigInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->encoderMock = $this->getMockBuilder(EncoderInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->indexerStateCollectionMock = $this->getMockBuilder(CollectionFactory::class) + ->disableOriginalConstructor() + ->onlyMethods(['create']) + ->addMethods(['getItems']) + ->getMock(); $helper = new ObjectManager($this); @@ -112,12 +139,19 @@ protected function setUp(): void 'resource' => $this->resourceMock, 'transactionManager' => $this->transactionMock, 'relationProcessor' => $this->objRelationMock, - 'random' => $this->randomMock + 'random' => $this->randomMock, + 'indexerConfig' => $this->indexerConfigMock, + 'encoder' => $this->encoderMock, + 'indexerStateCollection' => $this->indexerStateCollectionMock ] ); } - private function setUpChangeEncryptionKey() + /** + * @param array $indexersData + * @param array $states + */ + private function setUpChangeEncryptionKey(array $indexersData, array $states) { $paths = ['path1', 'path2']; $table = ['item1', 'item2']; @@ -138,19 +172,50 @@ private function setUpChangeEncryptionKey() $this->selectMock->expects($this->any())->method('update')->willReturnSelf(); $this->writerMock->expects($this->once())->method('saveConfig'); $this->adapterMock->expects($this->once())->method('getTransactionLevel')->willReturn(1); + + $indexerStateCollection = $this->getMockBuilder(StateCollection::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->indexerStateCollectionMock->expects($this->once()) + ->method('create')->willReturn($indexerStateCollection); + + $finalStates = []; + + foreach ($states as $key => $state) { + if (is_callable($state)) { + $finalStates[$key] = $state($this); + } + } + + $indexerStateCollection->method('getItems') + ->willReturn($finalStates); + + $this->indexerConfigMock->expects($this->any())->method('getIndexers')->willReturn($indexersData); } - public function testChangeEncryptionKey() + /** + * @param array $indexersData + * @param array $states + * @dataProvider loadDataDataProvider + */ + public function testChangeEncryptionKey(array $indexersData, array $states) { - $this->setUpChangeEncryptionKey(); + + $this->setUpChangeEncryptionKey($indexersData, $states); $this->randomMock->expects($this->never())->method('getRandomBytes'); $key = 'key'; $this->assertEquals($key, $this->model->changeEncryptionKey($key)); } - public function testChangeEncryptionKeyAutogenerate() + /** + * @param array $indexersData + * @param array $states + * @dataProvider loadDataDataProvider + */ + public function testChangeEncryptionKeyAutogenerate(array $indexersData, array $states) { - $this->setUpChangeEncryptionKey(); + $this->setUpChangeEncryptionKey($indexersData, $states); $this->randomMock->expects($this->once())->method('getRandomBytes')->willReturn('abc'); $this->assertEquals( ConfigOptionsListConstants::STORE_KEY_ENCODED_RANDOM_STRING_PREFIX . 'abc', @@ -171,4 +236,48 @@ public function testChangeEncryptionKeyThrowsException() $this->fail('An expected exception was not signaled.'); } + + /** + * @param array $data + * @return MockObject|State + */ + private function getStateMock(array $data = []) + { + /** @var MockObject|State $state */ + $state = $this->getMockBuilder(State::class) + ->disableOriginalConstructor() + ->getMock(); + if (isset($data['indexer_id'])) { + $state->method('getIndexerId') + ->willReturn($data['indexer_id']); + } + + return $state; + } + + /** + * @return array + */ + public static function loadDataDataProvider() + { + return [ + [ + 'indexersData' => [ + 'indexer_2' => [ + 'indexer_id' => 'indexer_2', + ], + 'indexer_3' => [ + 'indexer_id' => 'indexer_3', + ], + 'indexer_1' => [ + 'indexer_id' => 'indexer_1', + ], + ], + 'states' => [ + 'indexer_2' => static fn (self $testCase) => $testCase->getStateMock(['indexer_id' => 'indexer_2']), + 'indexer_3' => static fn (self $testCase) => $testCase->getStateMock(['indexer_id' => 'indexer_3']), + ], + ] + ]; + } } diff --git a/app/code/Magento/EncryptionKey/composer.json b/app/code/Magento/EncryptionKey/composer.json index 804b7ac289e60..7e61119035fe1 100644 --- a/app/code/Magento/EncryptionKey/composer.json +++ b/app/code/Magento/EncryptionKey/composer.json @@ -8,7 +8,8 @@ "php": "~8.2.0||~8.3.0||~8.4.0", "magento/framework": "*", "magento/module-backend": "*", - "magento/module-config": "*" + "magento/module-config": "*", + "magento/module-indexer": "*" }, "type": "magento2-module", "license": [