From af56f3c37e8d5d0eed1831a95de4a5e0133f58ca Mon Sep 17 00:00:00 2001
From: webchick <webchick@24967.no-reply.drupal.org>
Date: Fri, 9 May 2014 09:29:22 -0700
Subject: [PATCH] Issue #2259525 by alexpott:
 System.action.user_add_role_action.ROLE_ID does not match any config schema.

---
 .../Drupal/Core/Config/TypedConfigManager.php |  15 +-
 .../Drupal/config/Tests/ConfigInstallTest.php |  12 +-
 .../Drupal/config/Tests/ConfigSchemaTest.php  |  74 ++++++----
 .../Drupal/config/Tests/DefaultConfigTest.php |  10 +-
 .../install/config_schema_test.noschema.yml}  |   0
 .../config_schema_test.schema_in_install.yml} |   0
 ...ema.somemodule.section_one.subsection.yml} |   0
 ...ema.somemodule.section_two.subsection.yml} |   0
 ...g_schema_test.someschema.with_parents.yml} |   0
 .../config_schema_test.someschema.yml}        |   0
 .../schema/config_schema_test.schema.yml      | 134 ++++++++++++++++++
 .../config_schema_test.info.yml               |   6 +
 .../config/schema/config_test.schema.yml      | 122 ----------------
 .../system/config/schema/system.schema.yml    |   2 +-
 .../user/config/schema/user.schema.yml        |  17 +--
 .../user/Tests/UserActionConfigSchemaTest.php |  56 ++++++++
 16 files changed, 276 insertions(+), 172 deletions(-)
 rename core/modules/config/tests/{config_test/config/install/config_test.noschema.yml => config_schema_test/config/install/config_schema_test.noschema.yml} (100%)
 rename core/modules/config/tests/{config_test/config/install/config_test.schema_in_install.yml => config_schema_test/config/install/config_schema_test.schema_in_install.yml} (100%)
 rename core/modules/config/tests/{config_test/config/install/config_test.someschema.somemodule.section_one.subsection.yml => config_schema_test/config/install/config_schema_test.someschema.somemodule.section_one.subsection.yml} (100%)
 rename core/modules/config/tests/{config_test/config/install/config_test.someschema.somemodule.section_two.subsection.yml => config_schema_test/config/install/config_schema_test.someschema.somemodule.section_two.subsection.yml} (100%)
 rename core/modules/config/tests/{config_test/config/install/config_test.someschema.with_parents.yml => config_schema_test/config/install/config_schema_test.someschema.with_parents.yml} (100%)
 rename core/modules/config/tests/{config_test/config/install/config_test.someschema.yml => config_schema_test/config/install/config_schema_test.someschema.yml} (100%)
 create mode 100644 core/modules/config/tests/config_schema_test/config/schema/config_schema_test.schema.yml
 create mode 100644 core/modules/config/tests/config_schema_test/config_schema_test.info.yml
 create mode 100644 core/modules/user/lib/Drupal/user/Tests/UserActionConfigSchemaTest.php

