Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorNick Thomas <nick@gitlab.com>2019-04-04 14:38:16 +0300
committerNick Thomas <nick@gitlab.com>2019-04-04 14:38:16 +0300
commit7af1ba122fb425214d6b7c9e51ea621a515d6ac0 (patch)
tree9f37fe6e0e7b68ab3bf36df2606936c51b701c0e /lib
parent60a0ef21d385e1943f9e6a68adc9d7e04e8d69c8 (diff)
parent8cf0d8926a325c8be7707c356ef20f42139d7bf3 (diff)
Merge branch '54417-graphql-type-authorization' into 'master'
GraphQL Type authorization Closes #54417 See merge request gitlab-org/gitlab-ce!25724
Diffstat (limited to 'lib')
-rw-r--r--lib/gitlab/graphql/authorize/authorize_field_service.rb94
-rw-r--r--lib/gitlab/graphql/authorize/instrumentation.rb44
-rw-r--r--lib/gitlab/graphql/errors.rb1
3 files changed, 100 insertions, 39 deletions
diff --git a/lib/gitlab/graphql/authorize/authorize_field_service.rb b/lib/gitlab/graphql/authorize/authorize_field_service.rb
new file mode 100644
index 00000000000..f3ca82ec697
--- /dev/null
+++ b/lib/gitlab/graphql/authorize/authorize_field_service.rb
@@ -0,0 +1,94 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Graphql
+ module Authorize
+ class AuthorizeFieldService
+ def initialize(field)
+ @field = field
+ @old_resolve_proc = @field.resolve_proc
+ end
+
+ def authorizations?
+ authorizations.present?
+ end
+
+ def authorized_resolve
+ proc do |obj, args, ctx|
+ resolved_obj = @old_resolve_proc.call(obj, args, ctx)
+ checker = build_checker(ctx[:current_user])
+
+ if resolved_obj.respond_to?(:then)
+ resolved_obj.then(&checker)
+ else
+ checker.call(resolved_obj)
+ end
+ end
+ end
+
+ private
+
+ def authorizations
+ @authorizations ||= (type_authorizations + field_authorizations).uniq
+ end
+
+ # Returns any authorize metadata from the return type of @field
+ def type_authorizations
+ type = @field.type
+
+ # When the return type of @field is a collection, find the singular type
+ if type.get_field('edges')
+ type = node_type_for_relay_connection(type)
+ elsif type.list?
+ type = node_type_for_basic_connection(type)
+ end
+
+ Array.wrap(type.metadata[:authorize])
+ end
+
+ # Returns any authorize metadata from @field
+ def field_authorizations
+ Array.wrap(@field.metadata[:authorize])
+ end
+
+ def build_checker(current_user)
+ lambda do |value|
+ # Load the elements if they were not loaded by BatchLoader yet
+ value = value.sync if value.respond_to?(:sync)
+
+ check = lambda do |object|
+ authorizations.all? do |ability|
+ Ability.allowed?(current_user, ability, object)
+ end
+ end
+
+ case value
+ when Array, ActiveRecord::Relation
+ value.select(&check)
+ else
+ value if check.call(value)
+ end
+ end
+ end
+
+ # Returns the singular type for relay connections.
+ # This will be the type class of edges.node
+ def node_type_for_relay_connection(type)
+ type = type.get_field('edges').type.unwrap.get_field('node')&.type
+
+ if type.nil?
+ raise Gitlab::Graphql::Errors::ConnectionDefinitionError,
+ 'Connection Type must conform to the Relay Cursor Connections Specification'
+ end
+
+ type
+ end
+
+ # Returns the singular type for basic connections, for example `[Types::ProjectType]`
+ def node_type_for_basic_connection(type)
+ type.unwrap
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/graphql/authorize/instrumentation.rb b/lib/gitlab/graphql/authorize/instrumentation.rb
index 593da8471dd..15ecc3b04f0 100644
--- a/lib/gitlab/graphql/authorize/instrumentation.rb
+++ b/lib/gitlab/graphql/authorize/instrumentation.rb
@@ -7,46 +7,12 @@ module Gitlab
# Replace the resolver for the field with one that will only return the
# resolved object if the permissions check is successful.
def instrument(_type, field)
- required_permissions = Array.wrap(field.metadata[:authorize])
- return field if required_permissions.empty?
+ service = AuthorizeFieldService.new(field)
- old_resolver = field.resolve_proc
-
- new_resolver = -> (obj, args, ctx) do
- resolved_obj = old_resolver.call(obj, args, ctx)
- checker = build_checker(ctx[:current_user], required_permissions)
-
- if resolved_obj.respond_to?(:then)
- resolved_obj.then(&checker)
- else
- checker.call(resolved_obj)
- end
- end
-
- field.redefine do
- resolve(new_resolver)
- end
- end
-
- private
-
- def build_checker(current_user, abilities)
- lambda do |value|
- # Load the elements if they weren't loaded by BatchLoader yet
- value = value.sync if value.respond_to?(:sync)
-
- check = lambda do |object|
- abilities.all? do |ability|
- Ability.allowed?(current_user, ability, object)
- end
- end
-
- case value
- when Array
- value.select(&check)
- else
- value if check.call(value)
- end
+ if service.authorizations?
+ field.redefine { resolve(service.authorized_resolve) }
+ else
+ field
end
end
end
diff --git a/lib/gitlab/graphql/errors.rb b/lib/gitlab/graphql/errors.rb
index fe74549e322..bcbba72e017 100644
--- a/lib/gitlab/graphql/errors.rb
+++ b/lib/gitlab/graphql/errors.rb
@@ -6,6 +6,7 @@ module Gitlab
BaseError = Class.new(GraphQL::ExecutionError)
ArgumentError = Class.new(BaseError)
ResourceNotAvailable = Class.new(BaseError)
+ ConnectionDefinitionError = Class.new(BaseError)
end
end
end