NelmioCorsBundle
NelmioCorsBundle copied to clipboard
Cannot use Symfony arrays in config
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
Did you try allow_origin: ['%env(csv:CORS_ALLOW_ORIGIN_CSV)%']
?
@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
I have the same error here.
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:
# ...
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.
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.
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?
Is there a decent workaround that is possible?
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
Yes, env params are only available at runtime
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?
We probably will have to wait for https://github.com/symfony/symfony/issues/40906? :thinking:
Hey! The same problem, does anybody find workaround?
We write our own solution. 🤣