summaryrefslogtreecommitdiff
path: root/rubocop/cop
diff options
context:
space:
mode:
authorBob Van Landuyt <bob@vanlanduyt.co>2019-06-18 16:18:26 +0200
committerBob Van Landuyt <bob@vanlanduyt.co>2019-06-21 13:00:50 +0200
commitac2d08212b20bae20c85ea225ec3fb0052a9e1af (patch)
tree37ef6b98f39bd9eeb25b3306b09a868728602dc0 /rubocop/cop
parent2c48cb24983314ab78963d2dfb2a74b0f9104fc8 (diff)
downloadgitlab-ce-ac2d08212b20bae20c85ea225ec3fb0052a9e1af.tar.gz
Add a cop to ensure we authorize GraphQL types
Diffstat (limited to 'rubocop/cop')
-rw-r--r--rubocop/cop/graphql/authorize_types.rb61
1 files changed, 61 insertions, 0 deletions
diff --git a/rubocop/cop/graphql/authorize_types.rb b/rubocop/cop/graphql/authorize_types.rb
new file mode 100644
index 00000000000..93fe80c3edf
--- /dev/null
+++ b/rubocop/cop/graphql/authorize_types.rb
@@ -0,0 +1,61 @@
+# frozen_string_literal: true
+
+require_relative '../../spec_helpers'
+
+module RuboCop
+ module Cop
+ module Graphql
+ class AuthorizeTypes < RuboCop::Cop::Cop
+ include SpecHelpers
+
+ MSG = 'Add an `authorize :ability` call to the type: '\
+ 'https://docs.gitlab.com/ee/development/api_graphql_styleguide.html#type-authorization'
+
+ TYPES_DIR = 'app/graphql/types'
+
+ # We want to exclude our own basetypes and scalars
+ WHITELISTED_TYPES = %w[BaseEnum BaseScalar BasePermissionType MutationType
+ QueryType GraphQL::Schema BaseUnion].freeze
+
+ def_node_search :authorize?, <<~PATTERN
+ (send nil? :authorize ...)
+ PATTERN
+
+ def on_class(node)
+ return unless in_type?(node)
+ return if whitelisted?(class_constant(node))
+ return if whitelisted?(superclass_constant(node))
+
+ add_offense(node, location: :expression) unless authorize?(node)
+ end
+
+ private
+
+ def in_type?(node)
+ return if in_spec?(node)
+
+ path = node.location.expression.source_buffer.name
+
+ path.include?(TYPES_DIR)
+ end
+
+ def whitelisted?(class_node)
+ return false unless class_node&.const_name
+
+ WHITELISTED_TYPES.any? { |whitelisted| class_node.const_name.include?(whitelisted) }
+ end
+
+ def class_constant(node)
+ node.descendants.first
+ end
+
+ def superclass_constant(class_node)
+ # First one is the class name itself, second is it's superclass
+ _class_constant, *others = class_node.descendants
+
+ others.find { |node| node.const_type? && node&.const_name != 'Types' }
+ end
+ end
+ end
+ end
+end