QA@IT

cancanでのControllerのNewメソッドの動作がうまくいかない

2306 PV

現在Rails3.2とcancan 1.6.8を使って開発をしています。そこでcancanを使って認可の制御をしているのですが、ControllerのNewメソッドにアクセスした際の挙動が期待通りの動きをせずにこまっています。

現在の状況を例に説明させていただきます。
まず現在あるクラスとしては、Userクラスがあり、このクラスは複数のBlogクラスを所持しています。
モデルのソースでいうと下記のようになります。

class Blog < ActiveRecord::Base
  attr_accessible :content, :title

  belongs_to :user
class User < ActiveRecord::Base
  has_many :blogs

ここでBlogクラスを所有しているUserだけが編集やアップデート等を行えるように認可をしたいです。
そこで、cancanを使いAbilityクラスにこのように書きました。

class Ability
  include CanCan::Ability

  def initialize(user)
    user ||= User.new # guest user (not logged in)

    can :read, Blog
    can :manage, Blog do |blog|
      user.id == blog.user_id
    end
  end
end

これでblogを新規作成(POSTの様な)する以外のメソッドはすべて正常にうごくようになりました。

しかしコントローラのnewメソッドを呼ばれると期待通りに動作しません。
なぜなら

    can :manage, Blog do |blog|
      p blog.user_id  #=> nil
      user.id == blog.user_id
    end

となってしまい、結果falseが返ってしまうからです。

元来、Controllerのnewメソッドでは

  def new
    @blog = User.find(params[:user_id]).blogs.build

とかいてあるので、user_idはセットされるのですが、どうもAbilityクラスのinitializeには反映されないようです。

思いつく解決策はいくつかあり

  • def initialize(user)でいう所のuserインスタンスに、adminみたいな特異メソッドをコントローラ内で追加して渡す。
  • def initialize(user)の中でどうにかしてparams[:user_id]を呼び出して、所有者であるuserのインスタンスを作成する
  • createだけはcancanでの制御をあきらめ、controller側で認可する処理を独自に書く。

があるのですが、1、2個目はどうやってやればいいか方法がわからず、3個目はスマートではありません。

何かいい方法はないでしょうか?ご回答お待ちしております。

  • 直接の解答ではないのですが、「cancanでのアクセス制御」と仰っているのは、`load_and_authorize_resource`filterのことでしょうか?そうであれば、このフィルタはアクションに入る前にかかるので、`blog.user_id => nil`は妥当だと思います。`manage`を使わずに、アクション毎に権限を設定してみてはいかがでしょう。 -
  • お返事ありがとうございます。今回は下記の方法でいきましたが、次にやるときはアクション毎に権限を設定する方法を調べてみようと思います。 -

回答

自己解決しました。

方法としては

  • userインスタンスに、adminみたいな特異メソッドをコントローラ内で追加して渡す。

でいきました。

実際のソースはこうなりました。

class BlogsController < ApplicationController
  def current_ability
    if current_user
      if current_user.id == @user.id
        def current_user.admin?
          true
        end
      end
    end
    @current_ability ||= Ability.new(current_user)
  end
end

これで問題は解決しましたが、もう少しスマートな方法、書き方があればご教授いただけると幸いです。
2、3日まって特に回答がないようであれば、この回答でAcceptします。

編集 履歴 (1)
ウォッチ

この質問への回答やコメントをメールでお知らせします。