I'm using the CanCanCan gem (v.12.0) for basic authorization. My User
model does not use the concept of roles. All I am trying to do is ensure a user is logged in before they can do anything with my main model, Topic
.
I believe I've written my Ability
class correctly. My TopicsController
has an index
method that, unsurprisingly, displays all of a user's topics.
The issue is that the first if
in index.html.erb
is succeeding when there is no logged in user - that doesn't make sense to me. Am I missing something obvious?
# ability.rb
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # default user
# as long as a user is logged in and owns the topic it can do anything it likes with it
can [:manage], Topic do |topic|
!user.new_record? && topic.try(:user) == user
end
end
--
# topics_controller.rb
class TopicsController < ApplicationController
load_and_authorize_resource # CanCanCan gem
def index
end
...
end
--
# index.html.erb
<% if can? :read, Topic %>
<% @topics.each do |topic| %>
<%# Display all the topics %>
<% else %>
<%# Handle the case where the user can't read Topics %>
<% end %>
You need to authorize the user for a particular instance:
<%= if can? :read, topic %>
When you attempt to authorize a user for an entire class, as you do above, CanCanCan ignores any conditions defined in the Ability because it can't determine a user
relation for the entire Topic
model; it can only do so for a single topic
instance. Unfortunately, AFAIK there's no way to authorize for a relation, so you'll have to either authorize for each instance in the relation.
Alternatively, in version 1.4, load_and_authorize_resource
in the controller should initialize @topics
based on the current_user
only if the Ability is defined without blocks. However, its nearly trivial to do this "manually":
@topics = current_user.topics