SodiumChachaPatch.php 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. declare(strict_types=1);
  7. namespace Magento\EncryptionKey\Setup\Patch\Data;
  8. use Magento\Framework\Setup\Patch\DataPatchInterface;
  9. use Magento\Framework\App\ObjectManager;
  10. /**
  11. * Migrate encrypted configuration values to the latest cipher
  12. */
  13. class SodiumChachaPatch implements DataPatchInterface
  14. {
  15. /**
  16. * @var \Magento\Framework\Config\ScopeInterface
  17. */
  18. private $scope;
  19. /**
  20. * @var \Magento\Framework\Setup\ModuleDataSetupInterface
  21. */
  22. private $moduleDataSetup;
  23. /**
  24. * @var \Magento\Config\Model\Config\Structure
  25. */
  26. private $structure;
  27. /**
  28. * @var \Magento\Framework\Encryption\EncryptorInterface
  29. */
  30. private $encryptor;
  31. /**
  32. * @var \Magento\Framework\App\State
  33. */
  34. private $state;
  35. /**
  36. * SodiumChachaPatch constructor.
  37. * @param \Magento\Framework\Setup\ModuleDataSetupInterface $moduleDataSetup
  38. * @param \Magento\Config\Model\Config\Structure\Proxy $structure
  39. * @param \Magento\Framework\Encryption\EncryptorInterface $encryptor
  40. * @param \Magento\Framework\App\State $state
  41. * @param \Magento\Framework\Config\ScopeInterface|null $scope
  42. */
  43. public function __construct(
  44. \Magento\Framework\Setup\ModuleDataSetupInterface $moduleDataSetup,
  45. \Magento\Config\Model\Config\Structure\Proxy $structure,
  46. \Magento\Framework\Encryption\EncryptorInterface $encryptor,
  47. \Magento\Framework\App\State $state,
  48. \Magento\Framework\Config\ScopeInterface $scope = null
  49. ) {
  50. $this->moduleDataSetup = $moduleDataSetup;
  51. $this->structure = $structure;
  52. $this->encryptor = $encryptor;
  53. $this->state = $state;
  54. $this->scope = $scope ?? ObjectManager::getInstance()->get(\Magento\Framework\Config\ScopeInterface::class);
  55. }
  56. /**
  57. * @inheritdoc
  58. */
  59. public function apply()
  60. {
  61. $this->moduleDataSetup->startSetup();
  62. $this->reEncryptSystemConfigurationValues();
  63. $this->moduleDataSetup->endSetup();
  64. }
  65. /**
  66. * @inheritdoc
  67. */
  68. public static function getDependencies()
  69. {
  70. return [];
  71. }
  72. /**
  73. * @inheritdoc
  74. */
  75. public function getAliases()
  76. {
  77. return [];
  78. }
  79. /**
  80. * Re encrypt sensitive data in the system configuration
  81. */
  82. private function reEncryptSystemConfigurationValues()
  83. {
  84. $table = $this->moduleDataSetup->getTable('core_config_data');
  85. $hasEncryptedData = $this->moduleDataSetup->getConnection()->fetchOne(
  86. $this->moduleDataSetup->getConnection()
  87. ->select()
  88. ->from($table, [new \Zend_Db_Expr('count(value)')])
  89. ->where('value LIKE ?', '0:2%')
  90. );
  91. if ($hasEncryptedData !== '0') {
  92. $currentScope = $this->scope->getCurrentScope();
  93. $structure = $this->structure;
  94. $paths = $this->state->emulateAreaCode(
  95. \Magento\Framework\App\Area::AREA_ADMINHTML,
  96. function () use ($structure) {
  97. $this->scope->setCurrentScope(\Magento\Framework\App\Area::AREA_ADMINHTML);
  98. /** Returns list of structure paths to be re encrypted */
  99. $paths = $structure->getFieldPathsByAttribute(
  100. 'backend_model',
  101. \Magento\Config\Model\Config\Backend\Encrypted::class
  102. );
  103. /** Returns list of mapping between configPath => [structurePaths] */
  104. $mappedPaths = $structure->getFieldPaths();
  105. foreach ($mappedPaths as $mappedPath => $data) {
  106. foreach ($data as $structurePath) {
  107. if ($structurePath !== $mappedPath && $key = array_search($structurePath, $paths)) {
  108. $paths[$key] = $mappedPath;
  109. }
  110. }
  111. }
  112. return array_unique($paths);
  113. }
  114. );
  115. $this->scope->setCurrentScope($currentScope);
  116. // walk through found data and re-encrypt it
  117. if ($paths) {
  118. $values = $this->moduleDataSetup->getConnection()->fetchPairs(
  119. $this->moduleDataSetup->getConnection()
  120. ->select()
  121. ->from($table, ['config_id', 'value'])
  122. ->where('path IN (?)', $paths)
  123. ->where('value NOT LIKE ?', '')
  124. );
  125. foreach ($values as $configId => $value) {
  126. $this->moduleDataSetup->getConnection()->update(
  127. $table,
  128. ['value' => $this->encryptor->encrypt($this->encryptor->decrypt($value))],
  129. ['config_id = ?' => (int)$configId]
  130. );
  131. }
  132. }
  133. }
  134. }
  135. }