Gem Build Status Coverage Status Code Climate GitHub issues

GraphQL::Pundit

Installation

Add this line to your application's Gemfile:

gem 'graphql-pundit'

And then execute:

$ bundle

Usage

Add the authorization middleware

Add the following to your GraphQL schema:

MySchema = GraphQL::Schema.define do
  ...
  instrument(:field, GraphQL::Pundit::Instrumenter.new)
  ...
end

By default, ctx[:current_user] will be used as the user to authorize. To change that behavior, pass a symbol to GraphQL::Pundit::Instrumenter.

GraphQL::Pundit::Instrumenter.new(:me) # will use ctx[:me]

Authorize fields

For each field you want to authorize via Pundit, add the following code to the field definition:

field :email do
  authorize # will use UserPolicy#email?
  resolve ...
end

By default, this will use the Policy for the parent object (the first argument passed to the resolve proc), checking for :email? for the current user. Sometimes, the field name will differ from the policy method name, in which case you can specify it explicitly:

field :email do
  authorize :read_email # will use UserPolicy#read_email?
  resolve ...
end

Now, in some cases you'll want to use a different policy, or in case of mutations, the passed object might be nil:

field :createUser
  authorize! :create, policy: User # or User.new; will use UserPolicy#create?
  resolve ...
end

This will use the :create? method of the UserPolicy. You can also pass in objects instead of a class (or symbol), if you wish to authorize the user for the specific object.

If you want to pass a different value to the policy, you can use the keyword argument record:

field :createUser
  authorize! :create, record: User.new # or User.new; will use UserPolicy#create?
  resolve ...
end

You can also pass a lambda as a record. This receives the usual three arguments (parent value, arguments, context) and returns the value to be used as a record.

You might have also noticed the use of authorize! instead of authorize in this example. The difference between the two is this:

  • authorize will set the field to nil if authorization fails
  • authorize! will set the field to nil and add an error to the response if authorization fails

You would normally want to use authorize for fields in queries, that only e.g. the owner of something can see, while authorize! would be usually used in mutations, where you want to communicate to the client that the operation failed because the user is unauthorized.

If you still need more control over how policies are called, you can pass a lambda to authorize:

field :email
  authorize ->(obj, args, ctx) { UserPolicy.new(obj, ctx[:me]).private_data?(:email) }
  resolve ...
end

If the lambda returns a falsy value or raises a Pundit::UnauthorizedError the field will resolve to nil, if it returns a truthy value, control will be passed to the resolve function. Of course, this can be used with authorize! as well.

Scopes

Pundit scopes are supported by using before_scope and after_scope in the field definition

field :posts
  after_scope
  resolve ...
end

Passing no arguments to after_scope and before_scope will infer the policy to use from the value it is passed: before_scope is run before resolve and will receive the parent object, after_scope will be run after resolve and receives the output of resolve. You can also pass a proc or a policy class to both _scopes:

field :posts
  before_scope ->(_root, _args, ctx) { Post.where(owner: ctx[:current_user]) }
  resolve ->(posts, args, ctx) { ... }
end
field :posts
  after_scope PostablePolicy
  resolve ...
end

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/ontohub/graphql-pundit.

License

The gem is available as open source under the terms of the MIT License.