palkan/action_policy-graphql

Handle record with preauthorize?

marlosirapuan opened this issue · 2 comments

Hi, @palkan. There is a way to get record or the ID of the record to handle before to execute an action without "preauthorize"?
I do something like this:

Mutation type:

field :update_user,
       mutation: Mutations::Users::UpdateUserMutation,
       authorize: { to: :update?, with: Mutations::UserPolicy }

Mutation:

class UpdateUserMutation < BaseMutation
  argument :id, String, required: true
  argument :data, Types::Inputs::User::UpdateUser, required: true

  type Types::Data::User

  def resolve(id:, data:)
    UserService.new.save(id, data)
  end

Policy:

module Mutations
  class UserPolicy < ApplicationPolicy
    def update?
      user.admin? || own?
    end

    private

    def own?
      record.id == user.id
    end
  end
end

That way I get "not authorized", but it still performs the service action even if not authorized. If I put "preauthorize" i can't get the "record" to handle.. 🤔

I would need something to handle the record and stop the execution of resolve if it was not authorized. Is it possible?

I would need something to handle the record and stop the execution of resolve if it was not authorized

You can do this by calling authorize! explicitly in your resolve method:

def resolve(id:, data:)
  user = User.find(id)
  authorize! user, to: :update?
  UserService.new.save(user, data)
end

That couldn't be achieved with neither authorize nor preauthorize because:

  • authorize uses the resolved value (the return value of resolve method) as its authorization object, and, as you noticed, applies authorization after the mutation is executed
  • the idea of preauthorize is to perform authorization before field resolution (and without any object); you can only rely on the field name here.

We do not have authorize: ... support for argument fields yet (see #24 for discussion), but that's what else you can do if you'd like to move authorization to schema definition, not resolving:

class UpdateUserMutation < BaseMutation
  argument :id, String, required: true, as: :user, prepare: ->(id, ctx) {
    User.find(id).tap do |user|
      # context must be passed explicitly in this case
      authorize! user, to: :update?, context: {user: ctx[:current_user]}
    end
  }

  def resolve(user:, data:)
     UserService.new.save(user, data)
  end
end

hmm.. i got it. Thank you! 👍