summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean McGivern <sean@gitlab.com>2019-05-08 08:46:56 +0000
committerSean McGivern <sean@gitlab.com>2019-05-08 08:46:56 +0000
commit9f888c7440a99f0c9bd59ac066fae88e7c863e41 (patch)
tree83f534032d8098414de3a3957f3a591e4e412e52
parent69cfdfaed3e5a63bc8af39ca4b42c932db1b7f75 (diff)
parentf80f68d520b98ae60300ecf0758ff241218e9cd0 (diff)
downloadgitlab-ce-9f888c7440a99f0c9bd59ac066fae88e7c863e41.tar.gz
Merge branch '58404-set-default-max-depth-for-GraphQL' into 'master'
58404 - setup max depth for graphql Closes #58404 See merge request gitlab-org/gitlab-ce!25737
-rw-r--r--app/graphql/gitlab_schema.rb42
-rw-r--r--changelogs/unreleased/58404-set-default-max-depth-for-GraphQL.yml5
-rw-r--r--spec/graphql/gitlab_schema_spec.rb86
-rw-r--r--spec/requests/api/graphql/gitlab_schema_spec.rb40
-rw-r--r--spec/support/helpers/graphql_helpers.rb6
5 files changed, 135 insertions, 44 deletions
diff --git a/app/graphql/gitlab_schema.rb b/app/graphql/gitlab_schema.rb
index a12568d5d31..897e12c1b56 100644
--- a/app/graphql/gitlab_schema.rb
+++ b/app/graphql/gitlab_schema.rb
@@ -7,6 +7,9 @@ class GitlabSchema < GraphQL::Schema
AUTHENTICATED_COMPLEXITY = 250
ADMIN_COMPLEXITY = 300
+ ANONYMOUS_MAX_DEPTH = 10
+ AUTHENTICATED_MAX_DEPTH = 15
+
use BatchLoader::GraphQL
use Gitlab::Graphql::Authorize
use Gitlab::Graphql::Present
@@ -23,21 +26,36 @@ class GitlabSchema < GraphQL::Schema
mutation(Types::MutationType)
- def self.execute(query_str = nil, **kwargs)
- kwargs[:max_complexity] ||= max_query_complexity(kwargs[:context])
+ class << self
+ def execute(query_str = nil, **kwargs)
+ kwargs[:max_complexity] ||= max_query_complexity(kwargs[:context])
+ kwargs[:max_depth] ||= max_query_depth(kwargs[:context])
- super(query_str, **kwargs)
- end
+ super(query_str, **kwargs)
+ end
+
+ private
+
+ def max_query_complexity(ctx)
+ current_user = ctx&.fetch(:current_user, nil)
+
+ if current_user&.admin
+ ADMIN_COMPLEXITY
+ elsif current_user
+ AUTHENTICATED_COMPLEXITY
+ else
+ DEFAULT_MAX_COMPLEXITY
+ end
+ end
- def self.max_query_complexity(ctx)
- current_user = ctx&.fetch(:current_user, nil)
+ def max_query_depth(ctx)
+ current_user = ctx&.fetch(:current_user, nil)
- if current_user&.admin
- ADMIN_COMPLEXITY
- elsif current_user
- AUTHENTICATED_COMPLEXITY
- else
- DEFAULT_MAX_COMPLEXITY
+ if current_user
+ AUTHENTICATED_MAX_DEPTH
+ else
+ ANONYMOUS_MAX_DEPTH
+ end
end
end
end
diff --git a/changelogs/unreleased/58404-set-default-max-depth-for-GraphQL.yml b/changelogs/unreleased/58404-set-default-max-depth-for-GraphQL.yml
new file mode 100644
index 00000000000..7e95158a0e0
--- /dev/null
+++ b/changelogs/unreleased/58404-set-default-max-depth-for-GraphQL.yml
@@ -0,0 +1,5 @@
+---
+title: 58404 - setup max depth for GraphQL
+merge_request: 25737
+author: Ken Ding
+type: added
diff --git a/spec/graphql/gitlab_schema_spec.rb b/spec/graphql/gitlab_schema_spec.rb
index 05f10fb40f0..c138c87c4ac 100644
--- a/spec/graphql/gitlab_schema_spec.rb
+++ b/spec/graphql/gitlab_schema_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
describe GitlabSchema do
+ let(:user) { build :user }
+
it 'uses batch loading' do
expect(field_instrumenters).to include(BatchLoader::GraphQL)
end
@@ -33,43 +35,75 @@ describe GitlabSchema do
expect(connection).to eq(Gitlab::Graphql::Connections::KeysetConnection)
end
- context 'for different types of users' do
- it 'returns DEFAULT_MAX_COMPLEXITY for no context' do
- expect(GraphQL::Schema)
- .to receive(:execute)
- .with('query', hash_including(max_complexity: GitlabSchema::DEFAULT_MAX_COMPLEXITY))
+ describe '.execute' do
+ context 'for different types of users' do
+ context 'when no context' do
+ it 'returns DEFAULT_MAX_COMPLEXITY' do
+ expect(GraphQL::Schema)
+ .to receive(:execute)
+ .with('query', hash_including(max_complexity: GitlabSchema::DEFAULT_MAX_COMPLEXITY))
- described_class.execute('query')
- end
+ described_class.execute('query')
+ end
+ end
- it 'returns DEFAULT_MAX_COMPLEXITY for no user' do
- expect(GraphQL::Schema)
- .to receive(:execute)
- .with('query', hash_including(max_complexity: GitlabSchema::DEFAULT_MAX_COMPLEXITY))
+ context 'when no user' do
+ it 'returns DEFAULT_MAX_COMPLEXITY' do
+ expect(GraphQL::Schema)
+ .to receive(:execute)
+ .with('query', hash_including(max_complexity: GitlabSchema::DEFAULT_MAX_COMPLEXITY))
- described_class.execute('query', context: {})
- end
+ described_class.execute('query', context: {})
+ end
- it 'returns AUTHENTICATED_COMPLEXITY for a logged in user' do
- user = build :user
+ it 'returns ANONYMOUS_MAX_DEPTH' do
+ expect(GraphQL::Schema)
+ .to receive(:execute)
+ .with('query', hash_including(max_depth: GitlabSchema::ANONYMOUS_MAX_DEPTH))
- expect(GraphQL::Schema).to receive(:execute).with('query', hash_including(max_complexity: GitlabSchema::AUTHENTICATED_COMPLEXITY))
+ described_class.execute('query', context: {})
+ end
+ end
- described_class.execute('query', context: { current_user: user })
- end
+ context 'when a logged in user' do
+ it 'returns AUTHENTICATED_COMPLEXITY' do
+ expect(GraphQL::Schema).to receive(:execute).with('query', hash_including(max_complexity: GitlabSchema::AUTHENTICATED_COMPLEXITY))
- it 'returns ADMIN_COMPLEXITY for an admin user' do
- user = build :user, :admin
+ described_class.execute('query', context: { current_user: user })
+ end
- expect(GraphQL::Schema).to receive(:execute).with('query', hash_including(max_complexity: GitlabSchema::ADMIN_COMPLEXITY))
+ it 'returns AUTHENTICATED_MAX_DEPTH' do
+ expect(GraphQL::Schema).to receive(:execute).with('query', hash_including(max_depth: GitlabSchema::AUTHENTICATED_MAX_DEPTH))
- described_class.execute('query', context: { current_user: user })
- end
+ described_class.execute('query', context: { current_user: user })
+ end
+ end
+
+ context 'when an admin user' do
+ it 'returns ADMIN_COMPLEXITY' do
+ user = build :user, :admin
+
+ expect(GraphQL::Schema).to receive(:execute).with('query', hash_including(max_complexity: GitlabSchema::ADMIN_COMPLEXITY))
+
+ described_class.execute('query', context: { current_user: user })
+ end
+ end
+
+ context 'when max_complexity passed on the query' do
+ it 'returns what was passed on the query' do
+ expect(GraphQL::Schema).to receive(:execute).with('query', hash_including(max_complexity: 1234))
+
+ described_class.execute('query', max_complexity: 1234)
+ end
+ end
- it 'returns what was passed on the query' do
- expect(GraphQL::Schema).to receive(:execute).with('query', { max_complexity: 1234 })
+ context 'when max_depth passed on the query' do
+ it 'returns what was passed on the query' do
+ expect(GraphQL::Schema).to receive(:execute).with('query', hash_including(max_depth: 1234))
- described_class.execute('query', max_complexity: 1234)
+ described_class.execute('query', max_depth: 1234)
+ end
+ end
end
end
diff --git a/spec/requests/api/graphql/gitlab_schema_spec.rb b/spec/requests/api/graphql/gitlab_schema_spec.rb
index b63b4fb34df..dd518274f82 100644
--- a/spec/requests/api/graphql/gitlab_schema_spec.rb
+++ b/spec/requests/api/graphql/gitlab_schema_spec.rb
@@ -3,15 +3,43 @@ require 'spec_helper'
describe 'GitlabSchema configurations' do
include GraphqlHelpers
- it 'shows an error if complexity is too high' do
- project = create(:project, :repository)
- query = graphql_query_for('project', { 'fullPath' => project.full_path }, %w(id name description))
+ let(:project) { create(:project, :repository) }
+ let(:query) { graphql_query_for('project', { 'fullPath' => project.full_path }, %w(id name description)) }
+ let(:current_user) { create(:user) }
- allow(GitlabSchema).to receive(:max_query_complexity).and_return 1
+ describe '#max_complexity' do
+ context 'when complexity is too high' do
+ it 'shows an error' do
+ allow(GitlabSchema).to receive(:max_query_complexity).and_return 1
- post_graphql(query, current_user: nil)
+ post_graphql(query, current_user: nil)
- expect(graphql_errors.first['message']).to include('which exceeds max complexity of 1')
+ expect(graphql_errors.first['message']).to include('which exceeds max complexity of 1')
+ end
+ end
+ end
+
+ describe '#max_depth' do
+ context 'when query depth is too high' do
+ it 'shows error' do
+ errors = [{ "message" => "Query has depth of 2, which exceeds max depth of 1" }]
+ allow(GitlabSchema).to receive(:max_query_depth).and_return 1
+
+ post_graphql(query)
+
+ expect(graphql_errors).to eq(errors)
+ end
+ end
+
+ context 'when query depth is within range' do
+ it 'has no error' do
+ allow(GitlabSchema).to receive(:max_query_depth).and_return 5
+
+ post_graphql(query)
+
+ expect(graphql_errors).to be_nil
+ end
+ end
end
context 'when IntrospectionQuery' do
diff --git a/spec/support/helpers/graphql_helpers.rb b/spec/support/helpers/graphql_helpers.rb
index b49d743fb9a..f15944652fd 100644
--- a/spec/support/helpers/graphql_helpers.rb
+++ b/spec/support/helpers/graphql_helpers.rb
@@ -102,6 +102,7 @@ module GraphqlHelpers
def all_graphql_fields_for(class_name, parent_types = Set.new)
allow_unlimited_graphql_complexity
+ allow_unlimited_graphql_depth
type = GitlabSchema.types[class_name.to_s]
return "" unless type
@@ -190,4 +191,9 @@ module GraphqlHelpers
allow_any_instance_of(GitlabSchema).to receive(:max_complexity).and_return nil
allow(GitlabSchema).to receive(:max_query_complexity).with(any_args).and_return nil
end
+
+ def allow_unlimited_graphql_depth
+ allow_any_instance_of(GitlabSchema).to receive(:max_depth).and_return nil
+ allow(GitlabSchema).to receive(:max_query_depth).with(any_args).and_return nil
+ end
end