我正在尝试测试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 gem的wiki中描述,名为Fetching Records。
当我将用户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
,因为“这是由Farm.accessible_by(Ability.new(user1), :read)
为索引操作自动完成的”。这适用于我的情况吗?我创建了另一个用户“user2”和另一个农场“我的小农场”。我将它们相互联系起来。这样,示例中的数据库共包含三个服务器场:
当我跑{{1}}时,我仍然只收到“Testfarm”。
答案 0 :(得分:0)
我的问题的答案包括多个部分。我希望这可以为处理类似配置的其他人澄清设置。
首先请注意order of ability rules does matter as described in Ability Precedence。在意识到这一事实后,我想出了一套更新的能力规则。
# /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
# logged in user
if user.has_role? :user
can :manage, Farm, :user_id => user.id
can :create, Farm
end
# guest user aka. anonymous
can :read, :all
end
end
end
在索引操作中保持简单。 load_and_authorize_resource
是你的朋友。
# /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
respond_with(@farms)
end
end
当您从场控制器请求数据时,不要忘记传递令牌。
# # /spec/api/v1/farm_spec.rb
get "#{url}.json", auth_token: :token
必须在User模型中添加令牌,如下所示。
# app/models/user.rb
class User < ActiveRecord::Base
before_save :ensure_authentication_token
可以在Devise的初始化程序中配置方法的名称。
# config/initializers/devise.rb
config.token_authentication_key = :auth_token