exAspArk / graphql-guard

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

Usage with graphql-ruby 1.8 class syntax #15

Closed SeanRoberts closed 6 years ago

SeanRoberts commented 6 years ago

graphql-ruby 1.8 is introducing a new class-based syntax, however it seems to break compatibility with graphql-guard.

class Types::Query < GraphQL::Schema::Object
  field :posts, [Types::Post], null: false do
    guard ->(obj, args, ctx) { true }
  end
end

results in undefined method 'guard' for GraphQL::Schema::Field

Is there a potential workaround (aside from just not using the new syntax)?

ssendev commented 6 years ago

https://github.com/rmosolgo/graphql-ruby/issues/1324 mentions https://github.com/rmosolgo/graphql-ruby/blob/master/guides/type_definitions/extensions.md which describes how one could implement it

christophweegen commented 6 years ago

Hey there,

I'm working with a policy object, but after changing my types to class-based syntax, the object doesn't work anymore too..

@exAspArk are you planning to adapt graphql-guard to the new class-based syntax?

Thanks for your efforts!

kymmt90 commented 6 years ago

As described in GraphQL - Extending the GraphQL-Ruby Type Definition System, extending GraphQL::Schema::Field works when using inline policies:

# app/graphql/fields/guardable.rb
class Fields::Guardable < GraphQL::Schema::Field
  def initialize(*args, guard:, **kwargs, &block)
    @guard = guard
    super *args, **kwargs, &block
  end

  def to_graphql
    field_defn = super
    field_defn.tap { |d| d.metadata[:guard] = @guard }
  end
end

# app/graphql/types/query.rb
class Types::Query < Types::BaseObject
  field_class Fields::Guardable

  field :viewer, Types::User, guard: ->(obj, args, ctx) { !ctx[:current_user].nil? }

  def viewer
    context[:current_user]
  end
end
kymmt90 commented 6 years ago

I noticed that accepts_definition is also useful for inline guards:

# app/graphql/fields/guardable.rb
class Fields::Guardable < GraphQL::Schema::Field
  accepts_definition :guard
end

# app/graphql/types/query.rb
class Types::Query < Types::BaseObject
  field_class Fields::Guardable

  field :viewer, Types::User do
    guard ->(obj, args, ctx) { !ctx[:current_user].nil? }
  end

  def viewer
    context[:current_user]
  end
end

ref: http://graphql-ruby.org/type_definitions/extensions.html#customization-compatibility

exAspArk commented 6 years ago

@kymmt90 fixed it in https://github.com/exAspArk/graphql-guard/pull/17. I released the changes in version 1.2.0 :)

RealSilo commented 6 years ago

@kymmt90 could you make the accepts_definition work? The first version is working for me:

class Fields::Authenticable < GraphQL::Schema::Field
  def initialize(*args, authenticate: false, **kwargs, &block)
    @authenticate = authenticate
    super(*args, **kwargs, &block)
  end

  def to_graphql
    field_defn = super
    field_defn.metadata[:authenticate] = @authenticate
    field_defn
  end
end
field :vehicles, Types::VehicleType.connection_type, null: false, authenticate: true do
    argument :orderBy, String, 'id', required: false
end

but the accepts_definition is not :(.

class Fields::Authenticable < GraphQL::Schema::Field
  accepts_definition :authenticate
end
field :vehicles, Types::VehicleType.connection_type, null: false do
    authenticate ->(obj, args, ctx) { ctx[:current_user].nil? }
    argument :orderBy, String, 'id', required: false
end

It says GraphQL::Field can't define 'authenticate'

In case of authentication doesn't really matter but for authorization where I wanna pass in policies I would prefer the second option.

exAspArk commented 6 years ago

@RealSilo not sure where the authenticate came from? On a field level, with graphql-guard, you should either use guard or mask.

If you'd like to add your custom definition, please try to find more information on graphql-ruby.org.

RealSilo commented 6 years ago

I am trying to implement a custom method (authenticate), but can't figure it out with the new class based syntax (I checked the docs). If you happen to have some example it would be great. Here is the old syntax: https://medium.com/impraise-design-engineering/generic-authorization-with-graphql-and-ruby-11f61f24c60b that I am trying to implement with the new class based syntax. In this example it's authorization instead of authentication but the pattern is the same.