diff --git a/config/schema/graphql.schema.yml b/config/schema/graphql.schema.yml index fd3657a7a..f90dc52f0 100644 --- a/config/schema/graphql.schema.yml +++ b/config/schema/graphql.schema.yml @@ -23,6 +23,15 @@ graphql.graphql_servers.*: batching: type: boolean label: 'Batching' + disable_introspection: + type: boolean + label: 'Disable Introspection' + query_depth: + type: integer + label: 'Max query depth' + query_complexity: + type: integer + label: 'Max query complexity' schema_configuration: type: 'graphql.schema.[%parent.schema]' persisted_queries_settings: diff --git a/src/Entity/Server.php b/src/Entity/Server.php index e569ea343..003d66744 100644 --- a/src/Entity/Server.php +++ b/src/Entity/Server.php @@ -25,6 +25,9 @@ use GraphQL\Server\Helper; use GraphQL\Type\Definition\ResolveInfo; use GraphQL\Validator\DocumentValidator; +use GraphQL\Validator\Rules\DisableIntrospection; +use GraphQL\Validator\Rules\QueryComplexity; +use GraphQL\Validator\Rules\QueryDepth; /** * The main GraphQL configuration and request entry point. @@ -59,7 +62,10 @@ * "endpoint", * "debug_flag", * "caching", - * "batching" + * "batching", + * "disable_introspection", + * "query_depth", + * "query_complexity" * }, * links = { * "collection" = "/admin/config/graphql/servers", @@ -123,6 +129,27 @@ class Server extends ConfigEntityBase implements ServerInterface { */ public $batching = TRUE; + /** + * Whether to disable query introspection. + * + * @var bool + */ + public $disable_introspection = FALSE; + + /** + * The query complexity. + * + * @var int|null + */ + public $query_complexity = NULL; + + /** + * The query depth. + * + * @var int|null + */ + public $query_depth = NULL; + /** * The server's endpoint. * @@ -137,7 +164,6 @@ class Server extends ConfigEntityBase implements ServerInterface { */ public $persisted_queries_settings = []; - /** * Persisted query plugin instances available on this server. * @@ -498,10 +524,90 @@ protected function getValidationRules() { return []; } - return array_values(DocumentValidator::defaultRules()); + $rules = array_values(DocumentValidator::defaultRules()); + if ($this->getDisableIntrospection()) { + $rules[] = new DisableIntrospection(); + } + if ($this->getQueryDepth()) { + $rules[] = new QueryDepth($this->getQueryDepth()); + } + if ($this->getQueryComplexity()) { + $rules[] = new QueryComplexity($this->getQueryComplexity()); + } + + return $rules; }; } + /** + * Gets disable introspection config. + * + * @return bool + * The disable introspection config, FALSE otherwise. + */ + public function getDisableIntrospection(): bool { + return (bool) $this->disable_introspection; + } + + /** + * Sets disable introspection config. + * + * @param bool $introspection + * The value for the disable introspection config. + * + * @return $this + */ + public function setDisableIntrospection(bool $introspection) { + $this->disable_introspection = $introspection; + return $this; + } + + /** + * Gets query depth config. + * + * @return int|null + * The query depth, NULL otherwise. + */ + public function getQueryDepth(): ?int { + return (int) $this->query_depth; + } + + /** + * Sets query depth config. + * + * @param int|null $depth + * The value for the query depth config. + * + * @return $this + */ + public function setQueryDepth(?int $depth) { + $this->query_depth = $depth; + return $this; + } + + /** + * Gets query complexity config. + * + * @return int|null + * The query complexity, NULL otherwise. + */ + public function getQueryComplexity(): ?int { + return (int) $this->query_complexity; + } + + /** + * Sets query complexity config. + * + * @param int|null $complexity + * The value for the query complexity config. + * + * @return $this + */ + public function setQueryComplexity(?int $complexity) { + $this->query_complexity = $complexity; + return $this; + } + /** * {@inheritDoc} */ diff --git a/src/Form/ServerForm.php b/src/Form/ServerForm.php index d8876765b..c1b747e37 100644 --- a/src/Form/ServerForm.php +++ b/src/Form/ServerForm.php @@ -186,6 +186,32 @@ public function form(array $form, FormStateInterface $formState): array { '#description' => $this->t('Whether caching of queries and partial results is enabled.'), ]; + $form['validation'] = [ + '#title' => $this->t('Validation rules'), + '#type' => 'fieldset', + ]; + + $form['validation']['disable_introspection'] = [ + '#title' => $this->t('Disable introspection'), + '#type' => 'checkbox', + '#default_value' => $server->get('disable_introspection'), + '#description' => $this->t('Security rule: Whether introspection should be disabled.'), + ]; + + $form['validation']['query_depth'] = [ + '#title' => $this->t('Max query depth'), + '#type' => 'number', + '#default_value' => $server->get('query_depth'), + '#description' => $this->t('Security rule: The maximum allowed depth of nested queries. Leave empty to set unlimited.'), + ]; + + $form['validation']['query_complexity'] = [ + '#title' => $this->t('Max query complexity'), + '#default_value' => $server->get('query_complexity'), + '#type' => 'number', + '#description' => $this->t('Security rule: The maximum allowed complexity of a query. Leave empty to set unlimited.'), + ]; + $debug_flags = $server->get('debug_flag') ?? 0; $form['debug_flag'] = [ '#title' => $this->t('Debug settings'),