Question

I'm using devise with my rails 4 app to handle the authentication, and no problems there. However, I want to make sure a logged in user can only view / edit (via the show and update actions) the items that his user owns (that are linked to his user_id).

I think I could hack something to make this all work by checking the current_user.id, but many users in Stackoverflow and other places say to use cancan-- however it appears cancan is dead and gone, and there's a replacement called cancancan, which may be ok, but I don't know.

Is there a standard way to do this in Rails 4, or is the best route to still use a third party gem like cancancan? Is there a better gem?

Was it helpful?

Solution

I've been using Pundit instead of Cancan for the last few projects I've done. It is lightweight, flexible and easy to use. Here's the link: https://github.com/elabs/pundit

In regards to your question, you will create policies for each model. For each action you define a method. It's super simple and explained on the link I've attached. Here as an example you have update in your model (models/post.rb):

def update
  @post = Post.find(params[:id])
  authorize @post
   if @post.update(post_params)
     redirect_to @post
   else
     render :edit
   end
end

Call authorize to define permissions.

In your policies/post.rb:

class PostPolicy < Struct.new(:user, :post)
  def update?
   user.admin? or not post.published?
  end
end

That returns true or false. In your case if you want to check if the user is a owner you can place the following if statement:

 if user.admin? || user.owner_of?(post)

You get the idea. You can also define scopes, etc.

OTHER TIPS

I don't think there's a standard, per se, but rather it's based on what you need. For Rails 4, cancancan brings a lot to the table and is built off of a gem that has been used regularly by the Rails community.

The only other alternatives I'm familiar with are protector and pundit - maybe check those out.

However, if cancancan and protector don't fit your needs, you could always roll your own authorization solution, but to me, why reinvent the wheel if cancancan will satisfy your needs.

I'd recommend Action Access, it's much simpler and straightforward. It boils down to this:

class ArticlesController < ApplicationController
  let :user, :all
  let :guest, [:show, :index]

  # ...

  def edit
    not_authorized! unless @article.user == current_user

    # ...
  end

  # ...

end

First of this will automatically lock the controller, allowing only users to access every action, guests can only show or index articles. Then not_authorized! will reject and redirect with an alert any user other than the owner of the article.

What's good about this is that it makes controllers to be self contained, everything related to the controller is within the controller. This also makes it very modular and avoids leaving forgotten trash anywhere else when you refactor.

It's completely independent of the authentication system (so no problem with Devise) but it bundles a set of handy model additions that allow to do things like:

<% if current_user.can? :edit, :article %>
  <%= link_to 'Edit article', edit_article_path(@article) %>
<% end %>

Here :article refers to ArticlesController, so the link will only be displayed if the current user is authorized to access the edit action in ArticlesController. It supports namespaces too.

You can lock controllers by default, customize the redirection path and the alert message, etc. Checkout the documentation for more.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top