exAspArk / graphql-guard

Simple authorization gem for GraphQL :lock:
MIT License
472 stars 36 forks source link

Support GraphQL Interpreter #39

Closed exAspArk closed 4 years ago

exAspArk commented 4 years ago

Requires graphql gem version >= 1.10 and GraphQL::Execution::Interpreter.

Fix https://github.com/exAspArk/graphql-guard/issues/37.

coveralls commented 4 years ago

Coverage Status

Coverage remained the same at 100.0% when pulling 3bfddcc538af24f60ade6f44bb3470750d498722 on graphql-interpreter into 9b0d11727aef1331425ca64bebcbc2a2a3990f1f on master.

msroot commented 4 years ago
Failures:

  1) Auth Users Valid currentUser with cookies
     Failure/Error: result = ApiSchema.execute(query, variables: variables, context: context, operation_name: operation_name)

     NoMethodError:
       undefined method `type_class' for Mutations::Users::SignInUser:Class
     # /Users/ioannis/.rvm/gems/ruby-2.6.3/bundler/gems/graphql-guard-aba85fc09a97/lib/graphql/guard.rb:77:in `policy_object_guard'
     # /Users/ioannis/.rvm/gems/ruby-2.6.3/bundler/gems/graphql-guard-aba85fc09a97/lib/graphql/guard.rb:48:in `find_guard_proc'
     # /Users/ioannis/.rvm/gems/ruby-2.6.3/bundler/gems/graphql-guard-aba85fc09a97/lib/graphql/guard.rb:65:in `ensure_guarded'
     # /Users/ioannis/.rvm/gems/ruby-2.6.3/bundler/gems/graphql-guard-aba85fc09a97/lib/graphql/guard.rb:40:in `trace'
     # /Users/ioannis/.rvm/gems/ruby-2.6.3/gems/graphql-1.10.4/lib/graphql/tracing.rb:83:in `call_tracers'
     # /Users/ioannis/.rvm/gems/ruby-2.6.3/gems/graphql-1.10.4/lib/graphql/tracing.rb:67:in `trace'
     # /Users/ioannis/.rvm/gems/ruby-2.6.3/gems/graphql-1.10.4/lib/graphql/execution/interpreter/runtime.rb:215:in `block (3 levels) in evaluate_selections'
     # /Users/ioannis/.rvm/gems/ruby-2.6.3/gems/graphql-1.10.4/lib/graphql/query.rb:327:in `block in with_error_handling'
     # /Users/ioannis/.rvm/gems/ruby-2.6.3/gems/graphql-1.10.4/lib/graphql/execution/errors.rb:30:in `with_error_handling'
     # /Users/ioannis/.rvm/gems/ruby-2.6.3/gems/graphql-1.10.4/lib/graphql/query.rb:326:in `with_error_handling'
     # /Users/ioannis/.rvm/gems/ruby-2.6.3/gems/graphql-1.10.4/lib/graphql/execution/interpreter/runtime.rb:214:in `block (2 levels) in evaluate_selections'
     # /Users/ioannis/.rvm/gems/ruby-2.6.3/gems/graphql-1.10.4/lib/graphql/execution/interpreter/runtime.rb:359:in `block in resolve_with_directives'
     # /Users/ioannis/.rvm/gems/ruby-2.6.3/gems/graphql-1.10.4/lib/graphql/execution/interpreter/runtime.rb:365:in `run_directive'
     # /Users/ioannis/.rvm/gems/ruby-2.6.3/gems/graphql-1.10.4/lib/graphql/execution/interpreter/runtime.rb:359:in `resolve_with_directives'
     # /Users/ioannis/.rvm/gems/ruby-2.6.3/gems/graphql-1.10.4/lib/graphql/execution/interpreter/runtime.rb:211:in `block in evaluate_selections'
     # /Users/ioannis/.rvm/gems/ruby-2.6.3/gems/graphql-1.10.4/lib/graphql/execution/interpreter/runtime.rb:124:in `each'
     # /Users/ioannis/.rvm/gems/ruby-2.6.3/gems/graphql-1.10.4/lib/graphql/execution/interpreter/runtime.rb:124:in `evaluate_selections'
     # /Users/ioannis/.rvm/gems/ruby-2.6.3/gems/graphql-1.10.4/lib/graphql/execution/interpreter/runtime.rb:323:in `block in continue_field'
     # /Users/ioannis/.rvm/gems/ruby-2.6.3/gems/graphql-1.10.4/lib/graphql/execution/interpreter/runtime.rb:435:in `after_lazy'
     # /Users/ioannis/.rvm/gems/ruby-2.6.3/gems/graphql-1.10.4/lib/graphql/execution/interpreter/runtime.rb:318:in `continue_field'
     # /Users/ioannis/.rvm/gems/ruby-2.6.3/gems/graphql-1.10.4/lib/graphql/execution/interpreter/runtime.rb:228:in `block (3 levels) in evaluate_selections'
     # /Users/ioannis/.rvm/gems/ruby-2.6.3/gems/graphql-1.10.4/lib/graphql/execution/interpreter/runtime.rb:435:in `after_lazy'
     # /Users/ioannis/.rvm/gems/ruby-2.6.3/gems/graphql-1.10.4/lib/graphql/execution/interpreter/runtime.rb:222:in `block (2 levels) in evaluate_selections'
     # /Users/ioannis/.rvm/gems/ruby-2.6.3/gems/graphql-1.10.4/lib/graphql/execution/interpreter/runtime.rb:359:in `block in resolve_with_directives'
     # /Users/ioannis/.rvm/gems/ruby-2.6.3/gems/graphql-1.10.4/lib/graphql/execution/interpreter/runtime.rb:365:in `run_directive'
     # /Users/ioannis/.rvm/gems/ruby-2.6.3/gems/graphql-1.10.4/lib/graphql/execution/interpreter/runtime.rb:359:in `resolve_with_directives'
     # /Users/ioannis/.rvm/gems/ruby-2.6.3/gems/graphql-1.10.4/lib/graphql/execution/interpreter/runtime.rb:211:in `block in evaluate_selections'
     # /Users/ioannis/.rvm/gems/ruby-2.6.3/gems/graphql-1.10.4/lib/graphql/execution/interpreter/runtime.rb:124:in `each'
     # /Users/ioannis/.rvm/gems/ruby-2.6.3/gems/graphql-1.10.4/lib/graphql/execution/interpreter/runtime.rb:124:in `evaluate_selections'
     # /Users/ioannis/.rvm/gems/ruby-2.6.3/gems/graphql-1.10.4/lib/graphql/execution/interpreter/runtime.rb:60:in `run_eager'
     # /Users/ioannis/.rvm/gems/ruby-2.6.3/gems/graphql-1.10.4/lib/graphql/execution/interpreter.rb:71:in `block in evaluate'
     # /Users/ioannis/.rvm/gems/ruby-2.6.3/gems/graphql-1.10.4/lib/graphql/tracing.rb:67:in `block in trace'
     # /Users/ioannis/.rvm/gems/ruby-2.6.3/gems/graphql-1.10.4/lib/graphql/tracing.rb:83:in `block (2 levels) in call_tracers'
     # /Users/ioannis/.rvm/gems/ruby-2.6.3/gems/graphql-1.10.4/lib/graphql/tracing.rb:81:in `call_tracers'
     # /Users/ioannis/.rvm/gems/ruby-2.6.3/gems/graphql-1.10.4/lib/graphql/tracing.rb:83:in `block in call_tracers'
     # /Users/ioannis/.rvm/gems/ruby-2.6.3/bundler/gems/graphql-guard-aba85fc09a97/lib/graphql/guard.rb:42:in `trace'
     # /Users/ioannis/.rvm/gems/ruby-2.6.3/gems/graphql-1.10.4/lib/graphql/tracing.rb:83:in `call_tracers

