Commit f237e589 authored by webchick's avatar webchick

Issue #1701014 by disasm, boztek, heyrocker, sun, xjm: Fixed Validate config object names.

parent 0f8cedfd
......@@ -182,6 +182,8 @@ function config_sync_changes(array $config_changes, StorageInterface $source_sto
$factory = drupal_container()->get('config.factory');
foreach (array('delete', 'create', 'change') as $op) {
foreach ($config_changes[$op] as $name) {
// Validate the configuration object name before importing it.
Config::validateName($name);
if ($op == 'delete') {
$target_storage->delete($name);
}
......@@ -256,6 +258,8 @@ function config_import_invoke_owner(array $config_changes, StorageInterface $sou
// Call to the configuration entity's storage controller to handle the
// configuration change.
$handled_by_module = FALSE;
// Validate the configuration object name before importing it.
Config::validateName($name);
if ($entity_type = config_get_entity_type_by_name($name)) {
$old_config = new Config($name, $target_storage);
$old_config->load();
......
......@@ -8,6 +8,7 @@
namespace Drupal\Core\Config;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Config\ConfigNameException;
use Symfony\Component\EventDispatcher\EventDispatcher;
/**
......@@ -15,6 +16,18 @@
*/
class Config {
/**
* The maximum length of a configuration object name.
*
* Many filesystems (including HFS, NTFS, and ext4) have a maximum file name
* length of 255 characters. To ensure that no configuration objects
* incompatible with this limitation are created, we enforce a maximum name
* length of 250 characters (leaving 5 characters for the file extension).
*
* @see http://en.wikipedia.org/wiki/Comparison_of_file_systems
*/
const MAX_NAME_LENGTH = 250;
/**
* The name of the configuration object.
*
......@@ -122,6 +135,37 @@ public function setName($name) {
return $this;
}
/**
* Validates the configuration object name.
*
* @throws \Drupal\Core\Config\ConfigNameException
*
* @see Config::MAX_NAME_LENGTH
*/
public static function validateName($name) {
// The name must be namespaced by owner.
if (strpos($name, '.') === FALSE) {
throw new ConfigNameException(format_string('Missing namespace in Config object name @name.', array(
'@name' => $name,
)));
}
// The name must be shorter than Config::MAX_NAME_LENGTH characters.
if (strlen($name) > self::MAX_NAME_LENGTH) {
throw new ConfigNameException(format_string('Config object name @name exceeds maximum allowed length of @length characters.', array(
'@name' => $name,
'@length' => self::MAX_NAME_LENGTH,
)));
}
// The name must not contain any of the following characters:
// : ? * < > " ' / \
if (preg_match('/[:?*<>"\'\/\\\\]/', $name)) {
throw new ConfigNameException(format_string('Invalid character in Config object name @name.', array(
'@name' => $name,
)));
}
}
/**
* Returns whether this configuration object is new.
*
......@@ -390,6 +434,8 @@ public function load() {
* The configuration object.
*/
public function save() {
// Validate the configuration object name before saving.
static::validateName($this->name);
if (!$this->isLoaded) {
$this->load();
}
......@@ -448,7 +494,7 @@ protected function notify($config_event_name) {
$this->eventDispatcher->dispatch('config.' . $config_event_name, new ConfigEvent($this));
}
/*
/**
* Merges data into a configuration object.
*
* @param array $data_to_merge
......
<?php
/**
* @file
* Contains \Drupal\Core\Config\ConfigNameException.
*/
namespace Drupal\Core\Config;
/**
* Exception thrown when a config object name is invalid.
*/
class ConfigNameException extends ConfigException {}
......@@ -7,6 +7,7 @@
namespace Drupal\config\Tests;
use Drupal\Core\Config\ConfigNameException;
use Drupal\simpletest\DrupalUnitTestBase;
/**
......@@ -103,4 +104,88 @@ function testCRUD() {
$this->assertIdentical($new_config->get('404'), $expected_values['404']);
}
/**
* Tests the validation of configuration object names.
*/
function testNameValidation() {
// Verify that an object name without namespace causes an exception.
$name = 'nonamespace';
$message = 'Expected ConfigNameException was thrown for a name without a namespace.';
try {
config($name)->save();
$this->fail($message);
}
catch (ConfigNameException $e) {
$this->pass($message);
}
// Verify that a name longer than the maximum length causes an exception.
$name = 'config_test.herman_melville.moby_dick_or_the_whale.harper_1851.now_small_fowls_flew_screaming_over_the_yet_yawning_gulf_a_sullen_white_surf_beat_against_its_steep_sides_then_all_collapsed_and_the_great_shroud_of_the_sea_rolled_on_as_it_rolled_five_thousand_years_ago';
$message = 'Expected ConfigNameException was thrown for a name longer than Config::MAX_NAME_LENGTH.';
try {
config($name)->save();
$this->fail($message);
}
catch (ConfigNameException $e) {
$this->pass($message);
}
// Verify that disallowed characters in the name cause an exception.
$characters = $test_characters = array(':', '?', '*', '<', '>', '"', '\'', '/', '\\');
foreach ($test_characters as $i => $c) {
try {
$name = 'namespace.object' . $c;
$config = config($name);
$config->save();
}
catch (ConfigNameException $e) {
unset($test_characters[$i]);
}
}
$this->assertTrue(empty($test_characters), format_string('Expected ConfigNameException was thrown for all invalid name characters: @characters', array(
'@characters' => implode(' ', $characters),
)));
// Verify that a valid config object name can be saved.
$name = 'namespace.object';
$message = 'ConfigNameException was not thrown for a valid object name.';
try {
$config = config($name);
$config->save();
$this->pass($message);
}
catch (\Exception $e) {
$this->fail($message);
}
// Verify an exception is thrown when importing configuration with an
// invalid name (missing a namespace).
$message = 'Expected ConfigNameException was thrown when attempting to install invalid configuration.';
try {
$this->enableModules(array('config_test_invalid_name'));
$this->fail($message);
}
catch (ConfigNameException $e) {
$this->pass($message);
}
// Write configuration with an invalid name (missing a namespace) to
// staging.
$storage = $this->container->get('config.storage');
$staging = $this->container->get('config.storage.staging');
$manifest_data = config('manifest.invalid_object_name')->get();
$manifest_data['new']['name'] = 'invalid';
$staging->write('manifest.invalid_object_name', $manifest_data);
// Verify that an exception is thrown when synchronizing.
$message = 'Expected ConfigNameException was thrown when attempting to sync invalid configuration.';
try {
config_import();
$this->fail($message);
}
catch (ConfigNameException $e) {
$this->pass($message);
}
}
}
name = Invalid configuration name
package = Core
version = VERSION
core = 8.x
hidden = TRUE
<?php
/**
* @file
* Test module containing a configuration file with an invalid name.
*/
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment