Guachiman

Basic Authorization gem. Based on RailsCast #385 Authorization from Scratch from Ryan Bates.

Installation

Add this line to your application's Gemfile:

gem 'guachiman'

And then execute:

$ bundle

Or install it yourself as:

$ gem install guachiman

Usage

Run rails g guachiman:install

This will generate a permission.rb file in app/models.

Include Guachiman::Permissible in ApplicationController and implement a current_user method there.

include Guachiman::Permissible

def current_user
  @current_user ||= User.find_by_auth_token(cookies[:auth_token]) if cookies[:auth_token]
end

You can also override these methods to handle failed authorizations for GET, non-AJAX requests:

def not_authorized
  redirect_to root_path, alert: t('flashes.not_authorized')
end

def not_signed_in
  session[:next] = request.url
  redirect_to , alert: t('flashes.please_sign_in')
end

And you can also override this method to handle failed non-GET or AJAX requests:

def render_unauthorized
  render text: "NO", status: :unauthorized
end

That's it, now you can describe your permissions in this way:

class Permission
  include Guachiman::Permissions
  include Guachiman::Params

  attr_reader :current_user, :current_request

  def initialize user, request
    @current_user    = user
    @current_request = request

    if current_user.nil?
      guest
    elsif current_user.admin?
      admin
    else
      member
    end
  end

private

  def guest
    allow :sessions, [:new, :create, :destroy]
    allow :users,    [:new, :create]

    allow_param :user, [:name, :email, :password]
  end

  def member
    guest
    allow :users, [:show, :edit, :update]
  end

  def admin
    allow_all!
  end
end
  • #allow takes a controller params key or array of keys and an array of actions.
  • #allow_param takes a model params key or array of keys and an array of attributes.
  • #allow_all! is a convinience method to allow all controllers, actions and parameteres.

You can also go a bit further in the way you specify your permissions, if you override current_resource:

class OrdersController < ApplicationController
...

private
  def current_resource
    @order ||= params[:id].present? ? Order.find(params[:id]) : Order.new
  end
end

The current_resource is passed to a block that needs to return a truthy object to allow the action.

def guest
  allow :sessions, [:new, :create, :destroy]
  allow :users,    [:new, :create]
  allow :orders,   [:show, :edit, :update] do |order|
    order.accessible_by_token? current_request.cookies['cart_token']
  end

  allow_param :user, [:name, :email, :password]
end

def member
  guest

  allow :users,  [:show, :edit, :update] do |user|
    current_user == user
  end
  allow :orders, [:show, :edit, :update] do |order|
    order.accessible_by_user? user
  end
end

You can also be more specific about the param permissions setting them to be read or write.

def member
  ...

  allow_read_param  :contact, [:name, :phone, :email]
  allow_write_param :contact, [:name, :phone]
end

That can also be useful on the views because you get a current_permission helper that you can use like this:

<%= form_for @contact do |f| %>
  <% current_permission.write_allowed_params[:contact].each do |p| %>
    <%= f.text_field p %>
  <% end %>

  <%= f.submit %>
<% end %>

License

MIT