This is a follow-up to another question as I noticed my problem is far more general than described there.
In a Rails app which uses JSONAPI::Resources and CanCanCan, I have a Caption model (belongs_to :video) and a Video model (has_many :captions). To define abilities, I use an ability.rb file which I boiled down to this for testing purposes:
class Ability
include CanCan::Ability
def initialize(person)
guest_actions
user_actions(person)
end
def guest_actions
# Guests can only access those captions which belong to a published video
can :read, Caption, video: { visible: true }
end
def author_actions(person)
# Registered users are only allowed to add captions to their own videos
can %i[create destroy], Caption, video: { creator: { id: person.id } }
end
end
However, this is not working. These specific ability definitions seem not to be ignored, but interpreted in a wrong way:
- Guests can actually access all captions. If I remove the above line in
guest_actions, guests can't access any caption and receive 401 Unauthorized instead. - Users get 403 Forbidden when trying to create a caption unless I remove the condition on the video in
author_actions.
Both CaptionController and VideoController extend ApplicationController, which contains load_and_authorize_resource only: %i[index show create update destroy].
There is one thing I noticed about this specific relationship: When I open the Rails console or run RSpec, there's an unexpected warning:
DEPRECATION WARNING: In CaptionResource you exposed a 'has_one' relationship using the 'belongs_to' class method. We think 'has_one' is more appropriate.
I'm surprised by this because my Caption model contains a video_id field. I temporarily changed the relationship to has_one (in the resource only as well as in the model and the resource), but it didn't make a difference in my tests.
What is wrong with my code? I have a few abilities defined in a similar way, and I just can't figure out what's the problem with Caption.