From 141cb6f304b1ab2087c75154e8d67057b8cd03bb Mon Sep 17 00:00:00 2001
From: Dries <dries@buytaert.net>
Date: Fri, 20 Dec 2013 11:27:41 -0500
Subject: [PATCH] =?UTF-8?q?Issue=20#2130811=20by=20alexpott,=20G=C3=A1bor?=
 =?UTF-8?q?=20Hojtsy,=20vijaycs85,=20sun,=20Wim=20Leers:=20Use=20config=20?=
 =?UTF-8?q?schema=20in=20saving=20and=20validating=20configuration=20form?=
 =?UTF-8?q?=20to=20ensure=20data=20is=20consistent=20and=20correct.?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 core/core.services.yml                        |   2 +-
 core/includes/config.inc                      |   5 +-
 core/includes/install.core.inc                |   9 +-
 core/lib/Drupal/Core/Config/Config.php        | 140 +++++++++++++++++-
 core/lib/Drupal/Core/Config/ConfigFactory.php |  23 ++-
 .../lib/Drupal/Core/Config/ConfigImporter.php |  19 ++-
 .../lib/Drupal/Core/Config/Schema/Mapping.php |   2 +-
 .../Drupal/Core/Config/Schema/Sequence.php    |  15 ++
 .../Core/TypedData/Plugin/DataType/Binary.php |   7 +
 .../TypedData/Plugin/DataType/Boolean.php     |   6 +
 .../Core/TypedData/Plugin/DataType/Float.php  |   6 +
 .../TypedData/Plugin/DataType/Integer.php     |   6 +
 .../Core/TypedData/Plugin/DataType/String.php |   6 +
 .../Core/TypedData/Plugin/DataType/Uri.php    |   2 +-
 .../Core/TypedData/PrimitiveInterface.php     |   6 +
 .../block/config/schema/block.schema.yml      |   3 +
 .../block/Tests/BlockStorageUnitTest.php      |   4 +-
 .../config/schema/breakpoint.schema.yml       |   9 ++
 .../Plugin/CKEditorPlugin/DrupalImage.php     |   1 -
 .../lib/Drupal/config/Form/ConfigSync.php     |  21 ++-
 .../config/Tests/ConfigImporterTest.php       |   3 +-
 .../Drupal/config/Tests/ConfigSchemaTest.php  |  64 +++++++-
 .../config/schema/config_test.schema.yml      |  40 +++++
 core/modules/editor/editor.admin.inc          |  25 ----
 core/modules/editor/editor.module             |   2 +-
 .../field/config/schema/field.schema.yml      |   3 +
 .../filter/config/schema/filter.schema.yml    |   3 +
 .../image/config/schema/image.schema.yml      |   6 +
 .../Tests/MigrateSystemConfigsTest.php        |   4 +-
 .../number/config/schema/number.schema.yml    |  12 +-
 .../options/config/schema/options.schema.yml  |  24 +--
 .../lib/Drupal/simpletest/TestBase.php        |   3 +-
 .../system/config/schema/system.schema.yml    |  32 +++-
 .../system/config/system.maintenance.yml      |   1 -
 .../config/schema/taxonomy.schema.yml         |   5 +-
 .../tour/config/schema/tour.schema.yml        |  12 ++
 .../user/config/schema/user.schema.yml        |   3 +
 37 files changed, 453 insertions(+), 81 deletions(-)

diff --git a/core/core.services.yml b/core/core.services.yml
index a46d1c706b9d..8f2965aa08f6 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -93,7 +93,7 @@ services:
     class: Drupal\Core\Config\ConfigFactory
     tags:
       - { name: persist }
-    arguments: ['@config.storage', '@config.context']
+    arguments: ['@config.storage', '@config.context', '@config.typed']
   config.storage.staging:
     class: Drupal\Core\Config\FileStorage
     factory_class: Drupal\Core\Config\FileStorageFactory
