manage cancancan all ability ruby-on-rails ruby authorization declarative-authorization

ruby on rails - cancancan - Proteger los atributos sensibles con la autorización declarativa



cancan gem (5)

Evitaría todas las soluciones basadas en el acceso de usuarios en el modelo porque parece potencialmente peligroso. Yo intentaría este enfoque:

class User < ActiveRecord::Base def update_attributes_as_user(values, user) values.each do |attribute, value| # Update the attribute if the user is allowed to @user.send("#{attribute}=", value) if user.modifiable_attributes.include?(attribute) end save end def modifiable_attributes admin? ? [:name, :email, :role] : [:name, :email] end end

Luego en su controlador cambie su acción de actualización desde:

@user.update_attributes(params[:user])

a

@user.update_attributes_as_user(params[:user], current_user)

¿Cuál es una buena manera de proteger los atributos por rol utilizando la declarative_authorization ? Por ejemplo, un usuario puede editar su información de contacto pero no su función.

Mi primera inclinación fue crear múltiples acciones de controlador para diferentes escenarios. Rápidamente me di cuenta de lo difícil que podría ser esto a medida que el número de atributos protegidos crece. Hacer esto para el rol de usuario es una cosa, pero puedo imaginar múltiples atributos protegidos. Agregar muchas acciones y rutas de control no se siente bien.

Mi segunda inclinación fue crear permisos en torno a atributos sensibles específicos y luego envolver los elementos del formulario con View hepers proporcionados por las autorizaciones declarativas. Sin embargo, el aspecto del modelo y del controlador es un poco confuso en mi mente. Las sugerencias serían impresionantes.

Por favor, aconseje sobre la mejor manera de proteger los atributos por rol utilizando las autorizaciones_certativas.


Para mí, este problema de filtrado es algo que debe aplicarse en el nivel del controlador.

Querrá tener algo en algún lugar que defina cómo decidir qué atributos se pueden escribir para un usuario determinado.

# On the user model class User < ActiveRecord::Base # ... # Return a list of symbols representing the accessible attributes def self.allowed_params(user) if user.admin? [:name, :email, :role] else [:name, email] end end end

Luego, en el controlador de la aplicación puede definir un método para filtrar parámetros.

class ApplicationController < ActionController::Base # ... protected def restrict_params(param, model, user) params[param].reject! do |k,v| !model.allowed_params(user).include?(k) end end # ... end

Y finalmente, en la acción de tu controlador puedes usar este filtro:

class UserController < ActionController::Base # ... def update restrict_params(:user, User, @current_user) # and continue as normal end # ... end

La idea es que luego podría definir allowed_params en cada uno de sus modelos, y hacer que los controladores para cada uno de estos utilicen el mismo método de filtro. Usted podría guardar algo de la placa de la caldera si tiene un método en el controlador de la aplicación que emita un filtro anterior, como este:

def self.param_restrictions(param, model) before_filter do restrict_params(param, model, @current_user) if params[param] end end # in UserController param_restrictions :user, User

Estos ejemplos pretenden ser ilustrativos en lugar de definitivos, espero que ayuden con la implementación de esto.



github.com/thefrontiergroup/scoped_attr_accessible , que se parece a lo que estás buscando. Solo necesita establecer el alcance al inicio de una solicitud para todos los modelos.

Para hacerlo, usa un before_filter en tu application_controller.rb:

before_filter do |controller| ScopedAttrAccessible.current_sanitizer_scope = controller.current_user.role end


EDITAR 2011-05-22
Algo similar está ahora en Rails a partir de 3.1RC https://github.com/rails/rails/blob/master/activerecord/test/cases/mass_assignment_security_test.rb por lo que sugeriría ir esa ruta ahora.

RESPUESTA ORIGINAL
Simplemente tuve que transferir lo que había estado usando anteriormente a Rails 3. Nunca he usado una autorización declarativa específicamente, pero esto es bastante simple y lo suficientemente directo como para que pueda adaptarse a él.

Rails 3 agregó mass_assignment_authorizer , lo que hace que todo esto sea realmente simple. Utilicé ese tutorial vinculado como base y lo hice encajar mejor en mi modelo de dominio, con herencia de clase y agrupando los atributos en roles.

En modelo

acts_as_accessible :admin => :all, :moderator => [:is_spam, :is_featured] attr_accessible :title, :body # :admin, :moderator, and anyone else can set these

En el controlador

post.accessed_by(current_user.roles.collect(&:code)) # or however yours works post.attributes = params[:post]

