Note that there are some explanatory texts on larger screens.

plurals
  1. POShould I skip authorization, with CanCan, of an action that instantiates a resource?
    primarykey
    data
    text
    <p>I am writing a web app to pick random lists of cards from larger, complete sets of cards. I have a Card model and a CardSet model. Both models have a full RESTful set of 7 actions (:index, :new, :show, etc). The CardSetsController has an extra action for creating random sets: <code>:random</code>.</p> <pre><code># app/models/card_set.rb class CardSet &lt; ActiveRecord::Base belongs_to :creator, :class_name =&gt; "User" has_many :memberships has_many :cards, :through =&gt; :memberships # app/models/card.rb class Card &lt; ActiveRecord::Base belongs_to :creator, :class_name =&gt; "User" has_many :memberships has_many :card_sets, :through =&gt; :memberships </code></pre> <p>I have added Devise for authentication and CanCan for authorizations. I have users with an 'editor' role. Editors are allowed to create new CardSets. Guest users (Users who have not logged in) can only use the <code>:index</code> and <code>:show</code> actions. These authorizations are working as designed. Editors can currently use both the <code>:random</code> and the <code>:new</code> actions without any problems. Guest users, as expected, cannot.</p> <pre><code># app/controllers/card_sets_controller.rb class CardSetsController &lt; ApplicationController before_filter :authenticate_user!, :except =&gt; [:show, :index] load_and_authorize_resource </code></pre> <p>I want to allow guest users to use the <code>:random</code> action, but not the <code>:new</code> action. In other words, they can see new random sets, but not save them. The "Save" button on the <code>:random</code> action's view is hidden (as designed) from the guest users. The problem is, the first thing the <code>:random</code> action does is build a new instance of the CardSet model to fill out the view. When cancan tries to <code>load_and_authorize_resource</code> a new CardSet, it throws a CanCan::AccessDenied exception. Therefore, the view never loads and the guest user is served a "You need to sign in or sign up before continuing" message.</p> <pre><code># app/controllers/card_sets_controllers.rb def random @card_set = CardSet.new( :name =&gt; "New Set of 10", :set_type =&gt; "Set of 10" ) </code></pre> <p>I realize that I can tell <code>load_and_authorize_resource</code> to skip the <code>:random</code> action by passing <code>:except =&gt; :random</code> to the call, but that just feels <strong><em>"wrong"</em></strong> for some reason.</p> <p>What's the <strong><em>"right"</em></strong> way to do this? Should I create the new random set without instantiating a new CardSet? Should I go ahead and add the exception?</p> <h2>Update</h2> <p>I didn't include my Ability class above. I've updated it to include the ':random' action, but it still isn't working quite right.</p> <pre><code>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 </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload