2010-12-13 33 views
4

Cela fait un certain temps que je me bats là-dedans et j'en suis finalement arrivé au point où il semble que CanCan ne vous autorise pas à autoriser une collection d'enregistrements. Par exemple:CanCan et des ensembles de données

ads_controller.rb

def index 
    @ads = Ad.where("ads.published_at >= ?", 30.days.ago).order("ads.published_at DESC") 
    authorize! :read, @ads 
end 

ability.rb

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

    if user 
    if user.role? :admin # Logged in as admin 
     can :manage, :all 
    else     # Logged in as general user 
     can :read, Ad 
     can :read_own, Ad, :user_id => user.id 
     can :create, Ad 
    end 
    else     # Not logged in (Guest) 
    can :read, Ad 
    end 
end 

Il en résulte le message d'accès non autorisé lorsque vous essayez d'accéder à l'action index.

You are not authorized to access this page. 

Cependant, si vous changez l'appel à l'action authorize d'index pour vérifier la classe annonce plutôt que la collection comme si

def index 
    @ads = Ad.where("ads.published_at >= ?", 30.days.ago) 
    authorize! :read, Ad 
end 

... ça fonctionne très bien.

Toute aide pour expliquer celui-ci serait grandement appréciée.

Merci d'avance.

ps. À l'origine, je recevais des boucles de redirection lorsque j'essayais de résoudre ce problème. Il s'avère qu'il y a un gotchya avec le rescue_from recommandé que vous mettez dans le contrôleur d'application pour vous donner de gentils messages d'erreur. Si votre root_path est défini au même endroit où votre autorisez! l'appel n'est pas vrai (ou échoue), vous obtiendrez une boucle de redirection. Commenter le rescue_from A appris que l'un à la dure.

Répondre

2

CanCan n'est pas conçu pour être utilisé comme ça. Vous pouvez vérifier si un utilisateur dispose d'autorisations sur la classe de modèle (par exemple Ad) ou sur une instance unique (par exemple, @ad).

Je vous suggère d'utiliser simplement accessible_by pour filtrer votre collection:

@ads = Ad.where("ads.published_at >= ?", 30.days.ago).accessible_by(current_ability) 
# @ads will be empty if none are accessible by current user 

raise CanCan::AccessDenied if @ads.empty? # handle however you like 

Une autre approche serait de définir une autorisation personnalisée en fonction des conditions que vous utilisez pour récupérer la collection:

# ability.rb 
can :read_ads_from_past_month, Ad, ["ads.published_at >= ?", 30.days.ago] 

# in your controller 
def index 
    authorize! :read_ads_from_past_month, Ad 
    @ads = Ad.where("ads.published_at >= ?", 30.days.ago) 
end 
+0

Merci bowsersenior. J'essayais quelque chose de similaire à votre deuxième option mais je ne pouvais pas le faire fonctionner - je pense que c'était conceptuel dans l'utilisation de la classe (Ad). Je vais recommencer. – Jason

2

I résolu ce problème splats d'utilisations. Dans cet exemple de code, j'essaie d'autoriser les utilisateurs sur une collection de TimeOffRequests. Ils doivent être autorisés si l'Utilisateur est un administrateur, un responsable ou si la demande de congé leur appartient.

# time_off_requests_controller.rb 
authorize! :read, *@time_off_requests 

# Ability.rb 
can :manage, TimeOffRequest do |*time_off_requests| 
    membership.has_any_role?(:admin, :manager) || 
    time_off_requests.all? { |tor| membership.id == tor.employee_id } 
end 

j'écrit à ce sujet en détail ici si vous êtes intéressé: http://zacstewart.com/2012/01/08/defining-abilities-for-collections-of-records-using-cancan.html