diff --git a/core/lib/Drupal/Core/Config/ConfigImporter.php b/core/lib/Drupal/Core/Config/ConfigImporter.php index 66f51e4..85f203e 100644 --- a/core/lib/Drupal/Core/Config/ConfigImporter.php +++ b/core/lib/Drupal/Core/Config/ConfigImporter.php @@ -263,6 +263,9 @@ public function import() { */ public function validate() { if (!$this->validated) { + if (!$this->storageComparer->validateSiteUuid()) { + throw new ConfigImporterException('Site UUID value in source storage does not match target storage value.'); + } $this->notify('validate'); $this->validated = TRUE; } diff --git a/core/lib/Drupal/Core/Config/StorageComparer.php b/core/lib/Drupal/Core/Config/StorageComparer.php index 834f439..8170517 100644 --- a/core/lib/Drupal/Core/Config/StorageComparer.php +++ b/core/lib/Drupal/Core/Config/StorageComparer.php @@ -195,4 +195,12 @@ protected function getTargetNames() { return $this->targetNames; } + /** + * {@inheritdoc} + */ + public function validateSiteUuid() { + $source = $this->sourceStorage->read('system.site'); + $target = $this->targetStorage->read('system.site'); + return $source['uuid'] == $target['uuid']; + } } diff --git a/core/lib/Drupal/Core/Config/StorageComparerInterface.php b/core/lib/Drupal/Core/Config/StorageComparerInterface.php index 5ca0f3e..fadc5dd 100644 --- a/core/lib/Drupal/Core/Config/StorageComparerInterface.php +++ b/core/lib/Drupal/Core/Config/StorageComparerInterface.php @@ -117,4 +117,11 @@ public function reset(); */ public function hasChanges($ops = array('delete', 'create', 'update')); + /** + * Validates that the system.site::uuid match in the source and target match. + * + * @return bool + * TRUE if there are changes to process and FALSE if not. + */ + public function validateSiteUuid(); } diff --git a/core/modules/config/lib/Drupal/config/Form/ConfigSync.php b/core/modules/config/lib/Drupal/config/Form/ConfigSync.php index 4287d86..55ec3e7 100644 --- a/core/modules/config/lib/Drupal/config/Form/ConfigSync.php +++ b/core/modules/config/lib/Drupal/config/Form/ConfigSync.php @@ -160,6 +160,11 @@ public function buildForm(array $form, array &$form_state) { $form['actions']['#access'] = FALSE; return $form; } + elseif (!$config_comparer->validateSiteUuid()) { + drupal_set_message($this->t('The value of the site UUID in the staging directory does not match the current value. Configuration synchronisation requires that sites have the same site uuid so that stuff works.'), 'error'); + $form['actions']['#access'] = FALSE; + return $form; + } else { // Store the comparer for use in the submit. $form_state['storage_comparer'] = $config_comparer; diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigExportImportUITest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigExportImportUITest.php index 539879a..492d713 100644 --- a/core/modules/config/lib/Drupal/config/Tests/ConfigExportImportUITest.php +++ b/core/modules/config/lib/Drupal/config/Tests/ConfigExportImportUITest.php @@ -42,6 +42,13 @@ class ConfigExportImportUITest extends WebTestBase { protected $admin_role; /** + * The site UUID. + * + * @var string + */ + protected $siteUuid; + + /** * Sort methods alphabetically in order to allow for a predictable sequence. */ const SORT_METHODS = TRUE; @@ -77,6 +84,7 @@ function testExport() { ->save(); $this->drupalPostForm('admin/config/development/configuration/full/export', array(), 'Export'); $this->tarball = $this->drupalGetContent(); + $this->siteUuid = \Drupal::config('system.site')->get('uuid'); } /** @@ -104,6 +112,8 @@ function testImport() { * The name of the tarball containing the configuration to be imported. */ protected function doImport($filename) { + // Ensure site uuids match so that import works. + \Drupal::config('system.site')->set('uuid', $this->siteUuid)->save(); $this->assertNotEqual($this->slogan, \Drupal::config('system.site')->get('slogan')); $this->drupalPostForm('admin/config/development/configuration/full/import', array('files[import_tarball]' => $filename), 'Upload'); $this->drupalPostForm(NULL, array(), 'Import all'); diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigImportUITest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigImportUITest.php index 2f4ac16..f218949 100644 --- a/core/modules/config/lib/Drupal/config/Tests/ConfigImportUITest.php +++ b/core/modules/config/lib/Drupal/config/Tests/ConfigImportUITest.php @@ -116,6 +116,23 @@ function testImportLock() { } /** + * Tests verification of site UUID before importing configuration. + */ + function testImportSiteUuidValidation() { + $staging = \Drupal::service('config.storage.staging'); + // Create updated configuration object. + $config_data = \Drupal::config('system.site')->get(); + // Generate a new site UUID. + $config_data['uuid'] = \Drupal::service('uuid')->generate(); + $staging->write('system.site', $config_data); + + // Verify that there are configuration differences to import. + $this->drupalGet('admin/config/development/configuration'); + $this->assertText(t('The value of the site UUID in the staging directory does not match the current value. Configuration synchronisation requires that sites have the same site uuid so that stuff works.')); + $this->assertNoFieldById('edit-submit', t('Import all')); + } + + /** * Tests the screen that shows differences between active and staging. */ function testImportDiff() { diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigImporterTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigImporterTest.php index ef9a9ee..6189fb4 100644 --- a/core/modules/config/lib/Drupal/config/Tests/ConfigImporterTest.php +++ b/core/modules/config/lib/Drupal/config/Tests/ConfigImporterTest.php @@ -97,6 +97,25 @@ function testEmptyImportFails() { } /** + * Tests verification of site UUID before importing configuration. + */ + function testSiteUuidValidate() { + $staging = \Drupal::service('config.storage.staging'); + // Create updated configuration object. + $config_data = \Drupal::config('system.site')->get(); + // Generate a new site UUID. + $config_data['uuid'] = \Drupal::service('uuid')->generate(); + $staging->write('system.site', $config_data); + try { + $this->configImporter->reset()->import(); + $this->assertFalse(FALSE, "ConfigImporterException not thrown, we didn't stop an invalid import due to mis-matching site UUID."); + } + catch (ConfigImporterException $e) { + $this->assertEqual($e->getMessage(), 'Site UUID value in source storage does not match target storage value.'); + } + } + + /** * Tests deletion of configuration during import. */ function testDeleted() { diff --git a/core/modules/system/config/schema/system.schema.yml b/core/modules/system/config/schema/system.schema.yml index 0dabf5e..0faadf8 100644 --- a/core/modules/system/config/schema/system.schema.yml +++ b/core/modules/system/config/schema/system.schema.yml @@ -35,6 +35,9 @@ system.site: langcode: type: string label: 'Default language' + uuid: + type: string + label: 'Site UUID' system.maintenance: type: mapping diff --git a/core/modules/system/config/system.site.yml b/core/modules/system/config/system.site.yml index b1c5df5..32b1806 100644 --- a/core/modules/system/config/system.site.yml +++ b/core/modules/system/config/system.site.yml @@ -8,3 +8,4 @@ page: admin_compact_mode: false weight_select_max: 100 langcode: en +uuid: '' diff --git a/core/modules/system/system.install b/core/modules/system/system.install index 14153a0..34d8dde 100644 --- a/core/modules/system/system.install +++ b/core/modules/system/system.install @@ -565,6 +565,11 @@ function system_install() { // Populate the cron key state variable. $cron_key = Crypt::randomStringHashed(55); \Drupal::state()->set('system.cron_key', $cron_key); + + // Populate the site UUID. + \Drupal::config('system.site') + ->set('uuid', \Drupal::service('uuid')->generate()) + ->save(); } /**