lib / active_record / acts_as_accessible.rb

# A way to have different attr_accessible attributes based on a Role # @see ActsAsAccessible::ActMethods#acts_as_accessible module ActiveRecord module ActsAsAccessible module ActMethods # In model # acts_as_accessible :admin => :all, :moderator => [:is_spam] # attr_accessible :title, :body # # In controller # post.accessed_by(current_user.roles.collect(&:code)) # post.attributes = params[:post] # # Warning: This frequently wouldn''t be the concern of the model where this is declared in, # but it is so much more useful to have it in there with the attr_accessible declaration. # OHWELL. # # @param [Hash] roles Hash of { :role => [:attr, :attr] } # @see acts_as_accessible_attributes def acts_as_accessible(*roles) roles_attributes_hash = Hash.new {|h,k| h[k] ||= [] } roles_attributes_hash = roles_attributes_hash.merge(roles.extract_options!).symbolize_keys if !self.respond_to? :acts_as_accessible_attributes attr_accessible write_inheritable_attribute :acts_as_accessible_attributes, roles_attributes_hash.symbolize_keys class_inheritable_reader :acts_as_accessible_attributes # extend ClassMethods unless (class << self; included_modules; end).include?(ClassMethods) include InstanceMethods unless included_modules.include?(InstanceMethods) else # subclass new_acts_as_accessible_attributes = self.acts_as_accessible_attributes.dup roles_attributes_hash.each do |role,attrs| new_acts_as_accessible_attributes[role] += attrs end write_inheritable_attribute :acts_as_accessible_attributes, new_acts_as_accessible_attributes.symbolize_keys end end end module InstanceMethods # @param [Array, NilClass] roles Array of Roles or nil to reset # @return [Array, NilClass] def accessed_by(*roles) if roles.any? case roles.first when NilClass @accessed_by = nil when Array @accessed_by = roles.first.flatten.collect(&:to_sym) else @accessed_by = roles.flatten.flatten.collect(&:to_sym) end end @accessed_by end private # This is what really does the work in attr_accessible/attr_protected. # This override adds the acts_as_accessible_attributes for the current accessed_by roles. # @see http://asciicasts.com/episodes/237-dynamic-attr-accessible def mass_assignment_authorizer attrs = [] if self.accessed_by self.accessed_by.each do |role| if self.acts_as_accessible_attributes.include? role if self.acts_as_accessible_attributes[role] == :all return self.class.protected_attributes else attrs += self.acts_as_accessible_attributes[role] end end end end super + attrs end end end end ActiveRecord::Base.send(:extend, ActiveRecord::ActsAsAccessible::ActMethods)

spec / lib / active_record / acts_as_accessible.rb

require ''spec_helper'' class TestActsAsAccessible include ActiveModel::MassAssignmentSecurity extend ActiveRecord::ActsAsAccessible::ActMethods attr_accessor :foo, :bar, :baz, :qux acts_as_accessible :dude => [:bar], :bra => [:baz, :qux], :admin => :all attr_accessible :foo def attributes=(values) sanitize_for_mass_assignment(values).each do |k, v| send("#{k}=", v) end end end describe TestActsAsAccessible do it "should still allow mass assignment to accessible attributes by default" do subject.attributes = {:foo => ''fooo''} subject.foo.should == ''fooo'' end it "should not allow mass assignment to non-accessible attributes by default" do subject.attributes = {:bar => ''baaar''} subject.bar.should be_nil end it "should allow mass assignment to acts_as_accessible attributes when passed appropriate accessed_by" do subject.accessed_by :dude subject.attributes = {:bar => ''baaar''} subject.bar.should == ''baaar'' end it "should allow mass assignment to multiple acts_as_accessible attributes when passed appropriate accessed_by" do subject.accessed_by :bra subject.attributes = {:baz => ''baaaz'', :qux => ''quuux''} subject.baz.should == ''baaaz'' subject.qux.should == ''quuux'' end it "should allow multiple accessed_by to be specified" do subject.accessed_by :dude, :bra subject.attributes = {:bar => ''baaar'', :baz => ''baaaz'', :qux => ''quuux''} subject.bar.should == ''baaar'' subject.baz.should == ''baaaz'' subject.qux.should == ''quuux'' end it "should allow :all access" do subject.accessed_by :admin subject.attributes = {:bar => ''baaar'', :baz => ''baaaz'', :qux => ''quuux''} subject.bar.should == ''baaar'' subject.baz.should == ''baaaz'' subject.qux.should == ''quuux'' end end