The mutation

module Mutations::Users
  class SignInUser < Mutations::BaseMutation
 end
end
class GraphqlPolicy
  RULES = {
    Types::QueryType => {
      orders: ->(obj, args, ctx) { 
        args[:user_id] == ctx[:current_user].id 
      }
    }
  }

  def self.guard(type, field)
    RULES.dig(type, field)
  end
end

So when i use pry


From: /Users/ioannis/.rvm/gems/ruby-2.6.3/bundler/gems/graphql-guard-aba85fc09a97/lib/graphql/guard.rb @ line 79 GraphQL::Guard#policy_object_guard:

    75: def policy_object_guard(type, field_name)
    76: 
    77:   @policy_object && @policy_object.guard(type.type_class, field_name)
    78: rescue 
 => 79:   binding.pry
    80: end

[1] pry(#<GraphQL::Guard>)> type.type_class
NoMethodError: undefined method `type_class' for Mutations::Users::SignInUser:Class
from (pry):1:in `rescue in policy_object_guard'
Caused by NoMethodError: undefined method `type_class' for Mutations::Users::SignInUser:Class
from /Users/ioannis/.rvm/gems/ruby-2.6.3/bundler/gems/graphql-guard-aba85fc09a97/lib/graphql/guard.rb:77:in `policy_object_guard'
[2] pry(#<GraphQL::Guard>)> 
[2] pry(#<GraphQL::Guard>)> type
=> Mutations::Users::SignInUser
[3] pry(#<GraphQL::Guard>)>

Gemfile.lock

GIT
  remote: https://github.com/exAspArk/graphql-guard.git
  revision: aba85fc09a97e617139212999312b8efbf5e13e8
  branch: graphql-interpreter
  specs:
    graphql-guard (1.3.1)
      graphql (>= 1.10.0, < 2)
    graphiql-rails (1.7.0)
      railties
      sprockets-rails
    graphql (1.10.4)
    graphql-batch (0.4.2)
      graphql (>= 1.3, < 2)
      promise.rb (~> 0.7.2)
    graphql-errors (0.4.0)
      graphql (>= 1.6.0, < 2)
    graphql-query-resolver (0.2.0)
      graphql (~> 1.0, >= 1.0.0)
    graphql-rails_logger (1.2.2)
      actionpack (> 5.0)
      activesupport (> 5.0)
      railties (> 5.0)
      rouge (~> 3.0)
class ApiSchema < GraphQL::Schema
  mutation(Types::MutationType)
  query(Types::QueryType)

  # Opt in to the new runtime (default in future graphql-ruby versions)
  use GraphQL::Execution::Interpreter
  use GraphQL::Analysis::AST

  # Add built-in connections for pagination
  use GraphQL::Pagination::Connections

  use GraphQL::Batch
  use GraphQL::Guard.new(policy_object: GraphqlPolicy)
exAspArk commented 4 years ago

@msroot thank you! I was able to reproduce the issue with mutations

msroot commented 4 years ago

I still have the same issue

  1) Orders Orders Retailer createOrder
     Failure/Error: result = ApiSchema.execute(query, variables: variables, context: context, operation_name: operation_name)

     NoMethodError:
       undefined method `type_class' for Mutations::Orders::CreateOrder:Class
     # /Users/ioannis/.rvm/gems/ruby-2.6.3/bundler/gems/graphql-guard-aba85fc09a97/lib/graphql/guard.rb:76:in `policy_object_guard'
     # /Users/ioannis/.rvm/gems/ruby-2.6.3/bundler/gems/graphql-guard-aba85fc09a97/lib/graphql/guard.rb:48:in `find_guard_proc'
     # /Users/ioannis/.rvm/gems/ruby-2.6.3/bundler/gems/graphql-guard-aba85fc09a97/lib/graphql/guard.rb:65:in `ensure_guarded'
     # /Users/ioannis/.rvm/gems/ruby-2.6.3/bundler/gems/graphql-guard-aba85fc09a97/lib/graphql/guard.rb:40:in `trace'
     # /Users/ioannis/.rvm/gems/ruby-2.6.3/gems/graphql-1.10.4/lib/graphql/tracing.rb:83:in `call_tracers'
     # /Users/ioannis/.rvm/gems/ruby-2.6.3/gems/graphql-1.10.4/lib/graphql/tracing.rb:67:in `trace'
     # /Users/ioannis/.rvm/gems/ruby-2.6.3/gems/graphql-1.10.4/lib/graphql/execution/interpreter/runtime.rb:215:in `block (3 levels) in evaluate_selections'

gem 'graphql-guard', github: 'exAspArk/graphql-guard', branch: 'graphql-interpreter', ref: 'aba85fc'
class GraphqlPolicy
  RULES = {
    Types::QueryType => {
      orders: ->(obj, args, ctx) { 
        args[:user_id] == ctx[:current_user].id 
      }
    }
  }

  def self.guard(type, field)
    RULES.dig(type, field)
  end
end
msroot commented 4 years ago

The only way i made it this work is:

module GraphQL
  class Guard

    def policy_object_guard(type, field_name)
      @policy_object && type && field_name && type.respond_to?(:type_class) && @policy_object.guard(type.type_class, field_name)
    end

    def inline_guard(type_or_field)
      return unless respond_to?(:graphql_definition)
      type_or_field.graphql_definition.metadata[:guard]
    end
 end
 end

So when i print the types:

  def self.guard(type, field)
    puts "#{type} - #{field}"

    RULES.dig(type, field)
  end

I get:

GraphQL::Types::Relay::PageInfo - hasNextPage
GraphQL::Types::Relay::PageInfo - *
#<Class:0x00007fedb9992600> - edges
#<Class:0x00007fedb9992600> - *
GraphQL::Types::Relay::BaseEdge - cursor
GraphQL::Types::Relay::BaseEdge - *
#<Class:0x00007fedb9992c18> - node
#<Class:0x00007fedb9992c18> - *
Types::UserType - id
Types::UserType - *
Types::UserType - email
Types::UserType - *
GraphQL::Types::Relay::BaseEdge - cursor
GraphQL::Types::Relay::BaseEdge - *
#<Class:0x00007fedb9992c18> - node

So they dont respond_to?(:graphql_definition)

I am using also

gem 'search_object'
gem 'search_object_graphql'

UPDATE:

I open one of <Class:0x00007fedb9992c18> and its is a "CreateRetailerPayload" All mutations have a PayloadType

https://github.com/rmosolgo/graphql-ruby/blob/master/lib/graphql/schema/mutation.rb#L63

 # Build a subclass of {.object_class} based on `self`.
# This value will be cached as `{.payload_type}`.
# Override this hook to customize return type generation.

https://github.com/rmosolgo/graphql-ruby/blob/dbb8a45a75a40cec53c094159370e731a36e7f60/lib/graphql/schema/resolver/has_payload_type.rb#L49

bilby91 commented 4 years ago

I have tested this change in one of my projects and the only change I had to make in order to make the suite pass was this one:

@@ -46,6 +46,6 @@ class Policy
   # @return [Proc]
   #
   def self.guard(type, field)
-    RULES.dig(type.metadata[:type_class], field)
+    RULES.dig(type, field)
   end
 end
exAspArk commented 4 years ago

@msroot, thanks for your feedback! I pushed a commit that fixes compatibility with mutations.

@bilby91 right, that's a breaking change. That's why these changes will be released in a new major version.