3

認証にはDevise、承認にはCanCanを使用しています。

目標

ユーザーとスポンサーシップの 2 つのモデルがあります。スポンサーシップは、ユーザーとしてのスポンサーとユーザーとしてのクライアントの間の has_many :through 関係を提供します。

sponsor?特権を持つユーザーが自分のスポンサーシップを管理できるように、CanCan を構成したいと考えていますSponsorship#client_id == user.id。ユーザーはadmin?、スポンサーシップを管理できる権限を持つこともできます。

モデル

class User < ActiveRecord::Base
  has_many :sponsor_links, :class_name => 'Sponsorship', :foreign_key => 'client_id'
  has_many :sponsors, :through => :sponsor_links, :class_name => 'User'

  has_many :client_links, :class_name => 'Sponsorship', :foreign_key => 'sponsor_id'
  has_many :clients, :through => :client_links, :class_name => 'User'

  def has_role?(role)
    ... return true if this user has <role> privileges
  end
end

class Sponsorship
  belongs_to :sponsor, :class_name => 'User'
  belongs_to :client, :class_name => 'User'
end

class Ability
  include CanCan::Ability

  def initialize(user)
    user ||= User.new  # handle guest user (not logged in)
    if user.has_role?(:admin)
      can :manage, :all
    elsif user.has_role?(:sponsor)
      # not sure if the third argument is correct...
      can :manage, Sponsorship, :sponsor => {:user_id => user.id}
    end
  end
end

ルート

スポンサー ユーザーが自分のクライアントを所有しているという事実を反映するために、ネストされたルートを設定しました。

resource :users, :only => [:index]
  resource :sponsorships
end

質問

SponsorshipsController でユーザー リソースとスポンサーシップ リソースを読み込んで承認する正しい方法は何ですか?

私が試したこと

これは、CanCan が簡単に処理できる通常のネストされたリソースに似ています。しかし、関係には非標準の名前 (たとえば、:sponsorships ではなく :sponsor_links) がありload_and_authorize_resource、SponsorshipsController で宣言を構成する方法がわかりません。

私が試した中でうまくいかなかった多くのことの中で;)、これはより単純なバージョンの1つです。(私の能力が適切に設定されていない可能性があることにも注意してください -- 上記を参照してください):

class SponsorshipsController < ApplicationController
  load_and_authorize_resource :sponsor_links, :class_name => "User"
  load_and_authorize_resource :sponsorships, :through => :sponsor_links
  respond_to :json

  # GET /users/:user_id/sponsorships.json
  def index
    respond_to @sponsorships
  end

  # GET /users/:user_id/sponsorships/:id.json
  def show
    respond_to @sponsorship
  end
end

CanCan::AccessDenied エラーをレスキューすることで、次のことがわかります。

  • indexユーザーの場合、:sponsorユーザーの認証は失敗します。
  • indexユーザーの:admin場合、スポンサーシップの認証に失敗します。
  • ではshow、ロールに関係なく、スポンサーシップの認証は失敗します。
4

1 に答える 1

1

部分的な答え

最初の問題は、Abilities 仕様に次のように書かれていました。

...if user.has_role?(:sponsor)
  can :manage, Sponsorship, :sponsor => {:user_id => user.id}
end

しかし、ちょうどあるべきだった

...if user.has_role?(:sponsor)
  can :manage, Sponsorship, :user_id => user.id
end

(覚えておいてください、子供たち、ユニットテストはあなたの友達です!どういうわけか私はそのレッスンを忘れていました。)

コントローラーでは、次のようにも変更しました。

  load_and_authorize_resource :sponsor_links, :class_name => "User"
  load_and_authorize_resource :sponsorships, :through => :sponsor_links

ただに

  load_and_authorize_resource :user
  load_and_authorize_resource :sponsorship

これはほとんど機能します: @user と @sponsorship をセットアップし、それらへのアクセスを承認します。しかし、index 関数は、:user_id が所有するスポンサーシップだけでなく、current_user がアクセスできるすべてのスポンサーシップをロードします。私の修正 - おそらく最適ではありません - からインデックス関数を書き直すことでした

  def index
    respond_with(@user, @sponsorships)
  end

  def index
    @sponsorships = @sponsorships.where(:sponsor_id => @user.id)
    respond_with(@user, @sponsorships)
  end

これらの変更により、すべてが機能します。

誰かがこれをより適切に表現する方法を持っているなら、私は知りたいです。

于 2012-04-11T02:55:02.273 に答える