- implement `validation::rules::disable_introspection` - add `RootNode::disable_introspection()` and `RootNode::enable_introspection()` methods
This commit is contained in:
parent
58ae682f91
commit
f98bdf1a50
7 changed files with 480 additions and 4 deletions
|
@ -70,6 +70,7 @@ All user visible changes to `juniper` crate will be documented in this file. Thi
|
||||||
- `LookAheadMethods::applies_for()` method. ([#1138], [#1145])
|
- `LookAheadMethods::applies_for()` method. ([#1138], [#1145])
|
||||||
- `LookAheadMethods::field_original_name()` and `LookAheadMethods::field_alias()` methods. ([#1199])
|
- `LookAheadMethods::field_original_name()` and `LookAheadMethods::field_alias()` methods. ([#1199])
|
||||||
- [`anyhow` crate] integration behind `anyhow` and `backtrace` [Cargo feature]s. ([#1215], [#988])
|
- [`anyhow` crate] integration behind `anyhow` and `backtrace` [Cargo feature]s. ([#1215], [#988])
|
||||||
|
- `RootNode::disable_introspection()` applying additional `validation::rules::disable_introspection`, and `RootNode::enable_introspection()` reverting it. ([#1227], [#456])
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
@ -88,6 +89,7 @@ All user visible changes to `juniper` crate will be documented in this file. Thi
|
||||||
- Stack overflow on nested GraphQL fragments. ([CVE-2022-31173])
|
- Stack overflow on nested GraphQL fragments. ([CVE-2022-31173])
|
||||||
|
|
||||||
[#113]: /../../issues/113
|
[#113]: /../../issues/113
|
||||||
|
[#456]: /../../issues/456
|
||||||
[#503]: /../../issues/503
|
[#503]: /../../issues/503
|
||||||
[#528]: /../../issues/528
|
[#528]: /../../issues/528
|
||||||
[#750]: /../../issues/750
|
[#750]: /../../issues/750
|
||||||
|
@ -140,6 +142,7 @@ All user visible changes to `juniper` crate will be documented in this file. Thi
|
||||||
[#1209]: /../../pull/1209
|
[#1209]: /../../pull/1209
|
||||||
[#1215]: /../../pull/1215
|
[#1215]: /../../pull/1215
|
||||||
[#1221]: /../../pull/1221
|
[#1221]: /../../pull/1221
|
||||||
|
[#1227]: /../../pull/1227
|
||||||
[ba1ed85b]: /../../commit/ba1ed85b3c3dd77fbae7baf6bc4e693321a94083
|
[ba1ed85b]: /../../commit/ba1ed85b3c3dd77fbae7baf6bc4e693321a94083
|
||||||
[CVE-2022-31173]: /../../security/advisories/GHSA-4rx6-g5vg-5f3j
|
[CVE-2022-31173]: /../../security/advisories/GHSA-4rx6-g5vg-5f3j
|
||||||
|
|
||||||
|
|
|
@ -58,7 +58,10 @@ use crate::{
|
||||||
executor::{execute_validated_query, get_operation},
|
executor::{execute_validated_query, get_operation},
|
||||||
introspection::{INTROSPECTION_QUERY, INTROSPECTION_QUERY_WITHOUT_DESCRIPTIONS},
|
introspection::{INTROSPECTION_QUERY, INTROSPECTION_QUERY_WITHOUT_DESCRIPTIONS},
|
||||||
parser::parse_document_source,
|
parser::parse_document_source,
|
||||||
validation::{validate_input_values, visit_all_rules, ValidatorContext},
|
validation::{
|
||||||
|
rules, validate_input_values, visit as visit_rule, visit_all_rules, MultiVisitorNil,
|
||||||
|
ValidatorContext,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
|
@ -158,6 +161,13 @@ where
|
||||||
{
|
{
|
||||||
let mut ctx = ValidatorContext::new(&root_node.schema, &document);
|
let mut ctx = ValidatorContext::new(&root_node.schema, &document);
|
||||||
visit_all_rules(&mut ctx, &document);
|
visit_all_rules(&mut ctx, &document);
|
||||||
|
if root_node.introspection_disabled {
|
||||||
|
visit_rule(
|
||||||
|
&mut MultiVisitorNil.with(rules::disable_introspection::factory()),
|
||||||
|
&mut ctx,
|
||||||
|
&document,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let errors = ctx.into_errors();
|
let errors = ctx.into_errors();
|
||||||
if !errors.is_empty() {
|
if !errors.is_empty() {
|
||||||
|
@ -201,6 +211,13 @@ where
|
||||||
{
|
{
|
||||||
let mut ctx = ValidatorContext::new(&root_node.schema, &document);
|
let mut ctx = ValidatorContext::new(&root_node.schema, &document);
|
||||||
visit_all_rules(&mut ctx, &document);
|
visit_all_rules(&mut ctx, &document);
|
||||||
|
if root_node.introspection_disabled {
|
||||||
|
visit_rule(
|
||||||
|
&mut MultiVisitorNil.with(rules::disable_introspection::factory()),
|
||||||
|
&mut ctx,
|
||||||
|
&document,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let errors = ctx.into_errors();
|
let errors = ctx.into_errors();
|
||||||
if !errors.is_empty() {
|
if !errors.is_empty() {
|
||||||
|
@ -246,6 +263,13 @@ where
|
||||||
{
|
{
|
||||||
let mut ctx = ValidatorContext::new(&root_node.schema, &document);
|
let mut ctx = ValidatorContext::new(&root_node.schema, &document);
|
||||||
visit_all_rules(&mut ctx, &document);
|
visit_all_rules(&mut ctx, &document);
|
||||||
|
if root_node.introspection_disabled {
|
||||||
|
visit_rule(
|
||||||
|
&mut MultiVisitorNil.with(rules::disable_introspection::factory()),
|
||||||
|
&mut ctx,
|
||||||
|
&document,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let errors = ctx.into_errors();
|
let errors = ctx.into_errors();
|
||||||
if !errors.is_empty() {
|
if !errors.is_empty() {
|
||||||
|
|
|
@ -44,6 +44,8 @@ pub struct RootNode<
|
||||||
pub subscription_info: SubscriptionT::TypeInfo,
|
pub subscription_info: SubscriptionT::TypeInfo,
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub schema: SchemaType<'a, S>,
|
pub schema: SchemaType<'a, S>,
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub introspection_disabled: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Metadata for a schema
|
/// Metadata for a schema
|
||||||
|
@ -147,7 +149,7 @@ where
|
||||||
mutation_info: MutationT::TypeInfo,
|
mutation_info: MutationT::TypeInfo,
|
||||||
subscription_info: SubscriptionT::TypeInfo,
|
subscription_info: SubscriptionT::TypeInfo,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
RootNode {
|
Self {
|
||||||
query_type: query_obj,
|
query_type: query_obj,
|
||||||
mutation_type: mutation_obj,
|
mutation_type: mutation_obj,
|
||||||
subscription_type: subscription_obj,
|
subscription_type: subscription_obj,
|
||||||
|
@ -159,9 +161,65 @@ where
|
||||||
query_info,
|
query_info,
|
||||||
mutation_info,
|
mutation_info,
|
||||||
subscription_info,
|
subscription_info,
|
||||||
|
introspection_disabled: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Disables introspection for this [`RootNode`], making it to return a [`FieldError`] whenever
|
||||||
|
/// its `__schema` or `__type` field is resolved.
|
||||||
|
///
|
||||||
|
/// By default, all introspection queries are allowed.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use juniper::{
|
||||||
|
/// # graphql_object, graphql_vars, EmptyMutation, EmptySubscription, GraphQLError,
|
||||||
|
/// # RootNode,
|
||||||
|
/// # };
|
||||||
|
/// #
|
||||||
|
/// pub struct Query;
|
||||||
|
///
|
||||||
|
/// #[graphql_object]
|
||||||
|
/// impl Query {
|
||||||
|
/// fn some() -> bool {
|
||||||
|
/// true
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// type Schema = RootNode<'static, Query, EmptyMutation<()>, EmptySubscription<()>>;
|
||||||
|
///
|
||||||
|
/// let schema = Schema::new(Query, EmptyMutation::new(), EmptySubscription::new())
|
||||||
|
/// .disable_introspection();
|
||||||
|
///
|
||||||
|
/// # // language=GraphQL
|
||||||
|
/// let query = "query { __schema { queryType { name } } }";
|
||||||
|
///
|
||||||
|
/// match juniper::execute_sync(query, None, &schema, &graphql_vars! {}, &()) {
|
||||||
|
/// Err(GraphQLError::ValidationError(errs)) => {
|
||||||
|
/// assert_eq!(
|
||||||
|
/// errs.first().unwrap().message(),
|
||||||
|
/// "GraphQL introspection is not allowed, but the operation contained `__schema`",
|
||||||
|
/// );
|
||||||
|
/// }
|
||||||
|
/// res => panic!("expected `ValidationError`, returned: {res:#?}"),
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn disable_introspection(mut self) -> Self {
|
||||||
|
self.introspection_disabled = true;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enables introspection for this [`RootNode`], if it was previously [disabled][1].
|
||||||
|
///
|
||||||
|
/// By default, all introspection queries are allowed.
|
||||||
|
///
|
||||||
|
/// [1]: RootNode::disable_introspection
|
||||||
|
pub fn enable_introspection(mut self) -> Self {
|
||||||
|
self.introspection_disabled = false;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "schema-language")]
|
#[cfg(feature = "schema-language")]
|
||||||
/// The schema definition as a `String` in the
|
/// The schema definition as a `String` in the
|
||||||
/// [GraphQL Schema Language](https://graphql.org/learn/schema/#type-language)
|
/// [GraphQL Schema Language](https://graphql.org/learn/schema/#type-language)
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
mod context;
|
mod context;
|
||||||
mod input_value;
|
mod input_value;
|
||||||
mod multi_visitor;
|
mod multi_visitor;
|
||||||
mod rules;
|
pub mod rules;
|
||||||
mod traits;
|
mod traits;
|
||||||
mod visitor;
|
mod visitor;
|
||||||
|
|
||||||
|
|
203
juniper/src/validation/rules/disable_introspection.rs
Normal file
203
juniper/src/validation/rules/disable_introspection.rs
Normal file
|
@ -0,0 +1,203 @@
|
||||||
|
//! Validation rule checking whether a GraphQL operation contains introspection (`__schema` or
|
||||||
|
//! `__type` fields).
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
ast::Field,
|
||||||
|
parser::Spanning,
|
||||||
|
validation::{ValidatorContext, Visitor},
|
||||||
|
value::ScalarValue,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Validation rule checking whether a GraphQL operation contains introspection (`__schema` or
|
||||||
|
/// `__type` fields).
|
||||||
|
pub struct DisableIntrospection;
|
||||||
|
|
||||||
|
/// Produces a new [`DisableIntrospection`] validation rule.
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub fn factory() -> DisableIntrospection {
|
||||||
|
DisableIntrospection
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, S> Visitor<'a, S> for DisableIntrospection
|
||||||
|
where
|
||||||
|
S: ScalarValue,
|
||||||
|
{
|
||||||
|
fn enter_field(
|
||||||
|
&mut self,
|
||||||
|
context: &mut ValidatorContext<'a, S>,
|
||||||
|
field: &'a Spanning<Field<S>>,
|
||||||
|
) {
|
||||||
|
let field_name = field.item.name.item;
|
||||||
|
if matches!(field_name, "__schema" | "__type") {
|
||||||
|
context.report_error(&error_message(field_name), &[field.item.name.span.start]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn error_message(field_name: &str) -> String {
|
||||||
|
format!("GraphQL introspection is not allowed, but the operation contained `{field_name}`")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::{error_message, factory};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
parser::SourcePosition,
|
||||||
|
validation::{expect_fails_rule, expect_passes_rule, RuleError},
|
||||||
|
value::DefaultScalarValue,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn allows_regular_fields() {
|
||||||
|
// language=GraphQL
|
||||||
|
expect_passes_rule::<_, _, DefaultScalarValue>(
|
||||||
|
factory,
|
||||||
|
r#"
|
||||||
|
query {
|
||||||
|
user {
|
||||||
|
name
|
||||||
|
... on User {
|
||||||
|
email
|
||||||
|
}
|
||||||
|
alias: email
|
||||||
|
... {
|
||||||
|
typeless
|
||||||
|
}
|
||||||
|
friends {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn allows_typename_field() {
|
||||||
|
// language=GraphQL
|
||||||
|
expect_passes_rule::<_, _, DefaultScalarValue>(
|
||||||
|
factory,
|
||||||
|
r#"
|
||||||
|
query {
|
||||||
|
__typename
|
||||||
|
user {
|
||||||
|
__typename
|
||||||
|
... on User {
|
||||||
|
__typename
|
||||||
|
}
|
||||||
|
... {
|
||||||
|
__typename
|
||||||
|
}
|
||||||
|
friends {
|
||||||
|
__typename
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn forbids_query_schema() {
|
||||||
|
// language=GraphQL
|
||||||
|
expect_fails_rule::<_, _, DefaultScalarValue>(
|
||||||
|
factory,
|
||||||
|
r#"
|
||||||
|
query {
|
||||||
|
__schema {
|
||||||
|
queryType {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
&[RuleError::new(
|
||||||
|
&error_message("__schema"),
|
||||||
|
&[SourcePosition::new(37, 2, 16)],
|
||||||
|
)],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn forbids_query_type() {
|
||||||
|
// language=GraphQL
|
||||||
|
expect_fails_rule::<_, _, DefaultScalarValue>(
|
||||||
|
factory,
|
||||||
|
r#"
|
||||||
|
query {
|
||||||
|
__type(
|
||||||
|
name: "Query"
|
||||||
|
) {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
&[RuleError::new(
|
||||||
|
&error_message("__type"),
|
||||||
|
&[SourcePosition::new(37, 2, 16)],
|
||||||
|
)],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn forbids_field_type() {
|
||||||
|
// language=GraphQL
|
||||||
|
expect_fails_rule::<_, _, DefaultScalarValue>(
|
||||||
|
factory,
|
||||||
|
r#"
|
||||||
|
query {
|
||||||
|
user {
|
||||||
|
name
|
||||||
|
... on User {
|
||||||
|
email
|
||||||
|
}
|
||||||
|
alias: email
|
||||||
|
... {
|
||||||
|
typeless
|
||||||
|
}
|
||||||
|
friends {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
__type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
&[RuleError::new(
|
||||||
|
&error_message("__type"),
|
||||||
|
&[SourcePosition::new(370, 14, 20)],
|
||||||
|
)],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn forbids_field_schema() {
|
||||||
|
// language=GraphQL
|
||||||
|
expect_fails_rule::<_, _, DefaultScalarValue>(
|
||||||
|
factory,
|
||||||
|
r#"
|
||||||
|
query {
|
||||||
|
user {
|
||||||
|
name
|
||||||
|
... on User {
|
||||||
|
email
|
||||||
|
}
|
||||||
|
alias: email
|
||||||
|
... {
|
||||||
|
typeless
|
||||||
|
}
|
||||||
|
friends {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
__schema
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
&[RuleError::new(
|
||||||
|
&error_message("__schema"),
|
||||||
|
&[SourcePosition::new(370, 14, 20)],
|
||||||
|
)],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,8 @@
|
||||||
|
//! Definitions of rules for validation.
|
||||||
|
|
||||||
mod arguments_of_correct_type;
|
mod arguments_of_correct_type;
|
||||||
mod default_values_of_correct_type;
|
mod default_values_of_correct_type;
|
||||||
|
pub mod disable_introspection;
|
||||||
mod fields_on_correct_type;
|
mod fields_on_correct_type;
|
||||||
mod fragments_on_composite_types;
|
mod fragments_on_composite_types;
|
||||||
mod known_argument_names;
|
mod known_argument_names;
|
||||||
|
@ -23,12 +26,13 @@ mod unique_variable_names;
|
||||||
mod variables_are_input_types;
|
mod variables_are_input_types;
|
||||||
mod variables_in_allowed_position;
|
mod variables_in_allowed_position;
|
||||||
|
|
||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::Document,
|
ast::Document,
|
||||||
validation::{visit, MultiVisitorNil, ValidatorContext},
|
validation::{visit, MultiVisitorNil, ValidatorContext},
|
||||||
value::ScalarValue,
|
value::ScalarValue,
|
||||||
};
|
};
|
||||||
use std::fmt::Debug;
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn visit_all_rules<'a, S: Debug>(ctx: &mut ValidatorContext<'a, S>, doc: &'a Document<S>)
|
pub fn visit_all_rules<'a, S: Debug>(ctx: &mut ValidatorContext<'a, S>, doc: &'a Document<S>)
|
||||||
|
|
184
tests/integration/tests/disabled_introspection.rs
Normal file
184
tests/integration/tests/disabled_introspection.rs
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
//! Checks whether [`RootNode::disable_introspection()`] works.
|
||||||
|
|
||||||
|
use futures::stream;
|
||||||
|
use juniper::{
|
||||||
|
execute, graphql_object, graphql_subscription, graphql_vars, resolve_into_stream, GraphQLError,
|
||||||
|
RootNode,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct Query;
|
||||||
|
|
||||||
|
#[graphql_object]
|
||||||
|
impl Query {
|
||||||
|
fn some() -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Mutation;
|
||||||
|
|
||||||
|
#[graphql_object]
|
||||||
|
impl Mutation {
|
||||||
|
fn another() -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Subscription;
|
||||||
|
|
||||||
|
#[graphql_subscription]
|
||||||
|
impl Subscription {
|
||||||
|
async fn another() -> stream::Empty<bool> {
|
||||||
|
stream::empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn query_schema() {
|
||||||
|
// language=GraphQL
|
||||||
|
let query = "query { __schema { queryType { name } } }";
|
||||||
|
|
||||||
|
let schema = RootNode::new(Query, Mutation, Subscription).disable_introspection();
|
||||||
|
|
||||||
|
match execute(query, None, &schema, &graphql_vars! {}, &()).await {
|
||||||
|
Err(GraphQLError::ValidationError(errors)) => {
|
||||||
|
assert_eq!(errors.len(), 1);
|
||||||
|
|
||||||
|
let err = errors.first().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
err.message(),
|
||||||
|
"GraphQL introspection is not allowed, but the operation contained `__schema`",
|
||||||
|
);
|
||||||
|
assert_eq!(err.locations()[0].index(), 8);
|
||||||
|
assert_eq!(err.locations()[0].line(), 0);
|
||||||
|
assert_eq!(err.locations()[0].column(), 8);
|
||||||
|
}
|
||||||
|
res => panic!("expected `ValidationError`, returned: {res:#?}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn mutation_schema() {
|
||||||
|
// language=GraphQL
|
||||||
|
let query = "mutation { __schema { queryType { name } } }";
|
||||||
|
|
||||||
|
let schema = RootNode::new(Query, Mutation, Subscription).disable_introspection();
|
||||||
|
|
||||||
|
match execute(query, None, &schema, &graphql_vars! {}, &()).await {
|
||||||
|
Err(GraphQLError::ValidationError(errors)) => {
|
||||||
|
assert_eq!(errors.len(), 2);
|
||||||
|
|
||||||
|
let err = errors.first().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
err.message(),
|
||||||
|
"GraphQL introspection is not allowed, but the operation contained `__schema`",
|
||||||
|
);
|
||||||
|
assert_eq!(err.locations()[0].index(), 11);
|
||||||
|
assert_eq!(err.locations()[0].line(), 0);
|
||||||
|
assert_eq!(err.locations()[0].column(), 11);
|
||||||
|
}
|
||||||
|
res => panic!("expected `ValidationError`, returned: {res:#?}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn subscription_schema() {
|
||||||
|
// language=GraphQL
|
||||||
|
let query = "subscription { __schema { queryType { name } } }";
|
||||||
|
|
||||||
|
let schema = RootNode::new(Query, Mutation, Subscription).disable_introspection();
|
||||||
|
|
||||||
|
match resolve_into_stream(query, None, &schema, &graphql_vars! {}, &()).await {
|
||||||
|
Err(GraphQLError::ValidationError(errors)) => {
|
||||||
|
assert_eq!(errors.len(), 2);
|
||||||
|
|
||||||
|
let err = errors.first().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
err.message(),
|
||||||
|
"GraphQL introspection is not allowed, but the operation contained `__schema`",
|
||||||
|
);
|
||||||
|
assert_eq!(err.locations()[0].index(), 15);
|
||||||
|
assert_eq!(err.locations()[0].line(), 0);
|
||||||
|
assert_eq!(err.locations()[0].column(), 15);
|
||||||
|
}
|
||||||
|
_ => panic!("expected `ValidationError`"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn query_type() {
|
||||||
|
// language=GraphQL
|
||||||
|
let query = r#"query { __type(name: "String") { name } }"#;
|
||||||
|
|
||||||
|
let schema = RootNode::new(Query, Mutation, Subscription).disable_introspection();
|
||||||
|
|
||||||
|
match execute(query, None, &schema, &graphql_vars! {}, &()).await {
|
||||||
|
Err(GraphQLError::ValidationError(errors)) => {
|
||||||
|
assert_eq!(errors.len(), 1);
|
||||||
|
|
||||||
|
let err = errors.first().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
err.message(),
|
||||||
|
"GraphQL introspection is not allowed, but the operation contained `__type`",
|
||||||
|
);
|
||||||
|
assert_eq!(err.locations()[0].index(), 8);
|
||||||
|
assert_eq!(err.locations()[0].line(), 0);
|
||||||
|
assert_eq!(err.locations()[0].column(), 8);
|
||||||
|
}
|
||||||
|
res => panic!("expected `ValidationError`, returned: {res:#?}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn mutation_type() {
|
||||||
|
// language=GraphQL
|
||||||
|
let query = r#"mutation { __type(name: "String") { name } }"#;
|
||||||
|
|
||||||
|
let schema = RootNode::new(Query, Mutation, Subscription).disable_introspection();
|
||||||
|
|
||||||
|
match execute(query, None, &schema, &graphql_vars! {}, &()).await {
|
||||||
|
Err(GraphQLError::ValidationError(errors)) => {
|
||||||
|
assert_eq!(errors.len(), 2);
|
||||||
|
|
||||||
|
let err = errors.first().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
err.message(),
|
||||||
|
"GraphQL introspection is not allowed, but the operation contained `__type`",
|
||||||
|
);
|
||||||
|
assert_eq!(err.locations()[0].index(), 11);
|
||||||
|
assert_eq!(err.locations()[0].line(), 0);
|
||||||
|
assert_eq!(err.locations()[0].column(), 11);
|
||||||
|
}
|
||||||
|
res => panic!("expected `ValidationError`, returned: {res:#?}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn subscription_type() {
|
||||||
|
// language=GraphQL
|
||||||
|
let query = r#"subscription Subscription { __type(name: "String") { name } }"#;
|
||||||
|
|
||||||
|
let schema = RootNode::new(Query, Mutation, Subscription).disable_introspection();
|
||||||
|
|
||||||
|
match resolve_into_stream(query, None, &schema, &graphql_vars! {}, &()).await {
|
||||||
|
Err(GraphQLError::ValidationError(errors)) => {
|
||||||
|
assert_eq!(errors.len(), 2);
|
||||||
|
|
||||||
|
let err = errors.first().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
err.message(),
|
||||||
|
"GraphQL introspection is not allowed, but the operation contained `__type`",
|
||||||
|
);
|
||||||
|
assert_eq!(err.locations()[0].index(), 28);
|
||||||
|
assert_eq!(err.locations()[0].line(), 0);
|
||||||
|
assert_eq!(err.locations()[0].column(), 28);
|
||||||
|
}
|
||||||
|
_ => panic!("expected `ValidationError`"),
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in a new issue