exAspArk / graphql-guard

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

Pass field name to guard block #34

Closed 23tux closed 4 years ago

23tux commented 4 years ago

I have my abilities defined in CanCanCan on a field level basis and I'd like to authorize the fields and objects inside a general guard block. I have a BaseObject from which every GraphQL object inherits:

class BaseObject < GraphQL::Schema::Object
  guard ->(obj, _args, context) do
    context.fetch(:current_ability).can?(:read, obj.object)
  end
end

But I'm missing the field name here, I'd like to do something like this:

class BaseObject < GraphQL::Schema::Object
  guard ->(obj, _args, ctx, field) do
    ctx.fetch(:current_ability).can?(:read, obj.object, field)
  end
end

Am I missing something? Defining the guard block at every field would lead to a lot of duplication and very noisy code:

  field :name, String do
    guard ->(obj, args, ctx) {  ctx.fetch(:current_ability).can?(:read, obj.object, :name) }
  end

(of course one could define some helpers to reduce the noise, but it would still be "define the guard at every field" by hand).

I also had a look at your Policy Objects (see https://github.com/exAspArk/graphql-guard#policy-object) but I didn't have any luck with them (it seems to me that the .guard method of the policy object is only called once and not on every call, but I'm not sure).

So, is there a way to get the field inside the guard block to lazy evaluate if the field is authorized or not?

23tux commented 4 years ago

Ahhhhh silly me... nevermind, I found it in the source code:

  guard ->(obj, _args, context) do
    context.fetch(:current_ability).can?(:read, obj.object, context.field.name)
  end
asgeo1 commented 2 years ago

Thanks, this was useful. I've found on latest versions of graphql-ruby I needed to use ctx[:current_field].original_name. Here's what I did with Pundit

    guard ->(obj, args, ctx) {
      field = ctx[:current_field].original_name
      policy = MyPolicy.new(ctx[:current_user], obj.object)
      policy.show? and (field.blank? or (field.present? and policy.permitted_attributes_for_show.include?(field)))
    }