mirror of
https://github.com/graphql-python/graphene.git
synced 2024-11-29 13:03:56 +03:00
feat: intropsection and custom validation
Signed-off-by: QuentinN42 <quentin@lieumont.fr>
This commit is contained in:
parent
84c5559ded
commit
f07d6a29c5
42
docs/security/customvalidation.rst
Normal file
42
docs/security/customvalidation.rst
Normal 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
|
|
@ -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.
|
||||
|
||||
|
|
52
docs/security/introspection.rst
Normal file
52
docs/security/introspection.rst
Normal 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,
|
||||
)
|
||||
)
|
|
@ -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,
|
||||
)
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user