Receber por email
← Voltar para o catálogo

rails-authorization-cancancan

Authorization and permissions management for Ruby on Rails applications using CanCanCan. Use when: (1) Implementing role-based access control (RBAC), (2) Defining user permissions and abilities, (3) Restricting resource access in controllers, (4) Filtering queries based on user permissions, (5) Hiding/showing UI elements based on authorization, (6) Testing authorization logic, (7) Managing admin v

13estrelas
Atualizado há 6 meses

Ver no GitHub ↗ Licença: MIT

Como adicionar

/plugin marketplace add Shoebtamboli/rails_claude_skills

O comando exato pode variar conforme o repositório. Confira o README no GitHub.

Para o autor da skill

Cole no README do seu repo

Mostra que sua skill está catalogada na Skillteca, gera backlink e tráfego rastreável.

[![Skillteca](https://www.skillteca.com.br/api/badge/rails-authorization-cancancan/svg)](https://www.skillteca.com.br/skills/rails-authorization-cancancan)

Alerta por categoria

Receba novas skills de Design e Frontend toda segunda

1 email curto, só com as skills novas de Design e Frontend. 4 minutos de leitura, sem spam, cancela com 1 clique.

Você confirma o email no primeiro envio. Sem spam. Cancela com 1 clique.

Ler descrição completa

Rails Authorization with CanCanCan

CanCanCan is a popular authorization library for Rails that restricts what resources a given user is allowed to access. It centralizes all permission logic in a single Ability class, keeping authorization rules DRY and maintainable.

Quick Setup

# Add to Gemfile
bundle add cancancan

# Generate Ability class
rails generate cancan:ability

This creates app/models/ability.rb where all authorization rules are defined.

Core Concepts

Defining Abilities

The Ability class centralizes all permission logic:

# app/models/ability.rb
class Ability
  include CanCan::Ability

  def initialize(user)
    # Guest users (not signed in)
    can :read, Post, published: true
    can :read, Comment

    # Signed-in users
    return unless user.present?

    can :read, Post
    can :create, Post
    can :update, Post, user_id: user.id
    can :destroy, Post, user_id: user.id

    can :create, Comment
    can :update, Comment, user_id: user.id
    can :destroy, Comment, user_id: user.id

    # Admin users
    return unless user.admin?

    can :manage, :all  # Can do anything
  end
end

Best Practice: Structure rules hierarchically (guest → user → admin) for clarity.

Actions and Resources

Standard CRUD Actions

:read    # :index and :show
:create  # :new and :create
:update  # :edit and :update
:destroy # :destroy

:manage  # All actions (use carefully!)

Custom Actions

can :publish, Post
can :archive, Post
can :approve, Comment

Multiple Resources

can :read, [Post, Comment, Category]
can :manage, [User, Post], user_id: user.id

Ability Conditions

Hash Conditions

# Simple equality
can :update, Post, user_id: user.id

# Multiple conditions (AND logic)
can :read, Post, published: true, category_id: user.accessible_category_ids

# SQL fragment (use sparingly)
can :read, Post, ["published_at <= ?", Time.zone.now]

Block Conditions

# Complex logic
can :update, Post do |post|
  post.user_id == user.id || user.admin?
end

# With associations
can :read, Post do |post|
  post.published? || post.user_id == user.id
end

# Accessing current user
can :destroy, Comment do |comment|
  comment.user_id == user.id && comment.created_at > 15.minutes.ago
end

Important: Block conditions cannot be used with accessible_by for database queries. Use hash conditions when you need to filter collections.

Combining Conditions

# Multiple can statements are OR'd together
can :read, Post, published: true          # Public posts
can :read, Post, user_id: user.id         # Own posts
# User can read posts that are EITHER published OR owned by them

Controller Integration

Manual Authorization

class PostsController < ApplicationController
  def show
    @post = Post.find(params[:id])
    authorize! :read, @post  # Raises CanCan::AccessDenied if not authorized
  end

  def update
    @post = Post.find(params[:id])
    authorize! :update, @post

    if @post.update(post_params)
      redirect_to @post
    else
      render :edit
    end
  end
end

Automatic Loading and Authorization

class PostsController < ApplicationController
  load_and_authorize_resource

  def index
    # @posts automatically loaded with accessible_by
  end

  def show
    # @post automatically loaded and authorized
  end

  def create
    # @post initialized and authorized
    if @post.save
      redirect_to @post
    else
      render :new
    end
  end

  def update
    # @post loaded and authorized
    if @post.update(post_params)
      redirect_to @post
    else
      render :edit
    end
  end
end

Benefits: Eliminates repetitive authorization code across RESTful actions.

Load and Authorize Options

# Specific actions only
load_and_authorize_resource only: [:show, :edit, :update, :destroy]
load_and_authorize_resource except: [:index]

# Different resource name
load_and_authorize_resource :article

# Custom find method
load_and_authorize_resource find_by: :slug

# Nested resources
class CommentsController < ApplicationController
  load_and_authorize_resource :post
  load_and_authorize_resource :comment, through: :post
end

# Skip loading (only authorize)
authorize_resource

# Skip authorization for specific actions
skip_authorize_resource only: [:index]

Fetching Authorized Records

accessible_by

Retrieve only records the user can access:

# In controller
def index
  @posts = Post.accessible_by(current_ability)
end

# With specific action
@posts = Post.accessible_by(current_ability, :read)
@editable_posts = Post.accessible_by(current_ability, :update)

# Chainable with ActiveRecord
@published_posts = Post.published.accessible_by(current_ability)
@posts = Post.accessible_by(current_ability).where(category_id: params[:category_id])

Performance: Uses SQL conditions from ability rules for efficient database queries.

View Helpers

Conditional UI Elements

# Check single permission
<% if can? :update, @post %>
  <%= link_to 'Edit', edit_post_path(@post) %>
<% end %>

<% if can? :destroy, @post %>
  <%= link_to 'Delete', @post, method: :delete, data: { confirm: 'Are you sure?' } %>
<% end %>

# Negative check
<% if cannot? :update, @post %>
  <p>You cannot edit this post</p>
<% end %>

# Multiple permissions
<% if can?(:update, @post) || can?(:destroy, @post) %>
  <div class="post-actions">
    <%= link_to 'Edit', edit_post_path(@post) if can? :update, @post %>
    <%= link_to 'Delete', @post, method: :delete if can? :destroy, @post %>
  </div>
<% end %>

# Check on class (useful in index views)
<% if can? :create, Post %>
  <%= link_to 'New Post', new_post_path %>
<% end %>

Navigation Menus

<nav>
  <%= link_to 'Posts', posts_path if can? :read, Post %>
  <%= link_to 'New Post', new_post_path if can? :create, Post %>
  <%= link_to 'Admin', admin_path if can? :manage, :all %>
</nav>

Handling Unauthorized Access

Exception Rescue

# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  rescue_from CanCan::AccessDenied do |exception|
    respond_to do |format|
      format.html { redirect_to root_path, alert: exception.message }
      format.json { render json: { error: exception.message }, status: :forbidden }
    end
  end
end

Custom Error Messages

# In Ability class
can :update, Post, user_id: user.id do |post|
  post.user_id == user.id
end

# In controller with custom message
authorize! :update, @post, message: "You can only edit your own posts"

Flash Messages

rescue_from CanCan::AccessDenied do |exception|
  redirect_to root_path, alert: "Access denied: #{exception.message}"
end

Common Patterns

Role-Based Authorization

# app/models/user.rb
class User < ApplicationRecord
  ROLES = %w[guest user moderator admin].freeze

  enum role: { guest: 0, user: 1, moderator: 2, admin: 3 }

  def role?(check_role)
    role.to_sym == check_role.to_sym
  end
end

# app/models/ability.rb
class Ability
  include CanCan::Ability

  def initialize(user)
    user ||= User.new # Guest user

    if user.admin?
      can :manage, :all
    elsif user.moderator?
      can :manage, Post
      can :manage, Comment
      can :read, User
    elsif user.user?
      can :read, :all
      can :create, Post
      can :manage, Post, user_id: user.id
      can :manage, Comment, user_id: user.id
    else
      can :read, Post, published: true
    end
  end
end

Organization/Tenant-Based Authorization

class Ability
  include CanCan::Ability

  def initialize(user)
    return unless user.present?

    # User can manage resources in their organization
    can :manage, Post, organization_id: user.organization_id
    can :manage, Comment, post: { organization_id: user.organization_id }

    # Admin can manage organization settings
Compartilhar X LinkedIn