Sirius
Sirius

Reputation: 77

Cancancan - define :create with association

I have the following situation in my Ruby on Rails application:

I have a blog with posts. Each post can have 1 to n authors, and each post is associated with several comments:

class Post < ApplicationRecord
    has_many :comments, dependent: :destroy
    has_many :authors

class Comment < ApplicationRecord
    belongs_to :post

I am using CanCanCan for authorization. Currently everyone is able to create comments on a post. I want to change this by introducing a lock_comments-attribute on my post model, and changing the authorization accordingly, so that it functions as follows:

Basically the authors should be able to disable comments on their articles, but they should still be able to write comments on their own articles, even if the comments are disabled for others.

I am a bit at a loss here and could not find anything in the documentation. How do I have to define my Ability for this to work?

Upvotes: 1

Views: 232

Answers (2)

Pascal Lindelauf
Pascal Lindelauf

Reputation: 4870

We've also been struggling with how to easily configure abilities like this and found the solution in using the extra arguments that you can use with with the can and can? functions. That way you can pass in the parent object on which you can perform the necessary checks.

Then the solution for you would become something like this when checking the authotization:

can? :create, Comment.new, @post

and the ability configuration then looks something like this:

can :create, Comment do |comment, post|
  !post.lock_comments || user.in?(post.authors)
end

Upvotes: 0

SteveTurczyn
SteveTurczyn

Reputation: 36870

I don't think you can do this in Ability because at the time you're trying to access create you don't know what the parent post is. You COULD do this if create_comment was a PostsController method...

can :create_comment, Post do |post|
  !post.lock_comments || user.in?(post.authors)
end

But if it's create in a CommentsController you'd need to do this with a before_action

class CommentsController < ApplicationController

  before_action :create_allowed, only: [:create]

  private

  def create_allowed
    post = Post.find(params[:comment][:post_id])
    return if post && (!post.lock_comments || current_user.in?(post.authors))
    flash[:error] = 'You are not allowed to do this'
    redirect_to root_path
  end
end

Upvotes: 1

Related Questions