diff --git a/core/lib/Drupal/Core/Config/TypedConfigManager.php b/core/lib/Drupal/Core/Config/TypedConfigManager.php
index 07b0384b3616..822830ee4b98 100644
--- a/core/lib/Drupal/Core/Config/TypedConfigManager.php
+++ b/core/lib/Drupal/Core/Config/TypedConfigManager.php
@@ -215,8 +215,11 @@ public function clearCachedDefinitions() {
    *   definition in below order:
    *     breakpoint.breakpoint.module.toolbar.*
    *     breakpoint.breakpoint.module.*.*
+   *     breakpoint.breakpoint.module.*
    *     breakpoint.breakpoint.*.*.*
+   *     breakpoint.breakpoint.*
    *     breakpoint.*.*.*.*
+   *     breakpoint.*
    *   Returns null, if no matching element.
    */
   protected function getFallbackName($name) {
@@ -227,8 +230,16 @@ protected function getFallbackName($name) {
         return $replaced;
       }
       else {
-        // No definition for this level(for example, breakpoint.breakpoint.*),
-        // check for next level (which is, breakpoint.*.*).
+        // No definition for this level. Collapse multiple wildcards to a single
+        // wildcard to see if there is a greedy match. For example,
+        // breakpoint.breakpoint.*.* becomes
+        // breakpoint.breakpoint.*
+        $one_star = preg_replace('/\.([\.\*]*)$/', '.*', $replaced);
+        if ($one_star != $replaced && isset($this->definitions[$one_star])) {
+          return $one_star;
+        }
+        // Check for next level. For example, if breakpoint.breakpoint.* has
+        // been checked and no match found then check breakpoint.*.*
         return $this->getFallbackName($replaced);
       }
     }
diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigInstallTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigInstallTest.php
index f2a10f3bd187..3e97adf1b845 100644
--- a/core/modules/config/lib/Drupal/config/Tests/ConfigInstallTest.php
+++ b/core/modules/config/lib/Drupal/config/Tests/ConfigInstallTest.php
@@ -44,14 +44,14 @@ function testModuleInstallation() {
 
     // Ensure that schema provided by modules that are not installed is not
     // available.
-    $this->assertFalse(\Drupal::service('config.typed')->hasConfigSchema('config_test.schema_in_install'), 'Configuration schema for config_test.schema_in_install does not exist.');
+    $this->assertFalse(\Drupal::service('config.typed')->hasConfigSchema('config_schema_test.schema_in_install'), 'Configuration schema for config_schema_test.schema_in_install does not exist.');
 
     // Install the test module.
-    $this->enableModules(array('config_test'));
-    $this->installConfig(array('config_test'));
+    $this->enableModules(array('config_test', 'config_schema_test'));
+    $this->installConfig(array('config_test', 'config_schema_test'));
 
     // After module installation the new schema should exist.
-    $this->assertTrue(\Drupal::service('config.typed')->hasConfigSchema('config_test.schema_in_install'), 'Configuration schema for config_test.schema_in_install exists.');
+    $this->assertTrue(\Drupal::service('config.typed')->hasConfigSchema('config_schema_test.schema_in_install'), 'Configuration schema for config_schema_test.schema_in_install exists.');
 
     // Verify that default module config exists.
     \Drupal::configFactory()->reset($default_config);
@@ -71,13 +71,13 @@ function testModuleInstallation() {
     $this->assertFalse(isset($GLOBALS['hook_config_test']['delete']));
 
     // Ensure that data type casting is applied during config installation.
-    $config = \Drupal::config('config_test.schema_in_install');
+    $config = \Drupal::config('config_schema_test.schema_in_install');
     $this->assertIdentical($config->get('integer'), 1);
 
     // Test that uninstalling configuration removes configuration schema.
     \Drupal::config('core.extension')->set('module', array())->save();
     \Drupal::service('config.manager')->uninstall('module', 'config_test');
-    $this->assertFalse(\Drupal::service('config.typed')->hasConfigSchema('config_test.schema_in_install'), 'Configuration schema for config_test.schema_in_install does not exist.');
+    $this->assertFalse(\Drupal::service('config.typed')->hasConfigSchema('config_schema_test.schema_in_install'), 'Configuration schema for config_schema_test.schema_in_install does not exist.');
 
   }
 }
diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigSchemaTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigSchemaTest.php
index e09f60387a20..1ac6573d5c5f 100644
--- a/core/modules/config/lib/Drupal/config/Tests/ConfigSchemaTest.php
+++ b/core/modules/config/lib/Drupal/config/Tests/ConfigSchemaTest.php
@@ -21,7 +21,7 @@ class ConfigSchemaTest extends DrupalUnitTestBase {
    *
    * @var array
    */
-  public static $modules = array('system', 'language', 'locale', 'field', 'image', 'config_test');
+  public static $modules = array('system', 'language', 'locale', 'field', 'image', 'config_schema_test');
 
   public static function getInfo() {
     return array(
@@ -33,7 +33,7 @@ public static function getInfo() {
 
   public function setUp() {
     parent::setUp();
-    $this->installConfig(array('system', 'image', 'config_test'));
+    $this->installConfig(array('system', 'image', 'config_schema_test'));
   }
 
   /**
@@ -41,21 +41,21 @@ public function setUp() {
    */
   function testSchemaMapping() {
     // Nonexistent configuration key will have Unknown as metadata.
-    $this->assertIdentical(FALSE, \Drupal::service('config.typed')->hasConfigSchema('config_test.no_such_key'));
-    $definition = \Drupal::service('config.typed')->getDefinition('config_test.no_such_key');
+    $this->assertIdentical(FALSE, \Drupal::service('config.typed')->hasConfigSchema('config_schema_test.no_such_key'));
+    $definition = \Drupal::service('config.typed')->getDefinition('config_schema_test.no_such_key');
     $expected = array();
     $expected['label'] = 'Unknown';
     $expected['class'] = '\Drupal\Core\Config\Schema\Property';
     $this->assertEqual($definition, $expected, 'Retrieved the right metadata for nonexistent configuration.');
 
     // Configuration file without schema will return Unknown as well.
-    $this->assertIdentical(FALSE, \Drupal::service('config.typed')->hasConfigSchema('config_test.noschema'));
-    $definition = \Drupal::service('config.typed')->getDefinition('config_test.noschema');
+    $this->assertIdentical(FALSE, \Drupal::service('config.typed')->hasConfigSchema('config_schema_test.noschema'));
+    $definition = \Drupal::service('config.typed')->getDefinition('config_schema_test.noschema');
     $this->assertEqual($definition, $expected, 'Retrieved the right metadata for configuration with no schema.');
 
     // Configuration file with only some schema.
-    $this->assertIdentical(TRUE, \Drupal::service('config.typed')->hasConfigSchema('config_test.someschema'));
-    $definition = \Drupal::service('config.typed')->getDefinition('config_test.someschema');
+    $this->assertIdentical(TRUE, \Drupal::service('config.typed')->hasConfigSchema('config_schema_test.someschema'));
+    $definition = \Drupal::service('config.typed')->getDefinition('config_schema_test.someschema');
     $expected = array();
     $expected['label'] = 'Schema test data';
     $expected['class'] = '\Drupal\Core\Config\Schema\Mapping';
@@ -64,7 +64,7 @@ function testSchemaMapping() {
     $this->assertEqual($definition, $expected, 'Retrieved the right metadata for configuration with only some schema.');
 
     // Check type detection on elements with undefined types.
-    $config = \Drupal::service('config.typed')->get('config_test.someschema');
+    $config = \Drupal::service('config.typed')->get('config_schema_test.someschema');
     $definition = $config['testitem']->getDataDefinition();
     $expected = array();
     $expected['label'] = 'Test item';
@@ -139,8 +139,8 @@ function testSchemaMapping() {
     $this->assertEqual($definition, $expected, 'Retrieved the right metadata for the first effect of image.style.medium');
 
     // More complex, multiple filesystem marker test.
-    $definition = \Drupal::service('config.typed')->getDefinition('config_test.someschema.somemodule.section_one.subsection');
-    // This should be the schema of config_test.someschema.somemodule.*.*.
+    $definition = \Drupal::service('config.typed')->getDefinition('config_schema_test.someschema.somemodule.section_one.subsection');
+    // This should be the schema of config_schema_test.someschema.somemodule.*.*.
     $expected = array();
     $expected['label'] = 'Schema multiple filesytem marker test';
     $expected['class'] = '\Drupal\Core\Config\Schema\Mapping';
@@ -149,24 +149,24 @@ function testSchemaMapping() {
     $expected['mapping']['testdescription']['type'] = 'text';
     $expected['mapping']['testdescription']['label'] = 'Description';
 
-    $this->assertEqual($definition, $expected, 'Retrieved the right metadata for config_test.someschema.somemodule.section_one.subsection');
+    $this->assertEqual($definition, $expected, 'Retrieved the right metadata for config_schema_test.someschema.somemodule.section_one.subsection');
 
-    $definition = \Drupal::service('config.typed')->getDefinition('config_test.someschema.somemodule.section_two.subsection');
+    $definition = \Drupal::service('config.typed')->getDefinition('config_schema_test.someschema.somemodule.section_two.subsection');
     // The other file should have the same schema.
-    $this->assertEqual($definition, $expected, 'Retrieved the right metadata for config_test.someschema.somemodule.section_two.subsection');
+    $this->assertEqual($definition, $expected, 'Retrieved the right metadata for config_schema_test.someschema.somemodule.section_two.subsection');
   }
 
   /**
    * Tests metadata retrieval with several levels of %parent indirection.
    */
   function testSchemaMappingWithParents() {
-    $config_data = \Drupal::service('config.typed')->get('config_test.someschema.with_parents');
+    $config_data = \Drupal::service('config.typed')->get('config_schema_test.someschema.with_parents');
 
     // Test fetching parent one level up.
     $entry = $config_data->get('one_level');
     $definition = $entry['testitem']->getDataDefinition();
     $expected = array(
-      'type' => 'config_test.someschema.with_parents.key_1',
+      'type' => 'config_schema_test.someschema.with_parents.key_1',
       'label' => 'Test item nested one level',
       'class' => '\Drupal\Core\TypedData\Plugin\DataType\String',
     );
@@ -176,7 +176,7 @@ function testSchemaMappingWithParents() {
     $entry = $config_data->get('two_levels');
     $definition = $entry['wrapper']['testitem']->getDataDefinition();
     $expected = array(
-      'type' => 'config_test.someschema.with_parents.key_2',
+      'type' => 'config_schema_test.someschema.with_parents.key_2',
       'label' => 'Test item nested two levels',
       'class' => '\Drupal\Core\TypedData\Plugin\DataType\String',
     );
@@ -186,7 +186,7 @@ function testSchemaMappingWithParents() {
     $entry = $config_data->get('three_levels');
     $definition = $entry['wrapper_1']['wrapper_2']['testitem']->getDataDefinition();
     $expected = array(
-      'type' => 'config_test.someschema.with_parents.key_3',
+      'type' => 'config_schema_test.someschema.with_parents.key_3',
       'label' => 'Test item nested three levels',
       'class' => '\Drupal\Core\TypedData\Plugin\DataType\String',
     );
@@ -265,8 +265,8 @@ public function testConfigSaveWithSchema() {
       // 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' => '',
+      'config_schema_test_integer' => '1',
+      'config_schema_test_integer_empty_string' => '',
     );
     $untyped_to_typed = $untyped_values;
 
@@ -285,21 +285,43 @@ public function testConfigSaveWithSchema() {
       'null_float' => NULL,
       'sequence' => array (TRUE, FALSE, TRUE),
       'not_present_in_schema' => TRUE,
-      'config_test_integer' => 1,
-      'config_test_integer_empty_string' => NULL,
+      'config_schema_test_integer' => 1,
+      'config_schema_test_integer_empty_string' => NULL,
     );
 
     // Save config which has a schema that enforces types.
-    \Drupal::config('config_test.schema_data_types')
+    \Drupal::config('config_schema_test.schema_data_types')
       ->setData($untyped_to_typed)
       ->save();
-    $this->assertIdentical(\Drupal::config('config_test.schema_data_types')->get(), $typed_values);
+    $this->assertIdentical(\Drupal::config('config_schema_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')
+    \Drupal::config('config_schema_test.no_schema_data_types')
       ->setData($untyped_values)
       ->save();
-    $this->assertIdentical(\Drupal::config('config_test.no_schema_data_types')->get(), $untyped_values);
+    $this->assertIdentical(\Drupal::config('config_schema_test.no_schema_data_types')->get(), $untyped_values);
+  }
+
+  /**
+   * Tests fallback to a greedy wildcard.
+   */
+  function testSchemaFallback() {
+    $definition = \Drupal::service('config.typed')->getDefinition('config_schema_test.wildcard_fallback.something');
+    // This should be the schema of config_schema_test.wildcard_fallback.*.
+    $expected = array();
+    $expected['label'] = 'Schema wildcard fallback test';
+    $expected['class'] = '\Drupal\Core\Config\Schema\Mapping';
+    $expected['mapping']['testid']['type'] = 'string';
+    $expected['mapping']['testid']['label'] = 'ID';
+    $expected['mapping']['testdescription']['type'] = 'text';
+    $expected['mapping']['testdescription']['label'] = 'Description';
+
+    $this->assertEqual($definition, $expected, 'Retrieved the right metadata for config_schema_test.wildcard_fallback.something');
+
+    $definition2 = \Drupal::service('config.typed')->getDefinition('config_schema_test.wildcard_fallback.something.something');
+    // This should be the schema of config_schema_test.wildcard_fallback.* as
+    //well.
+    $this->assertIdentical($definition, $definition2);
   }
 
 }
diff --git a/core/modules/config/lib/Drupal/config/Tests/DefaultConfigTest.php b/core/modules/config/lib/Drupal/config/Tests/DefaultConfigTest.php
index 8a5d02253ddd..9ce5ceab11bf 100644
--- a/core/modules/config/lib/Drupal/config/Tests/DefaultConfigTest.php
+++ b/core/modules/config/lib/Drupal/config/Tests/DefaultConfigTest.php
@@ -57,13 +57,9 @@ public function testDefaultConfig() {
         continue;
       }
 
-      // 1. config_test.noschema has to be skipped as it tests
-      // TypedConfigManagerInterface::hasConfigSchema() method.
-      // 2. config.someschema has to be skipped as it tests schema default data
-      // type fallback.
-      // 3. config_test.schema_in_install is testing that schema are used during
-      // configuration installation.
-      if ($config_name == 'config_test.noschema' || $config_name == 'config_test.someschema' || $config_name == 'config_test.schema_in_install') {
+      // Skip files provided by the config_schema_test module since that module
+      // is explicitly for testing schema.
+      if (strpos($config_name, 'config_schema_test') === 0) {
         continue;
       }
 
diff --git a/core/modules/config/tests/config_test/config/install/config_test.noschema.yml b/core/modules/config/tests/config_schema_test/config/install/config_schema_test.noschema.yml
similarity index 100%
rename from core/modules/config/tests/config_test/config/install/config_test.noschema.yml
rename to core/modules/config/tests/config_schema_test/config/install/config_schema_test.noschema.yml
diff --git a/core/modules/config/tests/config_test/config/install/config_test.schema_in_install.yml b/core/modules/config/tests/config_schema_test/config/install/config_schema_test.schema_in_install.yml
similarity index 100%
rename from core/modules/config/tests/config_test/config/install/config_test.schema_in_install.yml
rename to core/modules/config/tests/config_schema_test/config/install/config_schema_test.schema_in_install.yml
diff --git a/core/modules/config/tests/config_test/config/install/config_test.someschema.somemodule.section_one.subsection.yml b/core/modules/config/tests/config_schema_test/config/install/config_schema_test.someschema.somemodule.section_one.subsection.yml
similarity index 100%
rename from core/modules/config/tests/config_test/config/install/config_test.someschema.somemodule.section_one.subsection.yml
rename to core/modules/config/tests/config_schema_test/config/install/config_schema_test.someschema.somemodule.section_one.subsection.yml
diff --git a/core/modules/config/tests/config_test/config/install/config_test.someschema.somemodule.section_two.subsection.yml b/core/modules/config/tests/config_schema_test/config/install/config_schema_test.someschema.somemodule.section_two.subsection.yml
similarity index 100%
rename from core/modules/config/tests/config_test/config/install/config_test.someschema.somemodule.section_two.subsection.yml
rename to core/modules/config/tests/config_schema_test/config/install/config_schema_test.someschema.somemodule.section_two.subsection.yml
diff --git a/core/modules/config/tests/config_test/config/install/config_test.someschema.with_parents.yml b/core/modules/config/tests/config_schema_test/config/install/config_schema_test.someschema.with_parents.yml
similarity index 100%
rename from core/modules/config/tests/config_test/config/install/config_test.someschema.with_parents.yml
rename to core/modules/config/tests/config_schema_test/config/install/config_schema_test.someschema.with_parents.yml
diff --git a/core/modules/config/tests/config_test/config/install/config_test.someschema.yml b/core/modules/config/tests/config_schema_test/config/install/config_schema_test.someschema.yml
similarity index 100%
rename from core/modules/config/tests/config_test/config/install/config_test.someschema.yml
rename to core/modules/config/tests/config_schema_test/config/install/config_schema_test.someschema.yml
diff --git a/core/modules/config/tests/config_schema_test/config/schema/config_schema_test.schema.yml b/core/modules/config/tests/config_schema_test/config/schema/config_schema_test.schema.yml
new file mode 100644
index 000000000000..9eec7d736f32
--- /dev/null
+++ b/core/modules/config/tests/config_schema_test/config/schema/config_schema_test.schema.yml
@@ -0,0 +1,134 @@
+# Schema for the configuration files of the Configuration Schema Test module.
+
+config_schema_test.someschema:
+  type: mapping
+  label: 'Schema test data'
+  mapping:
+    testitem:
+      label: 'Test item'
+    testlist:
+      label: 'Test list'
+
+config_schema_test.someschema.with_parents:
+  label: 'Schema test data with parenting'
+  type: mapping
+  mapping:
+    one_level:
+      label: 'Parenting one level up'
+      type: mapping
+      mapping:
+        target_key:
+          label: 'Key used in parent relation'
+          type: string
+        testitem:
+          type: config_schema_test.someschema.with_parents.[%parent.target_key]
+    two_levels:
+      label: 'Parenting two levels up'
+      type: mapping
+      mapping:
+        target_key:
+          label: 'Key used in parent relation'
+          type: string
+        wrapper:
+          label: 'Wrapper'
+          type: mapping
+          mapping:
+            testitem:
+              type: config_schema_test.someschema.with_parents.[%parent.%parent.target_key]
+    three_levels:
+      label: 'Parenting three levels up'
+      type: mapping
+      mapping:
+        target_key:
+          label: 'Key used in parent relation'
+          type: string
+        wrapper_1:
+          label: 'Wrapper 1'
+          type: mapping
+          mapping:
+            wrapper_2:
+              label: 'Wrapper 2'
+              type: mapping
+              mapping:
+                testitem:
+                  type: config_schema_test.someschema.with_parents.[%parent.%parent.%parent.target_key]
+
+config_schema_test.someschema.with_parents.key_1:
+  label: 'Test item nested one level'
+  type: string
+config_schema_test.someschema.with_parents.key_2:
+  label: 'Test item nested two levels'
+  type: string
+config_schema_test.someschema.with_parents.key_3:
+  label: 'Test item nested three levels'
+  type: string
+
+config_schema_test.someschema.somemodule.*.*:
+  type: mapping
+  label: 'Schema multiple filesytem marker test'
+  mapping:
+    testid:
+      type: string
+      label: 'ID'
+    testdescription:
+      type: text
+      label: 'Description'
+
+config_schema_test.wildcard_fallback.*:
+  type: mapping
+  label: 'Schema wildcard fallback test'
+  mapping:
+    testid:
+      type: string
+      label: 'ID'
+    testdescription:
+      type: text
+      label: 'Description'
+
+config_schema_test.schema_data_types:
+  type: mapping
+  label: 'Config test schema'
+  mapping:
+    config_schema_test_integer:
+      type: config_schema_test_integer
+    config_schema_test_integer_empty_string:
+      type: config_schema_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
+
+config_schema_test.schema_in_install:
+  label: 'Schema test data with parenting'
+  type: mapping
+  mapping:
+    integer:
+      type: integer
+      label: 'Integer'
+
+config_schema_test_integer:
+  type: integer
+  label: 'Config test integer'
diff --git a/core/modules/config/tests/config_schema_test/config_schema_test.info.yml b/core/modules/config/tests/config_schema_test/config_schema_test.info.yml
new file mode 100644
index 000000000000..9836e8b59b82
--- /dev/null
+++ b/core/modules/config/tests/config_schema_test/config_schema_test.info.yml
@@ -0,0 +1,6 @@
+name: 'Configuration schema test'
+type: module
+package: Testing
+version: VERSION
+core: 8.x
+hidden: true
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 5a594e4601b7..984026fd0400 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
@@ -1,79 +1,5 @@
 # Schema for the configuration files of the Configuration Test module.
 
-config_test.someschema:
-  type: mapping
-  label: 'Schema test data'
-  mapping:
-    testitem:
-      label: 'Test item'
-    testlist:
-      label: 'Test list'
-
-config_test.someschema.with_parents:
-  label: 'Schema test data with parenting'
-  type: mapping
-  mapping:
-    one_level:
-      label: 'Parenting one level up'
-      type: mapping
-      mapping:
-        target_key:
-          label: 'Key used in parent relation'
-          type: string
-        testitem:
-          type: config_test.someschema.with_parents.[%parent.target_key]
-    two_levels:
-      label: 'Parenting two levels up'
-      type: mapping
-      mapping:
-        target_key:
-          label: 'Key used in parent relation'
-          type: string
-        wrapper:
-          label: 'Wrapper'
-          type: mapping
-          mapping:
-            testitem:
-              type: config_test.someschema.with_parents.[%parent.%parent.target_key]
-    three_levels:
-      label: 'Parenting three levels up'
-      type: mapping
-      mapping:
-        target_key:
-          label: 'Key used in parent relation'
-          type: string
-        wrapper_1:
-          label: 'Wrapper 1'
-          type: mapping
-          mapping:
-            wrapper_2:
-              label: 'Wrapper 2'
-              type: mapping
-              mapping:
-                testitem:
-                  type: config_test.someschema.with_parents.[%parent.%parent.%parent.target_key]
-
-config_test.someschema.with_parents.key_1:
-  label: 'Test item nested one level'
-  type: string
-config_test.someschema.with_parents.key_2:
-  label: 'Test item nested two levels'
-  type: string
-config_test.someschema.with_parents.key_3:
-  label: 'Test item nested three levels'
-  type: string
-
-config_test.someschema.somemodule.*.*:
-  type: mapping
-  label: 'Schema multiple filesytem marker test'
-  mapping:
-    testid:
-      type: string
-      label: 'ID'
-    testdescription:
-      type: text
-      label: 'Description'
-
 config_test.dynamic.*:
   type: mapping
   label: 'Config test entity'
@@ -94,54 +20,6 @@ config_test.dynamic.*:
       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
-
-config_test.schema_in_install:
-  label: 'Schema test data with parenting'
-  type: mapping
-  mapping:
-    integer:
-      type: integer
-      label: 'Integer'
-
 config_test_dynamic:
   type: mapping
   mapping:
diff --git a/core/modules/system/config/schema/system.schema.yml b/core/modules/system/config/schema/system.schema.yml
index 2d9d11df9eff..690a3ecbd267 100644
--- a/core/modules/system/config/schema/system.schema.yml
+++ b/core/modules/system/config/schema/system.schema.yml
@@ -330,7 +330,7 @@ system.action.*:
       type: string
       label: 'Plugin'
     configuration:
-      type: action.configuration.[plugin]
+      type: action.configuration.[%parent.plugin]
     dependencies:
       type: config_dependencies
       label: 'Dependencies'
diff --git a/core/modules/user/config/schema/user.schema.yml b/core/modules/user/config/schema/user.schema.yml
index 23a688a1785c..7f6192162968 100644
--- a/core/modules/user/config/schema/user.schema.yml
+++ b/core/modules/user/config/schema/user.schema.yml
@@ -145,8 +145,12 @@ user.role.*:
       label: 'Default language'
 
 action.configuration.user_add_role_action:
-  type: action_configuration_default
-  label: 'Add a role to the selected users configuration'
+  type: mapping
+  label: 'Configuration for the add role action'
+  mapping:
+    rid:
+      type: string
+      label: 'The ID of the role to add'
 
 action.configuration.user_block_user_action:
   type: action_configuration_default
@@ -158,14 +162,11 @@ action.configuration.user_cancel_user_action:
 
 action.configuration.user_remove_role_action:
   type: mapping
-  label: 'Remove a role from the selected users configuration'
+  label: 'Configuration for the remove role action'
   mapping:
     rid:
-      type: sequence
-      label: 'Roles'
-      sequence:
-        - type: sequence
-          label: 'Role'
+      type: string
+      label: 'The ID of the role to remove'
 
 action.configuration.user_unblock_user_action:
   type: action_configuration_default
diff --git a/core/modules/user/lib/Drupal/user/Tests/UserActionConfigSchemaTest.php b/core/modules/user/lib/Drupal/user/Tests/UserActionConfigSchemaTest.php
new file mode 100644
index 000000000000..3e616d5b6004
--- /dev/null
+++ b/core/modules/user/lib/Drupal/user/Tests/UserActionConfigSchemaTest.php
@@ -0,0 +1,56 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\user\Tests\UserActionConfigSchemaTest.
+ */
+
+namespace Drupal\user\Tests;
+
+use Drupal\config\Tests\ConfigSchemaTestBase;
+
+/**
+ * Tests the User action config schema.
+ */
+class UserActionConfigSchemaTest extends ConfigSchemaTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('user');
+
+  /**
+   * A user with administrative permissions.
+   *
+   * @var \Drupal\user\UserInterface
+   */
+  protected $adminUser;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'User action config schema',
+      'description' => 'Ensures the user action for adding and removing roles have valid config schema.',
+      'group' => 'User',
+    );
+  }
+
+  /**
+   * Tests whether the user action config schema are valid.
+   */
+  function testValidUserActionConfigSchema() {
+    $rid = $this->drupalCreateRole(array());
+
+    // Test user_add_role_action configuration.
+    $config = \Drupal::config('system.action.user_add_role_action.' . $rid);
+    $this->assertEqual($config->get('id'), 'user_add_role_action.' . $rid);
+    $this->assertConfigSchema(\Drupal::service('config.typed'), $config->getName(), $config->get());
+
+    // Test user_remove_role_action configuration.
+    $config = \Drupal::config('system.action.user_remove_role_action.' . $rid);
+    $this->assertEqual($config->get('id'), 'user_remove_role_action.' . $rid);
+    $this->assertConfigSchema(\Drupal::service('config.typed'), $config->getName(), $config->get());
+  }
+
+}
-- 
GitLab