Add validation constraints to user.settings, use/introduce symfony/expression-language to improve config validation DX

Created on 20 October 2023, about 1 year ago
Updated 18 April 2024, 8 months ago

Problem/Motivation

User settings have 3 property paths that are not yet validatable:

$  ./vendor/bin/drush config:inspect --filter-keys=user.settings --detail --list-constraints  --fields=key,validatability,constraints
➜  πŸ€– Analyzing…

 ----------------------------------------------------- ------------- --------------------------------------------------------------------------------------------- 
  Key                                                   Validatable   Validation constraints                                                                       
 ----------------------------------------------------- ------------- --------------------------------------------------------------------------------------------- 
  user.settings                                         84%           ValidKeys: '<infer>'                                                                         
                                                                      RequiredKeys: '<infer>'                                                                      
   user.settings:                                       Validatable   ValidKeys: '<infer>'                                                                         
                                                                      RequiredKeys: '<infer>'                                                                      
   user.settings:_core                                  Validatable   ValidKeys:                                                                                   
                                                                        - default_config_hash                                                                      
                                                                      RequiredKeys: '<infer>'                                                                      
   user.settings:_core.default_config_hash              Validatable   NotNull: {  }                                                                                
                                                                      Regex: '/^[a-zA-Z0-9\-_]+$/'                                                                 
                                                                      Length: 43                                                                                   
                                                                      ↣ PrimitiveType: {  }                                                                        
   user.settings:anonymous                              Validatable   Regex:                                                                                       
                                                                        pattern: '/([^\PC])/u'                                                                     
                                                                        match: false                                                                               
                                                                        message: 'Labels are not allowed to span multiple lines or contain control characters.'    
                                                                      NotBlank: {  }                                                                               
                                                                      ↣ PrimitiveType: {  }                                                                        
   user.settings:cancel_method                          NOT           ⚠️  @todo Add validation constraints here                                                    
   user.settings:langcode                               Validatable   NotNull: {  }                                                                                
                                                                      Choice:                                                                                      
                                                                        callback: 'Drupal\Core\TypedData\Plugin\DataType\LanguageReference::getAllValidLangcodes'  
                                                                      ↣ PrimitiveType: {  }                                                                        
   user.settings:notify                                 Validatable   ValidKeys: '<infer>'                                                                         
                                                                      RequiredKeys: '<infer>'                                                                      
   user.settings:notify.cancel_confirm                  Validatable   ↣ PrimitiveType: {  }                                                                        
   user.settings:notify.password_reset                  Validatable   ↣ PrimitiveType: {  }                                                                        
   user.settings:notify.register_admin_created          Validatable   ↣ PrimitiveType: {  }                                                                        
   user.settings:notify.register_no_approval_required   Validatable   ↣ PrimitiveType: {  }                                                                        
   user.settings:notify.register_pending_approval       Validatable   ↣ PrimitiveType: {  }                                                                        
   user.settings:notify.status_activated                Validatable   ↣ PrimitiveType: {  }                                                                        
   user.settings:notify.status_blocked                  Validatable   ↣ PrimitiveType: {  }                                                                        
   user.settings:notify.status_canceled                 Validatable   ↣ PrimitiveType: {  }                                                                        
   user.settings:password_reset_timeout                 NOT           ⚠️  @todo Add validation constraints here                                                    
   user.settings:password_strength                      Validatable   ↣ PrimitiveType: {  }                                                                        
   user.settings:register                               NOT           ⚠️  @todo Add validation constraints here                                                    
   user.settings:verify_mail                            Validatable   ↣ PrimitiveType: {  }                                                                        
 ----------------------------------------------------- ------------- ---------------------------------------------------------------------------------------------

Steps to reproduce

  1. Get a local git clone of Drupal core 11.x.
  2. composer require drupal/config_inspector β€” or manually install https://www.drupal.org/project/config_inspector/releases/2.1.5 β†’ or newer (which supports Drupal 11!)
  3. composer require drush/drush
  4. vendor/bin/drush config:inspect --filter-keys=user.settings --detail --list-constraints

Proposed resolution

Add validation constraints to:

  1. cancel_method
  2. password_reset_timeout
  3. register

This requires looking at the existing code and admin UI (if any) to understand which values could be considered valid. Eventually this needs to be reviewed by the relevant subsystem maintainer.

For examples, search *.schema.yml files for the string constraints: 😊

Reach out to @borisson_ or @wimleers in the #distributions-and-recipes.

Remaining tasks

User interface changes

None.

API changes

None.

Data model changes

More validation πŸš€

Release notes snippet

None.

πŸ“Œ Task
Status

Needs work

Version

11.0 πŸ”₯

Component
User moduleΒ  β†’

Last updated 5 days ago

Created by

