9

Przerzucam kod z aplikacji wbudowanej w niestandardowy niestandardowy framework PHP do Ruby on Rails (wersja 3). W wersji PHP wszystkie kontrolery są naprawdę grube, z cienkimi modelami, z którymi zawsze się nie zgadzałem, więc cieszę się z tego, jak Rails dokonuje walidacji na poziomie modelu, co prawdopodobnie stanowi 90% tego, co dzieje się w tych kontrolerach tłuszczu. obecnie.Rails 3 Identyfikacja ActiveRecord na podstawie uprawnień użytkownika

Jednym z problemów, przed którymi stoję, i nie wiem, jak rozwiązać, jest jednak to, że różne zasady sprawdzania poprawności zależą od tego, kto dokonuje zmiany modelu. Na przykład administrator lub oryginalny twórca rekordu powinien móc np. Oznaczyć rekord jako usunięty (usuwanie miękkie), podczas gdy wszyscy inni nie powinni.

class Something < ActiveRecord::Base 
    ... 
    validates :deleted, :owned_by_active_user => true 
    ... 
end 

class OwnedByActiveUserValidator < ActiveModel::EachValidator 
    validate_each(record, attr_name, attr_value) 
    # Bad idea to have the model know about things such as sessions? 
    unless active_user.admin? || active_user.own?(record) 
     record.errors.add :base, "You do not have permission to delete this record" 
    end 
    end 
end 

Ponieważ sam model (w teorii) nieświadomy użytkownik, który sprawia, że ​​zmiany, co jest „Szyny drogę” do zrobienia tego typu rzeczy? Czy powinienem ustawić aktywnego użytkownika jako wirtualny atrybut w rekordzie (w rzeczywistości nie zapisany w DB), czy powinienem po prostu wykonać te kontrole w kontrolerze? Muszę przyznać, że posiadanie uprawnień do sprawdzania modelu na aktywnym użytkowniku wydaje się dziwne i zwiększa złożoność, jeśli chodzi o testowanie modelu.

Jednym z powodów, dla których chciałbym zachować jak najwięcej z tego modelu, jest to, że chcę zapewnić zarówno interfejs API (dostępny przez OAuth), jak i stronę internetową, bez duplikowania zbyt wielu kodów, takich jak te rodzaje kontroli uprawnień.

Odpowiedz

10

Jest to naprawdę zadanie kontrolera do obsługi autoryzacji lub delegowania uprawnień do warstwy autoryzacji. Modele nie powinny wiedzieć o tym, kto jest aktualnie zalogowany i jakie są jego uprawnienia, ani nie muszą się tym przejmować - to jest zadanie kontrolera lub jakiejkolwiek warstwy pomocniczej, którą kontroler przekazuje.

Należy dokonać :deleted in- attr_accessible do masowego przypisania poprzez new, create lub update_attributes. Kontroler powinien sprawdzać autoryzację uwierzytelnionego użytkownika oddzielnie i zadzwonić pod numer deleted= osobno, jeśli uwierzytelniony użytkownik jest uprawniony.

Istnieje kilka bibliotek autoryzacji i frameworków, które pomagają w autoryzowaniu lub działają jako warstwa autoryzacji, na przykład cancan.

+0

Dzięki, muszę się z tobą zgodzić. Moje modele powinny działać tak, jak im się mówi (pod warunkiem, że pozwala na to logika biznesowa). Moi kontrolerzy powinni zdecydować, kto im mówi. Po prostu upewnię się, że usunę te krwawe szczegóły najlepiej jak potrafię. – d11wtq

+0

Dogma ... Wiem, jak to się robi, ale wydaje mi się, że umieszczenie kontroli dostępu bezpośrednio w modelach to eleganckie rozwiązanie, jeśli uważasz, że modele są twoją warstwą danych API/reguł biznesowych. Pozwala to również uniknąć powtarzania tych samych informacji w wielu kontrolerach, które dotykają tego samego modelu.Używanie sprawdzania poprawności również wydaje się bardzo wygodne - może generować błędy typu "przepraszam, nie masz uprawnień do edycji tego pola". Chociaż to pole nie powinno być pokazywane w pierwszej kolejności. Chciałbym mieć rozwiązanie, które umożliwiłoby introspekcję dla twórców formularzy. – odigity

