2009-05-23 3 views
1

私はRuby on Railsを使用してブログアプリケーションを作成しています。私のPostsControllerには、ログインしたユーザーが自分の投稿だけを編集または削除できるようにするコードが含まれています。Rails:ユーザーのなりすましのチェックDRY

私は表示するフラッシュメッセージのための単一の引数でプライベートメソッドに出て、このコードを因数分解しようとしたが、私はこれをしなかったし、別の著者の記事を編集して、それをテストしたとき、私はActionController::DoubleRenderErrorだ - 「のみレンダリングまたはリダイレクトすることができます1回のアクションにつき1回。

これらのチェックはどうすればいいですか?DRY?明白な方法は、beforeフィルタを使用することですが、destroyメソッドは異なるフラッシュを表示する必要があります。ここで

は、関連するコントローラのコードです:

before_filter :find_post_by_slug!, :only => [:edit, :show] 

def edit 

    # FIXME Refactor this into a separate method 
    if @post.user != current_user 
    flash[:notice] = "You cannot edit another author’s posts." 
    redirect_to root_path and return 
    end 
    ... 
end 

def update 
    @post = Post.find(params[:id]) 

    # FIXME Refactor this into a separate method 
    if @post.user != current_user 
    flash[:notice] = "You cannot edit another author’s posts." 
    redirect_to root_path and return 
    end 
    ... 
end 

def destroy 
    @post = Post.find_by_slug(params[:slug]) 

    # FIXME Refactor this into a separate method 
    if @post.user != current_user 
    flash[:notice] = "You cannot delete another author’s posts." 
    redirect_to root_path and return 
    end 
    ... 
end 

private 
def find_post_by_slug! 
    slug = params[:slug] 
    @post = Post.find_by_slug(slug) if slug 
    raise ActiveRecord::RecordNotFound if @post.nil? 
end 

答えて

2

フィルタ前のアプローチはまだOKです。コントローラーのaction_nameメソッドを使用して、要求されたアクションにアクセスできます。

before_filter :check_authorization 

... 

protected 

def check_authorization 
    @post = Post.find_by_slug(params[:slug]) 
    if @post.user != current_user 
    flash[:notice] = (action_name == "destroy") ? 
     "You cannot delete another author’s posts." : 
     "You cannot edit another author’s posts." 
    redirect_to root_path and return false 
    end 
end 

真ん中にその三元演算子があります。 :)自然にあなたはあなたが好きなロジックを何でもすることができます。

好きなようにメソッドを使用して、失敗した場合に明示的に返すことで2重レンダリングを回避することもできます。ここでの鍵は、二重レンダリングしないように戻ることです。

def destroy 
    @post = Post.find_by_slug(params[:slug]) 
    return unless authorized_to('delete') 
    ... 
end 

protected 

def authorized_to(mess_with) 
    if @post.user != current_user 
    flash[:notice] = "You cannot #{mess_with} another author’s posts." 
    redirect_to root_path and return false 
    end 
    return true 
end 

あなたはこのように(悪い承認を扱う、承認)の挙動の異なる部分を分割して(私の意見では)より多くのそれを簡素化することができます:私は、すべての負荷を軽減することを好む、個人的に

def destroy 
    @post = Post.find_by_slug(params[:slug]) 
    punt("You cannot mess with another author's post") and return unless author_of(@post) 
    ... 
end 

protected 

def author_of(post) 
    post.user == current_user 
end 

def punt(message) 
    flash[:notice] = message 
    redirect_to root_path 
end 

このルーチンはプラグインに作用します。個人的にお気に入りの承認プラグインはAuthorizationです。私はここ数年間、大きな成功を収めてきました。あなたがその最後の溶液中の醜い*リターンが気に入らない場合は、フィルタの周りに使用することができますし、条件付きでユーザーがいる場合にのみ得

permit "author of :post" 
+0

認証チェックの前にクエリを作成しないでください! –

+0

@Pedro特定モデルのコピーを取得する前に、特定のモデルに基づいて認証(認証ではない)を確認する方法を説明しなければなりません。 :) –

+0

あなたの認証方法は0の質問をします。 ユーザーが権限を持っているかどうかを確認する前に、検索を行います。 –

1

簡単な答えは、両方に合う何かにメッセージを変更することです:「あなたは、他の著者の記事を台無しすることはできません」

+0

ええ、私は本当にそれをしたくありません。 –

1

:上のバリエーションを使用するようにコントローラをリファクタリングでしょう

認可された

around_filter :check_authorization, :only => [:destroy, :update] 

private 
def check_authorization 
    @post = Post.find_by_slug(params[:slug]) 
    if @post.user == current_user 
     yield 
    else 
     flash[:notice] = case action_name 
     when "destroy" 
      "You cannot delete another author's posts." 
     when "update" 
      "You cannot edit another author's posts." 
     end 
     redirect_to root_path 
    end 
end 

* - これはコードワイズですが、完全に有効です。私はちょうどそれがスタイルに賢明であることがわかります、それは適合しない傾向があります。

また、私はこれをテストしていないし、100%確実ではないと確信しています。

関連する問題