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

Cannot use Symfony arrays in config #148

Open
atay opened this issue Feb 20, 2020 · 14 comments
Open

Cannot use Symfony arrays in config #148

atay opened this issue Feb 20, 2020 · 14 comments

Comments

@atay
Copy link

atay commented Feb 20, 2020

In the latest version I've been trying to use:

nelmio_cors:
    paths:
        '^/':
            allow_origin: '%env(csv:CORS_ALLOW_ORIGIN_CSV)%'

where CORS_ALLOW_ORIGIN_CSV is a CSV value, but NelmioCorsBundle recognizes it as a string value instead of array and returns an error Fatal error: Uncaught ErrorException: Warning: in_array() expects parameter 2 to be array, string given in /var/www/pimcore/vendor/nelmio/cors-bundle/DependencyInjection/NelmioCorsExtension.php:60

@Juarrow
Copy link

Juarrow commented Mar 12, 2020

Did you try allow_origin: ['%env(csv:CORS_ALLOW_ORIGIN_CSV)%'] ?

@atay
Copy link
Author

atay commented Mar 13, 2020

@Incubbus yes, but this variable is still a string, it's converted to array later, so it does not throw Exception as above, but it just doesn't work

@pounard
Copy link

pounard commented Mar 17, 2021

I have the same error here.

@pounard
Copy link

pounard commented Mar 17, 2021

In my case, I have a fixed number of allowed origins, so I was able to workaround this limitation using this procedure:

First in config/services.yaml:

parameters:
    computed_absolute_url: "%router.request_context.scheme%://%router.request_context.host%%env(ABSOLUTE_URL_PORT)%%router.request_context.base_url%"

    # Allowed CORS origins.
    env(NELMIO_CORS_ORIGIN_FRONTEND): "http://localhost:8080/"
    env(NELMIO_CORS_ORIGIN_BACKEND): "%computed_absolute_url%"
    nelmio_cors_origin_frontend: "%env(resolve:NELMIO_CORS_ORIGIN_FRONTEND)%"
    nelmio_cors_origin_backend: "%env(resolve:NELMIO_CORS_ORIGIN_BACKEND)%"

Please note that there are a few custom env vars hanging in computed_absolute_url but you get the point.

Then in config/packages/nelmio_cors.yaml:

nelmio_cors:
    defaults:
        allow_origin: ["%nelmio_cors_origin_frontend%", "%nelmio_cors_origin_backend%"]
        # ...
    paths:
        # ...

@pounard
Copy link

pounard commented Mar 17, 2021

The error we get is mostly due to the fact that the extension does some work around the defaults, but when reaching the extension being called, env variables are not resolved. There might be a few options to make it work: either resolve those at runtime (ie. upon service initialisation during request) or may be trying in a compiler pass ? But that second option probably wouldn't work, althought it might worth it to at least try.

@speller
Copy link

speller commented Mar 14, 2022

Any updates on fixing this issue? I would like to have only one configuration that reads the list of allowed origins from an environment variable instead of having multiple hard-coded configurations and bandaids. This is definitely a bundle configuration issue.

@Seldaek
Copy link
Member

Seldaek commented Feb 15, 2023

If someone can send a PR for this it'd be welcome. I'm not super familiar with env var params. I guess this might need to allow strings in the Configuration class check at runtime once env vars are resolved that we have an array?

@MetalArend
Copy link

Is there a decent workaround that is possible?

@WietseGielen
Copy link

WietseGielen commented Apr 5, 2023

same issue here..

I've encountered this issue and started debugging because I didn't understand what was going wrong and I still don't.

Symfony version: 5.4.16
nelmio/cors-bundle: 2.2.0

Steps to reproduce

In my parameters.yaml I've got the following content:

env(TEST2): '["http://test1.localhost/","http://test2.localhost/"]'
test: ["http://test1.localhost/","http://test2.localhost/"]
test2: '%env(json:TEST2)%'

When I dump how env(TEST2) is processed, I get the following result:

wietse@201b65690067:/var/vhost$ console debug:container --env-var=TEST2     

Symfony Container Environment Variables
=======================================

 // Displaying detailed environment variable usage matching TEST2                                                       

%env(json:TEST2)%
-----------------

 ----------------- --------------------------------------------------------- 
  Default value     "["http://test1.localhost/","http://test2.localhost/"]"  
  Real value        n/a                                                      
  Processed value   [                                                        
                      "http://test1.localhost/",                             
                      "http://test2.localhost/"                              
                    ]                                                        
 ----------------- --------------------------------------------------------- 

I bind the two params I've created to some params I can inject in a controller in services.yaml

services:
    _defaults:
        autowire: true
        autoconfigure: true
        bind:
            $test: '%test%'
            $test2: '%test2%'

I create some controller method:

/** @Route(path="/test", methods={"GET"}) */
public function test(): Response
{
    return new JsonResponse(['test' => $this->test, 'test2' => $this->test2]);
}

and the response I get is when calling the endpoint via an HTTP request is:

{
    "test": [
        "http://test1.localhost/",
        "http://test2.localhost/"
    ],
    "test2": [
        "http://test1.localhost/",
        "http://test2.localhost/"
    ]
}

