9

estoy cambiantes código de una aplicación construida en un marco PHP personalizado no estándar en Ruby on Rails (versión 3). En la versión de PHP todos los controladores son realmente gordos, con modelos delgados, con los que siempre he estado en desacuerdo, así que disfruto la forma en que Rails hace la validación a nivel de modelo, que es probablemente el 90% de lo que está sucediendo en estos reguladores de grasa actualmente.validación Carriles 3 ActiveRecord basado en permisos de usuario

Uno de los problemas que se me presentan, y no está seguro de cómo resolver sin embargo, es el de las diferentes reglas de validación sobre la base de que está haciendo el cambio en el modelo. Por ejemplo, un administrador o el creador original del registro debería poder hacer cosas como marcar un registro como eliminado (eliminación suave) mientras que los demás no deberían hacerlo.

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 

Dado que el modelo en sí es (en teoría) desconocen el usuario que está realizando el cambio, lo que es el "carriles manera" de hacer este tipo de cosas? ¿Debo configurar el usuario activo como un atributo virtual en el registro (no realmente guardado en la base de datos), o debería simplemente realizar estas comprobaciones en el controlador? Tengo que admitir que es extraño que el modelo verifique los permisos del usuario activo y agrega complejidad cuando se trata de probar el modelo.

Una de las razones por las que deseo mantener todo esto en el modelo es porque quiero proporcionar tanto una API (a la que se accede por OAuth) como un sitio web, sin duplicar demasiado código, como estos tipos de controles de permisos.

Respuesta

10

Es realmente el trabajo del controlador para gestionar la autorización, o para delegar autorización a una capa de autorización. Los modelos no deben saber ni deben preocuparse por quién está conectado actualmente y cuáles son sus permisos: ese es el trabajo del controlador o la capa auxiliar de autenticación que el controlador delegue.

Debe hacer :deleted in- attr_accessible a la asignación de masas a través de new, create o update_attributes. El controlador debe verificar las autorizaciones del usuario autenticado por separado y llamar al deleted= por separado, si el usuario autenticado está autorizado.

Existen varias bibliotecas y marcos de autorización para ayudar con la autorización o para funcionar como una capa de autorización, como cancan.

+0

Gracias, tengo que estar de acuerdo con usted. Mis modelos deben hacer lo que se les ordena (siempre que la lógica comercial lo permita). Mis controladores deberían decidir quién les dice. Solo me aseguraré de abstraer los detalles sangrientos lo mejor que pueda. – d11wtq

+0

dogma ... sé que esto es como se hace comúnmente, pero me parece que la puesta de control de acceso en los modelos es directamente una solución elegante si se tiene en cuenta los modelos que sea su nivel de API regla de datos/negocio. También evita repetir la misma información en múltiples controladores que tocan el mismo modelo.Y el uso de validadores para este fin también parece muy conveniente: puede generar errores como "lo siento, no tienes permiso para editar ese campo". Sin embargo, ese campo no debería haber sido mostrado en primer lugar. Ojalá hubiera una solución que permitiera la introspección para los constructores de formularios. – odigity

+0

Puede crear clases de modelo de formulario (no respaldado por base de datos) que implementen la validación y los métodos necesarios para el generador de formularios. – yfeldblum

6

que resolvería esto con un before_filter en mi controlador, en lugar de con validaciones en mi modelo.

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

Yo tampoco obtengo el voto abajo aquí. Su sugerencia es efectivamente lo que dice la respuesta aceptada. – d11wtq

+0

Excepto que está agregando un mensaje al flash ... no invalida el registro, guarde – courtsimas

3

Me he encontrado con el mismo problema en Rails 2.3 y finalmente se me ocurrió esta solución. En su modelo, usted define algún atributo, según el que active/desactive la validación. De lo que el control de este atributo se establece en función de la fecha disponible para el controlador (por ejemplo, privilegios de usuario en su caso) de la siguiente manera:

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 

Creo que en Rails 3 se puede hacer de una manera similar.

+0

Tengo que decir que esto suena un poco fortuito en las primeras impresiones, pero si funciona bien para usted;) – d11wtq

+0

¿Qué pasa con este enfoque? No hay nada de azaroso (lo siento, no sé cómo hacer un adjetivo en inglés a partir de esta palabra :). Sí, escribí un código pero no lo revisé. Esperaba que entendieras la idea. La idea es usar: if o: a menos que los parámetros en las macros de validación se definan en un modelo. De modo que algunas partes de la lógica de validación se pueden activar/desactivar. La capa de controlador sabe cómo activar/desactivar una lógica de validación particular para un modelo, pero no sabe nada sobre la implementación de la lógica de validación. – cryo28

+0

Por supuesto, puede combinar opciones de validación en trozos más grandes (no es necesario definir un atributo para cada validación). Así que por favor, eche un vistazo a http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html y se centran en: si, y: a no ser que los parámetros de validación de macros en particular, antes de decidir que este código funciona para mí sólo como una coincidencia :) – cryo28

Cuestiones relacionadas