# JSONAPI::Authorization
[![Build Status](https://img.shields.io/travis/venuu/jsonapi-authorization/master.svg?style=flat&maxAge=3600)](https://travis-ci.org/venuu/jsonapi-authorization) [![Gem Version](https://img.shields.io/gem/v/jsonapi-authorization.svg?style=flat&maxAge=3600)](https://rubygems.org/gems/jsonapi-authorization)
**NOTE:** This README is the documentation for `JSONAPI::Authorization`. If you are viewing this at the
[project page on Github](https://github.com/venuu/jsonapi-authorization) you are viewing the documentation for the `master`
branch. This may contain information that is not relevant to the release you are using. Please see the README for the
[version](https://github.com/venuu/jsonapi-authorization/releases) you are using.
---
`JSONAPI::Authorization` adds authorization to the [jsonapi-resources][jr] (JR) gem using [Pundit][pundit].
[jr]: https://github.com/cerebris/jsonapi-resources "A resource-focused Rails library for developing JSON API compliant servers."
[pundit]: https://github.com/elabs/pundit "Minimal authorization through OO design and pure Ruby classes"
The core design principle of `JSONAPI::Authorization` is:
**Prefer being overly restrictive rather than too permissive by accident.**
What follows is that we want to have:
1. Whitelist over blacklist -approach for authorization
2. Fall back on a more strict authorization
## Caveats
Make sure to test for authorization in your application, too. We should have coverage of all operations, though. If that isn't the case, please [open an issue][issues].
If you're using custom processors, make sure that they extend `JSONAPI::Authorization::AuthorizingProcessor`, or authorization will not be performed for that resource.
This gem should work out-of-the box for simple cases. The default authorizer might be overly restrictive for cases where you are touching relationships.
**If you are modifying relationships**, you should read the [relationship authorization documentation](docs/relationship-authorization.md).
The API is subject to change between minor version bumps until we reach v1.0.0.
## Installation
Add this line to your application's Gemfile:
```ruby
gem 'jsonapi-authorization'
```
And then execute:
$ bundle
Or install it yourself as:
$ gem install jsonapi-authorization
## Compatibility
* `v0.6.x` supports JR `v0.7.x`
* `v0.8.x` supports JR `v0.8.x`
* `v1.x.x` and `v2.0.x` releases support JR `v0.9.x`
We aim to support the same Ruby and Ruby on Rails versions as `jsonapi-resources` does. If that's not the case, please [open an issue][issues].
## Usage
First make sure you have a Pundit policy specified for every backing model that your JR resources use.
Hook up this gem as the default processor for JR, and optionally allow rescuing from `Pundit::NotAuthorizedError` to output better errors for unauthorized requests:
```ruby
# config/initializers/jsonapi-resources.rb
JSONAPI.configure do |config|
config.default_processor_klass = JSONAPI::Authorization::AuthorizingProcessor
config.exception_class_whitelist = [Pundit::NotAuthorizedError]
end
```
Make all your JR controllers specify the user in the `context` and rescue errors thrown by unauthorized requests:
```ruby
class BaseResourceController < ActionController::Base
include JSONAPI::ActsAsResourceController
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
private
def context
{user: current_user}
end
def user_not_authorized
head :forbidden
end
end
```
Have your JR resources include the `JSONAPI::Authorization::PunditScopedResource` module.
```ruby
class BaseResource < JSONAPI::Resource
include JSONAPI::Authorization::PunditScopedResource
abstract
end
```
### Policies
To check whether an action is allowed JSONAPI::Authorization calls the respective actions of your pundit policies
(`index?`, `show?`, `create?`, `update?`, `destroy?`).
For relationship operations by default `update?` is being called for all affected resources.
For a finer grained control you can define methods to authorize relationship changes. For example:
```ruby
class ArticlePolicy
# (...)
def add_to_comments?(new_comments)
record.published && new_comments.all? { |comment| comment.author == user }
end
def replace_comments?(new_comments)
allowed = record.comments.all? { |comment| new_comments.include?(comment) || add_to_comments?([comment])}
allowed && new_comments.all? { |comment| record.comments.include?(comment) || remove_from_comments?(comment) }
end
def remove_from_comments?(comment)
comment.author == user || user.admin?
end
end
```
For thorough documentation about custom policy methods, check out the [relationship authorization docs](docs/relationship-authorization.md).
## Configuration
You can use a custom authorizer class by specifying a configure block in an initializer file. If using a custom authorizer class, be sure to require them at the top of the initializer before usage.
```ruby
JSONAPI::Authorization.configure do |config|
config.authorizer = MyCustomAuthorizer
end
```
By default JSONAPI::Authorization uses the `:user` key from the JSONAPI context hash as the Pundit user. If you would like to use `:current_user` or some other key, it can be configured as well.
```ruby
JSONAPI::Authorization.configure do |config|
config.pundit_user = :current_user
# or a block can be provided
config.pundit_user = ->(context){ context[:current_user] }
end
```
## Troubleshooting
### "Unable to find policy" exception for a request
The exception might look like this for resource class `ArticleResource` that is backed by `Article` model:
```
unable to find policy `ArticlePolicy` for `Article'
```
This means that you don't have a policy class created for your model. Create one and the error should go away.
## Development
After checking out the repo, run `bundle install` to install dependencies. Then, run `bundle exec 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](https://rubygems.org).
## Credits
Originally based on discussion and code samples by [@barelyknown](https://github.com/barelyknown) and others in [cerebris/jsonapi-resources#16](https://github.com/cerebris/jsonapi-resources/issues/16).
## Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/venuu/jsonapi-authorization.
[issues]: https://github.com/venuu/jsonapi-authorization/issues
## Contributors
Thanks goes to these wonderful people ([emoji key](https://github.com/kentcdodds/all-contributors#emoji-key)):
| [
Vesa Laakso](http://vesalaakso.com)
[๐ป](https://github.com/Venuu/jsonapi-authorization/commits?author=valscion "Code") [๐](https://github.com/Venuu/jsonapi-authorization/commits?author=valscion "Documentation") [๐](#infra-valscion "Infrastructure (Hosting, Build-Tools, etc)") [โ ๏ธ](https://github.com/Venuu/jsonapi-authorization/commits?author=valscion "Tests") [๐](https://github.com/Venuu/jsonapi-authorization/issues?q=author%3Avalscion "Bug reports") [๐ฌ](#question-valscion "Answering Questions") [๐](#review-valscion "Reviewed Pull Requests") | [
Emil Sรฅgfors](https://github.com/lime)
[๐ป](https://github.com/Venuu/jsonapi-authorization/commits?author=lime "Code") [๐](https://github.com/Venuu/jsonapi-authorization/commits?author=lime "Documentation") [๐](#infra-lime "Infrastructure (Hosting, Build-Tools, etc)") [โ ๏ธ](https://github.com/Venuu/jsonapi-authorization/commits?author=lime "Tests") [๐](https://github.com/Venuu/jsonapi-authorization/issues?q=author%3Alime "Bug reports") [๐ฌ](#question-lime "Answering Questions") [๐](#review-lime "Reviewed Pull Requests") | [
Matthias Grundmann](https://github.com/matthias-g)
[๐ป](https://github.com/Venuu/jsonapi-authorization/commits?author=matthias-g "Code") [๐](https://github.com/Venuu/jsonapi-authorization/commits?author=matthias-g "Documentation") [โ ๏ธ](https://github.com/Venuu/jsonapi-authorization/commits?author=matthias-g "Tests") [๐ฌ](#question-matthias-g "Answering Questions") | [
Thibaud Guillaume-Gentil](http://thibaud.gg)
[๐ป](https://github.com/Venuu/jsonapi-authorization/commits?author=thibaudgg "Code") | [
Daniel Schweighรถfer](http://netsteward.net)
[๐ป](https://github.com/Venuu/jsonapi-authorization/commits?author=acid "Code") | [
Bruno Sofiato](https://github.com/bsofiato)
[๐ป](https://github.com/Venuu/jsonapi-authorization/commits?author=bsofiato "Code") | [
Adam Robertson](https://github.com/arcreative)
[๐](https://github.com/Venuu/jsonapi-authorization/commits?author=arcreative "Documentation") |
| :---: | :---: | :---: | :---: | :---: | :---: | :---: |
| [
Greg Fisher](https://github.com/gnfisher)
[๐ป](https://github.com/Venuu/jsonapi-authorization/commits?author=gnfisher "Code") [โ ๏ธ](https://github.com/Venuu/jsonapi-authorization/commits?author=gnfisher "Tests") | [
Sam](http://samlh.com)
[๐ป](https://github.com/Venuu/jsonapi-authorization/commits?author=handlers "Code") [โ ๏ธ](https://github.com/Venuu/jsonapi-authorization/commits?author=handlers "Tests") | [
Justas Palumickas](https://jpalumickas.com)
[๐](https://github.com/Venuu/jsonapi-authorization/issues?q=author%3Ajpalumickas "Bug reports") [๐ป](https://github.com/Venuu/jsonapi-authorization/commits?author=jpalumickas "Code") [โ ๏ธ](https://github.com/Venuu/jsonapi-authorization/commits?author=jpalumickas "Tests") | [
Nicholas Rutherford](http://www.google.co.uk/profiles/nick.rutherford)
[๐ป](https://github.com/Venuu/jsonapi-authorization/commits?author=nruth "Code") [โ ๏ธ](https://github.com/Venuu/jsonapi-authorization/commits?author=nruth "Tests") [๐](#infra-nruth "Infrastructure (Hosting, Build-Tools, etc)") | [
Matthijsy](https://github.com/Matthijsy)
[๐](https://github.com/Venuu/jsonapi-authorization/issues?q=author%3AMatthijsy "Bug reports") [โ ๏ธ](https://github.com/Venuu/jsonapi-authorization/commits?author=Matthijsy "Tests") [๐ป](https://github.com/Venuu/jsonapi-authorization/commits?author=Matthijsy "Code") |
This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!