0
votes

I have an action in my app called "like" that is an action inside the book controller, ie, my Devise users can "like" a book which adds it to their profile.

In CanCan, I want users with role=="author" to be able to create, update, delete their own books, which works fine. The problem is that I want any user to be able to "like" any book. When I add that ability, I am letting users "manage" all books - how do I restrict it so all users can Like/Unlike books, but author users can manage books that they own?

Any tips are appreciated!

ability.rb

def initialize(user)
    # Guest User 
    can :read, :all
    unless user 
      can :read, :all
    else
      # All registered users
      can :read, :all  
      can :manage, Like
      can :manage, Book 
      # Authors 
      if user.role == 'author'
        can :read, :all
        can :create, Recommendation
        can :manage, Recommendation do |r|
            user && r.user == user
        end
        can :create, Book
        can :manage, Book do |b|
            user && b.user == user
            end 
      # Admins
      elsif user.role == 'admin'
        can :manage, :all
      end
    end 
  end  

likes controller

before_action :set_project
load_and_authorize_resource

def create
    if Like.create(liked: @book, user: current_user)
      redirect_to @book, notice: 'Book has been favorited'
    else
      redirect_to @book, alert: 'Something went wrong'
    end
  end
  def destroy
    Like.where(liked_id: @book.id, user_id: current_user.id).first.destroy
    redirect_to @book, notice: 'Book is no longer in favorites'
  end

  private

  def set_project
    @book = Book.find(params[:book_id] || params[:id])
  end
  def like_params
      params.require(:like).permit(:user_id, :book_id)
  end
end

book show

<% unless current_user.liked_books.exists?(id: @book.id) %>
<%= link_to 'Add to likes', like_path, method: :post %>
<% else %>
<%= link_to 'Remove from likes', like_path(@book), method: :delete %>
<% end %>
1

1 Answers

1
votes

There is a violation of CRUD principle. First of all remove like action from book_controller.rb. It's a like, not a book. This action should be create in like_controller.rb. The relation should be like this:

class Book < ActiveRecord::Base
  has_many :likes, dependent: :destroy
end

class Like < ActiveRecord::Base
  belongs_to :book
  belongs_to :user
end

Now, to add like to book, just create it, linked with user and book.

Like.new(book_id: params[:book_id], user_id: params[:user_id])

List likes:

Book.find(id).likes # list all likes for book
User.find(id).likes # list all likes made by user

Permissions should be like this:

#Registered user
can :create, Like
can :destroy, Like.where(user_id: user.id)