Autorização com pattern e PORO, pundit no Rails
Neste post, vou explicar como implementar autorização em uma aplicação Ruby on Rails usando a gem Pundit. Abordaremos desde a instalação e configuração até a criação de testes para controllers, políticas e views. Também discutiremos a diferença entre autenticação e autorização e o padrão de design que o Pundit segue.
O que é Pundit?
Pundit é uma gem popular para gerenciar autorizações em aplicações Rails. Ela fornece um conjunto de helpers para definir políticas de acesso, facilitando a gestão de permissões de forma clara e modular.
Autenticação vs. Autorização
- Autenticação é o processo de verificar a identidade de um usuário (ex: login).
- Autorização é o processo de determinar se um usuário autenticado tem permissão para realizar uma ação específica.
Instalando o Pundit
-
Adicione a gem ao seu Gemfile:
gem 'pundit'
-
Execute o bundle para instalar a gem:
bundle install
-
Gere a instalação do Pundit:
rails g pundit:install
Configurando Pundit
-
Inclua o Pundit nos seus controllers:
# app/controllers/application_controller.rb include Pundit
-
Crie uma política:
rails g pundit:policy post
Criando Políticas
- Defina regras na classe de política:
# app/policies/post_policy.rb class PostPolicy attr_reader :user, :post def initialize(user, post) @user = user @post = post end def update? user.admin? || post.user == user end end
Usando Pundit nos Controllers
- Autorize ações no controller:
# app/controllers/posts_controller.rb def update @post = Post.find(params[:id]) authorize @post # Resto do código de atualização end
Testando com RSpec
Testando Políticas
- Crie um teste para a política:
# spec/policies/post_policy_spec.rb describe PostPolicy do subject { described_class } let(:user) { User.create } let(:post) { Post.create(user: user) } permissions :update? do it "permite a atualização se o usuário é o autor do post" do expect(subject).to permit(user, post) end it "não permite a atualização se o usuário não é o autor do post" do other_user = User.create expect(subject).not_to permit(other_user, post) end end end
Testando Controllers
- Teste a autorização no controller:
# spec/controllers/posts_controller_spec.rb describe PostsController do describe "PUT #update" do let(:user) { User.create } let(:post) { Post.create(user: user) } context "quando o usuário está autorizado" do before { sign_in user } it "atualiza o post" do put :update, params: { id: post.id, post: { title: "Novo Título" } } expect(response).to be_successful end end context "quando o usuário não está autorizado" do it "não atualiza o post e redireciona" do put :update, params: { id: post.id, post: { title: "Novo Título" } } expect(response).to redirect_to(root_path) end end end end
Testando Views
-
Teste a visibilidade dos elementos com base na política:
# spec/views/posts/show.html.erb_spec.rb it "mostra o link de edição para usuários autorizados" do assign(:post, post) enable_pundit(view, user) render expect(rendered).to include('Editar') end
-
Use os helpers do Pundit nas views:
<% if policy(@post).update? %> <%= link_to 'Editar', edit_post_path(@post) %> <% end %>
Rails API
Passo 1: Adicionar a Gem Pundit
-
Adicione a gem Pundit ao seu
Gemfile
:gem 'pundit'
-
Execute o comando para instalar a gem:
bundle install
Passo 2: Configurar o Pundit
-
Gere o instalador do Pundit:
rails g pundit:install
-
Isso criará um arquivo de política de aplicação em
app/policies/application_policy.rb
. -
Inclua o Pundit no
ApplicationController
:# app/controllers/application_controller.rb include Pundit
Passo 3: Criar Políticas
-
Crie uma política para um modelo, por exemplo,
Post
:rails g pundit:policy post
-
Defina as regras na política:
# app/policies/post_policy.rb class PostPolicy < ApplicationPolicy def update? user.admin? || record.user == user end end
Passo 4: Usar Pundit nos Controllers
- Autorize as ações no controller da API:
# app/controllers/api/v1/posts_controller.rb class Api::V1::PostsController < ApplicationController def update post = Post.find(params[:id]) authorize post # Código de atualização end end
Passo 5: Testar com RSpec
Testando Políticas
- Crie um teste para a política:
# spec/policies/post_policy_spec.rb RSpec.describe PostPolicy, type: :policy do let(:user) { create(:user) } let(:post) { create(:post, user: user) } subject { described_class } permissions :update? do it "permite a atualização para o autor do post" do expect(subject).to permit(user, post) end it "não permite a atualização para outros usuários" do other_user = create(:user) expect(subject).not_to permit(other_user, post) end end end
Testando Controllers
- Teste a autorização no controller da API:
# spec/controllers/api/v1/posts_controller_spec.rb RSpec.describe Api::V1::PostsController, type: :controller do describe 'PUT #update' do let(:user) { create(:user) } let(:post) { create(:post, user: user) } before do request.headers.merge!(user.create_new_auth_token) end it 'atualiza o post para usuários autorizados' do put :update, params: { id: post.id, post: { title: 'Novo Título' } } expect(response).to have_http_status(:success) end it 'não permite a atualização para usuários não autorizados' do other_user = create(:user) request.headers.merge!(other_user.create_new_auth_token) put :update, params: { id: post.id, post: { title: 'Novo Título' } } expect(response).to have_http_status(:forbidden) end end end
Com o Pundit, você pode gerenciar autorizações de forma eficiente e modular em sua aplicação Rails. Lembre-se de que a autorização é um aspecto crítico da segurança da aplicação e deve ser tratada com cuidado.
Ao escolher uma gem para gerenciar autorizações em uma aplicação Ruby on Rails, as duas opções mais populares são Pundit e CanCanCan. Cada uma tem suas características e melhor adequação dependendo do projeto. Vamos explorar as diferenças entre elas e quando você pode preferir uma sobre a outra.
Pundit
Características:
- Pundit utiliza uma abordagem minimalista e orientada a objetos.
- As autorizações são definidas em classes de política separadas, uma para cada modelo.
- Pundit não impõe uma estrutura específica, dando mais liberdade ao desenvolvedor.
- É mais adequado para aplicações onde as regras de autorização são complexas e variadas.
Quando usar:
- Se você prefere uma abordagem mais explícita e orientada a objetos.
- Quando as regras de autorização são complexas e específicas para cada recurso.
- Se você deseja manter as regras de autorização desacopladas do modelo.
CanCanCan
Características:
- CanCanCan é uma continuação do CanCan, originalmente desenvolvido por Ryan Bates.
- Centraliza as regras de autorização em uma única classe
Ability
. - Usa uma DSL (Domain Specific Language) para definir habilidades, o que pode tornar o código mais legível.
- É mais adequado para aplicações com regras de autorização mais simples e diretas.
Quando usar:
- Se você prefere ter todas as regras de autorização em um único local.
- Quando as regras de autorização são relativamente simples e não variam muito entre os modelos.
- Se você deseja uma configuração rápida e fácil com menos personalização.
Outras Gems
Outras gems, como Rolify e Devise, também são frequentemente usadas em conjunto com Pundit e CanCanCan, mas para propósitos ligeiramente diferentes. Rolify é usada para gerenciar roles (papéis) de usuários, enquanto Devise é mais focada na autenticação.
- Pundit é ideal para aplicações que exigem regras de autorização complexas e personalizadas, com uma abordagem mais orientada a objetos.
- CanCanCan é mais adequado para aplicações que podem centralizar suas regras de autorização e que têm requisitos de autorização mais simples e diretos.
Cada projeto tem suas necessidades específicas, então a escolha entre Pundit e CanCanCan (ou outra gem) deve ser baseada nas características do projeto e nas preferências da equipe de desenvolvimento.