diff --git a/src/DependencyInjection/OverblogGraphQLExtension.php b/src/DependencyInjection/OverblogGraphQLExtension.php index 7191c3466..448d84ce6 100644 --- a/src/DependencyInjection/OverblogGraphQLExtension.php +++ b/src/DependencyInjection/OverblogGraphQLExtension.php @@ -182,15 +182,17 @@ private function setConfigBuilders(array $config, ContainerBuilder $container): private function setSecurity(array $config, ContainerBuilder $container): void { $executorDefinition = $container->getDefinition(Executor::class); - if ($config['security']['enable_introspection']) { - $executorDefinition->addMethodCall('enableIntrospectionQuery'); - } else { - $executorDefinition->addMethodCall('disableIntrospectionQuery'); - } foreach ($config['security'] as $key => $value) { $container->setParameter(sprintf('%s.%s', $this->getAlias(), $key), $value); } + + // Pass the parameter reference (not the compile-time value) so that an + // "enable_introspection" backed by an env variable is resolved at + // runtime instead of always evaluating truthy as a placeholder string. + $executorDefinition->addMethodCall('setIntrospectionQueryEnabled', [ + sprintf('%%%s.enable_introspection%%', $this->getAlias()), + ]); } private function setErrorHandler(array $config, ContainerBuilder $container): void diff --git a/src/Request/Executor.php b/src/Request/Executor.php index b4464a5e6..5d6936b44 100644 --- a/src/Request/Executor.php +++ b/src/Request/Executor.php @@ -117,12 +117,22 @@ public function setMaxQueryComplexity(int $maxQueryComplexity): void public function enableIntrospectionQuery(): void { - DocumentValidator::addRule(new DisableIntrospection(DisableIntrospection::DISABLED)); + $this->setIntrospectionQueryEnabled(true); } public function disableIntrospectionQuery(): void { - DocumentValidator::addRule(new DisableIntrospection(DisableIntrospection::ENABLED)); + $this->setIntrospectionQueryEnabled(false); + } + + /** + * Toggles the introspection query at runtime so the value can be provided by + * an environment variable (resolved when the service is instantiated) rather + * than being evaluated at container-compile time. + */ + public function setIntrospectionQueryEnabled(bool $enabled): void + { + DocumentValidator::addRule(new DisableIntrospection($enabled ? DisableIntrospection::DISABLED : DisableIntrospection::ENABLED)); } /** diff --git a/tests/Functional/App/config/enableIntrospectionEnvVar/config.yml b/tests/Functional/App/config/enableIntrospectionEnvVar/config.yml new file mode 100644 index 000000000..d05fb1425 --- /dev/null +++ b/tests/Functional/App/config/enableIntrospectionEnvVar/config.yml @@ -0,0 +1,17 @@ +imports: + - { resource: ../config.yml } + - { resource: ../connection/services.yml } + +overblog_graphql: + security: + enable_introspection: '%env(bool:GRAPHQL_ENABLE_INTROSPECTION)%' + definitions: + class_namespace: "Overblog\\GraphQLBundle\\IntrospectionEnvVar\\__DEFINITIONS__" + schema: + query: Query + mutation: ~ + mappings: + types: + - + type: yaml + dir: "%kernel.project_dir%/config/queryComplexity/mapping" diff --git a/tests/Functional/Security/EnableIntrospectionEnvVarTest.php b/tests/Functional/Security/EnableIntrospectionEnvVarTest.php new file mode 100644 index 000000000..1bb5f012c --- /dev/null +++ b/tests/Functional/Security/EnableIntrospectionEnvVarTest.php @@ -0,0 +1,94 @@ + [ + [ + 'message' => 'GraphQL introspection is not allowed, but the query contained __schema or __type', + 'locations' => [ + [ + 'line' => 2, + 'column' => 3, + ], + ], + ], + ], + ]; + + $this->assertResponse($this->introspectionQuery, $expected, self::ANONYMOUS_USER, 'enableIntrospectionEnvVar'); + } finally { + $this->restoreEnv($previous); + } + } + + public function testIntrospectionEnabledViaEnvVar(): void + { + $previous = getenv(self::ENV_VAR); + putenv(self::ENV_VAR.'=true'); + $_ENV[self::ENV_VAR] = 'true'; + $_SERVER[self::ENV_VAR] = 'true'; + + try { + $client = self::createClientAuthenticated(self::ANONYMOUS_USER, 'enableIntrospectionEnvVar'); + $result = self::sendRequest($client, $this->introspectionQuery, true); + + static::assertArrayHasKey('data', $result); + static::assertArrayNotHasKey('errors', $result); + } finally { + $this->restoreEnv($previous); + } + } + + /** + * @param string|false $previous the value returned by getenv() before the test + */ + private function restoreEnv($previous): void + { + if (false === $previous) { + putenv(self::ENV_VAR); + unset($_ENV[self::ENV_VAR], $_SERVER[self::ENV_VAR]); + } else { + putenv(self::ENV_VAR.'='.$previous); + $_ENV[self::ENV_VAR] = $previous; + $_SERVER[self::ENV_VAR] = $previous; + } + } +}