Problem/Motivation
#3324150: Add validation constraints to config_entity.dependencies introduced the ValidKeys constraint for use on type: mapping. This validation constraint has particularly strong test coverage (see \Drupal\KernelTests\Core\TypedData\ValidKeysConstraintValidatorTest).
It allows either specifying an explicit list of allowed keys, or inferring them based on the keys defined for a mapping in the config schema.
It is currently adopted by:
config_dependencies_base→ automatically inferred:ValidKeys: '<infer>'config_dependencies→ automatically inferred:ValidKeys: '<infer>'_core_config_info→ NOT automatically inferred:ValidKeys: ['default_config_hash'](because this is a low-level config schema type that is NOT extensible, although it absolutely could useValidKeys: '<infer>'too)
It made sense to introduce this constraint first and adopt it narrowly, and to then adopt it more widely. That's what this issue is about.
The whole point of type: mapping is that you have known keys. If you don't know, you have to use type: sequence. But there is no validation happening on this today 🙈.
Only the config_test.validation mapping is validated, for testing purposes, in \Drupal\KernelTests\Config\TypedConfigTest::testSimpleConfigValidation(), by \Drupal\config_test\ConfigValidation::validateMapping(). That is essentially a hardcoded equivalent of the ValidKeys validation constraint (introduced in #1928868: Typed config incorrectly implements Typed Data interfaces and last refined in #2925689: ConfigValidation class contains code that is brittle and changing for every addition).
Steps to reproduce
N/A
Proposed resolution
Use this constraint by default for type: mapping:
mapping:
label: Mapping
class: '\Drupal\Core\Config\Schema\Mapping'
definition_class: '\Drupal\Core\TypedData\MapDataDefinition'
+ constraints:
+ # By default, only allow the explicitly listed mapping keys.
+ ValidKeys: '<infer>'
This has zero consequences today because no configuration is validated. (It's only checked if it matches the structure of the config schema, but only fairly loosely.)
The comment captures what it does. It makes type: mapping all across Drupal core actually sanely validatable by default! 🚀
Remaining tasks
TBD
User interface changes
None.
API changes
None.
Data model changes
type: mapping's implementation now actually matches its documentation: only known keys are allowed.
Release notes snippet
N/A
Issue fork drupal-3376794
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #3
wim leersClarifying the current status of Drupal core.
Comment #4
wim leersAt this point, we could choose to remove the test-only validation constraint:
… because it literally is surfacing the same problem that
ValidKeys: '<infer>'surfaces, but with more precision. Applying the above diff would result inkeeping this 👆 and removing this 👇
However, it's harmless to keep both. And in a way, it serves as a nice example to show that it's always possible to add additional validation constraints. So I vote to keep it as-is.
Comment #5
wim leersThanks to
\Drupal\Core\Config\TypedConfigManager::determineType()and::getFallbackName(), e.g.type: field.storage_settings.[%parent.type]automatically resolves tofield.storage_settings.boolean, but then falls back tofield.storage_settings.*(because there's no specific settings for the boolean field type).However,
type: field.storage_settings.*does not specify a concrete mapping … and nor doestype: mapping! That's what this was crashing on.So either we need to re-create all of the inheritance/extension logic in
\Drupal\Core\Validation\Plugin\Validation\Constraint\ValidKeysConstraint::inferKeys()… or we just rely on\Drupal\Core\Config\TypedConfigManager::getDefinitionWithReplacements()doing that for us like it has done for the past ~decade. Then all we need to is ensure that even the root type (type: mapping) has a list of allowed mapping keys specified, even if that list is empty. That way, it is nicely inherited down the chain (type: mapping→type: field.storage_settings.*→type: field.storage_settings.boolean) for us 🤓Then everything starts working! 👍
Comment #6
borisson_In comment #4 Wim wasn't sure about keeping the
ValidMappingconstraint. It is a different implementation, so not completely the same and I agree that it is valuable to keep both. It is a good example to keep.I'm not sure I completely understand the comment in #5, but I can see why setting the
ValidKeys: '<infer>'on the root mapping type bubbles up, so that makes a lot of sense.This is probably the simplest of all the config schema validation mapping issues that was committed over the last few weeks and it provides some additional strictness. It is weird to see nothing in core actually have an error against this, as opposed to some of the other issues, but the test coverage proves this works.
Comment #7
wim leersIt's not that weird, because we're not validating all config in Drupal core yet. #3361534: KernelTestBase::$strictConfigSchema = TRUE and BrowserTestBase::$strictConfigSchema = TRUE do not actually strictly validate would change that, and would likely trigger additional failures.
Regarding #5: see the test failures for the commits prior to the last, they contain output like this:
That means the
mappingkey-value pair was not defined on the given config schema type definition. So, for example,field.storage_settings.*does not contain it. And hence the assertion failed.By adding
mapping: {}totype: mapping's definition, we ensure that themappingkey-value pair is always defined for all mappings (and child types such asconfig_object, as well as grandchild types such asfield.storage_settings.*). That's what #5 tried to explain, probably not concretely enough 🙈Comment #8
borisson_Now that I am more confident I'll RTBC this one, let's break #3361534: KernelTestBase::$strictConfigSchema = TRUE and BrowserTestBase::$strictConfigSchema = TRUE do not actually strictly validate even harder.
Comment #9
wim leers🤣 Thanks… I guess? :P
Comment #10
borisson_@Wim, can you reroll this issue now that #3361534: KernelTestBase::$strictConfigSchema = TRUE and BrowserTestBase::$strictConfigSchema = TRUE do not actually strictly validate is in?
Comment #11
wim leersDid that!
Tests are still running, but I already see several failures. Yay for #3361534: KernelTestBase::$strictConfigSchema = TRUE and BrowserTestBase::$strictConfigSchema = TRUE do not actually strictly validate keeping us more honest going forward! 😊
Will investigate the failures as soon as the full test suite finishes running.
Comment #12
wim leersWell that turned out to be very simple! 🤩 Barely any violations in Drupal core! 👍
Comment #13
borisson_This is great, super happy to see that the strict config schema checking found 5 more fails.
Comment #15
longwaveCommitted b90b2e3 and pushed to 11.x. Thanks!
Comment #17
wim leers