diff --git a/core/includes/config.inc b/core/includes/config.inc
index 8e9ce5e02790..d0f1d7bed6fd 100644
--- a/core/includes/config.inc
+++ b/core/includes/config.inc
@@ -67,6 +67,7 @@ function ($value) use ($name) {
     $config_factory = Drupal::service('config.factory');
     $context = new FreeConfigContext(Drupal::service('event_dispatcher'), Drupal::service('uuid'));
     $target_storage = Drupal::service('config.storage');
+    $typed_config = Drupal::service('config.typed');
     $config_factory->enterContext($context);
     foreach ($config_to_install as $name) {
       // Only import new config.
@@ -74,7 +75,7 @@ function ($value) use ($name) {
         continue;
       }
 
-      $new_config = new Config($name, $target_storage, $context);
+      $new_config = new Config($name, $target_storage, $context, $typed_config);
       $data = $source_storage->read($name);
       if ($data !== FALSE) {
         $new_config->setData($data);
@@ -214,7 +215,7 @@ function config_get_entity_type_by_name($name) {
  *
  * @see \Drupal\Core\TypedData\TypedDataManager::create()
  *
- * @return \Drupal\Core\TypedData\TypedConfigManager
+ * @return \Drupal\Core\Config\TypedConfigManager
  */
 function config_typed() {
   return drupal_container()->get('config.typed');
diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index db9cf588693f..8762f0b1e361 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -386,9 +386,16 @@ function install_begin_request(&$install_state) {
       ->setFactoryService(new Reference('config.context.factory'))
       ->setFactoryMethod('get');
 
+    $container->register('config.storage.schema', 'Drupal\Core\Config\Schema\SchemaStorage');
+
+    $container->register('config.typed', 'Drupal\Core\Config\TypedConfigManager')
+      ->addArgument(new Reference('config.storage'))
+      ->addArgument(new Reference('config.storage.schema'));
+
     $container->register('config.factory', 'Drupal\Core\Config\ConfigFactory')
       ->addArgument(new Reference('config.storage'))
-      ->addArgument(new Reference('config.context'));
+      ->addArgument(new Reference('config.context'))
+      ->addArgument(new Reference('config.typed'));
 
     // Register the 'language_manager' service.
     $container
diff --git a/core/lib/Drupal/Core/Config/Config.php b/core/lib/Drupal/Core/Config/Config.php
index 807d930b7619..f04bbe0c69d8 100644
--- a/core/lib/Drupal/Core/Config/Config.php
+++ b/core/lib/Drupal/Core/Config/Config.php
@@ -8,8 +8,12 @@
 namespace Drupal\Core\Config;
 
 use Drupal\Component\Utility\NestedArray;
+use Drupal\Component\Utility\String;
 use Drupal\Core\Config\ConfigNameException;
 use Drupal\Core\Config\Context\ContextInterface;
+use Drupal\Core\TypedData\PrimitiveInterface;
+use Drupal\Core\TypedData\Type\FloatInterface;
+use Drupal\Core\TypedData\Type\IntegerInterface;
 
 /**
  * Defines the default configuration object.
@@ -77,6 +81,20 @@ class Config {
    */
   protected $isLoaded = FALSE;
 
+  /**
+   * The config schema wrapper object for this configuration object.
+   *
+   * @var \Drupal\Core\Config\Schema\Element
+   */
+  protected $schemaWrapper;
+
+  /**
+   * The typed config manager.
+   *
+   * @var \Drupal\Core\Config\TypedConfigManager
+   */
+  protected $typedConfigManager;
+
   /**
    * Constructs a configuration object.
    *
@@ -87,11 +105,14 @@ class Config {
    *   configuration data.
    * @param \Drupal\Core\Config\Context\ContextInterface $context
    *   The configuration context used for this configuration object.
+   * @param \Drupal\Core\Config\TypedConfigManager $typed_config
+   *   The typed configuration manager service.
    */
-  public function __construct($name, StorageInterface $storage, ContextInterface $context) {
+  public function __construct($name, StorageInterface $storage, ContextInterface $context, TypedConfigManager $typed_config) {
     $this->name = $name;
     $this->storage = $storage;
     $this->context = $context;
+    $this->typedConfigManager = $typed_config;
   }
 
   /**
@@ -404,6 +425,17 @@ public function load() {
   public function save() {
     // Validate the configuration object name before saving.
     static::validateName($this->name);
+
+    // If there is a schema for this configuration object, cast all values to
+    // conform to the schema.
+    if ($this->typedConfigManager->hasConfigSchema($this->name)) {
+      // Ensure that the schema wrapper has the latest data.
+      $this->schemaWrapper = NULL;
+      foreach ($this->data as $key => $value) {
+        $this->data[$key] = $this->castValue($key, $value);
+      }
+    }
+
     if (!$this->isLoaded) {
       $this->load();
     }
@@ -466,4 +498,110 @@ public function merge(array $data_to_merge) {
     $this->replaceData(NestedArray::mergeDeepArray(array($this->data, $data_to_merge), TRUE));
     return $this;
   }
+
+  /**
+   * Gets the schema wrapper for the whole configuration object.
+   *
+   * The schema wrapper is dependent on the configuration name and the whole
+   * data structure, so if the name or the data changes in any way, the wrapper
+   * should be reset.
+   *
+   * @return \Drupal\Core\Config\Schema\Element
+   */
+  protected function getSchemaWrapper() {
+    if (!isset($this->schemaWrapper)) {
+      $definition = $this->typedConfigManager->getDefinition($this->name);
+      $this->schemaWrapper = $this->typedConfigManager->create($definition, $this->data);
+    }
+    return $this->schemaWrapper;
+  }
+
+  /**
+   * Gets the definition for the configuration key.
+   *
+   * @param string $key
+   *   A string that maps to a key within the configuration data.
+   *
+   * @return \Drupal\Core\Config\Schema\Element
+   *
+   * @throws \Drupal\Core\Config\ConfigException
+   *   Thrown when schema is incomplete.
+   */
+  protected function getSchemaForKey($key) {
+    $parts = explode('.', $key);
+    $schema_wrapper = $this->getSchemaWrapper();
+    if (count($parts) == 1) {
+      $schema = $schema_wrapper->get($key);
+    }
+    else {
+      $schema = clone $schema_wrapper;
+      foreach ($parts as $nested_key) {
+        if (!is_object($schema) || !method_exists($schema, 'get')) {
+          throw new ConfigException(String::format("Incomplete schema for !key key in configuration object !name.", array('!name' => $this->name, '!key' => $key)));
+        }
+        else {
+          $schema = $schema->get($nested_key);
+        }
+      }
+    }
+    return $schema;
+  }
+
+  /**
+   * Casts the value to correct data type using the configuration schema.
+   *
+   * @param string $key
+   *   A string that maps to a key within the configuration data.
+   * @param string $value
+   *   Value to associate with the key.
+   *
+   * @return mixed
+   *   The value cast to the type indicated in the schema.
+   */
+  protected function castValue($key, $value) {
+    if ($value === NULL) {
+      $value = NULL;
+    }
+    elseif (is_scalar($value)) {
+      try {
+        $element = $this->getSchemaForKey($key);
+        if ($element instanceof PrimitiveInterface) {
+          // Special handling for integers and floats since the configuration
+          // system is primarily concerned with saving values from the Form API
+          // we have to special case the meaning of an empty string for numeric
+          // types. In PHP this would be casted to a 0 but for the purposes of
+          // configuration we need to treat this as a NULL.
+          if ($value === '' && ($element instanceof IntegerInterface || $element instanceof FloatInterface)) {
+            $value = NULL;
+          }
+          else {
+            $value = $element->getCastedValue();
+          }
+        }
+        else {
+          // Config only supports primitive data types. If the config schema
+          // does define a type $element will be an instance of
+          // \Drupal\Core\Config\Schema\Property. Convert it to string since it
+          // is the safest possible type.
+          $value = $element->getString();
+        }
+      }
+      catch (\Exception $e) {
+        // @todo throw an exception due to an incomplete schema. Only possible
+        //   once https://drupal.org/node/1910624 is complete.
+      }
+    }
+    else {
+      // Any non-scalar value must be an array.
+      if (!is_array($value)) {
+        $value = (array) $value;
+      }
+      // Recurse into any nested keys.
+      foreach ($value as $nested_value_key => $nested_value) {
+        $value[$nested_value_key] = $this->castValue($key . '.' . $nested_value_key, $nested_value);
+      }
+    }
+    return $value;
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Config/ConfigFactory.php b/core/lib/Drupal/Core/Config/ConfigFactory.php
index 60b3d0d352ac..7b1e97ce9df8 100644
--- a/core/lib/Drupal/Core/Config/ConfigFactory.php
+++ b/core/lib/Drupal/Core/Config/ConfigFactory.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\Config;
 
 use Drupal\Core\Config\Context\ContextInterface;
+use Drupal\Core\Config\TypedConfigManager;
 
 /**
  * Defines the configuration object factory.
@@ -51,16 +52,26 @@ class ConfigFactory {
    */
   protected $cache = array();
 
+  /**
+   * The typed config manager.
+   *
+   * @var \Drupal\Core\Config\TypedConfigManager
+   */
+  protected $typedConfigManager;
+
   /**
    * Constructs the Config factory.
    *
-   * @param \Drupal\Core\Config\StorageInterface
+   * @param \Drupal\Core\Config\StorageInterface $storage
    *   The configuration storage engine.
-   * @param \Drupal\Core\Config\Context\ContextInterface
+   * @param \Drupal\Core\Config\Context\ContextInterface $context
    *   Configuration context object.
+   * @param \Drupal\Core\Config\TypedConfigManager $typed_config
+   *   The typed configuration manager.
    */
-  public function __construct(StorageInterface $storage, ContextInterface $context) {
+  public function __construct(StorageInterface $storage, ContextInterface $context, TypedConfigManager $typed_config) {
     $this->storage = $storage;
+    $this->typedConfigManager = $typed_config;
     $this->enterContext($context);
   }
 
@@ -80,7 +91,7 @@ public function get($name) {
       return $this->cache[$cache_key];
     }
 
-    $this->cache[$cache_key] = new Config($name, $this->storage, $context);
+    $this->cache[$cache_key] = new Config($name, $this->storage, $context, $this->typedConfigManager);
     return $this->cache[$cache_key]->init();
   }
 
@@ -115,7 +126,7 @@ public function loadMultiple(array $names) {
       $storage_data = $this->storage->readMultiple($names);
       foreach ($storage_data as $name => $data) {
         $cache_key = $this->getCacheKey($name, $context);
-        $this->cache[$cache_key] = new Config($name, $this->storage, $context);
+        $this->cache[$cache_key] = new Config($name, $this->storage, $context, $this->typedConfigManager);
         $this->cache[$cache_key]->initWithData($data);
         $list[$name] = $this->cache[$cache_key];
       }
@@ -172,7 +183,7 @@ public function rename($old_name, $new_name) {
     }
     else {
       // Create the config object if it's not yet loaded into the static cache.
-      $config = new Config($old_name, $this->storage, $context);
+      $config = new Config($old_name, $this->storage, $context, $this->typedConfigManager);
     }
 
     $this->cache[$new_cache_key] = $config;
diff --git a/core/lib/Drupal/Core/Config/ConfigImporter.php b/core/lib/Drupal/Core/Config/ConfigImporter.php
index f96d965b3843..66f51e452c96 100644
--- a/core/lib/Drupal/Core/Config/ConfigImporter.php
+++ b/core/lib/Drupal/Core/Config/ConfigImporter.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\Config;
 
 use Drupal\Core\Config\Context\FreeConfigContext;
+use Drupal\Core\Config\TypedConfigManager;
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Lock\LockBackendInterface;
 use Drupal\Component\Uuid\UuidInterface;
@@ -102,6 +103,13 @@ class ConfigImporter {
    */
   protected $uuidService;
 
+  /**
+   * The typed config manager.
+   *
+   * @var \Drupal\Core\Config\TypedConfigManager
+   */
+  protected $typedConfigManager;
+
   /**
    * Constructs a configuration import object.
    *
@@ -118,14 +126,17 @@ class ConfigImporter {
    *   The lock backend to ensure multiple imports do not occur at the same time.
    * @param \Drupal\Component\Uuid\UuidInterface $uuid_service
    *   The UUID service.
+   * @param \Drupal\Core\Config\TypedConfigManager $typed_config
+   *   The typed configuration manager.
    */
-  public function __construct(StorageComparerInterface $storage_comparer, EventDispatcherInterface $event_dispatcher, ConfigFactory $config_factory, EntityManagerInterface $entity_manager, LockBackendInterface $lock, UuidInterface $uuid_service) {
+  public function __construct(StorageComparerInterface $storage_comparer, EventDispatcherInterface $event_dispatcher, ConfigFactory $config_factory, EntityManagerInterface $entity_manager, LockBackendInterface $lock, UuidInterface $uuid_service, TypedConfigManager $typed_config) {
     $this->storageComparer = $storage_comparer;
     $this->eventDispatcher = $event_dispatcher;
     $this->configFactory = $config_factory;
     $this->entityManager = $entity_manager;
     $this->lock = $lock;
     $this->uuidService = $uuid_service;
+    $this->typedConfigManager = $typed_config;
     $this->processed = $this->storageComparer->getEmptyChangelist();
     // Use an override free context for importing so that overrides to do not
     // pollute the imported data. The context is hard coded to ensure this is
@@ -264,7 +275,7 @@ public function validate() {
   protected function importConfig() {
     foreach (array('delete', 'create', 'update') as $op) {
       foreach ($this->getUnprocessed($op) as $name) {
-        $config = new Config($name, $this->storageComparer->getTargetStorage(), $this->context);
+        $config = new Config($name, $this->storageComparer->getTargetStorage(), $this->context, $this->typedConfigManager);
         if ($op == 'delete') {
           $config->delete();
         }
@@ -297,11 +308,11 @@ protected function importInvokeOwner() {
         // 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, $this->storageComparer->getTargetStorage(), $this->context);
+          $old_config = new Config($name, $this->storageComparer->getTargetStorage(), $this->context, $this->typedConfigManager);
           $old_config->load();
 
           $data = $this->storageComparer->getSourceStorage()->read($name);
-          $new_config = new Config($name, $this->storageComparer->getTargetStorage(), $this->context);
+          $new_config = new Config($name, $this->storageComparer->getTargetStorage(), $this->context, $this->typedConfigManager);
           if ($data !== FALSE) {
             $new_config->setData($data);
           }
diff --git a/core/lib/Drupal/Core/Config/Schema/Mapping.php b/core/lib/Drupal/Core/Config/Schema/Mapping.php
index 9701df0de038..92e9a90f53ff 100644
--- a/core/lib/Drupal/Core/Config/Schema/Mapping.php
+++ b/core/lib/Drupal/Core/Config/Schema/Mapping.php
@@ -27,7 +27,7 @@ class Mapping extends ArrayElement implements ComplexDataInterface {
   protected function parse() {
     $elements = array();
     foreach ($this->definition['mapping'] as $key => $definition) {
-      if (isset($this->value[$key])) {
+      if (isset($this->value[$key]) || array_key_exists($key, $this->value)) {
         $elements[$key] = $this->parseElement($key, $this->value[$key], $definition);
       }
     }
diff --git a/core/lib/Drupal/Core/Config/Schema/Sequence.php b/core/lib/Drupal/Core/Config/Schema/Sequence.php
index a399583c635e..44dbe1d6b2f3 100644
--- a/core/lib/Drupal/Core/Config/Schema/Sequence.php
+++ b/core/lib/Drupal/Core/Config/Schema/Sequence.php
@@ -49,4 +49,19 @@ public function onChange($delta) {
       $this->parent->onChange($this->name);
     }
   }
+
+  /**
+   * Gets a typed configuration element from the sequence.
+   *
+   * @param string $key
+   *   The key of the sequence to get.
+   *
+   * @return \Drupal\Core\Config\Schema\Element
+   *   Typed configuration element.
+   */
+  public function get($key) {
+    $elements = $this->parse();
+    return $elements[$key];
+  }
+
 }
diff --git a/core/lib/Drupal/Core/TypedData/Plugin/DataType/Binary.php b/core/lib/Drupal/Core/TypedData/Plugin/DataType/Binary.php
index 3fc1440512d7..a1258d44520c 100644
--- a/core/lib/Drupal/Core/TypedData/Plugin/DataType/Binary.php
+++ b/core/lib/Drupal/Core/TypedData/Plugin/DataType/Binary.php
@@ -87,4 +87,11 @@ public function getString() {
     }
     return $contents;
   }
+
+  /**
+   * @inheritdoc
+   */
+  public function getCastedValue() {
+    return $this->getValue();
+  }
 }
diff --git a/core/lib/Drupal/Core/TypedData/Plugin/DataType/Boolean.php b/core/lib/Drupal/Core/TypedData/Plugin/DataType/Boolean.php
index 7acb3dd343e0..2e5677a565c1 100644
--- a/core/lib/Drupal/Core/TypedData/Plugin/DataType/Boolean.php
+++ b/core/lib/Drupal/Core/TypedData/Plugin/DataType/Boolean.php
@@ -23,4 +23,10 @@
  */
 class Boolean extends PrimitiveBase implements BooleanInterface {
 
+  /**
+   * @inheritdoc
+   */
+  public function getCastedValue() {
+    return (bool) $this->value;
+  }
 }
diff --git a/core/lib/Drupal/Core/TypedData/Plugin/DataType/Float.php b/core/lib/Drupal/Core/TypedData/Plugin/DataType/Float.php
index d1ff6a892f95..5153cb5d12cf 100644
--- a/core/lib/Drupal/Core/TypedData/Plugin/DataType/Float.php
+++ b/core/lib/Drupal/Core/TypedData/Plugin/DataType/Float.php
@@ -23,4 +23,10 @@
  */
 class Float extends PrimitiveBase implements FloatInterface {
 
+  /**
+   * @inheritdoc
+   */
+  public function getCastedValue() {
+    return (float) $this->value;
+  }
 }
diff --git a/core/lib/Drupal/Core/TypedData/Plugin/DataType/Integer.php b/core/lib/Drupal/Core/TypedData/Plugin/DataType/Integer.php
index 4d89e2fa48eb..d7b1517700b1 100644
--- a/core/lib/Drupal/Core/TypedData/Plugin/DataType/Integer.php
+++ b/core/lib/Drupal/Core/TypedData/Plugin/DataType/Integer.php
@@ -23,4 +23,10 @@
  */
 class Integer extends PrimitiveBase implements IntegerInterface {
 
+  /**
+   * @inheritdoc
+   */
+  public function getCastedValue() {
+    return (int) $this->value;
+  }
 }
diff --git a/core/lib/Drupal/Core/TypedData/Plugin/DataType/String.php b/core/lib/Drupal/Core/TypedData/Plugin/DataType/String.php
index 36696c5023f3..d2d5a17eb7d1 100644
--- a/core/lib/Drupal/Core/TypedData/Plugin/DataType/String.php
+++ b/core/lib/Drupal/Core/TypedData/Plugin/DataType/String.php
@@ -23,4 +23,10 @@
  */
 class String extends PrimitiveBase implements StringInterface {
 
+  /**
+   * @inheritdoc
+   */
+  public function getCastedValue() {
+    return $this->getString();
+  }
 }
diff --git a/core/lib/Drupal/Core/TypedData/Plugin/DataType/Uri.php b/core/lib/Drupal/Core/TypedData/Plugin/DataType/Uri.php
index 24fd9b64eb03..e9a08911c1e3 100644
--- a/core/lib/Drupal/Core/TypedData/Plugin/DataType/Uri.php
+++ b/core/lib/Drupal/Core/TypedData/Plugin/DataType/Uri.php
@@ -21,6 +21,6 @@
  *   label = @Translation("URI")
  * )
  */
-class Uri extends PrimitiveBase implements UriInterface {
+class Uri extends String implements UriInterface {
 
 }
diff --git a/core/lib/Drupal/Core/TypedData/PrimitiveInterface.php b/core/lib/Drupal/Core/TypedData/PrimitiveInterface.php
index b2293e28b055..01ad0d9aed1f 100644
--- a/core/lib/Drupal/Core/TypedData/PrimitiveInterface.php
+++ b/core/lib/Drupal/Core/TypedData/PrimitiveInterface.php
@@ -28,4 +28,10 @@ public function getValue();
    */
   public function setValue($value);
 
+  /**
+   * Gets the primitive data value casted to the correct PHP type.
+   *
+   * @return mixed
+   */
+  public function getCastedValue();
 }
diff --git a/core/modules/block/config/schema/block.schema.yml b/core/modules/block/config/schema/block.schema.yml
index 61d66e59a476..b6ba2aee7cba 100644
--- a/core/modules/block/config/schema/block.schema.yml
+++ b/core/modules/block/config/schema/block.schema.yml
@@ -87,6 +87,9 @@ block.block.*:
         view_mode:
           type: string
           label: 'View mode'
+        module:
+          type: string
+          label: 'Module'
     langcode:
       type: string
       label: 'Default language'
diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockStorageUnitTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockStorageUnitTest.php
index 82827abd8b11..99977cbc2085 100644
--- a/core/modules/block/lib/Drupal/block/Tests/BlockStorageUnitTest.php
+++ b/core/modules/block/lib/Drupal/block/Tests/BlockStorageUnitTest.php
@@ -96,7 +96,7 @@ protected function createTests() {
       'status' => TRUE,
       'langcode' => language_default()->id,
       'theme' => 'stark',
-      'region' => -1,
+      'region' => '-1',
       'plugin' => 'test_html_id',
       'settings' => array(
         'cache' => 1,
@@ -106,7 +106,7 @@ protected function createTests() {
       ),
       'visibility' => NULL,
     );
-    $this->assertIdentical($actual_properties, $expected_properties, 'The block properties are exported correctly.');
+    $this->assertIdentical($actual_properties, $expected_properties);
 
     $this->assertTrue($entity->getPlugin() instanceof TestHtmlIdBlock, 'The entity has an instance of the correct block plugin.');
   }
diff --git a/core/modules/breakpoint/config/schema/breakpoint.schema.yml b/core/modules/breakpoint/config/schema/breakpoint.schema.yml
index eee2445ce46f..6281da79d938 100644
--- a/core/modules/breakpoint/config/schema/breakpoint.schema.yml
+++ b/core/modules/breakpoint/config/schema/breakpoint.schema.yml
@@ -36,6 +36,9 @@ breakpoint.breakpoint.*.*.*:
     langcode:
       type: string
       label: 'Default language'
+    status:
+      type: boolean
+      label: 'Enabled'
 
 breakpoint.breakpoint_group.*.*.*:
   type: mapping
@@ -65,3 +68,9 @@ breakpoint.breakpoint_group.*.*.*:
     sourceType:
       type: string
       label: 'Group source type'
+    langcode:
+      type: string
+      label: 'Default language'
+    status:
+      type: boolean
+      label: 'Enabled'
diff --git a/core/modules/ckeditor/lib/Drupal/ckeditor/Plugin/CKEditorPlugin/DrupalImage.php b/core/modules/ckeditor/lib/Drupal/ckeditor/Plugin/CKEditorPlugin/DrupalImage.php
index 9fd6ec2e59ab..9fc56a2f6a1f 100644
--- a/core/modules/ckeditor/lib/Drupal/ckeditor/Plugin/CKEditorPlugin/DrupalImage.php
+++ b/core/modules/ckeditor/lib/Drupal/ckeditor/Plugin/CKEditorPlugin/DrupalImage.php
@@ -82,7 +82,6 @@ public function settingsForm(array $form, array &$form_state, Editor $editor) {
    *
    * @see \Drupal\editor\Form\EditorImageDialog
    * @see editor_image_upload_settings_form()
-   * @see editor_image_upload_settings_validate()
    */
   function validateImageUploadSettings(array $element, array &$form_state) {
     $settings = &$form_state['values']['editor']['settings']['plugins']['drupalimage']['image_upload'];
diff --git a/core/modules/config/lib/Drupal/config/Form/ConfigSync.php b/core/modules/config/lib/Drupal/config/Form/ConfigSync.php
index 5654d45f14df..4287d8674696 100644
--- a/core/modules/config/lib/Drupal/config/Form/ConfigSync.php
+++ b/core/modules/config/lib/Drupal/config/Form/ConfigSync.php
@@ -16,6 +16,7 @@
 use Drupal\Core\Config\ConfigImporter;
 use Drupal\Core\Config\ConfigException;
 use Drupal\Core\Config\ConfigFactory;
+use Drupal\Core\Config\TypedConfigManager;
 use Drupal\Core\Routing\UrlGeneratorInterface;
 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -72,6 +73,13 @@ class ConfigSync extends FormBase {
    */
   protected $uuidService;
 
+  /**
+   * The typed config manager.
+   *
+   * @var \Drupal\Core\Config\TypedConfigManager
+   */
+  protected $typedConfigManager;
+
   /**
    * Constructs the object.
    *
@@ -90,9 +98,11 @@ class ConfigSync extends FormBase {
    * @param \Drupal\Core\Routing\UrlGeneratorInterface $url_generator
    *   The url generator service.
    * @param \Drupal\Component\Uuid\UuidInterface $uuid_service
-   * The UUID Service.
+   *   The UUID Service.
+   * @param \Drupal\Core\Config\TypedConfigManager $typed_config
+   *   The typed configuration manager.
    */
-  public function __construct(StorageInterface $sourceStorage, StorageInterface $targetStorage, LockBackendInterface $lock, EventDispatcherInterface $event_dispatcher, ConfigFactory $config_factory, EntityManagerInterface $entity_manager, UrlGeneratorInterface $url_generator, UuidInterface $uuid_service) {
+  public function __construct(StorageInterface $sourceStorage, StorageInterface $targetStorage, LockBackendInterface $lock, EventDispatcherInterface $event_dispatcher, ConfigFactory $config_factory, EntityManagerInterface $entity_manager, UrlGeneratorInterface $url_generator, UuidInterface $uuid_service, TypedConfigManager $typed_config) {
     $this->sourceStorage = $sourceStorage;
     $this->targetStorage = $targetStorage;
     $this->lock = $lock;
@@ -101,6 +111,7 @@ public function __construct(StorageInterface $sourceStorage, StorageInterface $t
     $this->entity_manager = $entity_manager;
     $this->urlGenerator = $url_generator;
     $this->uuidService = $uuid_service;
+    $this->typedConfigManager = $typed_config;
   }
 
   /**
@@ -115,7 +126,8 @@ public static function create(ContainerInterface $container) {
       $container->get('config.factory'),
       $container->get('entity.manager'),
       $container->get('url_generator'),
-      $container->get('uuid')
+      $container->get('uuid'),
+      $container->get('config.typed')
     );
   }
 
@@ -222,7 +234,8 @@ public function submitForm(array &$form, array &$form_state) {
       $this->configFactory,
       $this->entity_manager,
       $this->lock,
-      $this->uuidService
+      $this->uuidService,
+      $this->typedConfigManager
     );
     if ($config_importer->alreadyImporting()) {
       drupal_set_message($this->t('Another request may be synchronizing configuration already.'));
diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigImporterTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigImporterTest.php
index cb4aeaae89b7..ef9a9ee5f20e 100644
--- a/core/modules/config/lib/Drupal/config/Tests/ConfigImporterTest.php
+++ b/core/modules/config/lib/Drupal/config/Tests/ConfigImporterTest.php
@@ -61,7 +61,8 @@ function setUp() {
       $this->container->get('config.factory'),
       $this->container->get('entity.manager'),
       $this->container->get('lock'),
-      $this->container->get('uuid')
+      $this->container->get('uuid'),
+      $this->container->get('config.typed')
     );
     $this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.staging'));
   }
diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigSchemaTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigSchemaTest.php
index 3b406b19e0dd..663b456f1b91 100644
--- a/core/modules/config/lib/Drupal/config/Tests/ConfigSchemaTest.php
+++ b/core/modules/config/lib/Drupal/config/Tests/ConfigSchemaTest.php
@@ -7,7 +7,6 @@
 
 namespace Drupal\config\Tests;
 
-use Drupal\Core\Config\TypedConfig;
 use Drupal\Core\TypedData\Type\IntegerInterface;
 use Drupal\Core\TypedData\Type\StringInterface;
 use Drupal\simpletest\DrupalUnitTestBase;
@@ -102,6 +101,8 @@ function testSchemaMapping() {
     $expected['label'] = 'Image style';
     $expected['class'] = '\Drupal\Core\Config\Schema\Mapping';
     $expected['mapping']['name']['type'] = 'string';
+    $expected['mapping']['uuid']['label'] = 'UUID';
+    $expected['mapping']['uuid']['type'] = 'string';
     $expected['mapping']['label']['type'] = 'label';
     $expected['mapping']['effects']['type'] = 'sequence';
     $expected['mapping']['effects']['sequence'][0]['type'] = 'mapping';
@@ -111,6 +112,8 @@ function testSchemaMapping() {
     $expected['mapping']['effects']['sequence'][0]['mapping']['uuid']['type'] = 'string';
     $expected['mapping']['langcode']['label'] = 'Default language';
     $expected['mapping']['langcode']['type'] = 'string';
+    $expected['mapping']['status']['label'] = 'Enabled';
+    $expected['mapping']['status']['type'] = 'boolean';
 
     $this->assertEqual($definition, $expected, 'Retrieved the right metadata for image.style.large');
 
@@ -242,4 +245,63 @@ function testSchemaData() {
     $this->assertEqual($site_slogan->getValue(), $new_slogan, 'Successfully updated the contained configuration data');
   }
 
+  /**
+   * Test configuration value data type enforcement using schemas.
+   */
+  public function testConfigSaveWithSchema() {
+    $untyped_values = array(
+      'string' => 1,
+      'empty_string' => '',
+      'null_string' => NULL,
+      'integer' => '100',
+      'null_integer' => '',
+      'boolean' => 1,
+      // If the config schema doesn't have a type it should be casted to string.
+      'no_type' => 1,
+      'mapping' => array(
+        'string' => 1
+      ),
+      'float' => '3.14',
+      'null_float' => '',
+      'sequence' => array (1, 0, 1),
+      // Not in schema and therefore should be left untouched.
+      'not_present_in_schema' => TRUE,
+      // Test a custom type.
+      'config_test_integer' => '1',
+      'config_test_integer_empty_string' => '',
+    );
+    $untyped_to_typed = $untyped_values;
+
+    $typed_values = array(
+      'string' => '1',
+      'empty_string' => '',
+      'null_string' => NULL,
+      'integer' => 100,
+      'null_integer' => NULL,
+      'boolean' => TRUE,
+      'no_type' => '1',
+      'mapping' => array(
+        'string' => '1'
+      ),
+      'float' => 3.14,
+      'null_float' => NULL,
+      'sequence' => array (TRUE, FALSE, TRUE),
+      'not_present_in_schema' => TRUE,
+      'config_test_integer' => 1,
+      'config_test_integer_empty_string' => NULL,
+    );
+
+    // Save config which has a schema that enforces types.
+    \Drupal::config('config_test.schema_data_types')
+      ->setData($untyped_to_typed)
+      ->save();
+    $this->assertIdentical(\Drupal::config('config_test.schema_data_types')->get(), $typed_values);
+
+    // Save config which does not have a schema that enforces types.
+    \Drupal::config('config_test.no_schema_data_types')
+      ->setData($untyped_values)
+      ->save();
+    $this->assertIdentical(\Drupal::config('config_test.no_schema_data_types')->get(), $untyped_values);
+  }
+
 }
diff --git a/core/modules/config/tests/config_test/config/schema/config_test.schema.yml b/core/modules/config/tests/config_test/config/schema/config_test.schema.yml
index 04329abf610e..597e9f7e0fae 100644
--- a/core/modules/config/tests/config_test/config/schema/config_test.schema.yml
+++ b/core/modules/config/tests/config_test/config/schema/config_test.schema.yml
@@ -91,3 +91,43 @@ config_test.dynamic.*:
     protected_property:
       type: string
       label: 'Protected property'
+
+config_test_integer:
+  type: integer
+  label: 'Config test integer'
+
+config_test.schema_data_types:
+  type: mapping
+  label: 'Config test schema'
+  mapping:
+    config_test_integer:
+      type: config_test_integer
+    config_test_integer_empty_string:
+      type: config_test_integer
+    integer:
+      type: integer
+    null_integer:
+      type: integer
+    float:
+      type: float
+    null_float:
+      type: float
+    string:
+      type: string
+    null_string:
+      type: string
+    empty_string:
+      type: string
+    boolean:
+      type: boolean
+    no_type:
+      label: 'No label'
+    mapping:
+      type: mapping
+      mapping:
+        string:
+          type: string
+    sequence:
+      type: sequence
+      sequence:
+        - type: boolean
diff --git a/core/modules/editor/editor.admin.inc b/core/modules/editor/editor.admin.inc
index eed97610d6e0..a29924b2e766 100644
--- a/core/modules/editor/editor.admin.inc
+++ b/core/modules/editor/editor.admin.inc
@@ -128,30 +128,5 @@ function editor_image_upload_settings_form(Editor $editor) {
     '#states' => $show_if_image_uploads_enabled,
   );
 
-  $form['#element_validate'] = array(
-    'editor_image_upload_settings_validate',
-  );
-
   return $form;
 }
-
-/**
- * #element_validate handler for editor_image_upload_settings_validate().
- *
- * Ensures each form item's value is cast to the proper type.
- *
- * @see \Drupal\editor\Form\EditorImageDialog
- * @ingroup forms
- */
-function editor_image_upload_settings_validate(array $element, array &$form_state) {
-  $cast_value = function($type, $element) use (&$form_state) {
-    $section = $element['#parents'];
-    $value = NestedArray::getValue($form_state['values'], $section);
-    settype($value, $type);
-    NestedArray::setValue($form_state['values'], $section, $value);
-  };
-
-  $cast_value('bool', $element['status']);
-  $cast_value('int', $element['max_dimensions']['width']);
-  $cast_value('int', $element['max_dimensions']['height']);
-}
diff --git a/core/modules/editor/editor.module b/core/modules/editor/editor.module
index 1234099b83dd..affe7111712e 100644
--- a/core/modules/editor/editor.module
+++ b/core/modules/editor/editor.module
@@ -552,7 +552,7 @@ function _editor_get_processed_text_fields(ContentEntityInterface $entity) {
     $settings = Field::fieldInfo()
       ->getInstance($entity->entityType(), $entity->bundle(), $field)
       ->getSettings();
-    return isset($settings['text_processing']) && $settings['text_processing'] === '1';
+    return isset($settings['text_processing']) && $settings['text_processing'] === TRUE;
   });
 }
 
diff --git a/core/modules/field/config/schema/field.schema.yml b/core/modules/field/config/schema/field.schema.yml
index 7c09ccea46a3..33b8d3e9da2c 100644
--- a/core/modules/field/config/schema/field.schema.yml
+++ b/core/modules/field/config/schema/field.schema.yml
@@ -24,6 +24,9 @@ field.field.*.*:
     langcode:
       type: string
       label: 'Default language'
+    name:
+      type: string
+      label: 'Name'
     entity_type:
       type: string
       label: 'Entity type'
diff --git a/core/modules/filter/config/schema/filter.schema.yml b/core/modules/filter/config/schema/filter.schema.yml
index 391b968107ed..3f3207123739 100644
--- a/core/modules/filter/config/schema/filter.schema.yml
+++ b/core/modules/filter/config/schema/filter.schema.yml
@@ -21,6 +21,9 @@ filter.format.*:
     name:
       type: label
       label: 'Name'
+    uuid:
+      type: string
+      label: 'UUID'
     status:
       type: boolean
       label: 'Enabled'
diff --git a/core/modules/image/config/schema/image.schema.yml b/core/modules/image/config/schema/image.schema.yml
index 87a8f1e65ef8..b0dfd56f3ac8 100644
--- a/core/modules/image/config/schema/image.schema.yml
+++ b/core/modules/image/config/schema/image.schema.yml
@@ -6,6 +6,9 @@ image.style.*:
   mapping:
     name:
       type: string
+    uuid:
+      type: string
+      label: 'UUID'
     label:
       type: label
     effects:
@@ -24,6 +27,9 @@ image.style.*:
     langcode:
       type: string
       label: 'Default language'
+    status:
+      type: boolean
+      label: 'Enabled'
 
 image.effect.image_crop:
   type: image_size
diff --git a/core/modules/migrate_drupal/lib/Drupal/migrate_drupal/Tests/MigrateSystemConfigsTest.php b/core/modules/migrate_drupal/lib/Drupal/migrate_drupal/Tests/MigrateSystemConfigsTest.php
index c09c390d0d04..a878076e111e 100644
--- a/core/modules/migrate_drupal/lib/Drupal/migrate_drupal/Tests/MigrateSystemConfigsTest.php
+++ b/core/modules/migrate_drupal/lib/Drupal/migrate_drupal/Tests/MigrateSystemConfigsTest.php
@@ -85,8 +85,8 @@ public function testSystemPerformance() {
     $executable = new MigrateExecutable($migration, new MigrateMessage());
     $executable->import();
     $config = \Drupal::config('system.performance');
-    $this->assertIdentical($config->get('css.preprocess'), 0);
-    $this->assertIdentical($config->get('js.preprocess'), 0);
+    $this->assertIdentical($config->get('css.preprocess'), FALSE);
+    $this->assertIdentical($config->get('js.preprocess'), FALSE);
     $this->assertIdentical($config->get('cache.page.max_age'), 0);
   }
 }
diff --git a/core/modules/number/config/schema/number.schema.yml b/core/modules/number/config/schema/number.schema.yml
index 087ef8ffb5fb..f597c0270eba 100644
--- a/core/modules/number/config/schema/number.schema.yml
+++ b/core/modules/number/config/schema/number.schema.yml
@@ -51,10 +51,10 @@ field.number_decimal.instance_settings:
   label: 'Decimal'
   mapping:
     min:
-      type: integer
+      type: float
       label: 'Minimum'
     max:
-      type: integer
+      type: float
       label: 'Maximum'
     prefix:
       type: string
@@ -71,7 +71,7 @@ field.number_decimal.value:
        label: 'Default value'
        mapping:
          value:
-           type: integer
+           type: float
            label: 'Value'
 
 field.number_float.settings:
@@ -86,10 +86,10 @@ field.number_float.instance_settings:
   label: 'Float'
   mapping:
     min:
-      type: integer
+      type: float
       label: 'Minimum'
     max:
-      type: integer
+      type: float
       label: 'Maximum'
     prefix:
       type: string
@@ -106,5 +106,5 @@ field.number_float.value:
       label: 'Default value'
       mapping:
         value:
-          type: integer
+          type: float
           label: 'Value'
diff --git a/core/modules/options/config/schema/options.schema.yml b/core/modules/options/config/schema/options.schema.yml
index 8d1481aa1306..367e4b580fb4 100644
--- a/core/modules/options/config/schema/options.schema.yml
+++ b/core/modules/options/config/schema/options.schema.yml
@@ -15,11 +15,9 @@ field.list_integer.settings:
       label: 'Allowed values function'
 
 field.list_integer.instance_settings:
-  type: mapping
   label: 'List (integer)'
-  sequence:
-    - type: string
-      label: 'setting'
+  type: mapping
+  mapping: {  }
 
 field.list_integer.value:
   type: sequence
@@ -47,11 +45,9 @@ field.list_float.settings:
       label: 'Allowed values function'
 
 field.list_float.instance_settings:
-  type: mapping
   label: 'List (float)'
-  sequence:
-    - type: string
-      label: 'setting'
+  type: mapping
+  mapping: {  }
 
 field.list_float.value:
   type: sequence
@@ -79,11 +75,9 @@ field.list_text.settings:
       label: 'Allowed values function'
 
 field.list_text.instance_settings:
-  type: mapping
   label: 'List (float)'
-  sequence:
-    - type: string
-      label: 'setting'
+  type: mapping
+  mapping: {  }
 
 field.list_text.value:
   type: sequence
@@ -111,11 +105,9 @@ field.list_boolean.settings:
       label: 'Allowed values function'
 
 field.list_boolean.instance_settings:
-  type: mapping
   label: 'List (boolean)'
-  sequence:
-    - type: string
-      label: 'setting'
+  type: mapping
+  mapping: {  }
 
 field.list_boolean.value:
   type: sequence
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
index 882542e97a67..1e2938416e3e 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
@@ -1400,7 +1400,8 @@ public function configImporter() {
         $this->container->get('config.factory'),
         $this->container->get('entity.manager'),
         $this->container->get('lock'),
-        $this->container->get('uuid')
+        $this->container->get('uuid'),
+        $this->container->get('config.typed')
       );
     }
     // Always recalculate the changelist when called.
diff --git a/core/modules/system/config/schema/system.schema.yml b/core/modules/system/config/schema/system.schema.yml
index 4f3625b1f31c..0dabf5e21da9 100644
--- a/core/modules/system/config/schema/system.schema.yml
+++ b/core/modules/system/config/schema/system.schema.yml
@@ -97,7 +97,7 @@ system.date:
         default:
           type: string
           label: 'Default time zone'
-      user:
+        user:
           type: mapping
           label: 'User'
           mapping:
@@ -220,7 +220,7 @@ system.performance:
           type: mapping
           label: 'Page cache'
           mapping:
-            enabled:
+            use_internal:
               type: boolean
               label: 'Cache pages for anonymous users'
             max_age:
@@ -236,6 +236,22 @@ system.performance:
         gzip:
           type: boolean
           label: 'Compress CSS files'
+    fast_404:
+      type: mapping
+      label: 'Fast 404 settings'
+      mapping:
+        enabled:
+          type: boolean
+          label: 'Fast 404 enabled'
+        paths:
+          type: string
+          label: 'Regular expression to match'
+        exclude_paths:
+          type: string
+          label: 'Regular expression to not match'
+        html:
+          type: string
+          label: 'Fast 404 page html'
     js:
       type: mapping
       label: 'JavaScript performance settings'
@@ -295,6 +311,9 @@ system.theme:
       sequence:
         - type: string
           label: 'Theme'
+    default:
+      type: string
+      label: 'Default theme'
 
 system.menu.*:
   type: mapping
@@ -303,6 +322,9 @@ system.menu.*:
     id:
       type: string
       label: 'Menu identifier'
+    uuid:
+      type: string
+      label: 'UUID'
     label:
       type: label
       label: 'Menu label'
@@ -312,3 +334,9 @@ system.menu.*:
     langcode:
       type: string
       label: 'Default language'
+    locked:
+      type: boolean
+      label: ''
+    status:
+      type: boolean
+      label: ''
diff --git a/core/modules/system/config/system.maintenance.yml b/core/modules/system/config/system.maintenance.yml
index 5ea379dff296..40cfeb21a516 100644
--- a/core/modules/system/config/system.maintenance.yml
+++ b/core/modules/system/config/system.maintenance.yml
@@ -1,3 +1,2 @@
-enabled: '0'
 message: '@site is currently under maintenance. We should be back shortly. Thank you for your patience.'
 langcode: en
diff --git a/core/modules/taxonomy/config/schema/taxonomy.schema.yml b/core/modules/taxonomy/config/schema/taxonomy.schema.yml
index 21d1322bfeb9..9170280ed1a0 100644
--- a/core/modules/taxonomy/config/schema/taxonomy.schema.yml
+++ b/core/modules/taxonomy/config/schema/taxonomy.schema.yml
@@ -21,6 +21,9 @@ taxonomy.vocabulary.*:
     vid:
       type: string
       label: 'Machine name'
+    uuid:
+      type: string
+      label: 'UUID'
     name:
       type: label
       label: 'Name'
@@ -28,7 +31,7 @@ taxonomy.vocabulary.*:
       type: label
       label: 'Description'
     hierarchy:
-      type: boolean
+      type: integer
       label: 'Hierarchy'
     weight:
       type: integer
diff --git a/core/modules/tour/config/schema/tour.schema.yml b/core/modules/tour/config/schema/tour.schema.yml
index ca8d781da047..b1a0c28d8feb 100644
--- a/core/modules/tour/config/schema/tour.schema.yml
+++ b/core/modules/tour/config/schema/tour.schema.yml
@@ -7,9 +7,18 @@ tour.tour.*:
     id:
       type: string
       label: 'ID'
+    uuid:
+      type: string
+      label: 'UUID'
+    module:
+      type: string
+      label: 'Providing module'
     label:
       type: label
       label: 'Label'
+    status:
+      type: boolean
+      label: 'Enabled'
     langcode:
       type: string
       label: 'Default language'
@@ -42,6 +51,9 @@ tour.tip:
     weight:
       type: integer
       label: 'Weight'
+    location:
+      type: string
+      label: 'Location'
     attributes:
       type: sequence
       label: 'Attributes'
diff --git a/core/modules/user/config/schema/user.schema.yml b/core/modules/user/config/schema/user.schema.yml
index 05155ff3472c..5aec5a2909c7 100644
--- a/core/modules/user/config/schema/user.schema.yml
+++ b/core/modules/user/config/schema/user.schema.yml
@@ -137,6 +137,9 @@ user.role.*:
       sequence:
         - type: string
           label: 'Permission'
+    status:
+      type: boolean
+      label: 'Status'
     langcode:
       type: string
       label: 'Default language'
-- 
GitLab