Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/kanety/ii_policy


https://github.com/kanety/ii_policy

Last synced: 2 months ago
JSON representation

Awesome Lists containing this project

README

        

# IIPolicy

A base policy to support management of authorization logic.

This gem is inspired by [pundit](https://github.com/varvet/pundit) specs.

## Dependencies

* ruby 2.3+
* activesupport 5.0+

## Installation

Add this line to your application's Gemfile:

```ruby
gem 'ii_policy'
```

Then execute:

$ bundle

## Usage

Prepare model:

```ruby
class Item < ActiveRecord::Base
end
```

Prepare controller with `current_user` and call `authorize`:

```ruby
class ItemsController < ActionController::Base
def index
@policy = authorize(ItemPolicy)
@items = Item.all
end

def show
@item = Item.find(params[:id])
@policy = authorize(@item)
end

def current_user
User.find(session[:login_user_id])
end
end
```

Create policy that has methods corresponding with actions of controller:

```ruby
class ItemPolicy < IIPolicy::Base
def index?
@user.admin?
end

def show?
@user.admin? && @item.status != 'deleted'
end
end
```

### Controller

`authorize` lookups policy and calls it's method corresponding with current action.
`authorize` takes following arguments:

```ruby
# no argument (policy class is looked up using the name of controller class)
authorize

# instance (policy class is looked up using the name of instance's class)
authorize(@item)

# policy class
authorize(ItemPolicy)

# with extra context as second argument
authorize(@item, something: 'something')
```

Context is set to `{ user: current_user }` in the controller by default.
You can set other context you want by overriding `policy_context`:

```ruby
class ItemsController < ActionController::Base
def policy_context
super.merge(something: 'something')
end
end
```

When current user is not authoized, `IIPolicy::AuthorizationError` is raised.
You can catch the error and render a special page using `rescue_from`:

```ruby
class ItemsController < ActionController::Base
rescue_from IIPolicy::AuthorizationError, with: -> { ... }
end
```

You can also create policy instance by yourself and check authorization using `allowed` method as follows:

```ruby
# policy class
policy(ItemPolicy).allowed(:index?)

# instance
policy(@item).allowed(:index?)
```

### Policy

Policy has following attributes:

```ruby
class ItemPolicy < IIPolicy::Base
def index?
puts "user: #{@user}"
puts "item: #{@item}"
puts "context: #{@context}"
end
end

policy = ItemPolicy.new(user: User.find(1), item: Item.find(1), something: 'something')
policy.allowed(:index?)
#=> user: #
# item: #
# context: #
```

You can call another policy method in the same context:

```ruby
class ItemPolicy < IIPolicy::Base
def another_show?
allowed(:show?)
end
end
```

You can use policy for another instance by using `policy`:

```ruby
class ItemPolicy < IIPolicy::Base
def another_show?
policy(@context.another_item).allowed(:show?)
end
end
```

#### Callbacks

Following callbacks are available:

* `before_call`
* `around_call`
* `after_call`

For example:

```ruby
class ItemPolicy < IIPolicy::Base
before_call do
@something = @context.something
end

def index?
@something == 'something'
end
end
```

#### Coactors

You can define multiple coactors by using `coact` as follows:

```ruby
# shared policy
class SharedPolicy < IIPolicy::Base
def show?
@user.admin?
end
end

# base policy
class ItemPolicy < IIPolicy::Base
coact SharedPolicy

def show?
@item.status != 'deleted'
end
end

policy = ItemPolicy.new(user: User.find(1), item: Item.find(1))
policy.allowed(:show?)
#=> true
```

In this example, `policy.allowed(:show?)` is evaluated by `SharedPolicy#show? && ItemPolicy#show?`.

See [coactive](https://github.com/kanety/coactive) for more `coact` examples:

### Lookup for policy

`authorize` and `policy` lookups policy class if the first argument of them is not a policy class.
So the name of policy class should be composed of the base name of model or controller.
For example:

```ruby
class ItemPolicy < IIPolicy::Base
end

class Item
end

class ItemsController < ActionController::Base
end

IIPolicy::Base.lookup(Item)
#=> ItemPolicy

IIPolicy::Base.lookup(Item.new)
#=> ItemPolicy

IIPolicy::Base.lookup(ItemsController)
#=> ItemPolicy
```

Note that superclass of model or controller is also looked up until policy is found.

```ruby
class ItemPolicy < IIPolicy::Base
end

class Item
end

class InheritedItem < Item
end

IIPolicy::Base.lookup(InheritedItem)
#=> ItemPolicy

IIPolicy::Base.lookup(InheritedItem.new)
#=> ItemPolicy
```

### Logging

Policy supports instrumentation hook supplied by `ActiveSupport::Notifications`.
You can enable log subscriber as follows:

```ruby
IIPolicy::LogSubscriber.attach_to :ii_policy
```

This subscriber will write logs in debug mode as the following example:

```
Calling ItemPolicy#index? with #
...
Called ItemPolicy#index? and return true (Duration: 0.1ms, Allocations: 9)
```

## Contributing

Bug reports and pull requests are welcome at https://github.com/kanety/ii_policy.

## License

The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).