2009-05-23 6 views
1

In einem Anfall von Unoriginalität, schreibe ich eine Blog-Anwendung mit Ruby on Rails. Meine PostsController enthält Code, der sicherstellt, dass der angemeldete Benutzer nur seine eigenen Beiträge bearbeiten oder löschen kann.Rails: Benutzer Spoofing Checks halten DRY

Ich habe versucht, diesen Code in eine private Methode mit einem einzigen Argument für die Flash-Nachricht zu zeigen, aber wenn ich dies getan und testete es durch Bearbeiten eines anderen Autors Beitrag, bekam ich eine ActionController::DoubleRenderError - "Kann nur rendern oder umleiten einmal pro Aktion ". Wie kann ich diese Überprüfungen DRY behalten? Der naheliegende Ansatz besteht darin, einen Vorher-Filter zu verwenden, aber die destroy-Methode muss einen anderen Flash anzeigen.

Hier ist der entsprechende Controller-Code:

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 

Antwort

2

Der Vorfilteransatz ist immer noch eine gute Option. Sie können mit der Methode action_name des Controllers auf die angeforderte Aktion zugreifen.

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 

Sorry für diesen ternären Operator in der Mitte dort. :) Natürlich kannst du jede Logik machen, die du magst.

Sie können auch eine Methode verwenden, wenn Sie möchten, und vermeiden Sie das doppelte Rendern, indem Sie explizit zurückgeben, wenn es fehlschlägt. Der Schlüssel hier ist, zurückzukehren, damit Sie nicht doppelt rendern.

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 

Sie könnten es mehr vereinfachen (meiner Meinung nach) durch die verschiedenen Teile des Verhaltens Aufspalten out (Autorisierung, schlechte Genehmigung Handhabung) wie folgt aus:

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 

persönlich ziehe ich alle abzuladen Diese Routine funktioniert zu einem Plugin. Mein persönliches Lieblingsautorisierungs-Plugin ist Authorization. Ich habe es in den letzten Jahren mit großem Erfolg benutzt.

, dass die Controller Refactoring würde Variationen verwenden:

permit "author of :post" 
+0

Keine Abfrage vor dem Auth-Check! –

+0

@Pedro Sie müssen mir erklären, wie Sie die Berechtigung (nicht die Authentifizierung) basierend auf einem bestimmten Modell überprüfen können, bevor Sie eine Kopie dieses Modells haben. :) –

+0

Ihre Autorisierungsmethode macht 0 Anfragen. Sie suchen nach, bevor Sie überprüfen, ob der Benutzer berechtigt ist oder nicht. –

1

Die einfache Antwort ist, die Nachricht zu etwas zu ändern, die beide passen: „Sie können nicht verwirren mit einem anderen Autor Beiträgen“

+0

Ja, aber ich will das nicht wirklich machen. –

1

Wenn Sie nicht wie die hässlichen * Rückkehr in dieser letzten Lösung, können Sie einen Filter verwenden, um und liefern nur bedingt, wenn der Benutzer autorisiert.

* - das ist meine Vorliebe, obwohl Code-weise es vollkommen gültig ist. Ich finde nur, dass es stilistisch nicht passt.

Ich sollte auch hinzufügen Ich habe das nicht getestet und bin nicht 100% sicher, es würde funktionieren, obwohl es leicht genug sein sollte, zu versuchen.