When I use '%test% as param in nelmio.defaults.allow_origin everything works as expected

nelmio_cors:
    defaults:
        allow_credentials: true
        origin_regex: true
        allow_origin: '%test%'
        allow_methods: ['GET', 'OPTIONS', 'POST', 'PUT', 'PATCH', 'DELETE']
        allow_headers: ['Content-Type', 'Authorization']
        expose_headers: ['Link', 'Content-Disposition']
        max_age: 3600
    paths:
        '^/': null

The value which comes in as allow_origin NelmioCorsExtension is the following:

array(2) {
  [0]=>
  string(23) "http://test1.localhost/"
  [1]=>
  string(23) "http://test2.localhost/"
}

So far so good..

When I now use '%test2% as param the following happens:

nelmio_cors:
    defaults:
        allow_credentials: true
        origin_regex: true
        allow_origin: '%test2%'
        allow_methods: ['GET', 'OPTIONS', 'POST', 'PUT', 'PATCH', 'DELETE']
        allow_headers: ['Content-Type', 'Authorization']
        expose_headers: ['Link', 'Content-Disposition']
        max_age: 3600
    paths:
        '^/': null

The value which comes in as allow_origin in NelmioCorsExtionsion is now:

env_75acc66cb0a67e10_json_TEST2_1e2da8c52400b233885b68ca5818285b

and this will throw the error:

in_array(): Argument #2 ($haystack) must be of type array, string given

when trying to do

in_array('*', $defaults['allow_origin'])

in NelmioCorsExtension::L49

When I now do the following in nelmio_cors_.yaml:

nelmio_cors:
    defaults:
        allow_credentials: true
        origin_regex: true
        allow_origin: [ '%test2%' ] 
        allow_methods: ['GET', 'OPTIONS', 'POST', 'PUT', 'PATCH', 'DELETE']
        allow_headers: ['Content-Type', 'Authorization']
        expose_headers: ['Link', 'Content-Disposition']
        max_age: 3600
    paths:
        '^/': null

I expect it passes NelmioCorsExtension because the value is now an array:

array(1) {
  [0]=>
  string(64) "env_75acc66cb0a67e10_json_TEST2_1e2da8c52400b233885b68ca5818285b"
}

But it now fails with the message:

 Invalid type for path "nelmio_cors.defaults.allow_origin.0". Expected one of "bool", "int", "float", "string", but got "array"

in BaseNode.php::L573:

exception trace:

Exception trace:
  at /var/vhost/vendor/symfony/config/Definition/BaseNode.php:573
 Symfony\Component\Config\Definition\BaseNode->doValidateType() at /var/vhost/vendor/symfony/config/Definition/BaseNode.php:407
 Symfony\Component\Config\Definition\BaseNode->normalize() at /var/vhost/vendor/symfony/config/Definition/BaseNode.php:390
 Symfony\Component\Config\Definition\BaseNode->normalize() at /var/vhost/vendor/symfony/config/Definition/PrototypedArrayNode.php:255
 Symfony\Component\Config\Definition\PrototypedArrayNode->normalizeValue() at /var/vhost/vendor/symfony/config/Definition/BaseNode.php:410
 Symfony\Component\Config\Definition\BaseNode->normalize() at /var/vhost/vendor/symfony/config/Definition/ArrayNode.php:287
 Symfony\Component\Config\Definition\ArrayNode->normalizeValue() at /var/vhost/vendor/symfony/config/Definition/BaseNode.php:410
 Symfony\Component\Config\Definition\BaseNode->normalize() at /var/vhost/vendor/symfony/config/Definition/ArrayNode.php:287
 Symfony\Component\Config\Definition\ArrayNode->normalizeValue() at /var/vhost/vendor/symfony/config/Definition/BaseNode.php:410
 Symfony\Component\Config\Definition\BaseNode->normalize() at /var/vhost/vendor/symfony/config/Definition/Processor.php:32
 Symfony\Component\Config\Definition\Processor->process() at /var/vhost/vendor/symfony/config/Definition/Processor.php:46
 Symfony\Component\Config\Definition\Processor->processConfiguration() at /var/vhost/vendor/symfony/dependency-injection/Compiler/ValidateEnvPlaceholdersPass.php:86
 Symfony\Component\DependencyInjection\Compiler\ValidateEnvPlaceholdersPass->process() at /var/vhost/vendor/symfony/dependency-injection/Compiler/Compiler.php:82
 Symfony\Component\DependencyInjection\Compiler\Compiler->compile() at /var/vhost/vendor/symfony/dependency-injection/ContainerBuilder.php:757
 Symfony\Component\DependencyInjection\ContainerBuilder->compile() at /var/vhost/vendor/symfony/http-kernel/Kernel.php:546
 Symfony\Component\HttpKernel\Kernel->initializeContainer() at /var/vhost/vendor/symfony/http-kernel/Kernel.php:787
 Symfony\Component\HttpKernel\Kernel->preBoot() at /var/vhost/vendor/symfony/http-kernel/Kernel.php:128
 Symfony\Component\HttpKernel\Kernel->boot() at /var/vhost/vendor/symfony/framework-bundle/Console/Application.php:168
 Symfony\Bundle\FrameworkBundle\Console\Application->registerCommands() at /var/vhost/vendor/symfony/framework-bundle/Console/Application.php:74
 Symfony\Bundle\FrameworkBundle\Console\Application->doRun() at /var/vhost/vendor/symfony/console/Application.php:171
 Symfony\Component\Console\Application->run() at /var/vhost/bin/console:43

