Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[How to] authorize specific arguments in a mutation #50

Closed
Tao-Galasse opened this issue Apr 17, 2024 · 1 comment
Closed

[How to] authorize specific arguments in a mutation #50

Tao-Galasse opened this issue Apr 17, 2024 · 1 comment

Comments

@Tao-Galasse
Copy link

Hi there @palkan! 👋

First of all, thanks for your amazing gem :)

Here is my issue: I would like to be able to authorize some specific arguments in a mutation, and not only the mutation as a whole.

Let's say I have a mutation to update a User. Maybe the user itself could update its first_name, but only the admin could upgrade its role, and the manager could assign it in a specific team.

I would like to do something like this:

class Mutations::UpdateUser < BaseMutation
  argument :user_id, ID, loads: Types::UserType, required: true
  argument :first_name, String, required: false # no specific authorization here
  argument :last_name, String, required: false # no specific authorization here
  argument :team_id, ID, loads: Types::Team, required: false, authorize: true
  argument :role, Types::RoleEnum, required: false, authorize: true

  def resolve(user:, **params)
    authorize! user, to: :update?
    user.update!(params)
    { user: user }
  end
end

And then, in my policy class, I could have something like this:

class UserPolicy < ApplicationPolicy
  def team? = user.manager? || user.admin?
  def role? = user.admin?

  def update? # my mutation authorization logic
end

For now, the only way to achieve this I found was to do all this argument-validation logic inside my resolver, but the implementation feels a bit lame.
For example:

def resolve(user:, team: nil, role: nil, **params)
  authorize! user, to: :update?
  authorize! user, to: :update_team? if team
  authorize! user, to: :update_role? if role
end

Is there a better way to manage this?

Thanks a lot :)

@palkan
Copy link
Owner

palkan commented Apr 18, 2024

Hey! Thank you for our feedback!

We've been thinking on a workaround for this problem for a while, see: #24 (and other linked discussions). Still, no interface we agreed upon.

Using the current API, I see two options:

  • Passing input as an optional context (authorize :input, optional: true in your policy and authorize! ..., context: {input:})
  • Using scopes to filter input parameters (similarly to permitted attributes)

The latter approach is preferable if you want to support partial application, i.e., ignore unpermitted fields and continue execution with what's left.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants