<commit_before>Canner
======

Canner is an authorization gem heavily modeled after Pundit.
The goal is to take away some of the magic that exists in other auth gems out there and
provide the flexibility needed for your specific app.

Who needs another auth gem?  There's a bunch of very good ones out there.
Pundit, cancan, cancancan, Declarative Authorization to name a few very good alternatives.

Unfortunately none of those solutions had built in support for a requirement I had on a project.
My application needed to authorize a user at a given store location.

Suppose I opened a store in Pittsburgh named Joe's Hash Rockets. The word quickly got around that Joe's
hash rockets are the best in town!  Business is booming and after some market research I determine that
Cleveland is a perfect place to open another store location.

The problem is that I have some reports that only the store manager should see.
In any of the above listed gems if you have a manager role you could see all the reports regardless
of the store location aka store branch.

Canner takes this additional layer of authorization in to account.
Instead of saying:
Can the currently signed in user access this particular report.
Canner can say:
Can the currently signed in user access this particular report for this particular branch.

To do this in other libraries you might find yourself creating roles like
pittsburgh_manager and cleveland_manager.  Or maybe writing some monkey patches to hack in what you need.

Canner is designed to give you an outline of what you'll need for your app's auth needs and leaves
the level of complexity needed up to the requirements of your particular application.

## Installation

I've only tested Canner on rails 4 but it should work find in rails 3.2 apps.

To install Canner just put the following in your gem file.
``` ruby
gem "canner"
```

Then run

``` ruby
bundle install
```

After the gem is installed you'll need to run the install generator:

``` ruby
rails g canner:install
```

This will create a policies directory and create the base_policy.rb

Now include canner in your application_controller.rb

``` ruby
include Canner
```

You'll then need to create a Policy class for the models you'd like to authorize.

``` ruby
rails g canner:policy user
```

Restart your server

## Policies

As mentioned Canner is strongly influenced by Pundit and is also based on Policy objects.
Your policy objects should be named using the pattern model_namePolicy.rb.
i.e UserPolicy, CustomerPolicy, AppPolicy.

Your policy models need to implement 2 methods:
``` ruby
def canner_scope
end

def can?
end
```

### canner_scope

The canner_scope method is used to scope the models consistently in your app.
For example in my app the Customers controller uses the canner_scope to
ensure only Users from the current_company are displayed.

``` ruby
class CustomersController < ApplicationController
  respond_to :html, :json
  before_action :authenticate_user!

  def index
    @customers = canner_scope(:index, :customer)

    can?(:index, :customer)
  end
end
```

and the policy is:

``` ruby
class CustomerPolicy < BasePolicy

  def canner_scope
    case @method
    when :index
      User.where(company_id: @current_branch.company.id)
    else
      User.none
    end
  end

  def can?
    case @method
    when :new, :index, :create, :update, :edit
      has_role?(:admin)
    else
      false
    end
  end

end
```

### can?

You probably recognize this method from Ryan Bates' cancan gem.  The idea is the same as well.

You use the can method to determine if the current_user is able to access an action or resource.

The example above uses a straightforward case statement to determine if the current_user can
access the current action or resource.

The symbols in the when portion of the case match your typical actions in the example but they
can be whatever you want really.

``` ruby
case @method
when :something_random
  has_role?(:admin)
else
  false
end

# Then in controller do:
can?(:something_random, :customer)
```

In english the can method is saying:
Can the currently signed in user access the something_random action?  Oh, and by the way please
use the CustomerPolicy's can? method to do the checking.

can?(:something_random, :user) would use the ... you guessed it UserPolicy's can? method.

### Force the Use of Policies

Also like Pundit you can force your app to use policies.
I recommend you do this so you don't forget to wrap authorization about some of your resources.

To make sure your controller actions are using the can? method add this to your
application_controller.rb

``` ruby
after_action :ensure_auth
```

And to make sure you are using the canner_scope do the following:
``` ruby
after_action :ensure_scope, only: :index
```

Note the use of only here.  You usually won't need the canner_scope on anything except
for the index to be strictly enforced.

### Handle Canner Authorization Failures

When a user does stumble onto something they don't have access to you'll want to politely
tell them about it and direct the app flow as you see fit.

Do accomplish this in your application_controller.rb add

``` ruby
  rescue_from Canner::NotAuthorizedError, with: :user_not_authorized
```

You can name your method whatever you want.  Mine is user_not_authorized and looks like this:

``` ruby
  private

  def user_not_authorized(exception)
    flash[:error] = exception.message
    redirect_to(request.referrer || root_path)
  end
```
<commit_msg>More doc edits<commit_after>Canner
======

Canner is an authorization gem heavily modeled after Pundit.
The goal is to take away some of the magic that exists in other auth gems out there and
provide the flexibility needed for your specific app.