+0

Można tworzyć klasy formularza (nie oparte na bazach danych), które implementują sprawdzanie poprawności i metody wymagane przez kreator formularzy. – yfeldblum

6

Rozwiązałbym to z before_filter w moim kontrolerze, zamiast z walidacjami w moim modelu.

class SomethingController < ApplicationController 
    before_filter :require_delete_permission, :only => [:destroy] 

    def destroy 
    # delete the record 
    end 

    private 

    def require_delete_permission 
    unless current_user.is_admin || record.owner == current_user 
     flash[:error] = 'You do not have delete permissions' 
     redirect_to somewhere 
    end 
    end 
end 
+0

Ja też nie rozumiem tutaj. Twoja sugestia jest skutecznie zgodna z przyjętą odpowiedzią. – d11wtq

+0

Poza tym, że dodaje wiadomość do lampy błyskowej ... nie unieważniając zapisu Zapis – courtsimas

3

Natrafiłem na ten sam problem w Rails 2.3 i na końcu wymyślono to rozwiązanie. W swoim modelu definiujesz pewien atrybut, w zależności od tego, który włączasz/wyłączasz sprawdzanie poprawności. Niż ty kontrolą ustawić ten atrybut w zależności od daty dostępnego do regulatora (np uprawnieniami użytkowników w danym przypadku) w następujący sposób:

Class Model < ActiveRecord::Base 
    attr_accessor :perform_validation_of_field1 #This is an attribute which controller will use to turn on/off some validation logic depending on the current user 

    validates_presence_of :field1, :if => :perform_validation_of_field1 
    #This validation (or any similar one) will occur only if controller sets model.perform_validation_of_field1 to true. 
end 

Class MyController < ActionController::Base 
    def update 
    @item = Model.find(params[:id]) 
    @item.update_attribute(params[:item]) 

    #The controller decides whether to turn on optional validations depending on current user privileges (without the knowledge of internal implementation of this validation logic) 
    @item.perform_validation_of_field1 = true unless active_user.admin? 

    if @item.save 
     flash[:success] = 'The record has been saved' 
     redirect_to ... 
    else 
     flash.now[:error] = 'The record has not passed validation checks' 
     render :action => :edit 
    end 
    end 

Myślę, że w Rails 3 można to zrobić w podobny sposób.

+0

Muszę powiedzieć, że to brzmi trochę przypadkowo na pierwsze wrażenia, ale jeśli to działa dla ciebie to świetnie;) – d11wtq

+0

Co jest nie tak z tym podejściem. Nie ma nic o przypadkowej (przepraszam, nie wiem jak zrobić przymiotnik w języku angielskim od tego słowa :). Tak, wpisałem kod, ale go nie sprawdziłem. Miałem nadzieję, że wpadniesz na ten pomysł. Pomysł polega na użyciu: if lub: unless, chyba że parametry w makrach walidacji są zdefiniowane w modelu. Niektóre elementy logiki walidacji można włączyć/wyłączyć. Warstwa kontrolera wie, jak włączyć/wyłączyć określoną logikę walidacji dla modelu, ale nic nie wie o implementacji logiki walidacyjnej. – cryo28

+0

Oczywiście można łączyć opcje sprawdzania poprawności w większe fragmenty (nie ma potrzeby definiowania jednego atrybutu dla każdego sprawdzania poprawności). Więc proszę spojrzeć na http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html i skupić się na: if i: chyba że parametry makr walidacji w szczególności przed podjęciem decyzji, że ten kod działa tylko dla mnie jako zbieg okoliczności :) – cryo28

Powiązane problemy