My guess is that the env variables are only processed after the bundle extension is loaded because env variables are only loaded at runtime.

Also interesting:

When I remove all checks in NelmioCorsExtension::load() which already expects an array. The configuration processor which is default is still failing because an allow_origin is defined as an ArrayNode. In other words, I expects this is a bigger issue in Symfony itself. Because it impossible to use %env(json:VARIABLE)% or %env(csv:VARIABLE)% as Symfony describes without failing on the Configuration validation

In ArrayNode.php line 260:
                                                                                                 
  [Symfony\Component\Config\Definition\Exception\InvalidTypeException]                           
  Invalid type for path "nelmio_cors.defaults.allow_origin". Expected "array", but got "string"  
                                                                                                 

Exception trace:
  at /var/vhost/vendor/symfony/config/Definition/ArrayNode.php:260
 Symfony\Component\Config\Definition\ArrayNode->validateType() at /var/vhost/vendor/symfony/config/Definition/BaseNode.php:564
 Symfony\Component\Config\Definition\BaseNode->doValidateType() at /var/vhost/vendor/symfony/config/Definition/BaseNode.php:407
 Symfony\Component\Config\Definition\BaseNode->normalize() at /var/vhost/vendor/symfony/config/Definition/ArrayNode.php:287
 Symfony\Component\Config\Definition\ArrayNode->normalizeValue() at /var/vhost/vendor/symfony/config/Definition/BaseNode.php:410
 Symfony\Component\Config\Definition\BaseNode->normalize() at /var/vhost/vendor/symfony/config/Definition/ArrayNode.php:287
 Symfony\Component\Config\Definition\ArrayNode->normalizeValue() at /var/vhost/vendor/symfony/config/Definition/BaseNode.php:410
 Symfony\Component\Config\Definition\BaseNode->normalize() at /var/vhost/vendor/symfony/config/Definition/Processor.php:32
 Symfony\Component\Config\Definition\Processor->process() at /var/vhost/vendor/symfony/config/Definition/Processor.php:46
 Symfony\Component\Config\Definition\Processor->processConfiguration() at /var/vhost/vendor/symfony/dependency-injection/Extension/Extension.php:113
 Symfony\Component\DependencyInjection\Extension\Extension->processConfiguration() at /var/vhost/vendor/nelmio/cors-bundle/DependencyInjection/NelmioCorsExtension.php:30
 Nelmio\CorsBundle\DependencyInjection\NelmioCorsExtension->load() at /var/vhost/vendor/symfony/dependency-injection/Compiler/MergeExtensionConfigurationPass.php:79
 Symfony\Component\DependencyInjection\Compiler\MergeExtensionConfigurationPass->process() at /var/vhost/vendor/symfony/http-kernel/DependencyInjection/MergeExtensionConfigurationPass.php:42
 Symfony\Component\HttpKernel\DependencyInjection\MergeExtensionConfigurationPass->process() at /var/vhost/vendor/symfony/dependency-injection/Compiler/Compiler.php:82
 Symfony\Component\DependencyInjection\Compiler\Compiler->compile() at /var/vhost/vendor/symfony/dependency-injection/ContainerBuilder.php:759
 Symfony\Component\DependencyInjection\ContainerBuilder->compile() at /var/vhost/vendor/symfony/http-kernel/Kernel.php:546
 Symfony\Component\HttpKernel\Kernel->initializeContainer() at /var/vhost/vendor/symfony/http-kernel/Kernel.php:787
 Symfony\Component\HttpKernel\Kernel->preBoot() at /var/vhost/vendor/symfony/http-kernel/Kernel.php:128
 Symfony\Component\HttpKernel\Kernel->boot() at /var/vhost/vendor/symfony/framework-bundle/Console/Application.php:168
 Symfony\Bundle\FrameworkBundle\Console\Application->registerCommands() at /var/vhost/vendor/symfony/framework-bundle/Console/Application.php:74
 Symfony\Bundle\FrameworkBundle\Console\Application->doRun() at /var/vhost/vendor/symfony/console/Application.php:171
 Symfony\Component\Console\Application->run() at /var/vhost/bin/console:43

@Seldaek
Copy link
Member

Seldaek commented Apr 12, 2023

Yes, env params are only available at runtime

@MetalArend
Copy link

But they are indeed available at runtime, to set up your application. So is there maybe a way to tell the bundle to avoid the check on compile time, and do a check on runtime?

@MetalArend
Copy link

We probably will have to wait for symfony/symfony#40906? 🤔

@YuriyChmil
Copy link

Hey! The same problem, does anybody find workaround?

@alexndlm
Copy link
Contributor

alexndlm commented Feb 7, 2024

We write our own solution. 🤣

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants