'Is it possible to use pundit authorization with graphql
I want to do something like this:
authorize record, :some_action
In resolving a graphql field or mutation(example is mutation)
module Mutations::CreateLink
CreateLink = GraphQL::Relay::Mutation.define do
name "CreateLink"
input_field :name, types.String
input_field :url, types.String
input_field :description, types.String
return_field :link, Types::LinkType
resolve -> (object, inputs, ctx) {
Rails::logger.ap ctx[:current_user]
Rails::logger.ap inputs[:name]
ctx[:current_user]
@link = Link.new(name: inputs[:name], url: inputs[:url], description: inputs[:description])
authorize @link, :create?
response = {
link: @link
}
}
end
end
When the above is run this error is shown: NoMethodError - GraphQL::Relay::Mutation can't define 'authorize'
Normally you write
include Pundit
In your controller, but adding it to the GraphqlController does not make any difference.
How do i make it so that graphql is aware of pundit and its methods?
Solution 1:[1]
The reason why include Pundit does not work is that includes are added to classes when you include a module and the authorize method would not work anyways since its meant to be included in controllers and makes assumptions based on that.
But its really easy to initialize policies manually:
class ApplicationPolicy
attr_reader :user, :record
def initialize(user, record)
@user = user
@record = record
end
end
And you can authorize by calling the appropriate method on the policy:
unless LinkPolicy.new(ctx[:current_user], @link).create?
raise Pundit::NotAuthorizedError, "not allowed to create? this #{@link.inspect}"
end
Solution 2:[2]
I found a somewhat slick solution in this blogpost Graphql ruby and authorization with pundit
It suggests that you add include Pundit in the graphqlcontroller and then add the controller as context like so:
context = {
current_user: current_user,
pundit: self
}
Then the authorize method is exposed like this in resolving fields and mutations:
ctx[:pundit].authorize @link, :create?
Pundit knows the user as the current_user in the context and throws appropriate errors.
Solution 3:[3]
Using a later version of graphql-ruby (I'm using 1.9.17 at the time of this writing), you can define an authorized? method in the mutation and include the following...
Pundit.authorize context[:current_user], @link, :create?
See https://github.com/varvet/pundit/blob/df96d2ae6bcf28991c1501d5ff0bde4c42aa4acd/lib/pundit.rb#L60-L76 for details on the Pundit authorize class method.
See https://graphql-ruby.org/mutations/mutation_authorization.html#can-this-user-perform-this-action for details on the authorized? method for graphql-ruby
Solution 4:[4]
The paid version of graphql-ruby has pundit integration.
But you can also use Pundit manually by calling:
Pundit.authorize(context[:current_user], record, :some_action)
Pundit.policy_scope(context[:current_user], RecordClass)
However, this won't satisfy verify_authorized and verify_policy_scoped. For that you need to call authorize and policy_scope on the controller, similar to @TamRock's answer:
graphql_controller.rb
class GraphqlController < ApplicationController
include Pundit::Authorization
after_action :verify_authorized
after_action :verify_policy_scoped
public :policy_scope
public :authorize
# ...
def execute
# ...
context = {
controller: self
}
# ...
end
end
Then in the resolver:
context[:controller].policy_scope(RecordClass)
context[:controller].authorize(record, :some_action)
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | max |
| Solution 2 | TamRock |
| Solution 3 | nratter |
| Solution 4 |
