README.md
# Pragma::Policy
[![Build Status](https://travis-ci.org/pragmarb/pragma-policy.svg?branch=master)](https://travis-ci.org/pragmarb/pragma-policy)
[![Coverage Status](https://coveralls.io/repos/github/pragmarb/pragma-policy/badge.svg?branch=master)](https://coveralls.io/github/pragmarb/pragma-policy?branch=master)
[![Maintainability](https://api.codeclimate.com/v1/badges/e51e8d7489eb72ab97ba/maintainability)](https://codeclimate.com/github/pragmarb/pragma-policy/maintainability)
Policies provide fine-grained access control for your API resources.
## Installation
Add this line to your application's Gemfile:
```ruby
gem 'pragma-policy'
```
And then execute:
```console
$ bundle
```
Or install it yourself as:
```console
$ gem install pragma-policy
```
## Usage
To create a policy, simply inherit from `Pragma::Policy::Base`:
```ruby
module API
module V1
module Article
class Policy < Pragma::Policy::Base
end
end
end
end
```
By default, the policy does not return any objects when scoping and forbids all operations.
You can start customizing your policy by defining a scope and operation predicates:
```ruby
module API
module V1
module Article
class Policy < Pragma::Policy::Base
class Scope < Pragma::Policy::Base::Scope
def resolve
scope.where('published = ? OR author_id = ?', true, user.id)
end
end
def show?
record.published? || record.author_id == user.id
end
def update?
record.author_id == user.id
end
def destroy?
record.author_id == user.id
end
end
end
end
end
```
You are ready to use your policy!
### Retrieving records
To retrieve all the records accessible by a user, use the `.accessible_by` class method:
```ruby
posts = API::V1::Article::Policy::Scope.new(user, Article.all).resolve
```
### Authorizing operations
To authorize an operation, first instantiate the policy, then use the predicate methods:
```ruby
policy = API::V1::Article::Policy.new(user, post)
fail 'You cannot update this post!' unless policy.update?
```
Since raising when the operation is forbidden is so common, we provide bang methods a shorthand
syntax. `Pragma::Policy::NotAuthorizedError` is raised if the predicate method returns `false`:
```ruby
policy = API::V1::Article::Policy.new(user, post)
policy.update! # raises if the user cannot update the post
```
### Reusing Pundit policies
If you already use [Pundit](https://github.com/varvet/pundit), there's no need to copy-paste
policies for your API. You can use `Pragma::Policy::Pundit` to delegate to your existing policies
and scopes:
```ruby
module API
module V1
module Article
class Policy < Pragma::Pundit::Policy
# This is optional: the inferred default would be ArticlePolicy.
self.pundit_klass = CustomArticlePolicy
end
end
end
end
```
Note that you can still override specific methods if you want, and we'll keep delegating the rest
to Pundit:
```ruby
module API
module V1
module Article
class Policy < Pragma::Pundit::Policy
def create?
# Your custom create policy here
end
end
end
end
end
```
### Passing additional context
If you want to pass additional context to the policy, just pass it instead of the user object.
Pragma::Policy never uses your context in any way, so you can pass whatever you want:
```ruby
policy = API::V1::Article::Policy.new(OpenStruct.new(ip: request.remote_ip, user: user), post)
policy.update!
```
In your policy, you can use `#context` as an alias for `#user` for convenience:
```ruby
module API
module V1
module Article
class Policy < Pragma::Pundit::Policy
def update?
record.author_id == context.user.id || context.ip == '127.0.0.1'
end
end
end
end
end
```
If you are using [pragma-rails](https://github.com/pragmarb/pragma-rails), you may change the
context passed to the policy by defining a `#policy_context` method on your controller. This way you
are not forced to override `#current_user` or `#pragma_user`:
```ruby
module API
module V1
class PostsController < ApplicationController
# ...
private
def policy_context
OpenStruct.new(ip: request.remote_ip, user: current_user)
end
end
end
end
```
## Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/pragmarb/pragma-policy.
## License
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).