mirror of
https://github.com/graphql-python/graphene.git
synced 2024-11-26 03:23:55 +03:00
add tests and docs for disable introspection rule
This commit is contained in:
parent
ec982ac50b
commit
4e32dac251
|
@ -16,7 +16,7 @@ queries. It takes in the following arguments.
|
||||||
- ``ignore`` Stops recursive depth checking based on a field name. Either a string or regexp to match the name, or a function that returns a boolean
|
- ``ignore`` Stops recursive depth checking based on a field name. Either a string or regexp to match the name, or a function that returns a boolean
|
||||||
- ``callback`` Called each time validation runs. Receives an Object which is a map of the depths for each operation.
|
- ``callback`` Called each time validation runs. Receives an Object which is a map of the depths for each operation.
|
||||||
|
|
||||||
Example
|
Usage
|
||||||
-------
|
-------
|
||||||
|
|
||||||
Here is how you would implement depth-limiting on your schema.
|
Here is how you would implement depth-limiting on your schema.
|
||||||
|
@ -33,7 +33,7 @@ Here is how you would implement depth-limiting on your schema.
|
||||||
|
|
||||||
schema = Schema(query=MyQuery)
|
schema = Schema(query=MyQuery)
|
||||||
|
|
||||||
# Queries which have a depth more than 20
|
# queries which have a depth more than 20
|
||||||
# will not be executed.
|
# will not be executed.
|
||||||
|
|
||||||
validation_errors = validate(
|
validation_errors = validate(
|
||||||
|
@ -47,6 +47,39 @@ Here is how you would implement depth-limiting on your schema.
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
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,
|
||||||
|
document_ast=parse('THE QUERY'),
|
||||||
|
rules=(
|
||||||
|
DisableIntrospection,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
Implementing custom validators
|
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>`_
|
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>`_
|
||||||
|
@ -56,7 +89,7 @@ perform validation, your validator class should define one or more of enter_* an
|
||||||
enter/leave items as well as details on function documentation, please see contents of the visitor module. To make
|
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
|
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
|
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 fields:
|
if any of those fields are blacklisted:
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
from graphql import GraphQLError
|
from graphql import GraphQLError
|
||||||
|
@ -70,7 +103,7 @@ if any of those fields are blacklisted fields:
|
||||||
|
|
||||||
|
|
||||||
def is_blacklisted_field(field_name: str):
|
def is_blacklisted_field(field_name: str):
|
||||||
return key.lower() in my_blacklist
|
return field_name.lower() in my_blacklist
|
||||||
|
|
||||||
|
|
||||||
class BlackListRule(ValidationRule):
|
class BlackListRule(ValidationRule):
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
from .depth_limit import depth_limit_validator
|
from .depth_limit import depth_limit_validator
|
||||||
from .disable_introspection import disable_introspection
|
from .disable_introspection import DisableIntrospection
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"depth_limit_validator",
|
"DisableIntrospection",
|
||||||
"disable_introspection"
|
"depth_limit_validator"
|
||||||
]
|
]
|
||||||
|
|
|
@ -116,7 +116,7 @@ def determine_depth(
|
||||||
if depth_so_far > max_depth:
|
if depth_so_far > max_depth:
|
||||||
context.report_error(
|
context.report_error(
|
||||||
GraphQLError(
|
GraphQLError(
|
||||||
f"'{operation_name}' exceeds maximum operation depth of {max_depth}",
|
f"'{operation_name}' exceeds maximum operation depth of {max_depth}.",
|
||||||
[node],
|
[node],
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -172,7 +172,7 @@ def determine_depth(
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
raise Exception(f"Depth crawler cannot handle: {node.kind}") # pragma: no cover
|
raise Exception(f"Depth crawler cannot handle: {node.kind}.") # pragma: no cover
|
||||||
|
|
||||||
|
|
||||||
def is_ignored(node: FieldNode, ignore: Optional[List[IgnoreType]] = None) -> bool:
|
def is_ignored(node: FieldNode, ignore: Optional[List[IgnoreType]] = None) -> bool:
|
||||||
|
@ -191,6 +191,6 @@ def is_ignored(node: FieldNode, ignore: Optional[List[IgnoreType]] = None) -> bo
|
||||||
if rule(field_name):
|
if rule(field_name):
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Invalid ignore option: {rule}")
|
raise ValueError(f"Invalid ignore option: {rule}.")
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
|
@ -5,18 +5,15 @@ from graphql.validation import ValidationRule
|
||||||
from ..utils.is_introspection_key import is_introspection_key
|
from ..utils.is_introspection_key import is_introspection_key
|
||||||
|
|
||||||
|
|
||||||
def disable_introspection():
|
class DisableIntrospection(ValidationRule):
|
||||||
class DisableIntrospection(ValidationRule):
|
def enter_field(self, node: FieldNode, *_args):
|
||||||
def enter_field(self, node: FieldNode, *_args):
|
field_name = node.name.value
|
||||||
field_name = node.name.value
|
if not is_introspection_key(field_name):
|
||||||
if not is_introspection_key(field_name):
|
return
|
||||||
return
|
|
||||||
|
|
||||||
self.report_error(
|
self.report_error(
|
||||||
GraphQLError(
|
GraphQLError(
|
||||||
f"Cannot query '{field_name}': introspection is disabled.",
|
f"Cannot query '{field_name}': introspection is disabled.",
|
||||||
node,
|
node,
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
)
|
||||||
return DisableIntrospection
|
|
||||||
|
|
|
@ -235,7 +235,7 @@ def test_should_catch_very_deep_query():
|
||||||
errors, result = run_query(query, 4)
|
errors, result = run_query(query, 4)
|
||||||
|
|
||||||
assert len(errors) == 1
|
assert len(errors) == 1
|
||||||
assert errors[0].message == "'anonymous' exceeds maximum operation depth of 4"
|
assert errors[0].message == "'anonymous' exceeds maximum operation depth of 4."
|
||||||
|
|
||||||
|
|
||||||
def test_should_ignore_field():
|
def test_should_ignore_field():
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from graphql import parse, validate
|
from graphql import parse, validate
|
||||||
|
|
||||||
from ...types import Schema, ObjectType, String
|
from ...types import Schema, ObjectType, String
|
||||||
from ..disable_introspection import disable_introspection
|
from ..disable_introspection import DisableIntrospection
|
||||||
|
|
||||||
|
|
||||||
class Query(ObjectType):
|
class Query(ObjectType):
|
||||||
|
@ -9,6 +9,10 @@ class Query(ObjectType):
|
||||||
required=True
|
required=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_name(root, info):
|
||||||
|
return "Hello world!"
|
||||||
|
|
||||||
|
|
||||||
schema = Schema(query=Query)
|
schema = Schema(query=Query)
|
||||||
|
|
||||||
|
@ -16,14 +20,24 @@ schema = Schema(query=Query)
|
||||||
def run_query(query: str):
|
def run_query(query: str):
|
||||||
document = parse(query)
|
document = parse(query)
|
||||||
|
|
||||||
result = None
|
|
||||||
|
|
||||||
errors = validate(
|
errors = validate(
|
||||||
schema=schema.graphql_schema,
|
schema=schema.graphql_schema,
|
||||||
document_ast=document,
|
document_ast=document,
|
||||||
rules=(
|
rules=(
|
||||||
disable_introspection(),
|
DisableIntrospection,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
return errors, result
|
return errors
|
||||||
|
|
||||||
|
|
||||||
|
def test_disallows_introspection_queries():
|
||||||
|
errors = run_query("{ __schema { queryType { name } } }")
|
||||||
|
|
||||||
|
assert len(errors) == 1
|
||||||
|
assert errors[0].message == "Cannot query '__schema': introspection is disabled."
|
||||||
|
|
||||||
|
|
||||||
|
def test_allows_non_introspection_queries():
|
||||||
|
errors = run_query("{ name }")
|
||||||
|
assert len(errors) == 0
|
||||||
|
|
Loading…
Reference in New Issue
Block a user