Who needs another auth gem?  There's a bunch of very good ones out there.
Pundit, cancan, cancancan, Declarative Authorization to name a few very good alternatives.

Unfortunately none of those solutions had built in support for a requirement I had on a project.
My application needed to authorize a user at a given store location.

Suppose I opened a store in Pittsburgh named <NAME>. The word quickly got around that Joe's
hash rockets are the best in town!  Business is booming and after some market research I determine that
Cleveland is a perfect place to open another store location.

The problem is that I have some reports that only the store manager should see.
In any of the above listed gems if you have a manager role you could see all the reports regardless
of the store location aka store branch.

Canner takes this additional layer of authorization in to account.
Instead of saying:
Can the currently signed in user access this particular report.
Canner can say:
Can the currently signed in user access this particular report for this particular branch.

To do this in other libraries you might find yourself creating roles like
pittsburgh_manager and cleveland_manager.  Or maybe writing some monkey patches to hack in what you need.

Canner is designed to give you an outline of what you'll need for your app's auth needs and leaves
the level of complexity needed up to the requirements of your particular application.

## Installation

I've only tested Canner on rails 4 but it should work find in rails 3.2 apps.

To install Canner just put the following in your gem file.
``` ruby
gem "canner"
```

Then run

``` ruby
bundle install
```

After the gem is installed you'll need to run the install generator:

``` ruby
rails g canner:install
```

This will create a policies directory and create the base_policy.rb

Now include canner in your application_controller.rb

``` ruby
include Canner
```

You'll then need to create a Policy class for the models you'd like to authorize.

``` ruby
rails g canner:policy user
```

Restart your server

## Policies

As mentioned Canner is strongly influenced by Pundit and is also based on Policy objects.
Your policy objects should be named using the pattern model_namePolicy.rb.
i.e UserPolicy, CustomerPolicy, AppPolicy.

Your policy models need to implement 2 methods:
``` ruby
def canner_scope
end

def can?
end
```

### canner_scope

The canner_scope method is used to scope the models consistently in your app.

For example in my app the Customers controller uses the canner_scope to
ensure only Users from the current_company are displayed.

``` ruby
class CustomersController < ApplicationController
  respond_to :html, :json
  before_action :authenticate_user!

  def index
    @customers = canner_scope(:index, :customer)

    can?(:index, :customer)
  end
end
```

and the policy is:

``` ruby
class CustomerPolicy < BasePolicy

  def canner_scope
    case @method
    when :index
      User.where(company_id: @current_branch.company.id)
    else
      User.none
    end
  end

  def can?
    case @method
    when :new, :index, :create, :update, :edit
      has_role?(:admin)
    else
      false
    end
  end

end
```

### can?

You probably recognize this method from <NAME>' cancan gem.  The idea is the same as well.

You use the can method to determine if the current_user is able to access an action or resource.

The example above uses a straightforward case statement to determine if the current_user can
access the current action or resource.

The symbols in the when portion of the case match your typical actions in the example but they
can be whatever you want really.

``` ruby
case @method
when :something_random
  has_role?(:admin)
else
  false
end

# Then in controller do:
can?(:something_random, :customer)
```

In english the can method is saying:

Can the currently signed in user access the something_random action?  Oh, and by the way please
use the CustomerPolicy's can? method to do the checking.

`can?(:something_random, :user)` would use the ... you guessed it UserPolicy's can? method.

### Force the Use of Policies

Also like Pundit you can force your app to use policies.
I recommend you do this so you don't forget to wrap authorization about some of your resources.

To make sure your controller actions are using the can? method add this to your
application_controller.rb

``` ruby
after_action :ensure_auth
```

And to make sure you are using the canner_scope do the following:
``` ruby
after_action :ensure_scope, only: :index
```

Note the use of only here.  You usually won't need the canner_scope on anything except
for the index to be strictly enforced.

### Handle Canner Authorization Failures

When a user does stumble onto something they don't have access to you'll want to politely
tell them about it and direct the app flow as you see fit.

To accomplish this in your application_controller.rb add

``` ruby
  rescue_from Canner::NotAuthorizedError, with: :user_not_authorized
```

You can name your method whatever you want.  Mine is user_not_authorized and looks like this:

``` ruby
  private

  def user_not_authorized(exception)
    flash[:error] = exception.message
    redirect_to(request.referrer || root_path)
  end
```

{'fraction_non_alphanumeric': 0.051177165712207984, 'fraction_numerical': 0.00072720661758022, 'mean_word_length': 4.174976481655691, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4272738', 'n_tokens_mistral': 3031, 'n_tokens_neox': 2860, 'n_words': 1609}