0

Farmモデルのユーザー認証をテストしようとしています。この場合は、ログイン時にすべてのファームへの読み取り:userアクセス権を持つロールです (ゲスト ユーザー aka. anonymous もそうです)。

# /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含まれていることがわかります。を調べると、と の両方含まれていることがわかります。仕様とアクションの両方で同じメソッドを使用しているため、これは奇妙です。私が使用するセットアップは、 Fetching Recordsというタイトルの CanCan gem の wiki で説明されています。Testfarmlast_responseTestfarm Access deniedaccessible_byindex

役に立たない回避策

userファームにユーザーを追加するAccess deniedと...

create(:farm, user: user, name: "Access denied")

...その後、テストは成功します。

質問

  1. すべてのユーザー (ゲスト ユーザーを含む) が読み取ることができるにもかかわらず、「アクセスが拒否されました」ファームが返されないのはなぜですか?
  2. get "#{url}.json"実際にユーザーのステータスを考慮していますか? これはすべて によって行われload_and_authorize_resourceますFarmsControllerか?
  3. ウィキは、「これはindex アクション@farms = Farm.accessible_by(current_ability, :read)によって自動的に行われる」ため、省略できると述べています。load_resourceこれは私の状況に当てはまりますか?

実験

別のユーザー「user2」と別のファーム「My little farm」を作成しました。それらを相互にリンクしました。このようにして、例のデータベースには 3 つのファームがすべて含まれています。

  • user1 に関連付けられたファーム「Testfarm」
  • ユーザーに関連付けられていないファーム「アクセスが拒否されました」
  • user2 に関連付けられたファーム「My little farm」。

実行するFarm.accessible_by(Ability.new(user1), :read)と、まだ「Testfarm」しか表示されません。

4

1 に答える 1

0

私の質問に対する答えは、複数の部分で構成されています。これにより、同様の構成を扱う他のすべての人にセットアップが明確になることを願っています.

1.能力優先

まず第一に、アビリティの優先順位で説明されているように、アビリティ ルールの順序が重要であることに注意してください。この事実に気づいた後、私は一連の能力ルールを更新することにしました。

# /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

2.FarmsContoller

index アクションでは単純にしてください。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

3. 認証トークンでリクエストを取得

ファーム コントローラーからデータを要求するときにトークンを渡すことを忘れないでください。

# # /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
于 2013-01-18T17:33:45.997 に答える