feat: intropsection and custom validation

Signed-off-by: QuentinN42 <quentin@lieumont.fr>
This commit is contained in:
QuentinN42 2023-03-16 14:27:31 -07:00
parent 84c5559ded
commit f07d6a29c5
No known key found for this signature in database
GPG Key ID: 2CD7D563712B3A50
4 changed files with 96 additions and 83 deletions

View File

@ -0,0 +1,42 @@
Implementing custom validators
==============================
GraphQL uses query validators to check if Query AST is valid and can be executed. Every GraphQL server implements
standard query validators. For example, there is an validator that tests if queried field exists on queried type, that
makes query fail with "Cannot query field on type" error if it doesn't.
If you need more complex validation than presented before, you can implement your own query validators. All custom query
validators should extend the `ValidationRule`_ base class importable from the ``graphql.validation.rules`` module. Query
validators are visitor classes. They are instantiated at the time of query validation with one required argument
(context: ASTValidationContext). In order to perform validation, your validator class should define one or more of
``enter_*`` and ``leave_*`` methods. For possible enter/leave items as well as details on function documentation, please
see contents of the visitor module. To make validation fail, you should call validator's report_error method with the
instance of GraphQLError describing failure reason.
Implementing your custom validators
-----------------------------------
Here is an example query validator that only allows queries fields with a name of even length.
.. code:: python
from graphql import GraphQLError
from graphql.language import FieldNode
from graphql.validation import ValidationRule
class MyCustomValidationRule(ValidationRule):
def enter_field(self, node: FieldNode, *_args):
if len(node.name.value) % 2 == 0:
# Here the query length is even, so we allow it.
return
else:
# Here the query length is odd, so we don't want to allow it.
# Calling self.report_error will make the query fail with the error message.
self.report_error(
GraphQLError(
f"Cannot query '{field_name}': length is odd.", node,
)
)
.. _ValidationRule: https://github.com/graphql-python/graphql-core/blob/v3.0.5/src/graphql/validation/rules/__init__.py#L37

View File

@ -17,7 +17,8 @@ the `Django documentation`_ on how to secure your API.
:maxdepth: 1
maxdepth
queryvalidation
introspection
customvalidation
We have seen the most efficient way to secure your GraphQL API.

View File

@ -0,0 +1,52 @@
Disable Introspection
=====================
What is the introspection ?
---------------------------
The introspection query is a query that allows you to ask the server what queries and mutations are supported. If you
comes from REST, you can view it as a openapi or swagger schema.
Disabling it or not ?
---------------------
Depending if you are building a private or a public API, you might want to disable introspection or not. If you are
building a public API, the introspection allows consumers (developers) to know what they can do with your API. If you
disable it, it will be harder for them to use your API. But if you are building a private API, the only consumers of
your API will be your own developers. In this case, you might want to keep the introspection open in staging
environments but close it in production to reduce the attack surface.
Keep in mind that disabling introspection does not prevent hackers to send queries to your API. It just makes it harder
to know what they can do with it.
Implementation
--------------
Graphene provides a validation rule to disable introspection. It ensures that your schema cannot be introspected. You
just need to import the ``DisableIntrospection`` class from ``graphene.validation``.
Here is a code example of how you can disable introspection for your schema.
.. code:: python
from graphql import validate, parse
from graphene import ObjectType, Schema, String
from graphene.validation import DisableIntrospection
class MyQuery(ObjectType):
name = String(required=True)
schema = Schema(query=MyQuery)
# introspection queries will not be executed.
validation_errors = validate(
schema=schema.graphql_schema,
document_ast=parse('THE QUERY'),
rules=(
DisableIntrospection,
)
)

View File

@ -1,82 +0,0 @@
Query Validation
================
GraphQL uses query validators to check if Query AST is valid and can be executed. Every GraphQL server implements
standard query validators. For example, there is an validator that tests if queried field exists on queried type, that
makes query fail with "Cannot query field on type" error if it doesn't.
To help with common use cases, graphene provides a few validation rules out of the box.
Disable Introspection
---------------------
the disable introspection validation rule ensures that your schema cannot be introspected.
This is a useful security measure in production environments.
Usage
-----
Here is how you would disable introspection for your schema.
.. code:: python
from graphql import validate, parse
from graphene import ObjectType, Schema, String
from graphene.validation import DisableIntrospection
class MyQuery(ObjectType):
name = String(required=True)
schema = Schema(query=MyQuery)
# introspection queries will not be executed.
validation_errors = validate(
schema=schema.graphql_schema,
document_ast=parse('THE QUERY'),
rules=(
DisableIntrospection,
)
)
Implementing custom validators
------------------------------
All custom query validators should extend the `ValidationRule <https://github.com/graphql-python/graphql-core/blob/v3.0.5/src/graphql/validation/rules/__init__.py#L37>`_
base class importable from the graphql.validation.rules module. Query validators are visitor classes. They are
instantiated at the time of query validation with one required argument (context: ASTValidationContext). In order to
perform validation, your validator class should define one or more of enter_* and leave_* methods. For possible
enter/leave items as well as details on function documentation, please see contents of the visitor module. To make
validation fail, you should call validator's report_error method with the instance of GraphQLError describing failure
reason. Here is an example query validator that visits field definitions in GraphQL query and fails query validation
if any of those fields are blacklisted:
.. code:: python
from graphql import GraphQLError
from graphql.language import FieldNode
from graphql.validation import ValidationRule
my_blacklist = (
"disallowed_field",
)
def is_blacklisted_field(field_name: str):
return field_name.lower() in my_blacklist
class BlackListRule(ValidationRule):
def enter_field(self, node: FieldNode, *_args):
field_name = node.name.value
if not is_blacklisted_field(field_name):
return
self.report_error(
GraphQLError(
f"Cannot query '{field_name}': field is blacklisted.", node,
)
)