Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DI] Validate env vars in config #23888

Closed
wants to merge 7 commits into
base: master
from

Conversation

@ro0NL
Copy link
Contributor

ro0NL commented Aug 14, 2017

Q A
Branch? 4.1/master
Bug fix? no
New feature? yes
BC breaks? no
Deprecations? no
Tests pass? yes
Fixed tickets #22151, #25868
License MIT
Doc PR symfony/symfony-docs#8382

This PR registers the env placeholders in Config\BaseNode with its default value or an empty string. It doesnt request real env vars during compilation,

What it does is if a config value exactly matches a env placeholder, we validate/normalize the default value/empty string but we keep returning the env placeholder as usual. If a placeholder occurs in the middle of a string it also proceeds as usual.

The latter to me is OK as you need to expect any string value during runtime anyway, including the empty string.

@ro0NL ro0NL force-pushed the ro0NL:config/placeholders branch from e790abd to df34e25 Aug 14, 2017

@nicolas-grekas nicolas-grekas added this to the 3.4 milestone Aug 15, 2017

@ro0NL ro0NL force-pushed the ro0NL:config/placeholders branch from 6815971 to 4675ef0 Aug 15, 2017

@nicolas-grekas

This comment has been minimized.

Copy link
Member

nicolas-grekas commented Aug 16, 2017

I suggest wait for #23901 before continuing on this topic - that will provide a much better base to fix this issue.

@ro0NL

This comment has been minimized.

Copy link
Contributor Author

ro0NL commented Sep 7, 2017

@nicolas-grekas could you briefly describe the path you have in mind for this, and how it leverages getProvidedTypes?

From a technical pov this approach was the only thing i could come up with, regarding decoupled state between di and config. I like how it works out-of-the-box for everyone :)

