Я пытаюсь проверить аутентификацию пользователя для модели Farm
, в данном случае для роли :user
, которая имеет доступ чтение ко всем фермам при входе в систему (как и гостевой пользователь, также известный как аноним) .
# /models/ability.rb
class Ability
include CanCan::Ability
def initialize(user)
# Create guest user aka. anonymous (not logged in) when user is nil.
user ||= User.new
if user.has_role? :admin
can :manage, :all
else # guest user aka. anonymous
can :read, :all
# logged in user
if user.has_role? :user
can :create, Farm
can :manage, Farm, :user_id => user.id
end
end
end
end
...
# /controllers/api/v1/farms_controller.rb
class Api::V1::FarmsController < ActionController::Base
load_and_authorize_resource
rescue_from CanCan::AccessDenied do |exception|
redirect_to farms_path, alert: exception.message
end
respond_to :json
def index
# Next line might be redundant refering to the CanCan wiki. See below..
@farms = Farm.accessible_by(current_ability, :read)
respond_with(@farms)
end
end
...
# /spec/api/v1/farm_spec.rb
require "spec_helper"
describe "/api/v1/farms" do
let(:user) { create(:user) } # lets call this user1 in the discussion
let(:token) { user.authentication_token }
before do
user.add_role :user
create(:farm, user: user, name: "Testfarm")
create(:farm, name: "Access denied")
@ability = Ability.new(user)
end
context "farms viewable by this logged-in user" do
let(:url) { "/api/v1/farms" }
it "json" do
get "#{url}.json"
farms_json = Farm.accessible_by(@ability, :read).to_json
assert last_response.ok?
last_response.body.should eql(farms_json)
last_response.status.should eql(200)
farms = JSON.parse(last_response.body)
farms.any? do |farm|
farm["name"] == "Testfarm"
end.should be_true
farms.any? do |farm|
farm["name"] == "Access denied"
end.should be_true
end
end
end
Эта проблема
Когда я просматриваю farms_json
, я вижу, что он содержит только Testfarm
. Когда я просматриваю last_response
, я вижу, что он содержит оба Testfarm
и Access denied
. Это странно, так как я использую один и тот же метод accessible_by
и в спецификации, и в действии index
. Настройка, которую я использую, описана в вики драгоценного камня CanCan под названием Извлечение записей.
Бесполезный обходной путь
Когда я добавляю пользователя user
в ферму Access denied
, например...
create(:farm, user: user, name: "Access denied")
... тогда тест проходит успешно.
Вопросы
- Почему ферма «Отказано в доступе» не возвращается, хотя ее может прочитать любой пользователь (включая гостевых пользователей)?
- Действительно ли
get "#{url}.json"
учитывает статус пользователя? Это все сделаноload_and_authorize_resource
вFarmsController
? - В вики упоминается, что
@farms = Farm.accessible_by(current_ability, :read)
можно опустить, поскольку "это делается автоматическиload_resource
для индексного действия». Это применимо к моей ситуации?
Эксперименты
Я создал еще одного пользователя «user2» и еще одну ферму «Моя маленькая ферма». Я связал их друг с другом. Таким образом, база данных в примере содержит всего три фермы:
- Ферма "Testfarm", связанная с user1
- Ферма «Отказано в доступе», не связанная ни с одним пользователем
- Ферма «Моя маленькая ферма», связанная с пользователем2.
Когда я запускаю Farm.accessible_by(Ability.new(user1), :read)
, я все равно получаю только "Testfarm".