Commit 270dcd2e authored by Crell's avatar Crell

Merge remote-tracking branch 'upstream/8.x' into kernel

parents b77faa14 352645e4
......@@ -598,7 +598,7 @@ function drupal_settings_initialize() {
global $base_url, $base_path, $base_root, $script_path;
// Export the following settings.php variables to the global namespace
global $databases, $cookie_domain, $conf, $installed_profile, $update_free_access, $db_url, $db_prefix, $drupal_hash_salt, $is_https, $base_secure_url, $base_insecure_url, $config_directory_name, $config_signature_key;
global $databases, $cookie_domain, $conf, $installed_profile, $update_free_access, $db_url, $db_prefix, $drupal_hash_salt, $is_https, $base_secure_url, $base_insecure_url, $config_directory_name;
$conf = array();
if (file_exists(DRUPAL_ROOT . '/' . conf_path() . '/settings.php')) {
......
......@@ -71,7 +71,7 @@ function config_install_default_config($module) {
* @return
* An array of file names under a branch.
*/
function config_get_signed_file_storage_names_with_prefix($prefix = '') {
function config_get_files_with_prefix($prefix = '') {
$files = glob(config_get_config_directory() . '/' . $prefix . '*.xml');
$clean_name = function ($value) {
return basename($value, '.xml');
......@@ -79,23 +79,6 @@ function config_get_signed_file_storage_names_with_prefix($prefix = '') {
return array_map($clean_name, $files);
}
/**
* Generates a hash of a config file's contents using our encryption key.
*
* @param $data
* The contents of a configuration file.
*
* @return
* A hash of the data.
*/
function config_sign_data($data) {
// The configuration key is loaded from settings.php and imported into the global namespace
global $config_signature_key;
// SHA-512 is both secure and very fast on 64 bit CPUs.
return hash_hmac('sha512', $data, $config_signature_key);
}
/**
* @todo
*
......
......@@ -3213,6 +3213,56 @@ function form_process_actions($element, &$form_state) {
return $element;
}
/**
* #process callback for #pattern form element property.
*
* @param $element
* An associative array containing the properties and children of the
* generic input element.
* @param $form_state
* The $form_state array for the form this element belongs to.
*
* @return
* The processed element.
*
* @see form_validate_pattern()
*/
function form_process_pattern($element, &$form_state) {
if (isset($element['#pattern']) && !isset($element['#attributes']['pattern'])) {
$element['#attributes']['pattern'] = $element['#pattern'];
$element['#element_validate'][] = 'form_validate_pattern';
}
return $element;
}
/**
* #element_validate callback for #pattern form element property.
*
* @param $element
* An associative array containing the properties and children of the
* generic form element.
* @param $form_state
* The $form_state array for the form this element belongs to.
*
* @see form_process_pattern()
*/
function form_validate_pattern($element, &$form_state) {
if ($element['#value'] !== '') {
// The pattern must match the entire string and should have the same
// behavior as the RegExp object in ECMA 262.
// - Use bracket-style delimiters to avoid introducing a special delimiter
// character like '/' that would have to be escaped.
// - Put in brackets so that the pattern can't interfere with what's
// prepended and appended.
$pattern = '{^(?:' . $element['#pattern'] . ')$}';
if (!preg_match($pattern, $element['#value'])) {
form_error($element, t('%name field is not in the right format.', array('%name' => $element['#title'])));
}
}
}
/**
* Processes a container element.
*
......@@ -3951,8 +4001,8 @@ function theme_tel($variables) {
* @param $variables
* An associative array containing:
* - element: An associative array containing the properties of the element.
* Properties used: #title, #value, #description, #size, #min, #max,
* #placeholder, #required, #attributes, #step.
* Properties used: #title, #value, #description, #min, #max, #placeholder,
* #required, #attributes, #step.
*
* @ingroup themeable
*/
......@@ -3960,7 +4010,7 @@ function theme_number($variables) {
$element = $variables['element'];
$element['#attributes']['type'] = 'number';
element_set_attributes($element, array('id', 'name', 'value', 'size', 'step', 'min', 'max', 'maxlength', 'placeholder'));
element_set_attributes($element, array('id', 'name', 'value', 'step', 'min', 'max', 'placeholder'));
_form_set_class($element, array('form-number'));
$output = '<input' . drupal_attributes($element['#attributes']) . ' />';
......@@ -3974,8 +4024,8 @@ function theme_number($variables) {
* @param $variables
* An associative array containing:
* - element: An associative array containing the properties of the element.
* Properties used: #title, #value, #description, #size, #min, #max,
* #required, #attributes, #step.
* Properties used: #title, #value, #description, #min, #max, #attributes,
* #step.
*
* @ingroup themeable
*/
......@@ -4031,6 +4081,36 @@ function form_validate_number(&$element, &$form_state) {
}
}
/**
* Determines the value for a range element.
*
* Make sure range elements always have a value. The 'required' attribute is not
* allowed for range elements.
*
* @param $element
* The form element whose value is being populated.
* @param $input
* The incoming input to populate the form element. If this is FALSE, the
* element's default value should be returned.
*
* @return
* The data that will appear in the $form_state['values'] collection for
* this element. Return nothing to use the default.
*/
function form_type_range_value($element, $input = FALSE) {
if ($input === '') {
$offset = ($element['#max'] - $element['#min']) / 2;
// Round to the step.
if (strtolower($element['#step']) != 'any') {
$steps = round($offset / $element['#step']);
$offset = $element['#step'] * $steps;
}
return $element['#min'] + $offset;
}
}
/**
* Returns HTML for a url form element.
*
......
......@@ -1022,11 +1022,6 @@ function install_settings_form_submit($form, &$form_state) {
'required' => TRUE,
);
$settings['config_signature_key'] = array(
'value' => drupal_hash_base64(drupal_random_bytes(55)),
'required' => TRUE,
);
// This duplicates drupal_get_token() because that function can't work yet.
// Wondering if it makes sense to move this later in the process, but its
// nice having all the settings stuff here.
......@@ -1036,7 +1031,7 @@ function install_settings_form_submit($form, &$form_state) {
// already has the db stuff in it, and right now in that case your
// config directory never gets created. So this needs to be moved elsewhere.
$settings['config_directory_name'] = array(
'value' => 'config_' . drupal_hmac_base64('', session_id() . $settings['config_signature_key']['value'] . $settings['drupal_hash_salt']['value']),
'value' => 'config_' . drupal_hmac_base64('', session_id() . $settings['drupal_hash_salt']['value']),
'required' => TRUE,
);
......
<?php
use Drupal\Core\Database\Database;
use Drupal\Core\Config\SignedFileStorage;
use Drupal\Core\Config\FileStorage;
/**
* Indicates that a module has not been installed yet.
......@@ -403,9 +403,9 @@ function drupal_uninstall_modules($module_list = array(), $uninstall_dependents
$parts = explode('/', $file);
$file = array_pop($parts);
$config_name = str_replace('.xml', '', $file);
$signed_storage = new SignedFileStorage($config_name);
$file_storage = new FileStorage($config_name);
// Delete the configuration from storage.
$signed_storage->delete();
$file_storage->delete();
}
}
......
<?php
namespace Drupal\Core\Config;
use Drupal\Core\Config\ConfigFileStorageException;
/**
* @todo
*/
class ConfigFileStorageSignatureException extends ConfigFileStorageException {}
......@@ -3,7 +3,7 @@
namespace Drupal\Core\Config;
use Drupal\Core\Config\DrupalConfigVerifiedStorageInterface;
use Drupal\Core\Config\SignedFileStorage;
use Drupal\Core\Config\FileStorage;
/**
* @todo
......@@ -13,11 +13,11 @@ abstract class DrupalConfigVerifiedStorage implements DrupalConfigVerifiedStorag
protected $name;
/**
* The local signed file object to read from and write to.
* The local file object to read from and write to.
*
* @var SignedFileStorage
* @var Drupal\Core\Config\FileStorage
*/
protected $signedFile;
protected $fileStorage;
/**
* Implements DrupalConfigVerifiedStorageInterface::__construct().
......@@ -27,16 +27,16 @@ function __construct($name) {
}
/**
* Instantiates a new signed file object or returns the existing one.
* Instantiates a new file storage object or returns the existing one.
*
* @return SignedFileStorage
* The signed file object for this configuration object.
* @return Drupal\Core\Config\FileStorage
* The file object for this configuration object.
*/
protected function signedFileStorage() {
if (!isset($this->signedFile)) {
$this->signedFile = new SignedFileStorage($this->name);
protected function fileStorage() {
if (!isset($this->fileStorage)) {
$this->fileStorage = new FileStorage($this->name);
}
return $this->signedFile;
return $this->fileStorage;
}
/**
......@@ -50,7 +50,7 @@ public function copyToFile() {
* Implements DrupalConfigVerifiedStorageInterface::deleteFile().
*/
public function deleteFile() {
return $this->signedFileStorage()->delete();
return $this->fileStorage()->delete();
}
/**
......@@ -67,7 +67,7 @@ public function copyFromFile() {
* @todo
*/
public function readFromFile() {
return $this->signedFileStorage()->read($this->name);
return $this->fileStorage()->read($this->name);
}
/**
......@@ -89,7 +89,7 @@ public function write($data) {
* Implements DrupalConfigVerifiedStorageInterface::writeToFile().
*/
public function writeToFile($data) {
return $this->signedFileStorage()->write($data);
return $this->fileStorage()->write($data);
}
/**
......
......@@ -6,7 +6,7 @@
* Defines an interface for verified storage manipulation.
*
* This class allows reading and writing configuration data from/to the
* verified storage and copying to/from the signed file storing the same data.
* verified storage and copying to/from the file storing the same data.
*/
interface DrupalConfigVerifiedStorageInterface {
......
......@@ -3,16 +3,15 @@
namespace Drupal\Core\Config;
/**
* Represents the signed file storage interface.
* Represents the file storage interface.
*
* Classes implementing this interface allow reading and writing configuration
* data to and from disk, while automatically managing and verifying
* cryptographic signatures.
* data to and from disk.
*/
class SignedFileStorage {
class FileStorage {
/**
* Constructs a SignedFileStorage object.
* Constructs a FileStorage object.
*
* @param string $name
* The name for the configuration data. Should be lowercase.
......@@ -22,24 +21,20 @@ public function __construct($name) {
}
/**
* Reads and returns a signed file and its signature.
* Reads and returns a file.
*
* @return
* An array with "signature" and "data" keys.
* The data of the file.
*
* @throws
* Exception
*/
protected function readWithSignature() {
$content = file_get_contents($this->getFilePath());
if ($content === FALSE) {
protected function readData() {
$data = file_get_contents($this->getFilePath());
if ($data === FALSE) {
throw new \Exception('Read file is invalid.');
}
$signature = file_get_contents($this->getFilePath() . '.sig');
if ($signature === FALSE) {
throw new \Exception('Signature file is invalid.');
}
return array('data' => $content, 'signature' => $signature);
return $data;
}
/**
......@@ -62,41 +57,6 @@ public function getFilePath() {
return config_get_config_directory() . '/' . $this->name . '.xml';
}
/**
* Recreates the signature for the file.
*/
public function resign() {
if ($this->exists()) {
$parts = $this->readWithSignature();
$this->write($parts['data']);
}
}
/**
* Cryptographically verifies the integrity of the configuration file.
*
* @param $contentOnSuccess
* Whether or not to return the contents of the verified configuration file.
*
* @return mixed
* If $contentOnSuccess was TRUE, returns the contents of the verified
* configuration file; otherwise returns TRUE on success. Always returns
* FALSE if the configuration file was not successfully verified.
*/
public function verify($contentOnSuccess = FALSE) {
if ($this->exists()) {
$split = $this->readWithSignature();
$expected_signature = config_sign_data($split['data']);
if ($expected_signature === $split['signature']) {
if ($contentOnSuccess) {
return $split['data'];
}
return TRUE;
}
}
return FALSE;
}
/**
* Writes the contents of the configuration file to disk.
*
......@@ -107,13 +67,9 @@ public function verify($contentOnSuccess = FALSE) {
* Exception
*/
public function write($data) {
$signature = config_sign_data($data);
if (!file_put_contents($this->getFilePath(), $data)) {
throw new \Exception('Failed to write configuration file: ' . $this->getFilePath());
}
if (!file_put_contents($this->getFilePath() . '.sig', $signature)) {
throw new \Exception('Failed to write signature file: ' . $this->getFilePath());
}
}
/**
......@@ -124,12 +80,10 @@ public function write($data) {
*/
public function read() {
if ($this->exists()) {
$verification = $this->verify(TRUE);
if ($verification === FALSE) {
throw new \Exception('Invalid signature in file header.');
}
return $verification;
$data = $this->readData();
return $data;
}
return FALSE;
}
/**
......@@ -138,6 +92,5 @@ public function read() {
public function delete() {
// Needs error handling and etc.
@drupal_unlink($this->getFilePath());
@drupal_unlink($this->getFilePath() . '.sig');
}
}
(function ($) {
"use strict";
/**
* Provides Ajax page updating via jQuery $.ajax (Asynchronous JavaScript and XML).
*
......
/**
* @file
* Conditionally hide or show the appropriate settings and saved defaults
......@@ -7,6 +6,8 @@
(function ($) {
"use strict";
Drupal.behaviors.authorizeFileTransferForm = {
attach: function(context) {
$('#edit-connection-settings-authorize-filetransfer-default').change(function() {
......
(function ($) {
"use strict";
/**
* Attaches the autocomplete behavior to all required fields.
*/
......
(function ($) {
"use strict";
/**
* Attaches the batch behavior to progress bars.
*/
......
(function ($) {
"use strict";
/**
* Toggle the visibility of a fieldset using smooth animations.
*/
......
......@@ -5,6 +5,8 @@ jQuery.noConflict();
(function ($) {
"use strict";
/**
* Attach all registered behaviors to a page element.
*
......
(function ($) {
"use strict";
/**
* Retrieves the summary for the first element.
*/
......
(function ($) {
"use strict";
/**
* Attach the machine-readable name form element behavior.
*/
......
(function ($) {
"use strict";
/**
* A progressbar object. Initialized with the given id. Must be inserted into
* the DOM afterwards through progressBar.element.
......
(function ($) {
"use strict";
/**
* The base States namespace.
*
......
(function ($) {
"use strict";
/**
* Drag and drop table rows with field manipulation.
*
......
(function ($) {
"use strict";
/**
* Attaches sticky table headers.
*/
......
(function ($) {
"use strict";
Drupal.behaviors.tableSelect = {
attach: function (context, settings) {
// Select the inner-most table in case of nested tables.
......
(function ($) {
"use strict";
/**
* Set the client's system time zone as default values of form fields.
*/
......
(function ($) {
"use strict";
/**
* This script transforms a set of fieldsets into a stack of vertical
* tabs. Another tab pane can be selected by clicking on the respective
......
(function ($) {
"use strict";
/**
* Provide the summary information for the block settings vertical tabs.
*/
......
(function ($) {
"use strict";
Drupal.behaviors.bookFieldsetSummaries = {
attach: function (context) {
$(context).find('fieldset.book-outline-form').drupalSetSummary(function (context) {
......
......@@ -102,7 +102,7 @@ function book_node_view_link(Node $node, $view_mode) {
if ((user_access('add content to books') || user_access('administer book outlines')) && node_access('create', $child_type) && $node->status == 1 && $node->book['depth'] < MENU_MAX_DEPTH) {
$links['book_add_child'] = array(
'title' => t('Add child page'),
'href' => 'node/add/' . str_replace('_', '-', $child_type),
'href' => 'node/add/' . $child_type,
'query' => array('parent' => $node->book['mlid']),
);
}
......