class KittyPolicy::GraphQL::FieldAuthorization

Public Class Methods

new(policy:, current_user_key: :current_user) click to toggle source
# File lib/kitty_policy/graphql/field_authorization.rb, line 6
def initialize(policy:, current_user_key: :current_user)
  @policy = policy
  @current_user_key = current_user_key
end

Public Instance Methods

instrument(_type, field) click to toggle source
# File lib/kitty_policy/graphql/field_authorization.rb, line 11
def instrument(_type, field)
  return instrument_with_authorize(field) if field.metadata.key?(:authorize)
  return instrument_with_authorize_object(field) if field.metadata.key?(:authorize_object)

  field
end

Private Instance Methods

instrument_with_authorize(field) click to toggle source
# File lib/kitty_policy/graphql/field_authorization.rb, line 20
def instrument_with_authorize(field)
  policy = @policy
  current_user_key = @current_user_key

  old_resolve = field.resolve_proc
  new_resolve = lambda do |type_or_object, arguments, context|
    object = type_or_object.is_a?(::GraphQL::Schema::Object) ? type_or_object.object : type_or_object

    if policy.can?(context[current_user_key], field.metadata[:authorize], object)
      old_resolve.call(type_or_object, arguments, context)
    elsif field.connection?
      # NOTE(rstankov): When field is connection we need to wrap it with connection option as done here:
      #   https://github.com/rmosolgo/graphql-ruby/blob/master/lib/graphql/relay/connection_resolve.rb#L37-L38
      #
      #   Normally GraphQL gem will handle this, however this instrument is bypassing it
      nodes = field.metadata[:fallback]

      parent = parent.is_a?(::GraphQL::Schema::Object) ? context.object.object : context.object

      connection_class = ::GraphQL::Relay::BaseConnection.connection_for_nodes(nodes)
      connection_class.new(nodes, arguments, field: field, max_page_size: field.connection_max_page_size, parent: parent, context: context)
    else
      field.metadata[:fallback]
    end
  end

  field.redefine do
    resolve new_resolve
  end
end
instrument_with_authorize_object(field) click to toggle source
# File lib/kitty_policy/graphql/field_authorization.rb, line 51
def instrument_with_authorize_object(field)
  raise "Can't use `authorize_object` on a connection" if field.connection?
  raise "Can't use `authorize_object` on an array" if field.type.list?

  policy = @policy
  current_user_key = @current_user_key

  old_resolve = field.resolve_proc
  new_resolve = lambda do |type_or_object, arguments, context|
    object = old_resolve.call(type_or_object, arguments, context)
    if object.nil?
      object
    elsif field.metadata.key?(:fallback)
      policy.can?(context[current_user_key], field.metadata[:authorize_object], object) ? object : field.metadata[:fallback]
    else
      policy.authorize!(context[current_user_key], field.metadata[:authorize_object], object)
      object
    end
  end

  field.redefine do
    resolve new_resolve
  end
end