My idea here was to generate a better default value using getEnv() actually, probably calling it before being compiled (as that's allowed anyway ^^)

@nicolas-grekas

This comment has been minimized.

Copy link
Member

nicolas-grekas commented Sep 8, 2017

The env placeholders contain the prefixes - so we can extract that, and use EnvPlaceholderParameterBag::getProvidedTypes() to get the type of each placeholder.
That should be enough, isn't it?

@ro0NL ro0NL force-pushed the ro0NL:config/placeholders branch from 4675ef0 to 8df1175 Sep 9, 2017

@ro0NL

This comment has been minimized.

Copy link
Contributor Author

ro0NL commented Sep 9, 2017

@nicolas-grekas first round, wdyt?

@ro0NL ro0NL force-pushed the ro0NL:config/placeholders branch from 8df1175 to bfb9a12 Sep 9, 2017

*/
public static function setPlaceholder($placeholder, $type)
{
switch ($type) {

This comment has been minimized.

@ro0NL

ro0NL Sep 9, 2017

Author Contributor

IMO. can be done at the calling side..

@ro0NL ro0NL force-pushed the ro0NL:config/placeholders branch 2 times, most recently from 2b6e7c0 to 4de0d17 Sep 9, 2017

@@ -92,7 +91,7 @@ public function __construct()
public function getPasses()
{
return array_merge(
array($this->mergePass),
array(new RegisterEnvVarProcessorsPass(), $this->mergePass),

This comment has been minimized.

@ro0NL

ro0NL Sep 9, 2017

Author Contributor

now runs even earlier.. anything to do? Patched PhpDumper test for now.

This comment has been minimized.

@nicolas-grekas

This comment has been minimized.

@nicolas-grekas

nicolas-grekas Oct 6, 2017

Member

Wait, I just realized that this may just make it impossible to register a new env processor in practice, isn't it?

@ro0NL ro0NL force-pushed the ro0NL:config/placeholders branch 2 times, most recently from 8e08b3a to 81c3d2b Sep 9, 2017

continue;
}
foreach ($placeholders as $placeholder) {
BaseNode::setPlaceholder($placeholder, reset($envTypes[$prefix]));

This comment has been minimized.

@ro0NL

ro0NL Sep 9, 2017

Author Contributor

What about getParam("env($env)") as default value instead, if available :) That justifies computing default values on the calling side.

This comment has been minimized.

@nicolas-grekas

nicolas-grekas Sep 9, 2017

Member

Let's do. We also need to validate the type of the default value before calling setPlaceholder (its type needs to be in $envTypes).

This comment has been minimized.

@nicolas-grekas

nicolas-grekas Sep 9, 2017

Member

BUT, calling "reset" here prevents taking advantage of the "multiple types provided" info. So maybe pass an array instead, $type=>$value?

@@ -43,14 +44,23 @@ public function process(ContainerBuilder $container)
foreach ($class::getProvidedTypes() as $prefix => $type) {
$processors[$prefix] = new ServiceClosureArgument(new Reference($id));
$types[$prefix] = self::validateProvidedTypes($type, $class);
if (!$types[$prefix]) {

This comment has been minimized.

@nicolas-grekas

nicolas-grekas Sep 9, 2017

Member

can this happen at all? explode always creates an array with at least one element, isn't it?

This comment has been minimized.

@ro0NL

ro0NL Sep 9, 2017

Author Contributor

can this happen at all?

nope :)

@ro0NL ro0NL force-pushed the ro0NL:config/placeholders branch 6 times, most recently from 16968f9 to 5fc3fa4 Sep 9, 2017

@ro0NL ro0NL force-pushed the ro0NL:config/placeholders branch 3 times, most recently from aff06ef to d7b66cd Mar 21, 2018

@@ -50,6 +56,39 @@ public function __construct(?string $name, NodeInterface $parent = null, string
$this->pathSeparator = $pathSeparator;
}
/**
* Register possible (dummy) values for a dynamic placeholder value. Matching configuration values will be processed

This comment has been minimized.

@fabpot

fabpot Mar 22, 2018

Member

Registers

This comment has been minimized.

@fabpot

fabpot Mar 22, 2018

Member

I would move all sentences but the first one as a phpdoc description.

This comment has been minimized.

@ro0NL

ro0NL Mar 22, 2018

Author Contributor

You mean split into short & long description right? Did that for now.

}
/**
* Set a common prefix for dynamic placeholder values. Matching configuration values will be skipped from being

This comment has been minimized.

@fabpot

fabpot Mar 22, 2018

Member

Sets + same comment as above about separating the first sentence from the other ones.

}
/**
* Reset all current placeholders available.

This comment has been minimized.

@fabpot

fabpot Mar 22, 2018

Member

Resets

public static function resetPlaceholders(): void
{
self::$placeholderUniquePrefix = null;
self::$placeholders = array();

This comment has been minimized.

@fabpot

fabpot Mar 22, 2018

Member

Having static properties like this is very dangerous as we need to make sure that they are reset properly. I can see the finally calls here and there to reset this, but is there is any other way? Avoiding static properties would be great.

This comment has been minimized.

@nicolas-grekas

nicolas-grekas Mar 22, 2018

Member

It's not that dangerous: placeholders are unique strings so there is not really any way to mess up with them, even if someone forgets to reset them.
About removing the static state, that's really hard...
BUT, @ro0NL we should make these methods @internal for sure.

This comment has been minimized.

@ro0NL

ro0NL Mar 22, 2018

Author Contributor

I think we can settle indeed, i dont see a elegant workaround to avoid static currently, that is without overhauling the config component itself :)

Now marked internal.

This comment has been minimized.

@ro0NL

ro0NL Mar 22, 2018

Author Contributor

also we deal with it properly where needed; it's not scattered all over the environment.

@@ -363,4 +459,85 @@ public function getParent()
* @return mixed The finalized value
*/
abstract protected function finalizeValue($value);
/**
* Test if placeholder values are allowed for this node.

This comment has been minimized.

@fabpot

fabpot Mar 22, 2018

Member

Tests

}
/**
* Get allowed dynamic types for this node.

This comment has been minimized.

@fabpot

@ro0NL ro0NL force-pushed the ro0NL:config/placeholders branch from d7b66cd to a90d34b Mar 22, 2018

/**
* Resets all current placeholders available.
*/
public static function resetPlaceholders(): void

This comment has been minimized.

@nicolas-grekas

nicolas-grekas Mar 23, 2018

Member

this one also needs to be internal

This comment has been minimized.

@ro0NL

ro0NL Mar 23, 2018

Author Contributor

oops :) updated.

@nicolas-grekas

This comment has been minimized.

Copy link
Member

nicolas-grekas commented Mar 27, 2018

Thank you @ro0NL.

@ro0NL ro0NL deleted the ro0NL:config/placeholders branch Mar 27, 2018

$container = new ContainerBuilder();
$container->registerExtension(new EnvExtension());
$container->prependExtensionConfig('env_extension', array(
'simple_array_node' => '%env(json:FOO)%',

This comment has been minimized.

@mcfedr

mcfedr Mar 27, 2018

Contributor

json: can return a simple_array, e.g. in the case the env is something like [1, 2] - of course json doesnt guarrentee that its like this, it could also be {"a": 1, "b": 2}, so its a bit of a question, is it valid or not - I think its more useful if it passes validation.

This comment has been minimized.

@ro0NL

ro0NL Mar 27, 2018

Author Contributor

yeah i think we need to reassure / revise integration a bit. Or put different im curious if the change to json prefix affects the possibilities here.

This comment has been minimized.

@mcfedr

mcfedr Mar 27, 2018

Contributor

the json change only added the possibility to accept null in addition to array so shouldn't change anything here - this was on purpose so that its still possible to do some validation based on it always being an array.

I wonder if it would make sense to add a simple_array: decoder to further help with validation

*/
protected function allowPlaceholders(): bool
{
return false;

This comment has been minimized.

@mcfedr

mcfedr Mar 27, 2018

Contributor

Why have you made it false by default for ArrayNode? It seems this will limit how much of a config you can change in other peoples bundles, i.e. you can only put env(json:FOO) if the owner things of you in advance.

This comment has been minimized.

@chalasr

chalasr Mar 27, 2018

Member

comments on closed PRs are likely to be lost, please consider opening a new issue/PR if you think something is buggy or can be improved. Thanks

This comment has been minimized.

@ro0NL

ro0NL Mar 27, 2018

Author Contributor

In this case i asked for it on another closed PR :P But yeah.. not sure if there's any real issue now (hence i asked :D)

@mcfedr see symfony/symfony-docs#8382 (comment) , tried to explain the general proces there.

This comment has been minimized.

@mcfedr

mcfedr Mar 27, 2018

Contributor

I see, makes sense, so for simple_array nodes it will accept env(json:FOO) but not when it should validate the structure.

@Majkl578

This comment has been minimized.

Copy link
Contributor

Majkl578 commented May 31, 2018

FYI: Just got hit by this BC break when upgrading to 4.1.0:

In ValidateEnvPlaceholdersPass.php line 54:
                                                                                                              
  [Symfony\Component\DependencyInjection\Exception\LogicException]                                            
  Invalid type for env parameter "env(ABC)". Expected "string", but got "float".

for configuration with env defaults:

parameters:
    env(ABC): 1.2

Issue & failing test coming soon.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.