πŸ‡§πŸ‡ͺBelgium borisson_ Mechelen, πŸ‡§πŸ‡ͺ

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @borisson_
  • First commit to issue fork.
  • πŸ‡¬πŸ‡§United Kingdom Eli-T Manchester
  • πŸ‡¬πŸ‡§United Kingdom Eli-T Manchester
  • last update about 1 year ago
    30,391 pass, 4 fail
  • @eli-t opened merge request.
  • Status changed to Needs review about 1 year ago
  • πŸ‡¬πŸ‡§United Kingdom Eli-T Manchester
  • last update about 1 year ago
    30,426 pass
  • πŸ‡§πŸ‡ͺBelgium borisson_ Mechelen, πŸ‡§πŸ‡ͺ

    This needs a review from one of the user.module maintainers, tagging it like that, but this change is great. Thanks @Eli-T!

  • πŸ‡ΈπŸ‡°Slovakia poker10

    What if a site altered these settings already (or if using a module which is altering these)? Is this a BC break? The same applies for all other issues where we are hardcoding values (dblog, olivero, ...). I think we should consider this very carefully.

  • πŸ‡ΊπŸ‡ΈUnited States moshe weitzman Boston, MA

    LGTM. I think this needs input from config validation maintainers as well (leaving tag in for this).

  • Status changed to RTBC about 1 year ago
  • πŸ‡§πŸ‡ͺBelgium wim leers Ghent πŸ‡§πŸ‡ͺπŸ‡ͺπŸ‡Ί

    Thanks @moshe weitzman for the fastest subsystem maintainer review sign-off I can recall in years πŸ₯³ πŸ™

    #8: it's up to that module to then alter the validation constraints in the config schema.

    Looks great! 😊

  • last update about 1 year ago
    30,483 pass
  • πŸ‡ΈπŸ‡°Slovakia poker10

    it's up to that module to then alter the validation constraints in the config schema.

    So is there a draft change record/release notes snippet for all these "Add validation constraints to ..." changes somewhere?

  • Status changed to Needs review about 1 year ago
  • πŸ‡§πŸ‡ͺBelgium wim leers Ghent πŸ‡§πŸ‡ͺπŸ‡ͺπŸ‡Ί

    @poker10: Please note that NOTHING in Drupal core or contrib is strictly validating config yet β€” only tests are, but contrib modules have no effect on that.

    Change record: https://www.drupal.org/node/3362879 β†’ . I've just updated it to document this, because you're right, that was missing: https://www.drupal.org/node/3362879/revisions/view/13198950/13294591 β†’

    That made me realize that in this particular example, it may be better to follow the example set by ✨ Add a `langcode` data type to config schema Fixed , which did:

      constraints:
        Choice:
          callback: 'Drupal\Core\TypedData\Plugin\DataType\LanguageReference::getAllValidLangcodes'
    

    Because in this case, we could say that all valid values for cancel_method are array_keys(user_cancel_methods().
    (All of this would have been trivial if these were plugin IDs because for that we have the PluginExists validation constraint, but the user cancelation methods system predates the plugin system!)

    That way, there is no need for altering the config schema anymore! 😊

  • Status changed to Needs work about 1 year ago
  • πŸ‡§πŸ‡ͺBelgium borisson_ Mechelen, πŸ‡§πŸ‡ͺ

    I agree that this makes more sense. The idea that alex mentioned at drupalcon Lille, was that config could be validatable without having drupal running will be more difficult with this change, but since we already have that example in the langcode data type that shouldn't hold this back.

  • πŸ‡§πŸ‡ͺBelgium borisson_ Mechelen, πŸ‡§πŸ‡ͺ

    It's not possible to define a callback like this, so we would have create a new function just to define this callback? I'm not sure that makes sens to do.
    callback: 'array_keys(user_cancel_methods)'

  • πŸ‡§πŸ‡ͺBelgium wim leers Ghent πŸ‡§πŸ‡ͺπŸ‡ͺπŸ‡Ί

    #13:

    config could be validatable without having drupal running will be more difficult with this change

    That is already the case for any configuration that is configuring e.g. plugins: only the Drupal site itself can only ever know which plugins are in fact installed πŸ˜…

    #14: correct, what you describe there is not yet possible. Well, technically it is, because closures are allowed β€” see https://symfony.com/doc/current/reference/constraints/Choice.html#supply.... But … PHP closures cannot be defined in YAML files πŸ˜…

  • πŸ‡§πŸ‡ͺBelgium wim leers Ghent πŸ‡§πŸ‡ͺπŸ‡ͺπŸ‡Ί

    ACTUALLY! I just discovered https://symfony.com/doc/current/reference/constraints/Expression.html. Combine that with https://stackoverflow.com/questions/50782963/symfony-expression-validation and we should be able to do exactly what you wrote in #14, @borisson_!

    I think this would be worth opening a blocking issue for? OTOH this would be the first use in core (because \Drupal\Core\TypedData\Plugin\DataType\LanguageReference::getAllValidLangcodes() actually is quite a bit more complex), so maybe this would be the appropriate issue?

  • πŸ‡§πŸ‡ͺBelgium borisson_ Mechelen, πŸ‡§πŸ‡ͺ

    O wow, that is great, this is a perfect usecase for that. I think we can add it in this issue, that would get the example next to the implementation.

  • πŸ‡§πŸ‡ͺBelgium borisson_ Mechelen, πŸ‡§πŸ‡ͺ

    To use this, we would also need symfony/expression-language, that feels like it would be something we need to do in another issue (and we'd need to do dependency evaluation for it as well).

    I'm not sure if that is something we'd want to do; would there be other places where we could use expression language as well? What do you all think?

  • πŸ‡§πŸ‡ͺBelgium wim leers Ghent πŸ‡§πŸ‡ͺπŸ‡ͺπŸ‡Ί

    But this issue is the one that justifies that addition. I think we should add that dependency here, and add the dependency evaluation to the issue summary.

    This would mean a massive DX improvement for making a lot of config validatable! 🀩

    Let's first prove that this works by implementing it and then worry about adding a dependency evaluation? πŸ€“ Tentatively tagging πŸ˜‡

  • πŸ‡ΊπŸ‡ΈUnited States DamienMcKenna NH, USA

    What is left to do on this issue?

Production build 0.71.5 2024