0

I'm trying to use CanCan to restrict comments visibility on my site. In my Comment model is defined an enum:

  enum access_right: {nobody: 0, somebody: 1, everyone: 2}

Here is an extract of my current ability.rb file:

class Ability
    include CanCan::Ability

    def initialize(user, session={})
        user ||= User.new # guest user (not logged in)
        # Comments
        can [:create, :read, :update, :destroy], Comment, person_id: person.id
        can [:read], Comment, access_right: [:everyone, Comment.access_rights[:everyone]]
        ...
    end
end

At first I was just using:

can [:read], Comment, access_right: 2

But even though this works when fetching records:

Comment.accessible_by(...)

it doesn't work when checking permission against an object like this:

can? :read, @a_comment

Because then @a_comment.access_right == "everyone" (and not 2)

So I looked online and found this : CanCanCommunity issue 102. The proposed solution didn't work for me, as using "everyone" directly like this:

can [:read], Comment, access_right: ["everyone", Comment.access_rights[:everyone]]

would give incorrect results. The sql query behind it when fetching records would look like this:

SELECT `comments`.* FROM `comments`  WHERE `comments`.`access_right` IN (0,2))

"everyone" seems to be casted to an integer (0) in this case.

I then found a workaround, my current ability file, thanks to symbols (:everyone). But then things won't work anymore when using can? :read, @a_comment (the sql is correct when using accessible_by)

Does anyone know how to correct this problem?

How to define abilities based on enums which can be both verified while fetching records and with can?

Thank you!

EDIT: It may be related to this CanCanCommunity issue 65 but I can't make it work.

1 Answers1

0

It can be achieved with CanCan block syntax:

# Please note this is an Array of Strings, not Symbols
allowed_access_rights = %w[everyone] 

can :read, Comment, ["access_right IN (?)", Comment.access_rights.values_at(allowed_access_rights)] do |comment|
  comment.access_right.in? allowed_access_rights
end

Although it's tempting to define scope on model and pass it here instead of using raw SQL condition, as in example:

can :read, Comment, Comment.with_access_right(allowed_access_rights) do |comment| … end

it is actually a poor idea because it is far less flexible, see CanCan docs.

skalee
  • 12,331
  • 6
  • 55
  • 57