Devo pular a autorização, com Cancan, de uma ação que instancia um recurso?
-
26-09-2019 - |
Pergunta
Estou escrevendo um aplicativo da web para escolher listas aleatórias de cartões de cartões maiores e completos. Eu tenho um modelo de cartão e um modelo de conjunto de cartões. Ambos os modelos têm um conjunto completo de 7 ações (: Índice, novo,: show etc.). O CardetsController tem uma ação extra para criar conjuntos aleatórios: :random
.
# app/models/card_set.rb
class CardSet < ActiveRecord::Base
belongs_to :creator, :class_name => "User"
has_many :memberships
has_many :cards, :through => :memberships
# app/models/card.rb
class Card < ActiveRecord::Base
belongs_to :creator, :class_name => "User"
has_many :memberships
has_many :card_sets, :through => :memberships
Adicionei o Deviso para autenticação e CanCan para autorizações. Tenho usuários com uma função de 'editor'. Os editores podem criar novos cartões. Usuários convidados (usuários que não fizeram login) podem usar apenas o :index
e :show
ações. Essas autorizações estão funcionando conforme projetado. Os editores podem atualmente usar os dois :random
e a :new
ações sem problemas. Usuários convidados, como esperado, não podem.
# app/controllers/card_sets_controller.rb
class CardSetsController < ApplicationController
before_filter :authenticate_user!, :except => [:show, :index]
load_and_authorize_resource
Eu quero permitir que usuários convidados usem o :random
ação, mas não o :new
ação. Em outras palavras, eles podem ver novos conjuntos aleatórios, mas não salvá -los. O botão "salvar" no :random
A visão da ação está oculta (como projetado) dos usuários convidados. O problema é que a primeira coisa que a :random
A ação faz é criar uma nova instância do modelo de conjunto de cartões para preencher a visualização. Quando Cancan tenta load_and_authorize_resource
Um novo conjunto de cartões, ele lança uma exceção de cancan :: accessdenied. Portanto, a visualização nunca é carregada e o usuário convidado é servido por uma mensagem "Você precisa fazer login ou se inscrever antes de continuar".
# app/controllers/card_sets_controllers.rb
def random
@card_set = CardSet.new( :name => "New Set of 10", :set_type => "Set of 10" )
Eu percebo que posso dizer load_and_authorize_resource
para pular o :random
ação passando :except => :random
para a ligação, mas isso apenas parece "errado" por algum motivo.
Qual é o "certo" maneira de fazer isso? Devo criar o novo conjunto aleatório sem instantar um novo conjunto de cartões? Devo ir em frente e adicionar a exceção?
Atualizar
Não incluí minha aula de habilidade acima. Eu o atualizei para incluir a ação ': aleatória', mas ainda não está funcionando bem.
class Ability
include CanCan::Ability
def initialize( user )
user ||= User.new # User hasn't logged in
if user.admin?
can :manage, :all if user.admin?
else
# All users, including guests:
can :read, [Card, CardSet]
can :random, CardSet
# All users, except guests:
can :create, [Card, CardSet] unless user.role.nil?
can :update, [Card, CardSet] do |c|
c.try( :creator ) == user || user.editor?
end
if user.editor?
can [:create, :update], [Card, CardSet]
end
end
end
end
Solução
Eu encontrei meu problema. Cancan não era o problema! A linha a seguir no meu controlador de conjunto de cartões foi lançar a exceção e redirecionar os usuários do meu convidado (não conectado) para a página de login:
before_filter :authenticate_user!, :except => [:show, :index]
Eu mudei para ler:
before_filter :authenticate_user!, :except => [:show, :index, :random]
E agora o código funciona como pretendido: os usuários dos hóspedes podem visualizar os novos conjuntos aleatórios criados, mas não podem 'salvá -los', a menos que faça login primeiro.
Portanto, meu verdadeiro problema estava com o Devise (ou, na verdade, minha configuração de invenção) e não com o CANCAN.
Outras dicas
Bem, a coisa certa seria usar a classe CANCAN CAPACIDADE para definir as regras de autorização corretas.
em habilidade.rb
def initialize(user)
#everyone
can [:read, :random], [CardSet]
#everyone who is editor
if user.editor?
can [:new, :create], [CardSet]
etc.
O problema é que a primeira coisa: a ação aleatória é criar uma nova instância do modelo de conjunto de cartões para preencher a visualização. Quando o Cancan tenta load_and_authorize_resource um novo conjunto de cartões, ele lança uma exceção de cancan :: accessdenied.
Enquanto a CANCAN autoriza a ação do controlador, a criação de uma nova instância em ação aleatória (ou seja, o CardSet.New) não está no escopo de Cancan. Você provavelmente recebe o erro porque não tem regras definidas no habilidade.rb para ação aleatória. Meu exemplo acima deve resolver seu problema