Search code examples
ruby-on-rails-4devisecancanrolifycancancan

How do I restrict a specific scope just to users with a specific role?


So I have a Post model. My posts can be either published or unpublished. I am using Rolify, CanCanCan and Devise.

What I want to happen is my :admin users, should be able to view the Post#Show action of all Posts, but my :member or guest users (i.e. non-logged in) should only ever be able to see Post.published posts.

My ability.rb looks like this:

   if user.has_role? :admin
        can :manage, :all
    #Member
    elsif user.has_role? :member
        can :read, :all
        can :create, Post
        can :status, Post
        can :update, Post do |post|
            post.try(:user) == user
        end
    #Guest
    else
        can :read, :all
        can :create, Post
        can :status, Post
    end

I tried doing this, for both :member and Guest, but it gave me an endless redirect loop on my Post#Index page - which is my root_path:

    can :read, Post.published

Where Post.published returns an array of all the posts with publication_status = "published".

This is how I declared that on my Post.rb:

enum publication_status: [ :unpublished, :published ]

How do I achieve this?


Solution

  • I figured this out.

    In my Post#Show, I simply did this:

      def show
        if current_user and current_user.has_role? :admin or :editor
          @post = Post.find(params[:id])
        else
          @post = Post.published.find(params[:id])
        end
      end
    

    That works like a charm.

    If someone has a more elegant way, say inside the ability.rb I would love to know.

    I tried many permutations using blocks and all this jazz in the ability.rb but nothing worked.

    I also had to remove the default added set_post method in my controller, because cancancan already loads_and_authorize the resource.