pax_global_header00006660000000000000000000000064125512677730014531gustar00rootroot0000000000000052 comment=0421e825911b05a77c6521171b43070c9e6c4b35 protected_attributes-1.1.3/000077500000000000000000000000001255126777300157725ustar00rootroot00000000000000protected_attributes-1.1.3/.gitignore000066400000000000000000000000721255126777300177610ustar00rootroot00000000000000*.gem *.rbc .bundle *.lock pkg .ruby-version .ruby-gemset protected_attributes-1.1.3/.travis.yml000066400000000000000000000012751255126777300201100ustar00rootroot00000000000000language: ruby sudo: false cache: bundler rvm: - 1.9.3 - 2.0.0 - 2.1 - 2.2 gemfile: - gemfiles/Gemfile-rails-4.0-stable - gemfiles/Gemfile-rails-4.1-stable - gemfiles/Gemfile-rails-4.2-stable - gemfiles/Gemfile-rails-4.0 - gemfiles/Gemfile-rails-4.1 - gemfiles/Gemfile-rails-4.2 notifications: email: false irc: on_success: change on_failure: always channels: - "irc.freenode.org#rails-contrib" campfire: on_success: change on_failure: always rooms: - secure: "RuC26E0TkxKzIQfqLuSHphe5FlgC1ULzhGpkZXqht3oDQjAuR+EmslxdWS9c\nVWwEr7PA+jnEp/8+sYM8TqHgLxFDHTTC6DgQoNNUY/Ti+mEyZunOPmXWP4UG\n6NLwr/rIuP1DEmDa6/q8V7qGn++SJrm1m4emGU4PbYNu6e6Nihc=" protected_attributes-1.1.3/CHANGELOG.md000066400000000000000000000041151255126777300176040ustar00rootroot00000000000000## 1.1.3 * Fix deprecation code. ## 1.1.2 * Deprecate `config.active_record.whitelist_attributes`. * Fix integration with associations. ## 1.1.1 * Fix strong parameters integration. * Remove warnings * Fix `find_or_*` and `first_or_*` methods integration. ## 1.1.0 * Integrate with strong parameters. This allows to migrate a codebase partially from `protected_attributes` to `strong_parameters`. Every model that does not use a protection macro (`attr_accessible` or `attr_protected`), will be protected by strong parameters. The behavior stays the same for models, which use a protection macro. To fully restore the old behavior set: config.action_controller.permit_all_parameters = true Or add a callback to your controllers like this: before_action { params.permit! } Fixes #41. ## 1.0.9 * Fixes ThroughAssociation#build_record method on rails 4.1.10+ Fixes #60, #63 * Fixes build_association method on rails 4.2.0+ Fixes https://github.com/rails/rails/issues/18121 ## 1.0.8 (June 16, 2014) * Support Rails 4.0.6+ and 4.1.2+. Fixes #35 ## 1.0.7 (March 12, 2014) * Fix STI support on Active Record <= 4.0.3. ## 1.0.6 (March 10, 2014) * Support to Rails 4.1 * Fix `CollectionProxy#new` method. Fixes #21 ## 1.0.5 (November 1, 2013) * Fix install error with Rails 4.0.1. Related with https://github.com/bundler/bundler/issues/2583 ## 1.0.4 (October 18, 2013) * Avoid override the entire Active Record initialization. Fixes rails/rails#12243 ## 1.0.3 (June 29, 2013) * Fix "uninitialized constant ActiveRecord::MassAssignmentSecurity::NestedAttributes::ClassMethods::REJECT_ALL_BLANK_PROC" error when using `:all_blank` option. Fixes #8 * Fix `NoMethodError` exception when calling `raise_nested_attributes_record_not_found`. ## 1.0.2 (June 25, 2013) * Sync #initialize override to latest rails implementation Fixes #14 ## 1.0.1 (April 6, 2013) * Fix "uninitialized constant `ActiveRecord::SchemaMigration`" error when checking pending migrations. Fixes rails/rails#10109 ## 1.0.0 (January 22, 2013) * First public version protected_attributes-1.1.3/Gemfile000066400000000000000000000000761255126777300172700ustar00rootroot00000000000000source 'https://rubygems.org' gem 'rails', '~> 4.0' gemspec protected_attributes-1.1.3/LICENSE.txt000066400000000000000000000020611255126777300176140ustar00rootroot00000000000000Copyright (c) 2012 Guillermo Iguaran MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.protected_attributes-1.1.3/README.md000066400000000000000000000076101255126777300172550ustar00rootroot00000000000000# Protected Attributes [![Build Status](https://api.travis-ci.org/rails/protected_attributes.svg?branch=master)](https://travis-ci.org/rails/protected_attributes) Protect attributes from mass-assignment in Active Record models. This plugin adds the class methods `attr_accessible` and `attr_protected` to your models to be able to declare white or black lists of attributes. Note: This plugin will be officially supported until the release of Rails 5.0. ## Installation Add this line to your application's `Gemfile`: gem 'protected_attributes' And then execute: bundle install ## Usage Mass assignment security provides an interface for protecting attributes from end-user injection. This plugin provides two class methods in Active Record classes to control access to their attributes. The `attr_protected` method takes a list of attributes that will be ignored in mass-assignment. For example: ```ruby attr_protected :admin ``` `attr_protected` also optionally takes a role option using `:as` which allows you to define multiple mass-assignment groupings. If no role is defined then attributes will be added to the `:default` role. ```ruby attr_protected :last_login, :as => :admin ``` A much better way, because it follows the whitelist-principle, is the `attr_accessible` method. It is the exact opposite of `attr_protected`, because it takes a list of attributes that will be mass-assigned if present. Any other attributes will be ignored. This way you won’t forget to protect attributes when adding new ones in the course of development. Here is an example: ```ruby attr_accessible :name attr_accessible :name, :is_admin, :as => :admin ``` If you want to set a protected attribute, you will to have to assign it individually: ```ruby params[:user] # => {:name => "owned", :is_admin => true} @user = User.new(params[:user]) @user.is_admin # => false, not mass-assigned @user.is_admin = true @user.is_admin # => true ``` When assigning attributes in Active Record using `attributes=` the `:default` role will be used. To assign attributes using different roles you should use `assign_attributes` which accepts an optional `:as` options parameter. If no `:as` option is provided then the `:default` role will be used. You can also bypass mass-assignment security by using the `:without_protection` option. Here is an example: ```ruby @user = User.new @user.assign_attributes(:name => 'Josh', :is_admin => true) @user.name # => Josh @user.is_admin # => false @user.assign_attributes({ :name => 'Josh', :is_admin => true }, :as => :admin) @user.name # => Josh @user.is_admin # => true @user.assign_attributes({ :name => 'Josh', :is_admin => true }, :without_protection => true) @user.name # => Josh @user.is_admin # => true ``` In a similar way, `new`, `create`, `create!`, `update_attributes` and `update_attributes!` methods all respect mass-assignment security and accept either `:as` or `:without_protection` options. For example: ```ruby @user = User.new({ :name => 'Sebastian', :is_admin => true }, :as => :admin) @user.name # => Sebastian @user.is_admin # => true @user = User.create({ :name => 'Sebastian', :is_admin => true }, :without_protection => true) @user.name # => Sebastian @user.is_admin # => true ``` By default the gem will use the strong parameters protection when assigning attribute, unless your model has `attr_accessible` or `attr_protected` calls. ### Errors By default, attributes in the params hash which are not allowed to be updated are just ignored. If you prefer an exception to be raised configure: ```ruby config.active_record.mass_assignment_sanitizer = :strict ``` Any protected attributes violation raises `ActiveModel::MassAssignmentSecurity::Error` then. ## Contributing 1. Fork it 2. Create your feature branch (`git checkout -b my-new-feature`) 3. Commit your changes (`git commit -am 'Add some feature'`) 4. Push to the branch (`git push origin my-new-feature`) 5. Create new Pull Request protected_attributes-1.1.3/Rakefile000066400000000000000000000003141255126777300174350ustar00rootroot00000000000000#!/usr/bin/env rake require "bundler/gem_tasks" require 'rake/testtask' Rake::TestTask.new do |t| t.libs = ["test"] t.pattern = "test/**/*_test.rb" t.ruby_opts = ['-w'] end task :default => :test protected_attributes-1.1.3/gemfiles/000077500000000000000000000000001255126777300175655ustar00rootroot00000000000000protected_attributes-1.1.3/gemfiles/Gemfile-rails-4.0000066400000000000000000000001141255126777300224630ustar00rootroot00000000000000source 'https://rubygems.org' gem 'rails', '~> 4.0.13' gemspec path: '..' protected_attributes-1.1.3/gemfiles/Gemfile-rails-4.0-stable000066400000000000000000000001541255126777300237370ustar00rootroot00000000000000source 'https://rubygems.org' gem 'rails', github: 'rails/rails', branch: '4-0-stable' gemspec path: '..' protected_attributes-1.1.3/gemfiles/Gemfile-rails-4.1000066400000000000000000000001131255126777300224630ustar00rootroot00000000000000source 'https://rubygems.org' gem 'rails', '~> 4.1.9' gemspec path: '..' protected_attributes-1.1.3/gemfiles/Gemfile-rails-4.1-stable000066400000000000000000000001541255126777300237400ustar00rootroot00000000000000source 'https://rubygems.org' gem 'rails', github: 'rails/rails', branch: '4-1-stable' gemspec path: '..' protected_attributes-1.1.3/gemfiles/Gemfile-rails-4.2000066400000000000000000000001131255126777300224640ustar00rootroot00000000000000source 'https://rubygems.org' gem 'rails', '~> 4.2.0' gemspec path: '..' protected_attributes-1.1.3/gemfiles/Gemfile-rails-4.2-stable000066400000000000000000000001541255126777300237410ustar00rootroot00000000000000source 'https://rubygems.org' gem 'rails', github: 'rails/rails', branch: '4-2-stable' gemspec path: '..' protected_attributes-1.1.3/lib/000077500000000000000000000000001255126777300165405ustar00rootroot00000000000000protected_attributes-1.1.3/lib/action_controller/000077500000000000000000000000001255126777300222605ustar00rootroot00000000000000protected_attributes-1.1.3/lib/action_controller/accessible_params_wrapper.rb000066400000000000000000000014031255126777300300030ustar00rootroot00000000000000require 'active_support/concern' require 'action_controller' require 'action_controller/metal/params_wrapper' module ActionController module ParamsWrapper class Options # :nodoc: undef :include def include return super if @include_set m = model synchronize do return super if @include_set @include_set = true unless super || exclude if m.respond_to?(:accessible_attributes) && m.accessible_attributes(:default).present? self.include = m.accessible_attributes(:default).to_a elsif m.respond_to?(:attribute_names) && m.attribute_names.any? self.include = m.attribute_names end end end end end end end protected_attributes-1.1.3/lib/active_model/000077500000000000000000000000001255126777300211735ustar00rootroot00000000000000protected_attributes-1.1.3/lib/active_model/mass_assignment_security.rb000066400000000000000000000325711255126777300266520ustar00rootroot00000000000000require 'active_support/concern' require 'active_support/core_ext/class/attribute' require 'active_support/core_ext/string/inflections' require 'active_model' require 'active_model/mass_assignment_security/permission_set' require 'active_model/mass_assignment_security/sanitizer' module ActiveModel # == Active Model Mass-Assignment Security # # Mass assignment security provides an interface for protecting attributes # from end-user assignment. For more complex permissions, mass assignment # security may be handled outside the model by extending a non-ActiveRecord # class, such as a controller, with this behavior. # # For example, a logged in user may need to assign additional attributes # depending on their role: # # class AccountsController < ApplicationController # include ActiveModel::MassAssignmentSecurity # # attr_accessible :first_name, :last_name # attr_accessible :first_name, :last_name, :plan_id, as: :admin # # def update # ... # @account.update_attributes(account_params) # ... # end # # protected # # def account_params # role = admin ? :admin : :default # sanitize_for_mass_assignment(params[:account], role) # end # # end # # === Configuration options # # * mass_assignment_sanitizer - Defines sanitize method. Possible # values are: # * :logger (default) - writes filtered attributes to logger # * :strict - raise ActiveModel::MassAssignmentSecurity::Error # on any protected attribute update. # # You can specify your own sanitizer object eg. MySanitizer.new. # See ActiveModel::MassAssignmentSecurity::LoggerSanitizer for # example implementation. module MassAssignmentSecurity extend ActiveSupport::Concern include ActiveModel::ForbiddenAttributesProtection included do class_attribute :_accessible_attributes, instance_writer: false class_attribute :_protected_attributes, instance_writer: false class_attribute :_active_authorizer, instance_writer: false class_attribute :_uses_mass_assignment_security, instance_writer: false self._uses_mass_assignment_security = false class_attribute :_mass_assignment_sanitizer, instance_writer: false self.mass_assignment_sanitizer = :logger end module ClassMethods # Attributes named in this macro are protected from mass-assignment # whenever attributes are sanitized before assignment. A role for the # attributes is optional, if no role is provided then :default # is used. A role can be defined by using the :as option with a # symbol or an array of symbols as the value. # # Mass-assignment to these attributes will simply be ignored, to assign # to them you can use direct writer methods. This is meant to protect # sensitive attributes from being overwritten by malicious users # tampering with URLs or forms. # # class Customer # include ActiveModel::MassAssignmentSecurity # # attr_accessor :name, :email, :logins_count # # attr_protected :logins_count # # Suppose that admin can not change email for customer # attr_protected :logins_count, :email, as: :admin # # def assign_attributes(values, options = {}) # sanitize_for_mass_assignment(values, options[:as]).each do |k, v| # send("#{k}=", v) # end # end # end # # When using the :default role: # # customer = Customer.new # customer.assign_attributes({ name: 'David', email: 'a@b.com', logins_count: 5 }, as: :default) # customer.name # => "David" # customer.email # => "a@b.com" # customer.logins_count # => nil # # And using the :admin role: # # customer = Customer.new # customer.assign_attributes({ name: 'David', email: 'a@b.com', logins_count: 5}, as: :admin) # customer.name # => "David" # customer.email # => nil # customer.logins_count # => nil # # customer.email = 'c@d.com' # customer.email # => "c@d.com" # # To start from an all-closed default and enable attributes as needed, # have a look at +attr_accessible+. # # Note that using Hash#except or Hash#slice in place of # +attr_protected+ to sanitize attributes provides basically the same # functionality, but it makes a bit tricky to deal with nested attributes. def attr_protected(*args) options = args.extract_options! role = options[:as] || :default self._protected_attributes = protected_attributes_configs.dup Array(role).each do |name| self._protected_attributes[name] = self.protected_attributes(name) + args end self._uses_mass_assignment_security = true self._active_authorizer = self._protected_attributes end # Specifies a white list of model attributes that can be set via # mass-assignment. # # Like +attr_protected+, a role for the attributes is optional, # if no role is provided then :default is used. A role can be # defined by using the :as option with a symbol or an array of # symbols as the value. # # This is the opposite of the +attr_protected+ macro: Mass-assignment # will only set attributes in this list, to assign to the rest of # attributes you can use direct writer methods. This is meant to protect # sensitive attributes from being overwritten by malicious users # tampering with URLs or forms. If you'd rather start from an all-open # default and restrict attributes as needed, have a look at # +attr_protected+. # # class Customer # include ActiveModel::MassAssignmentSecurity # # attr_accessor :name, :credit_rating # # # Both admin and default user can change name of a customer # attr_accessible :name, as: [:admin, :default] # # Only admin can change credit rating of a customer # attr_accessible :credit_rating, as: :admin # # def assign_attributes(values, options = {}) # sanitize_for_mass_assignment(values, options[:as]).each do |k, v| # send("#{k}=", v) # end # end # end # # When using the :default role: # # customer = Customer.new # customer.assign_attributes({ name: 'David', credit_rating: 'Excellent', last_login: 1.day.ago }, as: :default) # customer.name # => "David" # customer.credit_rating # => nil # # customer.credit_rating = 'Average' # customer.credit_rating # => "Average" # # And using the :admin role: # # customer = Customer.new # customer.assign_attributes({ name: 'David', credit_rating: 'Excellent', last_login: 1.day.ago }, as: :admin) # customer.name # => "David" # customer.credit_rating # => "Excellent" # # Note that using Hash#except or Hash#slice in place of # +attr_accessible+ to sanitize attributes provides basically the same # functionality, but it makes a bit tricky to deal with nested attributes. def attr_accessible(*args) options = args.extract_options! role = options[:as] || :default self._accessible_attributes = accessible_attributes_configs.dup Array(role).each do |name| self._accessible_attributes[name] = self.accessible_attributes(name) + args end self._uses_mass_assignment_security = true self._active_authorizer = self._accessible_attributes end # Returns an instance of ActiveModel::MassAssignmentSecurity::BlackList # with the attributes protected by #attr_protected method. If no +role+ # is provided, then :default is used. # # class Customer # include ActiveModel::MassAssignmentSecurity # # attr_accessor :name, :email, :logins_count # # attr_protected :logins_count # attr_protected :logins_count, :email, as: :admin # end # # Customer.protected_attributes # # => # # # Customer.protected_attributes(:default) # # => # # # Customer.protected_attributes(:admin) # # => # def protected_attributes(role = :default) protected_attributes_configs[role] end # Returns an instance of ActiveModel::MassAssignmentSecurity::WhiteList # with the attributes protected by #attr_accessible method. If no +role+ # is provided, then :default is used. # # class Customer # include ActiveModel::MassAssignmentSecurity # # attr_accessor :name, :credit_rating # # attr_accessible :name, as: [:admin, :default] # attr_accessible :credit_rating, as: :admin # end # # Customer.accessible_attributes # # => # # # Customer.accessible_attributes(:default) # # => # # # Customer.accessible_attributes(:admin) # # => # def accessible_attributes(role = :default) accessible_attributes_configs[role] end # Returns a hash with the protected attributes (by #attr_accessible or # #attr_protected) per role. # # class Customer # include ActiveModel::MassAssignmentSecurity # # attr_accessor :name, :credit_rating # # attr_accessible :name, as: [:admin, :default] # attr_accessible :credit_rating, as: :admin # end # # Customer.active_authorizers # # => { # # :admin=> #, # # :default=># # # } def active_authorizers self._active_authorizer ||= protected_attributes_configs end alias active_authorizer active_authorizers # Returns an empty array by default. You can still override this to define # the default attributes protected by #attr_protected method. # # class Customer # include ActiveModel::MassAssignmentSecurity # # def self.attributes_protected_by_default # [:name] # end # end # # Customer.protected_attributes # # => # def attributes_protected_by_default [] end # Defines sanitize method. # # class Customer # include ActiveModel::MassAssignmentSecurity # # attr_accessor :name # # attr_protected :name # # def assign_attributes(values) # sanitize_for_mass_assignment(values).each do |k, v| # send("#{k}=", v) # end # end # end # # # See ActiveModel::MassAssignmentSecurity::StrictSanitizer for more information. # Customer.mass_assignment_sanitizer = :strict # # customer = Customer.new # customer.assign_attributes(name: 'David') # # => ActiveModel::MassAssignmentSecurity::Error: Can't mass-assign protected attributes for Customer: name # # Also, you can specify your own sanitizer object. # # class CustomSanitizer < ActiveModel::MassAssignmentSecurity::Sanitizer # def process_removed_attributes(klass, attrs) # raise StandardError # end # end # # Customer.mass_assignment_sanitizer = CustomSanitizer.new # # customer = Customer.new # customer.assign_attributes(name: 'David') # # => StandardError: StandardError def mass_assignment_sanitizer=(value) self._mass_assignment_sanitizer = if value.is_a?(Symbol) const_get(:"#{value.to_s.camelize}Sanitizer").new(self) else value end end private def protected_attributes_configs self._protected_attributes ||= begin Hash.new { |h,k| h[k] = BlackList.new(attributes_protected_by_default) } end end def accessible_attributes_configs self._accessible_attributes ||= begin Hash.new { |h,k| h[k] = WhiteList.new } end end end protected def sanitize_for_mass_assignment(attributes, role = nil) #:nodoc: if _uses_mass_assignment_security _mass_assignment_sanitizer.sanitize(self.class, attributes, mass_assignment_authorizer(role)) else sanitize_forbidden_attributes(attributes) end end def mass_assignment_authorizer(role) #:nodoc: self.class.active_authorizer[role || :default] end end end protected_attributes-1.1.3/lib/active_model/mass_assignment_security/000077500000000000000000000000001255126777300263155ustar00rootroot00000000000000protected_attributes-1.1.3/lib/active_model/mass_assignment_security/permission_set.rb000066400000000000000000000012751255126777300317120ustar00rootroot00000000000000require 'set' module ActiveModel module MassAssignmentSecurity class PermissionSet < Set #:nodoc: def +(values) super(values.compact.map(&:to_s)) end def include?(key) super(remove_multiparameter_id(key)) end def deny?(key) raise NotImplementedError, "#deny?(key) supposed to be overwritten" end protected def remove_multiparameter_id(key) key.to_s.gsub(/\(.+/, '') end end class WhiteList < PermissionSet #:nodoc: def deny?(key) !include?(key) end end class BlackList < PermissionSet #:nodoc: def deny?(key) include?(key) end end end end protected_attributes-1.1.3/lib/active_model/mass_assignment_security/sanitizer.rb000066400000000000000000000037001255126777300306520ustar00rootroot00000000000000module ActiveModel module MassAssignmentSecurity class Sanitizer #:nodoc: # Returns all attributes not denied by the authorizer. def sanitize(klass, attributes, authorizer) rejected = [] sanitized_attributes = attributes.reject do |key, value| rejected << key if authorizer.deny?(key) end process_removed_attributes(klass, rejected) unless rejected.empty? sanitized_attributes end protected def process_removed_attributes(klass, attrs) raise NotImplementedError, "#process_removed_attributes(klass, attrs) is intended to be overwritten by a subclass" end end class LoggerSanitizer < Sanitizer #:nodoc: def initialize(target) @target = target super() end def logger @target.logger end def logger? @target.respond_to?(:logger) && @target.logger end def backtrace if defined? Rails.backtrace_cleaner Rails.backtrace_cleaner.clean(caller) else caller end end def process_removed_attributes(klass, attrs) if logger? logger.warn do "WARNING: Can't mass-assign protected attributes for #{klass.name}: #{attrs.join(', ')}\n" + backtrace.map { |trace| "\t#{trace}" }.join("\n") end end end end class StrictSanitizer < Sanitizer #:nodoc: def initialize(target = nil) super() end def process_removed_attributes(klass, attrs) unless (attrs - insensitive_attributes).empty? raise ActiveModel::MassAssignmentSecurity::Error.new(klass, attrs) end end def insensitive_attributes ['id'] end end class Error < StandardError #:nodoc: def initialize(klass, attrs) super("Can't mass-assign protected attributes for #{klass.name}: #{attrs.join(', ')}") end end end end protected_attributes-1.1.3/lib/active_record/000077500000000000000000000000001255126777300213515ustar00rootroot00000000000000protected_attributes-1.1.3/lib/active_record/mass_assignment_security.rb000066400000000000000000000023121255126777300270160ustar00rootroot00000000000000require "active_record" def active_record_40? ActiveRecord::VERSION::MAJOR == 4 && ActiveRecord::VERSION::MINOR == 0 end require "active_record/mass_assignment_security/associations" require "active_record/mass_assignment_security/attribute_assignment" require "active_record/mass_assignment_security/core" require "active_record/mass_assignment_security/nested_attributes" require "active_record/mass_assignment_security/persistence" require "active_record/mass_assignment_security/reflection" require "active_record/mass_assignment_security/relation" require "active_record/mass_assignment_security/validations" require "active_record/mass_assignment_security/associations" require "active_record/mass_assignment_security/inheritance" class ActiveRecord::Base include ActiveRecord::MassAssignmentSecurity::Core include ActiveRecord::MassAssignmentSecurity::AttributeAssignment include ActiveRecord::MassAssignmentSecurity::Persistence include ActiveRecord::MassAssignmentSecurity::Validations include ActiveRecord::MassAssignmentSecurity::NestedAttributes include ActiveRecord::MassAssignmentSecurity::Inheritance end class ActiveRecord::SchemaMigration < ActiveRecord::Base attr_accessible :version end protected_attributes-1.1.3/lib/active_record/mass_assignment_security/000077500000000000000000000000001255126777300264735ustar00rootroot00000000000000protected_attributes-1.1.3/lib/active_record/mass_assignment_security/associations.rb000066400000000000000000000103371255126777300315230ustar00rootroot00000000000000module ActiveRecord module Associations class Association undef :build_record def build_record(attributes, options) reflection.build_association(attributes, options) do |record| attributes = create_scope.except(*(record.changed - [reflection.foreign_key])) record.assign_attributes(attributes, without_protection: true) end end private :build_record end class CollectionAssociation undef :build undef :create undef :create! def build(attributes = {}, options = {}, &block) if attributes.is_a?(Array) attributes.collect { |attr| build(attr, options, &block) } else add_to_target(build_record(attributes, options)) do |record| yield(record) if block_given? end end end def create(attributes = {}, options = {}, &block) create_record(attributes, options, &block) end def create!(attributes = {}, options = {}, &block) create_record(attributes, options, true, &block) end def create_record(attributes, options, raise = false, &block) unless owner.persisted? raise ActiveRecord::RecordNotSaved, "You cannot call create unless the parent is saved" end if attributes.is_a?(Array) attributes.collect { |attr| create_record(attr, options, raise, &block) } else transaction do add_to_target(build_record(attributes, options)) do |record| yield(record) if block_given? insert_record(record, true, raise) end end end end private :create_record end class CollectionProxy undef :create undef :create! def build(attributes = {}, options = {}, &block) @association.build(attributes, options, &block) end alias_method :new, :build def create(attributes = {}, options = {}, &block) @association.create(attributes, options, &block) end def create!(attributes = {}, options = {}, &block) @association.create!(attributes, options, &block) end end module ThroughAssociation undef :build_record if respond_to?(:build_record, false) private def build_record(attributes, options={}) inverse = source_reflection.inverse_of target = through_association.target if inverse && target && !target.is_a?(Array) attributes[inverse.foreign_key] = target.id end super(attributes, options) end end class HasManyThroughAssociation undef :build_record undef :options_for_through_record if respond_to?(:options_for_through_record, false) def build_record(attributes, options = {}) ensure_not_nested record = super(attributes, options) inverse = source_reflection.inverse_of if inverse if inverse.macro == :has_many record.send(inverse.name) << build_through_record(record) elsif inverse.macro == :has_one record.send("#{inverse.name}=", build_through_record(record)) end end record end private :build_record def options_for_through_record [through_scope_attributes, without_protection: true] end private :options_for_through_record end class SingularAssociation undef :create undef :create! undef :build def create(attributes = {}, options = {}, &block) create_record(attributes, options, &block) end def create!(attributes = {}, options = {}, &block) create_record(attributes, options, true, &block) end def build(attributes = {}, options = {}) record = build_record(attributes, options) yield(record) if block_given? set_new_record(record) record end def create_record(attributes, options = {}, raise_error = false) record = build_record(attributes, options) yield(record) if block_given? saved = record.save set_new_record(record) raise RecordInvalid.new(record) if !saved && raise_error record end private :create_record end end end protected_attributes-1.1.3/lib/active_record/mass_assignment_security/attribute_assignment.rb000066400000000000000000000056711255126777300332640ustar00rootroot00000000000000require 'active_model/mass_assignment_security' require 'active_record' module ActiveRecord module MassAssignmentSecurity module AttributeAssignment extend ActiveSupport::Concern include ActiveModel::MassAssignmentSecurity module ClassMethods private # The primary key and inheritance column can never be set by mass-assignment for security reasons. def attributes_protected_by_default default = [ primary_key, inheritance_column ] default << 'id' unless primary_key.eql? 'id' default end end # Allows you to set all the attributes for a particular mass-assignment # security role by passing in a hash of attributes with keys matching # the attribute names (which again matches the column names) and the role # name using the :as option. # # To bypass mass-assignment security you can use the :without_protection => true # option. # # class User < ActiveRecord::Base # attr_accessible :name # attr_accessible :name, :is_admin, :as => :admin # end # # user = User.new # user.assign_attributes({ :name => 'Josh', :is_admin => true }) # user.name # => "Josh" # user.is_admin? # => false # # user = User.new # user.assign_attributes({ :name => 'Josh', :is_admin => true }, :as => :admin) # user.name # => "Josh" # user.is_admin? # => true # # user = User.new # user.assign_attributes({ :name => 'Josh', :is_admin => true }, :without_protection => true) # user.name # => "Josh" # user.is_admin? # => true def assign_attributes(new_attributes, options = {}) return if new_attributes.blank? attributes = new_attributes.stringify_keys multi_parameter_attributes = [] nested_parameter_attributes = [] previous_options = @mass_assignment_options @mass_assignment_options = options unless options[:without_protection] attributes = sanitize_for_mass_assignment(attributes, mass_assignment_role) end attributes.each do |k, v| if k.include?("(") multi_parameter_attributes << [ k, v ] elsif v.is_a?(Hash) nested_parameter_attributes << [ k, v ] else _assign_attribute(k, v) end end assign_nested_parameter_attributes(nested_parameter_attributes) unless nested_parameter_attributes.empty? assign_multiparameter_attributes(multi_parameter_attributes) unless multi_parameter_attributes.empty? ensure @mass_assignment_options = previous_options end protected def mass_assignment_options @mass_assignment_options ||= {} end def mass_assignment_role mass_assignment_options[:as] || :default end end end end protected_attributes-1.1.3/lib/active_record/mass_assignment_security/core.rb000066400000000000000000000004471255126777300277550ustar00rootroot00000000000000module ActiveRecord module MassAssignmentSecurity module Core private def init_attributes(attributes, options) assign_attributes(attributes, options) end def init_internals super @mass_assignment_options = nil end end end end protected_attributes-1.1.3/lib/active_record/mass_assignment_security/inheritance.rb000066400000000000000000000015431255126777300313140ustar00rootroot00000000000000module ActiveRecord module MassAssignmentSecurity module Inheritance extend ActiveSupport::Concern module ClassMethods private # Detect the subclass from the inheritance column of attrs. If the inheritance column value # is not self or a valid subclass, raises ActiveRecord::SubclassNotFound # If this is a StrongParameters hash, and access to inheritance_column is not permitted, # this will ignore the inheritance column and return nil def subclass_from_attributes?(attrs) active_authorizer[:default].deny?(inheritance_column) ? nil : super end # Support Active Record <= 4.0.3, which uses the old method signature. def subclass_from_attrs(attrs) active_authorizer[:default].deny?(inheritance_column) ? nil : super end end end end end protected_attributes-1.1.3/lib/active_record/mass_assignment_security/nested_attributes.rb000066400000000000000000000152221255126777300325520ustar00rootroot00000000000000module ActiveRecord module MassAssignmentSecurity module NestedAttributes extend ActiveSupport::Concern module ClassMethods REJECT_ALL_BLANK_PROC = proc { |attributes| attributes.all? { |key, value| key == '_destroy' || value.blank? } } def accepts_nested_attributes_for(*attr_names) options = { :allow_destroy => false, :update_only => false } options.update(attr_names.extract_options!) options.assert_valid_keys(:allow_destroy, :reject_if, :limit, :update_only) options[:reject_if] = REJECT_ALL_BLANK_PROC if options[:reject_if] == :all_blank attr_names.each do |association_name| if reflection = reflect_on_association(association_name) reflection.options[:autosave] = true add_autosave_association_callbacks(reflection) nested_attributes_options = self.nested_attributes_options.dup nested_attributes_options[association_name.to_sym] = options self.nested_attributes_options = nested_attributes_options type = (reflection.collection? ? :collection : :one_to_one) generated_methods_module = active_record_40? ? generated_feature_methods : generated_association_methods # def pirate_attributes=(attributes) # assign_nested_attributes_for_one_to_one_association(:pirate, attributes, mass_assignment_options) # end generated_methods_module.module_eval <<-eoruby, __FILE__, __LINE__ + 1 if method_defined?(:#{association_name}_attributes=) remove_method(:#{association_name}_attributes=) end def #{association_name}_attributes=(attributes) assign_nested_attributes_for_#{type}_association(:#{association_name}, attributes, mass_assignment_options) end eoruby else raise ArgumentError, "No association found for name `#{association_name}'. Has it been defined yet?" end end end end private UNASSIGNABLE_KEYS = %w( id _destroy ) def assign_nested_attributes_for_one_to_one_association(association_name, attributes, assignment_opts = {}) options = self.nested_attributes_options[association_name] attributes = attributes.with_indifferent_access if (options[:update_only] || !attributes['id'].blank?) && (record = send(association_name)) && (options[:update_only] || record.id.to_s == attributes['id'].to_s) assign_to_or_mark_for_destruction(record, attributes, options[:allow_destroy], assignment_opts) unless call_reject_if(association_name, attributes) elsif attributes['id'].present? && !assignment_opts[:without_protection] raise_nested_attributes_record_not_found!(association_name, attributes['id']) elsif !reject_new_record?(association_name, attributes) method = "build_#{association_name}" if respond_to?(method) send(method, attributes.except(*unassignable_keys(assignment_opts)), assignment_opts) else raise ArgumentError, "Cannot build association `#{association_name}'. Are you trying to build a polymorphic one-to-one association?" end end end def assign_nested_attributes_for_collection_association(association_name, attributes_collection, assignment_opts = {}) options = self.nested_attributes_options[association_name] unless attributes_collection.is_a?(Hash) || attributes_collection.is_a?(Array) raise ArgumentError, "Hash or Array expected, got #{attributes_collection.class.name} (#{attributes_collection.inspect})" end if limit = options[:limit] limit = case limit when Symbol send(limit) when Proc limit.call else limit end if limit && attributes_collection.size > limit raise TooManyRecords, "Maximum #{limit} records are allowed. Got #{attributes_collection.size} records instead." end end if attributes_collection.is_a? Hash keys = attributes_collection.keys attributes_collection = if keys.include?('id') || keys.include?(:id) [attributes_collection] else attributes_collection.values end end association = association(association_name) existing_records = if association.loaded? association.target else attribute_ids = attributes_collection.map {|a| a['id'] || a[:id] }.compact attribute_ids.empty? ? [] : association.scope.where(association.klass.primary_key => attribute_ids) end attributes_collection.each do |attributes| attributes = attributes.with_indifferent_access if attributes['id'].blank? unless reject_new_record?(association_name, attributes) association.build(attributes.except(*unassignable_keys(assignment_opts)), assignment_opts) end elsif existing_record = existing_records.detect { |record| record.id.to_s == attributes['id'].to_s } unless association.loaded? || call_reject_if(association_name, attributes) # Make sure we are operating on the actual object which is in the association's # proxy_target array (either by finding it, or adding it if not found) target_record = association.target.detect { |record| record == existing_record } if target_record existing_record = target_record else association.add_to_target(existing_record) end end if !call_reject_if(association_name, attributes) assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy], assignment_opts) end elsif assignment_opts[:without_protection] association.build(attributes.except(*unassignable_keys(assignment_opts)), assignment_opts) else raise_nested_attributes_record_not_found!(association_name, attributes['id']) end end end def assign_to_or_mark_for_destruction(record, attributes, allow_destroy, assignment_opts) record.assign_attributes(attributes.except(*unassignable_keys(assignment_opts)), assignment_opts) record.mark_for_destruction if has_destroy_flag?(attributes) && allow_destroy end def unassignable_keys(assignment_opts) assignment_opts[:without_protection] ? UNASSIGNABLE_KEYS - %w[id] : UNASSIGNABLE_KEYS end end end end protected_attributes-1.1.3/lib/active_record/mass_assignment_security/persistence.rb000066400000000000000000000070551255126777300313530ustar00rootroot00000000000000require 'active_support/concern' module ActiveRecord module MassAssignmentSecurity # = Active Record Persistence module Persistence extend ActiveSupport::Concern module ClassMethods # Creates an object (or multiple objects) and saves it to the database, if validations pass. # The resulting object is returned whether the object was saved successfully to the database or not. # # The +attributes+ parameter can be either a Hash or an Array of Hashes. These Hashes describe the # attributes on the objects that are to be created. # # +create+ respects mass-assignment security and accepts either +:as+ or +:without_protection+ options # in the +options+ parameter. # # ==== Examples # # Create a single new object # User.create(:first_name => 'Jamie') # # # Create a single new object using the :admin mass-assignment security role # User.create({ :first_name => 'Jamie', :is_admin => true }, :as => :admin) # # # Create a single new object bypassing mass-assignment security # User.create({ :first_name => 'Jamie', :is_admin => true }, :without_protection => true) # # # Create an Array of new objects # User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }]) # # # Create a single object and pass it into a block to set other attributes. # User.create(:first_name => 'Jamie') do |u| # u.is_admin = false # end # # # Creating an Array of new objects using a block, where the block is executed for each object: # User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }]) do |u| # u.is_admin = false # end def create(attributes = nil, options = {}, &block) if attributes.is_a?(Array) attributes.collect { |attr| create(attr, options, &block) } else object = new(attributes, options, &block) object.save object end end end # Updates the attributes of the model from the passed-in hash and saves the # record, all wrapped in a transaction. If the object is invalid, the saving # will fail and false will be returned. # # When updating model attributes, mass-assignment security protection is respected. # If no +:as+ option is supplied then the +:default+ role will be used. # If you want to bypass the forbidden attributes protection then you can do so using # the +:without_protection+ option. def update(attributes, options = {}) # The following transaction covers any possible database side-effects of the # attributes assignment. For example, setting the IDs of a child collection. with_transaction_returning_status do assign_attributes(attributes, options) save end end alias :update_attributes :update # Updates its receiver just like +update_attributes+ but calls save! instead # of +save+, so an exception is raised if the record is invalid. def update!(attributes, options = {}) # The following transaction covers any possible database side-effects of the # attributes assignment. For example, setting the IDs of a child collection. with_transaction_returning_status do assign_attributes(attributes, options) save! end end alias :update_attributes! :update! end end end protected_attributes-1.1.3/lib/active_record/mass_assignment_security/reflection.rb000066400000000000000000000007041255126777300311530ustar00rootroot00000000000000module ActiveRecord module Reflection if defined?(AbstractReflection) class AbstractReflection undef :build_association def build_association(*options, &block) klass.new(*options, &block) end end else class AssociationReflection undef :build_association def build_association(*options, &block) klass.new(*options, &block) end end end end end protected_attributes-1.1.3/lib/active_record/mass_assignment_security/relation.rb000066400000000000000000000055511255126777300306430ustar00rootroot00000000000000module ActiveRecord class Relation undef :first_or_create undef :first_or_create! undef :first_or_initialize undef :find_or_initialize_by undef :find_or_create_by undef :find_or_create_by! # Tries to load the first record; if it fails, then create is called with the same arguments as this method. # # Expects arguments in the same format as +Base.create+. # # ==== Examples # # Find the first user named Penélope or create a new one. # User.where(:first_name => 'Penélope').first_or_create # # => # # # Find the first user named Penélope or create a new one. # # We already have one so the existing record will be returned. # User.where(:first_name => 'Penélope').first_or_create # # => # # # Find the first user named Scarlett or create a new one with a particular last name. # User.where(:first_name => 'Scarlett').first_or_create(:last_name => 'Johansson') # # => # # # Find the first user named Scarlett or create a new one with a different last name. # # We already have one so the existing record will be returned. # User.where(:first_name => 'Scarlett').first_or_create do |user| # user.last_name = "O'Hara" # end # # => def first_or_create(attributes = nil, options = {}, &block) first || create(attributes, options, &block) end # Like first_or_create but calls create! so an exception is raised if the created record is invalid. # # Expects arguments in the same format as Base.create!. def first_or_create!(attributes = nil, options = {}, &block) first || create!(attributes, options, &block) end # Like first_or_create but calls new instead of create. # # Expects arguments in the same format as Base.new. def first_or_initialize(attributes = nil, options = {}, &block) first || new(attributes, options, &block) end def find_or_initialize_by(attributes, options = {}, &block) find_by(attributes) || new(attributes, options, &block) end def find_or_create_by(attributes, options = {}, &block) find_by(attributes) || create(attributes, options, &block) end def find_or_create_by!(attributes, options = {}, &block) find_by(attributes) || create!(attributes, options, &block) end end module QueryMethods protected def sanitize_forbidden_attributes(attributes) #:nodoc: if !model._uses_mass_assignment_security sanitize_for_mass_assignment(attributes) else attributes end end end end protected_attributes-1.1.3/lib/active_record/mass_assignment_security/validations.rb000066400000000000000000000012721255126777300313370ustar00rootroot00000000000000require 'active_support/concern' module ActiveRecord module MassAssignmentSecurity module Validations extend ActiveSupport::Concern module ClassMethods # Creates an object just like Base.create but calls save! instead of +save+ # so an exception is raised if the record is invalid. def create!(attributes = nil, options = {}, &block) if attributes.is_a?(Array) attributes.collect { |attr| create!(attr, options, &block) } else object = new(attributes, options) yield(object) if block_given? object.save! object end end end end end end protected_attributes-1.1.3/lib/protected_attributes.rb000066400000000000000000000006021255126777300233220ustar00rootroot00000000000000require "active_model/mass_assignment_security" require "protected_attributes/railtie" if defined? Rails::Railtie require "protected_attributes/version" ActiveSupport.on_load :active_record do require "active_record/mass_assignment_security" end ActiveSupport.on_load :action_controller do require "action_controller/accessible_params_wrapper" end module ProtectedAttributes end protected_attributes-1.1.3/lib/protected_attributes/000077500000000000000000000000001255126777300227775ustar00rootroot00000000000000protected_attributes-1.1.3/lib/protected_attributes/railtie.rb000066400000000000000000000007101255126777300247530ustar00rootroot00000000000000module ProtectedAttributes class Railtie < ::Rails::Railtie initializer "protected_attributes.active_record", :before => "active_record.set_configs" do |app| if app.config.respond_to?(:active_record) && app.config.active_record.delete(:whitelist_attributes) ActiveSupport::Deprecation.warn "config.active_record.whitelist_attributes is deprecated and have no effect. Remove its call from the configuration." end end end end protected_attributes-1.1.3/lib/protected_attributes/version.rb000066400000000000000000000000631255126777300250100ustar00rootroot00000000000000module ProtectedAttributes VERSION = "1.1.3" end protected_attributes-1.1.3/protected_attributes.gemspec000066400000000000000000000021101255126777300235700ustar00rootroot00000000000000# -*- encoding: utf-8 -*- lib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'protected_attributes/version' Gem::Specification.new do |gem| gem.name = "protected_attributes" gem.version = ProtectedAttributes::VERSION gem.authors = ["David Heinemeier Hansson"] gem.email = ["david@loudthinking.com"] gem.description = %q{Protect attributes from mass assignment} gem.summary = %q{Protect attributes from mass assignment in Active Record models} gem.homepage = "https://github.com/rails/protected_attributes" gem.license = "MIT" gem.files = Dir["LICENSE.txt", "README.md", "lib/**/*"] gem.require_paths = ["lib"] gem.add_dependency "activemodel", ">= 4.0.1", "< 5.0" gem.add_development_dependency "activerecord", ">= 4.0.1", "< 5.0" gem.add_development_dependency "actionpack", ">= 4.0.1", "< 5.0" gem.add_development_dependency "railties", ">= 4.0.1", "< 5.0" gem.add_development_dependency "sqlite3" gem.add_development_dependency "mocha" end protected_attributes-1.1.3/test/000077500000000000000000000000001255126777300167515ustar00rootroot00000000000000protected_attributes-1.1.3/test/abstract_unit.rb000066400000000000000000000104361255126777300221440ustar00rootroot00000000000000require 'action_dispatch' require 'action_controller' require 'active_support/dependencies' def active_support_4_0? ActiveSupport::VERSION::MAJOR == 4 && ActiveSupport::VERSION::MINOR == 0 end if active_support_4_0? require 'active_support/core_ext/class/attribute_accessors' else require 'active_support/core_ext/module/attribute_accessors' end module SetupOnce extend ActiveSupport::Concern included do cattr_accessor :setup_once_block self.setup_once_block = nil setup :run_setup_once end module ClassMethods def setup_once(&block) self.setup_once_block = block end end private def run_setup_once if self.setup_once_block self.setup_once_block.call self.setup_once_block = nil end end end SharedTestRoutes = ActionDispatch::Routing::RouteSet.new module ActiveSupport class TestCase include SetupOnce # Hold off drawing routes until all the possible controller classes # have been loaded. setup_once do SharedTestRoutes.draw do get ':controller(/:action)' end ActionDispatch::IntegrationTest.app.routes.draw do get ':controller(/:action)' end end end end class RoutedRackApp attr_reader :routes def initialize(routes, &blk) @routes = routes @stack = ActionDispatch::MiddlewareStack.new(&blk).build(@routes) end def call(env) @stack.call(env) end end class ActionDispatch::IntegrationTest < ActiveSupport::TestCase setup do @routes = SharedTestRoutes end def self.build_app(routes = nil) RoutedRackApp.new(routes || ActionDispatch::Routing::RouteSet.new) do |middleware| middleware.use "ActionDispatch::DebugExceptions" middleware.use "ActionDispatch::Callbacks" middleware.use "ActionDispatch::ParamsParser" middleware.use "ActionDispatch::Cookies" middleware.use "ActionDispatch::Flash" middleware.use "Rack::Head" yield(middleware) if block_given? end end self.app = build_app # Stub Rails dispatcher so it does not get controller references and # simply return the controller#action as Rack::Body. class StubDispatcher < ::ActionDispatch::Routing::RouteSet::Dispatcher protected def controller_reference(controller_param) controller_param end def dispatch(controller, action, env) [200, {'Content-Type' => 'text/html'}, ["#{controller}##{action}"]] end end def self.stub_controllers old_dispatcher = ActionDispatch::Routing::RouteSet::Dispatcher ActionDispatch::Routing::RouteSet.module_eval { remove_const :Dispatcher } ActionDispatch::Routing::RouteSet.module_eval { const_set :Dispatcher, StubDispatcher } yield ActionDispatch::Routing::RouteSet.new ensure ActionDispatch::Routing::RouteSet.module_eval { remove_const :Dispatcher } ActionDispatch::Routing::RouteSet.module_eval { const_set :Dispatcher, old_dispatcher } end def with_routing(&block) temporary_routes = ActionDispatch::Routing::RouteSet.new old_app, self.class.app = self.class.app, self.class.build_app(temporary_routes) old_routes = SharedTestRoutes silence_warnings { Object.const_set(:SharedTestRoutes, temporary_routes) } yield temporary_routes ensure self.class.app = old_app silence_warnings { Object.const_set(:SharedTestRoutes, old_routes) } end def with_autoload_path(path) path = File.join(File.dirname(__FILE__), "fixtures", path) if ActiveSupport::Dependencies.autoload_paths.include?(path) yield else begin ActiveSupport::Dependencies.autoload_paths << path yield ensure ActiveSupport::Dependencies.autoload_paths.reject! {|p| p == path} ActiveSupport::Dependencies.clear end end end end module ActionController class Base include ActionController::Testing # This stub emulates the Railtie including the URL helpers from a Rails application include SharedTestRoutes.url_helpers include SharedTestRoutes.mounted_helpers #self.view_paths = FIXTURE_LOAD_PATH def self.test_routes(&block) routes = ActionDispatch::Routing::RouteSet.new routes.draw(&block) include routes.url_helpers end end class TestCase include ActionDispatch::TestProcess setup do @routes = SharedTestRoutes end end end protected_attributes-1.1.3/test/accessible_params_wrapper_test.rb000066400000000000000000000047751255126777300255520ustar00rootroot00000000000000require 'test_helper' require 'abstract_unit' require 'action_controller/accessible_params_wrapper' module ParamsWrapperTestHelp def with_default_wrapper_options(&block) @controller.class._set_wrapper_options({:format => [:json]}) @controller.class.inherited(@controller.class) yield end def assert_parameters(expected) assert_equal expected, self.class.controller_class.last_parameters end end class AccessibleParamsWrapperTest < ActionController::TestCase include ParamsWrapperTestHelp class UsersController < ActionController::Base class << self attr_accessor :last_parameters end def parse self.class.last_parameters = request.params.except(:controller, :action) head :ok end end class User; end class Person; end tests UsersController def teardown UsersController.last_parameters = nil end def test_derived_wrapped_keys_from_matching_model User.expects(:respond_to?).with(:accessible_attributes).returns(false) User.expects(:respond_to?).with(:attribute_names).returns(true) User.expects(:attribute_names).twice.returns(["username"]) with_default_wrapper_options do @request.env['CONTENT_TYPE'] = 'application/json' post :parse, { 'username' => 'sikachu', 'title' => 'Developer' } assert_parameters({ 'username' => 'sikachu', 'title' => 'Developer', 'user' => { 'username' => 'sikachu' }}) end end def test_derived_wrapped_keys_from_specified_model with_default_wrapper_options do Person.expects(:respond_to?).with(:accessible_attributes).returns(false) Person.expects(:respond_to?).with(:attribute_names).returns(true) Person.expects(:attribute_names).twice.returns(["username"]) UsersController.wrap_parameters Person @request.env['CONTENT_TYPE'] = 'application/json' post :parse, { 'username' => 'sikachu', 'title' => 'Developer' } assert_parameters({ 'username' => 'sikachu', 'title' => 'Developer', 'person' => { 'username' => 'sikachu' }}) end end def test_accessible_wrapped_keys_from_matching_model User.expects(:respond_to?).with(:accessible_attributes).returns(true) User.expects(:accessible_attributes).with(:default).twice.returns(["username"]) with_default_wrapper_options do @request.env['CONTENT_TYPE'] = 'application/json' post :parse, { 'username' => 'sikachu', 'title' => 'Developer' } assert_parameters({ 'username' => 'sikachu', 'title' => 'Developer', 'user' => { 'username' => 'sikachu' }}) end end end protected_attributes-1.1.3/test/ar_helper.rb000066400000000000000000000040001255126777300212310ustar00rootroot00000000000000require 'active_record' ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:') ActiveRecord::Schema.verbose = false ActiveRecord::Schema.define do create_table :accounts, :force => true do |t| t.integer :firm_id t.string :firm_name t.integer :credit_limit end create_table :companies, :force => true do |t| t.string :type t.integer :firm_id t.string :firm_name t.string :name t.integer :client_of t.integer :rating, :default => 1 t.integer :account_id t.string :description, :default => "" end add_index :companies, [:firm_id, :type, :rating], :name => "company_index" add_index :companies, [:firm_id, :type], :name => "company_partial_index", :where => "rating > 10" create_table :keyboards, :force => true, :id => false do |t| t.primary_key :key_number t.string :name end create_table :people, :force => true do |t| t.string :first_name, :null => false t.string :gender, :limit => 1 t.string :comments t.references :best_friend t.references :best_friend_of t.timestamps null: false end create_table :subscribers, :force => true, :id => false do |t| t.string :nick, :null => false t.string :name end add_index :subscribers, :nick, :unique => true create_table :books, :force => true do |t| t.string :title end create_table :tasks, :force => true do |t| t.datetime :starting t.datetime :ending end create_table :pirates, :force => true do |t| t.string :name end create_table :groups, :force => true do |t| end create_table :memberships, :force => true do |t| t.integer "group_id" t.integer "pirate_id" end create_table :teams, :force => true create_table :wolves, :force => true create_table :vampires, :force => true create_table :battles, :force => true do |t| t.integer "team_id" t.integer "battle_id" t.string "battle_type" end end QUOTED_TYPE = ActiveRecord::Base.connection.quote_column_name('type') protected_attributes-1.1.3/test/attribute_sanitization_test.rb000066400000000000000000001223351255126777300251420ustar00rootroot00000000000000require 'test_helper' require 'ar_helper' require 'active_record/mass_assignment_security' require 'models/battle' require 'models/company' require 'models/group' require 'models/keyboard' require 'models/membership' require 'models/person' require 'models/pirate' require 'models/subscriber' require 'models/task' require 'models/team' require 'models/vampire' require 'models/wolf' module MassAssignmentTestHelpers def teardown ActiveRecord::Base.send(:descendants).each do |klass| begin klass.delete_all rescue end end end def attributes_hash { :id => 5, :first_name => 'Josh', :gender => 'm', :comments => 'rides a sweet bike' } end def assert_default_attributes(person, create = false) unless create assert_nil person.id else assert !!person.id end assert_equal 'Josh', person.first_name assert_equal 'm', person.gender assert_nil person.comments end def assert_admin_attributes(person, create = false) unless create assert_nil person.id else assert !!person.id end assert_equal 'Josh', person.first_name assert_equal 'm', person.gender assert_equal 'rides a sweet bike', person.comments end def assert_all_attributes(person) assert_equal 5, person.id assert_equal 'Josh', person.first_name assert_equal 'm', person.gender assert_equal 'rides a sweet bike', person.comments end def with_strict_sanitizer ActiveRecord::Base.mass_assignment_sanitizer = :strict yield ensure ActiveRecord::Base.mass_assignment_sanitizer = :logger end end module MassAssignmentRelationTestHelpers def setup super @person = LoosePerson.create(attributes_hash) end end class AttributeSanitizationTest < ActiveSupport::TestCase include MassAssignmentTestHelpers def test_customized_primary_key_remains_protected subscriber = Subscriber.new(:nick => 'webster123', :name => 'nice try') assert_nil subscriber.id keyboard = Keyboard.new(:key_number => 9, :name => 'nice try') assert_nil keyboard.id end def test_customized_primary_key_remains_protected_when_referred_to_as_id subscriber = Subscriber.new(:id => 'webster123', :name => 'nice try') assert_nil subscriber.id keyboard = Keyboard.new(:id => 9, :name => 'nice try') assert_nil keyboard.id end def test_mass_assigning_invalid_attribute firm = Firm.new assert_raise(ActiveRecord::UnknownAttributeError) do firm.attributes = { "id" => 5, "type" => "Client", "i_dont_even_exist" => 20 } end end def test_mass_assigning_does_not_choke_on_nil assert_nil Firm.new.assign_attributes(nil) end def test_mass_assigning_does_not_choke_on_empty_hash assert_nil Firm.new.assign_attributes({}) end def test_assign_attributes_uses_default_role_when_no_role_is_provided p = LoosePerson.new p.assign_attributes(attributes_hash) assert_default_attributes(p) end def test_assign_attributes_skips_mass_assignment_security_protection_when_without_protection_is_used p = LoosePerson.new p.assign_attributes(attributes_hash, :without_protection => true) assert_all_attributes(p) end def test_assign_attributes_with_default_role_and_attr_protected_attributes p = LoosePerson.new p.assign_attributes(attributes_hash, :as => :default) assert_default_attributes(p) end def test_assign_attributes_with_admin_role_and_attr_protected_attributes p = LoosePerson.new p.assign_attributes(attributes_hash, :as => :admin) assert_admin_attributes(p) end def test_assign_attributes_with_default_role_and_attr_accessible_attributes p = TightPerson.new p.assign_attributes(attributes_hash, :as => :default) assert_default_attributes(p) end def test_assign_attributes_with_admin_role_and_attr_accessible_attributes p = TightPerson.new p.assign_attributes(attributes_hash, :as => :admin) assert_admin_attributes(p) end def test_new_with_attr_accessible_attributes p = TightPerson.new(attributes_hash) assert_default_attributes(p) end def test_new_with_attr_protected_attributes p = LoosePerson.new(attributes_hash) assert_default_attributes(p) end def test_create_with_attr_accessible_attributes p = TightPerson.create(attributes_hash) assert_default_attributes(p, true) end def test_create_with_attr_protected_attributes p = LoosePerson.create(attributes_hash) assert_default_attributes(p, true) end def test_new_with_admin_role_with_attr_accessible_attributes p = TightPerson.new(attributes_hash, :as => :admin) assert_admin_attributes(p) end def test_new_with_admin_role_with_attr_protected_attributes p = LoosePerson.new(attributes_hash, :as => :admin) assert_admin_attributes(p) end def test_create_with_admin_role_with_attr_accessible_attributes p = TightPerson.create(attributes_hash, :as => :admin) assert_admin_attributes(p, true) end def test_create_with_admin_role_with_attr_protected_attributes p = LoosePerson.create(attributes_hash, :as => :admin) assert_admin_attributes(p, true) end def test_create_with_bang_with_admin_role_with_attr_accessible_attributes p = TightPerson.create!(attributes_hash, :as => :admin) assert_admin_attributes(p, true) end def test_create_with_bang_with_admin_role_with_attr_protected_attributes p = LoosePerson.create!(attributes_hash, :as => :admin) assert_admin_attributes(p, true) end def test_new_with_without_protection_with_attr_accessible_attributes p = TightPerson.new(attributes_hash, :without_protection => true) assert_all_attributes(p) end def test_new_with_without_protection_with_attr_protected_attributes p = LoosePerson.new(attributes_hash, :without_protection => true) assert_all_attributes(p) end def test_create_with_without_protection_with_attr_accessible_attributes p = TightPerson.create(attributes_hash, :without_protection => true) assert_all_attributes(p) end def test_create_with_without_protection_with_attr_protected_attributes p = LoosePerson.create(attributes_hash, :without_protection => true) assert_all_attributes(p) end def test_create_with_bang_with_without_protection_with_attr_accessible_attributes p = TightPerson.create!(attributes_hash, :without_protection => true) assert_all_attributes(p) end def test_create_with_bang_with_without_protection_with_attr_protected_attributes p = LoosePerson.create!(attributes_hash, :without_protection => true) assert_all_attributes(p) end def test_protection_against_class_attribute_writers attribute_writers = [:logger, :configurations, :primary_key_prefix_type, :table_name_prefix, :table_name_suffix, :pluralize_table_names, :default_timezone, :schema_format, :lock_optimistically, :timestamped_migrations, :default_scopes, :connection_handler, :nested_attributes_options, :attribute_method_matchers, :time_zone_aware_attributes, :skip_time_zone_conversion_for_attributes] attribute_writers.push(:_attr_readonly) if active_record_40? attribute_writers.each do |method| assert_respond_to Task, method assert_respond_to Task, "#{method}=" assert_respond_to Task.new, method unless method == :configurations && !active_record_40? assert !Task.new.respond_to?("#{method}=") end end def test_new_with_protected_inheritance_column firm = Company.new(type: "Firm") assert_equal Company, firm.class end def test_new_with_accessible_inheritance_column corporation = Corporation.new(type: "SpecialCorporation") assert_equal SpecialCorporation, corporation.class end def test_new_with_invalid_inheritance_column_class assert_raise(ActiveRecord::SubclassNotFound) { Corporation.new(type: "InvalidCorporation") } end def test_new_with_unrelated_inheritance_column_class assert_raise(ActiveRecord::SubclassNotFound) { Corporation.new(type: "Person") } end def test_update_attributes_as_admin person = TightPerson.create({ "first_name" => 'Joshua' }) person.update_attributes({ "first_name" => 'Josh', "gender" => 'm', "comments" => 'from NZ' }, :as => :admin) person.reload assert_equal 'Josh', person.first_name assert_equal 'm', person.gender assert_equal 'from NZ', person.comments end def test_update_attributes_without_protection person = TightPerson.create({ "first_name" => 'Joshua' }) person.update_attributes({ "first_name" => 'Josh', "gender" => 'm', "comments" => 'from NZ' }, :without_protection => true) person.reload assert_equal 'Josh', person.first_name assert_equal 'm', person.gender assert_equal 'from NZ', person.comments end def test_update_attributes_with_bang_as_admin person = TightPerson.create({ "first_name" => 'Joshua' }) person.update_attributes!({ "first_name" => 'Josh', "gender" => 'm', "comments" => 'from NZ' }, :as => :admin) person.reload assert_equal 'Josh', person.first_name assert_equal 'm', person.gender assert_equal 'from NZ', person.comments end def test_update_attributes_with_bang_without_protection person = TightPerson.create({ "first_name" => 'Joshua' }) person.update_attributes!({ "first_name" => 'Josh', "gender" => 'm', "comments" => 'from NZ' }, :without_protection => true) person.reload assert_equal 'Josh', person.first_name assert_equal 'm', person.gender assert_equal 'from NZ', person.comments end def test_update_as_admin person = TightPerson.create({ "first_name" => 'Joshua' }) person.update({ "first_name" => 'Josh', "gender" => 'm', "comments" => 'from NZ' }, :as => :admin) person.reload assert_equal 'Josh', person.first_name assert_equal 'm', person.gender assert_equal 'from NZ', person.comments end def test_update_without_protection person = TightPerson.create({ "first_name" => 'Joshua' }) person.update({ "first_name" => 'Josh', "gender" => 'm', "comments" => 'from NZ' }, :without_protection => true) person.reload assert_equal 'Josh', person.first_name assert_equal 'm', person.gender assert_equal 'from NZ', person.comments end def test_update_with_bang_as_admin person = TightPerson.create({ "first_name" => 'Joshua' }) person.update!({ "first_name" => 'Josh', "gender" => 'm', "comments" => 'from NZ' }, :as => :admin) person.reload assert_equal 'Josh', person.first_name assert_equal 'm', person.gender assert_equal 'from NZ', person.comments end def test_update_with_bang_without_protection person = TightPerson.create({ "first_name" => 'Joshua' }) person.update!({ "first_name" => 'Josh', "gender" => 'm', "comments" => 'from NZ' }, :without_protection => true) person.reload assert_equal 'Josh', person.first_name assert_equal 'm', person.gender assert_equal 'from NZ', person.comments end end class MassAssignmentSecurityRelationTest < ActiveSupport::TestCase include MassAssignmentTestHelpers def test_find_or_initialize_by_with_attr_accessible_attributes p = TightPerson.where(first_name: 'Josh').first_or_initialize(attributes_hash) assert_default_attributes(p) end def test_find_or_initialize_by_with_admin_role_with_attr_accessible_attributes p = TightPerson.where(first_name: 'Josh').first_or_initialize(attributes_hash, as: :admin) assert_admin_attributes(p) end def test_find_or_initialize_by_with_attr_protected_attributes p = LoosePerson.where(first_name: 'Josh').first_or_initialize(attributes_hash) assert_default_attributes(p) end def test_find_or_initialize_by_with_admin_role_with_attr_protected_attributes p = LoosePerson.where(first_name: 'Josh').first_or_initialize(attributes_hash, as: :admin) assert_admin_attributes(p) end def test_find_or_create_by_with_attr_accessible_attributes p = TightPerson.where(first_name: 'Josh').first_or_create(attributes_hash) assert_default_attributes(p, true) end def test_find_or_create_by_with_admin_role_with_attr_accessible_attributes p = TightPerson.where(first_name: 'Josh').first_or_create(attributes_hash, as: :admin) assert_admin_attributes(p, true) end def test_find_or_create_by_with_attr_protected_attributes p = LoosePerson.where(first_name: 'Josh').first_or_create(attributes_hash) assert_default_attributes(p, true) end def test_find_or_create_by_with_admin_role_with_attr_protected_attributes p = LoosePerson.where(first_name: 'Josh').first_or_create(attributes_hash, as: :admin) assert_admin_attributes(p, true) end def test_find_or_create_by_bang_with_attr_accessible_attributes p = TightPerson.where(first_name: 'Josh').first_or_create!(attributes_hash) assert_default_attributes(p, true) end def test_find_or_create_by_bang_with_admin_role_with_attr_accessible_attributes p = TightPerson.where(first_name: 'Josh').first_or_create!(attributes_hash, as: :admin) assert_admin_attributes(p, true) end def test_find_or_create_by_bang_with_attr_protected_attributes p = LoosePerson.where(first_name: 'Josh').first_or_create!(attributes_hash) assert_default_attributes(p, true) end def test_find_or_create_by_bang_with_admin_role_with_attr_protected_attributes p = LoosePerson.where(first_name: 'Josh').first_or_create!(attributes_hash, as: :admin) assert_admin_attributes(p, true) end end class MassAssignmentSecurityFindersTest < ActiveSupport::TestCase include MassAssignmentTestHelpers def test_find_or_initialize_by_with_attr_accessible_attributes p = TightPerson.find_or_initialize_by(attributes_hash) assert_default_attributes(p) end def test_find_or_initialize_by_with_admin_role_with_attr_accessible_attributes p = TightPerson.find_or_initialize_by(attributes_hash, as: :admin) assert_admin_attributes(p) end def test_find_or_initialize_by_with_attr_protected_attributes p = LoosePerson.find_or_initialize_by(attributes_hash) assert_default_attributes(p) end def test_find_or_initialize_by_with_admin_role_with_attr_protected_attributes p = LoosePerson.find_or_initialize_by(attributes_hash, as: :admin) assert_admin_attributes(p) end def test_find_or_create_by_with_attr_accessible_attributes p = TightPerson.find_or_create_by(attributes_hash) assert_default_attributes(p, true) end def test_find_or_create_by_with_admin_role_with_attr_accessible_attributes p = TightPerson.find_or_create_by(attributes_hash, as: :admin) assert_admin_attributes(p, true) end def test_find_or_create_by_with_attr_protected_attributes p = LoosePerson.find_or_create_by(attributes_hash) assert_default_attributes(p, true) end def test_find_or_create_by_with_admin_role_with_attr_protected_attributes p = LoosePerson.find_or_create_by(attributes_hash, as: :admin) assert_admin_attributes(p, true) end def test_find_or_create_by_bang_with_attr_accessible_attributes p = TightPerson.find_or_create_by!(attributes_hash) assert_default_attributes(p, true) end def test_find_or_create_by_bang_with_admin_role_with_attr_accessible_attributes p = TightPerson.find_or_create_by!(attributes_hash, as: :admin) assert_admin_attributes(p, true) end def test_find_or_create_by_bang_with_attr_protected_attributes p = LoosePerson.find_or_create_by!(attributes_hash) assert_default_attributes(p, true) end def test_find_or_create_by_bang_with_admin_role_with_attr_protected_attributes p = LoosePerson.find_or_create_by!(attributes_hash, as: :admin) assert_admin_attributes(p, true) end end if active_record_40? # This class should be deleted when we remove activerecord-deprecated_finders as a # dependency. class MassAssignmentSecurityDeprecatedFindersTest < ActiveSupport::TestCase include MassAssignmentTestHelpers def setup super @deprecation_behavior = ActiveSupport::Deprecation.behavior ActiveSupport::Deprecation.behavior = :silence end def teardown super ActiveSupport::Deprecation.behavior = @deprecation_behavior end def test_find_or_initialize_by_with_attr_accessible_attributes p = TightPerson.find_or_initialize_by_first_name('Josh', attributes_hash) assert_default_attributes(p) end def test_find_or_initialize_by_with_admin_role_with_attr_accessible_attributes p = TightPerson.find_or_initialize_by_first_name('Josh', attributes_hash, :as => :admin) assert_admin_attributes(p) end def test_find_or_initialize_by_with_attr_protected_attributes p = LoosePerson.find_or_initialize_by_first_name('Josh', attributes_hash) assert_default_attributes(p) end def test_find_or_initialize_by_with_admin_role_with_attr_protected_attributes p = LoosePerson.find_or_initialize_by_first_name('Josh', attributes_hash, :as => :admin) assert_admin_attributes(p) end def test_find_or_create_by_with_attr_accessible_attributes p = TightPerson.find_or_create_by_first_name('Josh', attributes_hash) assert_default_attributes(p, true) end def test_find_or_create_by_with_admin_role_with_attr_accessible_attributes p = TightPerson.find_or_create_by_first_name('Josh', attributes_hash, :as => :admin) assert_admin_attributes(p, true) end def test_find_or_create_by_with_attr_protected_attributes p = LoosePerson.find_or_create_by_first_name('Josh', attributes_hash) assert_default_attributes(p, true) end def test_find_or_create_by_with_admin_role_with_attr_protected_attributes p = LoosePerson.find_or_create_by_first_name('Josh', attributes_hash, :as => :admin) assert_admin_attributes(p, true) end end end class MassAssignmentSecurityHasOneRelationsTest < ActiveSupport::TestCase include MassAssignmentTestHelpers include MassAssignmentRelationTestHelpers # build def test_has_one_build_with_attr_protected_attributes best_friend = @person.build_best_friend(attributes_hash) assert_default_attributes(best_friend) end def test_has_one_build_with_attr_accessible_attributes best_friend = @person.build_best_friend(attributes_hash) assert_default_attributes(best_friend) end def test_has_one_build_with_admin_role_with_attr_protected_attributes best_friend = @person.build_best_friend(attributes_hash, :as => :admin) assert_admin_attributes(best_friend) end def test_has_one_build_with_admin_role_with_attr_accessible_attributes best_friend = @person.build_best_friend(attributes_hash, :as => :admin) assert_admin_attributes(best_friend) end def test_has_one_build_without_protection best_friend = @person.build_best_friend(attributes_hash, :without_protection => true) assert_all_attributes(best_friend) end def test_has_one_build_with_strict_sanitizer with_strict_sanitizer do best_friend = @person.build_best_friend(attributes_hash.except(:id, :comments)) assert_equal @person.id, best_friend.best_friend_id end end # create def test_has_one_create_with_attr_protected_attributes best_friend = @person.create_best_friend(attributes_hash) assert_default_attributes(best_friend, true) end def test_has_one_create_with_attr_accessible_attributes best_friend = @person.create_best_friend(attributes_hash) assert_default_attributes(best_friend, true) end def test_has_one_create_with_admin_role_with_attr_protected_attributes best_friend = @person.create_best_friend(attributes_hash, :as => :admin) assert_admin_attributes(best_friend, true) end def test_has_one_create_with_admin_role_with_attr_accessible_attributes best_friend = @person.create_best_friend(attributes_hash, :as => :admin) assert_admin_attributes(best_friend, true) end def test_has_one_create_without_protection best_friend = @person.create_best_friend(attributes_hash, :without_protection => true) assert_all_attributes(best_friend) end def test_has_one_create_with_strict_sanitizer with_strict_sanitizer do best_friend = @person.create_best_friend(attributes_hash.except(:id, :comments)) assert_equal @person.id, best_friend.best_friend_id end end # create! def test_has_one_create_with_bang_with_attr_protected_attributes best_friend = @person.create_best_friend!(attributes_hash) assert_default_attributes(best_friend, true) end def test_has_one_create_with_bang_with_attr_accessible_attributes best_friend = @person.create_best_friend!(attributes_hash) assert_default_attributes(best_friend, true) end def test_has_one_create_with_bang_with_admin_role_with_attr_protected_attributes best_friend = @person.create_best_friend!(attributes_hash, :as => :admin) assert_admin_attributes(best_friend, true) end def test_has_one_create_with_bang_with_admin_role_with_attr_accessible_attributes best_friend = @person.create_best_friend!(attributes_hash, :as => :admin) assert_admin_attributes(best_friend, true) end def test_has_one_create_with_bang_without_protection best_friend = @person.create_best_friend!(attributes_hash, :without_protection => true) assert_all_attributes(best_friend) end def test_has_one_create_with_bang_with_strict_sanitizer with_strict_sanitizer do best_friend = @person.create_best_friend!(attributes_hash.except(:id, :comments)) assert_equal @person.id, best_friend.best_friend_id end end end class MassAssignmentSecurityBelongsToRelationsTest < ActiveSupport::TestCase include MassAssignmentTestHelpers include MassAssignmentRelationTestHelpers # build def test_belongs_to_build_with_attr_protected_attributes best_friend = @person.build_best_friend_of(attributes_hash) assert_default_attributes(best_friend) end def test_belongs_to_build_with_attr_accessible_attributes best_friend = @person.build_best_friend_of(attributes_hash) assert_default_attributes(best_friend) end def test_belongs_to_build_with_admin_role_with_attr_protected_attributes best_friend = @person.build_best_friend_of(attributes_hash, :as => :admin) assert_admin_attributes(best_friend) end def test_belongs_to_build_with_admin_role_with_attr_accessible_attributes best_friend = @person.build_best_friend_of(attributes_hash, :as => :admin) assert_admin_attributes(best_friend) end def test_belongs_to_build_without_protection best_friend = @person.build_best_friend_of(attributes_hash, :without_protection => true) assert_all_attributes(best_friend) end # create def test_belongs_to_create_with_attr_protected_attributes best_friend = @person.create_best_friend_of(attributes_hash) assert_default_attributes(best_friend, true) end def test_belongs_to_create_with_attr_accessible_attributes best_friend = @person.create_best_friend_of(attributes_hash) assert_default_attributes(best_friend, true) end def test_belongs_to_create_with_admin_role_with_attr_protected_attributes best_friend = @person.create_best_friend_of(attributes_hash, :as => :admin) assert_admin_attributes(best_friend, true) end def test_belongs_to_create_with_admin_role_with_attr_accessible_attributes best_friend = @person.create_best_friend_of(attributes_hash, :as => :admin) assert_admin_attributes(best_friend, true) end def test_belongs_to_create_without_protection best_friend = @person.create_best_friend_of(attributes_hash, :without_protection => true) assert_all_attributes(best_friend) end def test_belongs_to_create_with_strict_sanitizer with_strict_sanitizer do best_friend = @person.create_best_friend_of(attributes_hash.except(:id, :comments)) assert_equal best_friend.id, @person.best_friend_of_id end end # create! def test_belongs_to_create_with_bang_with_attr_protected_attributes best_friend = @person.create_best_friend!(attributes_hash) assert_default_attributes(best_friend, true) end def test_belongs_to_create_with_bang_with_attr_accessible_attributes best_friend = @person.create_best_friend!(attributes_hash) assert_default_attributes(best_friend, true) end def test_belongs_to_create_with_bang_with_admin_role_with_attr_protected_attributes best_friend = @person.create_best_friend!(attributes_hash, :as => :admin) assert_admin_attributes(best_friend, true) end def test_belongs_to_create_with_bang_with_admin_role_with_attr_accessible_attributes best_friend = @person.create_best_friend!(attributes_hash, :as => :admin) assert_admin_attributes(best_friend, true) end def test_belongs_to_create_with_bang_without_protection best_friend = @person.create_best_friend!(attributes_hash, :without_protection => true) assert_all_attributes(best_friend) end def test_belongs_to_create_with_bang_with_strict_sanitizer with_strict_sanitizer do best_friend = @person.create_best_friend_of!(attributes_hash.except(:id, :comments)) assert_equal best_friend.id, @person.best_friend_of_id end end end class MassAssignmentSecurityHasManyRelationsTest < ActiveSupport::TestCase include MassAssignmentTestHelpers include MassAssignmentRelationTestHelpers # build def test_has_many_build_with_attr_protected_attributes best_friend = @person.best_friends.build(attributes_hash) assert_default_attributes(best_friend) end def test_has_many_build_with_attr_accessible_attributes best_friend = @person.best_friends.build(attributes_hash) assert_default_attributes(best_friend) end def test_has_many_build_with_admin_role_with_attr_protected_attributes best_friend = @person.best_friends.build(attributes_hash, :as => :admin) assert_admin_attributes(best_friend) end def test_has_many_build_with_admin_role_with_attr_accessible_attributes best_friend = @person.best_friends.build(attributes_hash, :as => :admin) assert_admin_attributes(best_friend) end def test_has_many_build_without_protection best_friend = @person.best_friends.build(attributes_hash, :without_protection => true) assert_all_attributes(best_friend) end def test_has_many_build_with_strict_sanitizer with_strict_sanitizer do best_friend = @person.best_friends.build(attributes_hash.except(:id, :comments)) assert_equal @person.id, best_friend.best_friend_id end end def test_has_many_through_build_with_attr_accessible_attributes group = Group.create! pirate = group.members.build(name: "Murphy") assert_equal "Murphy", pirate.name end # new def test_has_many_new_with_attr_protected_attributes best_friend = @person.best_friends.new(attributes_hash) assert_default_attributes(best_friend) end def test_has_many_new_with_attr_accessible_attributes best_friend = @person.best_friends.new(attributes_hash) assert_default_attributes(best_friend) end def test_has_many_new_with_admin_role_with_attr_protected_attributes best_friend = @person.best_friends.new(attributes_hash, :as => :admin) assert_admin_attributes(best_friend) end def test_has_many_new_with_admin_role_with_attr_accessible_attributes best_friend = @person.best_friends.new(attributes_hash, :as => :admin) assert_admin_attributes(best_friend) end def test_has_many_new_without_protection best_friend = @person.best_friends.new(attributes_hash, :without_protection => true) assert_all_attributes(best_friend) end def test_has_many_new_with_strict_sanitizer with_strict_sanitizer do best_friend = @person.best_friends.new(attributes_hash.except(:id, :comments)) assert_equal @person.id, best_friend.best_friend_id end end # create def test_has_many_create_with_attr_protected_attributes best_friend = @person.best_friends.create(attributes_hash) assert_default_attributes(best_friend, true) end def test_has_many_create_with_attr_accessible_attributes best_friend = @person.best_friends.create(attributes_hash) assert_default_attributes(best_friend, true) end def test_has_many_create_with_admin_role_with_attr_protected_attributes best_friend = @person.best_friends.create(attributes_hash, :as => :admin) assert_admin_attributes(best_friend, true) end def test_has_many_create_with_admin_role_with_attr_accessible_attributes best_friend = @person.best_friends.create(attributes_hash, :as => :admin) assert_admin_attributes(best_friend, true) end def test_has_many_create_without_protection best_friend = @person.best_friends.create(attributes_hash, :without_protection => true) assert_all_attributes(best_friend) end def test_has_many_create_with_strict_sanitizer with_strict_sanitizer do best_friend = @person.best_friends.create(attributes_hash.except(:id, :comments)) assert_equal @person.id, best_friend.best_friend_id end end # create! def test_has_many_create_with_bang_with_attr_protected_attributes best_friend = @person.best_friends.create!(attributes_hash) assert_default_attributes(best_friend, true) end def test_has_many_create_with_bang_with_attr_accessible_attributes best_friend = @person.best_friends.create!(attributes_hash) assert_default_attributes(best_friend, true) end def test_has_many_create_with_bang_with_admin_role_with_attr_protected_attributes best_friend = @person.best_friends.create!(attributes_hash, :as => :admin) assert_admin_attributes(best_friend, true) end def test_has_many_create_with_bang_with_admin_role_with_attr_accessible_attributes best_friend = @person.best_friends.create!(attributes_hash, :as => :admin) assert_admin_attributes(best_friend, true) end def test_has_many_create_with_bang_without_protection best_friend = @person.best_friends.create!(attributes_hash, :without_protection => true) assert_all_attributes(best_friend) end def test_has_many_create_with_bang_with_strict_sanitizer with_strict_sanitizer do best_friend = @person.best_friends.create!(attributes_hash.except(:id, :comments)) assert_equal @person.id, best_friend.best_friend_id end end # concat def test_concat_has_many_through_association_member group = Group.create! pirate = Pirate.create! group.members << pirate assert_equal pirate.memberships.first, group.memberships.first end def test_concat_has_many_through_polymorphic_association team = Team.create! vampire = Vampire.create! wolf = Wolf.create! team.vampire_battles << vampire wolf.teams << team assert_equal team.wolf_battles.first, wolf end end class MassAssignmentSecurityNestedAttributesTest < ActiveSupport::TestCase include MassAssignmentTestHelpers def nested_attributes_hash(association, collection = false, except = [:id]) if collection { :first_name => 'David' }.merge(:"#{association}_attributes" => [attributes_hash.except(*except)]) else { :first_name => 'David' }.merge(:"#{association}_attributes" => attributes_hash.except(*except)) end end # build def test_has_one_new_with_attr_protected_attributes person = LoosePerson.new(nested_attributes_hash(:best_friend)) assert_default_attributes(person.best_friend) end def test_has_one_new_with_attr_accessible_attributes person = TightPerson.new(nested_attributes_hash(:best_friend)) assert_default_attributes(person.best_friend) end def test_has_one_new_with_admin_role_with_attr_protected_attributes person = LoosePerson.new(nested_attributes_hash(:best_friend), :as => :admin) assert_admin_attributes(person.best_friend) end def test_has_one_new_with_admin_role_with_attr_accessible_attributes person = TightPerson.new(nested_attributes_hash(:best_friend), :as => :admin) assert_admin_attributes(person.best_friend) end def test_has_one_new_without_protection person = LoosePerson.new(nested_attributes_hash(:best_friend, false, nil), :without_protection => true) assert_all_attributes(person.best_friend) end def test_belongs_to_new_with_attr_protected_attributes person = LoosePerson.new(nested_attributes_hash(:best_friend_of)) assert_default_attributes(person.best_friend_of) end def test_belongs_to_new_with_attr_accessible_attributes person = TightPerson.new(nested_attributes_hash(:best_friend_of)) assert_default_attributes(person.best_friend_of) end def test_belongs_to_new_with_admin_role_with_attr_protected_attributes person = LoosePerson.new(nested_attributes_hash(:best_friend_of), :as => :admin) assert_admin_attributes(person.best_friend_of) end def test_belongs_to_new_with_admin_role_with_attr_accessible_attributes person = TightPerson.new(nested_attributes_hash(:best_friend_of), :as => :admin) assert_admin_attributes(person.best_friend_of) end def test_belongs_to_new_without_protection person = LoosePerson.new(nested_attributes_hash(:best_friend_of, false, nil), :without_protection => true) assert_all_attributes(person.best_friend_of) end def test_has_many_new_with_attr_protected_attributes person = LoosePerson.new(nested_attributes_hash(:best_friends, true)) assert_default_attributes(person.best_friends.first) end def test_has_many_new_with_attr_accessible_attributes person = TightPerson.new(nested_attributes_hash(:best_friends, true)) assert_default_attributes(person.best_friends.first) end def test_has_many_new_with_admin_role_with_attr_protected_attributes person = LoosePerson.new(nested_attributes_hash(:best_friends, true), :as => :admin) assert_admin_attributes(person.best_friends.first) end def test_has_many_new_with_admin_role_with_attr_accessible_attributes person = TightPerson.new(nested_attributes_hash(:best_friends, true), :as => :admin) assert_admin_attributes(person.best_friends.first) end def test_has_many_new_without_protection person = LoosePerson.new(nested_attributes_hash(:best_friends, true, nil), :without_protection => true) assert_all_attributes(person.best_friends.first) end # create def test_has_one_create_with_attr_protected_attributes person = LoosePerson.create(nested_attributes_hash(:best_friend)) assert_default_attributes(person.best_friend, true) end def test_has_one_create_with_attr_accessible_attributes person = TightPerson.create(nested_attributes_hash(:best_friend)) assert_default_attributes(person.best_friend, true) end def test_has_one_create_with_admin_role_with_attr_protected_attributes person = LoosePerson.create(nested_attributes_hash(:best_friend), :as => :admin) assert_admin_attributes(person.best_friend, true) end def test_has_one_create_with_admin_role_with_attr_accessible_attributes person = TightPerson.create(nested_attributes_hash(:best_friend), :as => :admin) assert_admin_attributes(person.best_friend, true) end def test_has_one_create_without_protection person = LoosePerson.create(nested_attributes_hash(:best_friend, false, nil), :without_protection => true) assert_all_attributes(person.best_friend) end def test_belongs_to_create_with_attr_protected_attributes person = LoosePerson.create(nested_attributes_hash(:best_friend_of)) assert_default_attributes(person.best_friend_of, true) end def test_belongs_to_create_with_attr_accessible_attributes person = TightPerson.create(nested_attributes_hash(:best_friend_of)) assert_default_attributes(person.best_friend_of, true) end def test_belongs_to_create_with_admin_role_with_attr_protected_attributes person = LoosePerson.create(nested_attributes_hash(:best_friend_of), :as => :admin) assert_admin_attributes(person.best_friend_of, true) end def test_belongs_to_create_with_admin_role_with_attr_accessible_attributes person = TightPerson.create(nested_attributes_hash(:best_friend_of), :as => :admin) assert_admin_attributes(person.best_friend_of, true) end def test_belongs_to_create_without_protection person = LoosePerson.create(nested_attributes_hash(:best_friend_of, false, nil), :without_protection => true) assert_all_attributes(person.best_friend_of) end def test_has_many_create_with_attr_protected_attributes person = LoosePerson.create(nested_attributes_hash(:best_friends, true)) assert_default_attributes(person.best_friends.first, true) end def test_has_many_create_with_attr_accessible_attributes person = TightPerson.create(nested_attributes_hash(:best_friends, true)) assert_default_attributes(person.best_friends.first, true) end def test_has_many_create_with_admin_role_with_attr_protected_attributes person = LoosePerson.create(nested_attributes_hash(:best_friends, true), :as => :admin) assert_admin_attributes(person.best_friends.first, true) end def test_has_many_create_with_admin_role_with_attr_accessible_attributes person = TightPerson.create(nested_attributes_hash(:best_friends, true), :as => :admin) assert_admin_attributes(person.best_friends.first, true) end def test_has_many_create_without_protection person = LoosePerson.create(nested_attributes_hash(:best_friends, true, nil), :without_protection => true) assert_all_attributes(person.best_friends.first) end # create! def test_has_one_create_with_bang_with_attr_protected_attributes person = LoosePerson.create!(nested_attributes_hash(:best_friend)) assert_default_attributes(person.best_friend, true) end def test_has_one_create_with_bang_with_attr_accessible_attributes person = TightPerson.create!(nested_attributes_hash(:best_friend)) assert_default_attributes(person.best_friend, true) end def test_has_one_create_with_bang_with_admin_role_with_attr_protected_attributes person = LoosePerson.create!(nested_attributes_hash(:best_friend), :as => :admin) assert_admin_attributes(person.best_friend, true) end def test_has_one_create_with_bang_with_admin_role_with_attr_accessible_attributes person = TightPerson.create!(nested_attributes_hash(:best_friend), :as => :admin) assert_admin_attributes(person.best_friend, true) end def test_has_one_create_with_bang_without_protection person = LoosePerson.create!(nested_attributes_hash(:best_friend, false, nil), :without_protection => true) assert_all_attributes(person.best_friend) end def test_belongs_to_create_with_bang_with_attr_protected_attributes person = LoosePerson.create!(nested_attributes_hash(:best_friend_of)) assert_default_attributes(person.best_friend_of, true) end def test_belongs_to_create_with_bang_with_attr_accessible_attributes person = TightPerson.create!(nested_attributes_hash(:best_friend_of)) assert_default_attributes(person.best_friend_of, true) end def test_belongs_to_create_with_bang_with_admin_role_with_attr_protected_attributes person = LoosePerson.create!(nested_attributes_hash(:best_friend_of), :as => :admin) assert_admin_attributes(person.best_friend_of, true) end def test_belongs_to_create_with_bang_with_admin_role_with_attr_accessible_attributes person = TightPerson.create!(nested_attributes_hash(:best_friend_of), :as => :admin) assert_admin_attributes(person.best_friend_of, true) end def test_belongs_to_create_with_bang_without_protection person = LoosePerson.create!(nested_attributes_hash(:best_friend_of, false, nil), :without_protection => true) assert_all_attributes(person.best_friend_of) end def test_has_many_create_with_bang_with_attr_protected_attributes person = LoosePerson.create!(nested_attributes_hash(:best_friends, true)) assert_default_attributes(person.best_friends.first, true) end def test_has_many_create_with_bang_with_attr_accessible_attributes person = TightPerson.create!(nested_attributes_hash(:best_friends, true)) assert_default_attributes(person.best_friends.first, true) end def test_has_many_create_with_bang_with_admin_role_with_attr_protected_attributes person = LoosePerson.create!(nested_attributes_hash(:best_friends, true), :as => :admin) assert_admin_attributes(person.best_friends.first, true) end def test_has_many_create_with_bang_with_admin_role_with_attr_accessible_attributes person = TightPerson.create!(nested_attributes_hash(:best_friends, true), :as => :admin) assert_admin_attributes(person.best_friends.first, true) end def test_has_many_create_with_bang_without_protection person = LoosePerson.create!(nested_attributes_hash(:best_friends, true, nil), :without_protection => true) assert_all_attributes(person.best_friends.first) end def test_mass_assignment_options_are_reset_after_exception person = NestedPerson.create!({ :first_name => 'David', :gender => 'm' }, :as => :admin) person.create_best_friend!({ :first_name => 'Jeremy', :gender => 'm' }, :as => :admin) attributes = { :best_friend_attributes => { :comments => 'rides a sweet bike' } } assert_raises(RuntimeError) { person.assign_attributes(attributes, :as => :admin) } assert_equal 'm', person.best_friend.gender person.best_friend_attributes = { :gender => 'f' } assert_equal 'm', person.best_friend.gender end def test_mass_assignment_options_are_nested_correctly person = NestedPerson.create!({ :first_name => 'David', :gender => 'm' }, :as => :admin) person.create_best_friend!({ :first_name => 'Jeremy', :gender => 'm' }, :as => :admin) attributes = { :best_friend_first_name => 'Josh', :best_friend_attributes => { :gender => 'f' } } person.assign_attributes(attributes, :as => :admin) assert_equal 'Josh', person.best_friend.first_name assert_equal 'f', person.best_friend.gender end end protected_attributes-1.1.3/test/mass_assignment_security/000077500000000000000000000000001255126777300240735ustar00rootroot00000000000000protected_attributes-1.1.3/test/mass_assignment_security/black_list_test.rb000066400000000000000000000007621255126777300275730ustar00rootroot00000000000000require 'test_helper' require 'active_model/mass_assignment_security' class BlackListTest < ActiveModel::TestCase def setup @black_list = ActiveModel::MassAssignmentSecurity::BlackList.new @included_key = 'admin' @black_list += [ @included_key ] end test "deny? is true for included items" do assert_equal true, @black_list.deny?(@included_key) end test "deny? is false for non-included items" do assert_equal false, @black_list.deny?('first_name') end end protected_attributes-1.1.3/test/mass_assignment_security/permission_set_test.rb000066400000000000000000000022171255126777300305240ustar00rootroot00000000000000require 'test_helper' require 'active_model/mass_assignment_security' class PermissionSetTest < ActiveModel::TestCase def setup @permission_list = ActiveModel::MassAssignmentSecurity::PermissionSet.new end test "+ stringifies added collection values" do symbol_collection = [ :admin ] new_list = @permission_list += symbol_collection assert new_list.include?('admin'), "did not add collection to #{@permission_list.inspect}}" end test "+ compacts added collection values" do added_collection = [ nil ] new_list = @permission_list + added_collection assert_equal new_list, @permission_list, "did not add collection to #{@permission_list.inspect}}" end test "include? normalizes multi-parameter keys" do multi_param_key = 'admin(1)' new_list = @permission_list += [ 'admin' ] assert new_list.include?(multi_param_key), "#{multi_param_key} not found in #{@permission_list.inspect}" end test "include? normal keys" do normal_key = 'admin' new_list = @permission_list += [ normal_key ] assert new_list.include?(normal_key), "#{normal_key} not found in #{@permission_list.inspect}" end end protected_attributes-1.1.3/test/mass_assignment_security/sanitizer_test.rb000066400000000000000000000034361255126777300274750ustar00rootroot00000000000000require 'test_helper' require 'active_model/mass_assignment_security' require 'active_support/logger' class SanitizerTest < ActiveModel::TestCase attr_accessor :logger class Authorizer < ActiveModel::MassAssignmentSecurity::PermissionSet def deny?(key) ['admin', 'id'].include?(key) end end def setup @logger_sanitizer = ActiveModel::MassAssignmentSecurity::LoggerSanitizer.new(self) @strict_sanitizer = ActiveModel::MassAssignmentSecurity::StrictSanitizer.new(self) @authorizer = Authorizer.new end test "sanitize attributes" do original_attributes = { 'first_name' => 'allowed', 'admin' => 'denied' } attributes = @logger_sanitizer.sanitize(self.class, original_attributes, @authorizer) assert attributes.key?('first_name'), "Allowed key shouldn't be rejected" assert !attributes.key?('admin'), "Denied key should be rejected" end test "debug mass assignment removal with LoggerSanitizer" do original_attributes = { 'first_name' => 'allowed', 'admin' => 'denied' } log = StringIO.new self.logger = ActiveSupport::Logger.new(log) @logger_sanitizer.sanitize(self.class, original_attributes, @authorizer) assert_match(/admin/, log.string, "Should log removed attributes: #{log.string}") end test "debug mass assignment removal with StrictSanitizer" do original_attributes = { 'first_name' => 'allowed', 'admin' => 'denied' } assert_raise ActiveModel::MassAssignmentSecurity::Error do @strict_sanitizer.sanitize(self.class, original_attributes, @authorizer) end end test "mass assignment insensitive attributes" do original_attributes = {'id' => 1, 'first_name' => 'allowed'} assert_nothing_raised do @strict_sanitizer.sanitize(self.class, original_attributes, @authorizer) end end end protected_attributes-1.1.3/test/mass_assignment_security/strong_parameters_fallback_test.rb000066400000000000000000000060331255126777300330370ustar00rootroot00000000000000require 'test_helper' require 'ar_helper' require 'rack/test' require 'action_controller/metal/strong_parameters' require 'active_record/mass_assignment_security' require 'active_model/mass_assignment_security' require 'models/book' require 'models/person' class StrongParametersFallbackTest < ActiveModel::TestCase test "AR, use strong parameters when no protection macro (attr_accessible, attr_protected) was used." do untrusted_params = ActionController::Parameters.new(title: 'Agile Development with Ruby on Rails') assert_raises(ActiveModel::ForbiddenAttributesError) { Book.new untrusted_params } assert_raises(ActiveModel::ForbiddenAttributesError) { Book.new.attributes = untrusted_params } assert_raises(ActiveModel::ForbiddenAttributesError) { Book.where(title: 'Agile Development with Ruby on Rails').first_or_initialize(untrusted_params) } assert_raises(ActiveModel::ForbiddenAttributesError) { Book.where(title: 'Agile Development with Ruby on Rails').first_or_create(untrusted_params) } assert_raises(ActiveModel::ForbiddenAttributesError) { Book.where(title: 'Agile Development with Ruby on Rails').first_or_create!(untrusted_params) } assert_raises(ActiveModel::ForbiddenAttributesError) { Book.find_or_initialize_by(untrusted_params) } assert_raises(ActiveModel::ForbiddenAttributesError) { Book.find_or_create_by(untrusted_params) } assert_raises(ActiveModel::ForbiddenAttributesError) { Book.find_or_create_by!(untrusted_params) } end test "AR, ignore strong parameters when protection macro was used" do untrusted_params = ActionController::Parameters.new(first_name: "John") assert_nothing_raised { TightPerson.new untrusted_params } assert_nothing_raised { TightPerson.new.attributes = untrusted_params } assert_nothing_raised { TightPerson.where(first_name: "John").first_or_initialize(untrusted_params) } assert_nothing_raised { TightPerson.where(first_name: "John").first_or_create(untrusted_params) } assert_nothing_raised { TightPerson.where(first_name: "John").first_or_create!(untrusted_params) } assert_nothing_raised { TightPerson.find_or_initialize_by(untrusted_params) } assert_nothing_raised { TightPerson.find_or_create_by(untrusted_params) } assert_nothing_raised { TightPerson.find_or_create_by!(untrusted_params) } end test "with PORO including MassAssignmentSecurity that uses a protection marco" do klass = Class.new do include ActiveModel::MassAssignmentSecurity attr_protected :admin end untrusted_params = ActionController::Parameters.new(admin: true) assert_equal({}, klass.new.send(:sanitize_for_mass_assignment, untrusted_params)) end test "with PORO including MassAssignmentSecurity that does not use a protection marco" do klass = Class.new do include ActiveModel::MassAssignmentSecurity end untrusted_params = ActionController::Parameters.new(name: "37 signals") assert_raises ActiveModel::ForbiddenAttributesError do klass.new.send :sanitize_for_mass_assignment, untrusted_params end end end protected_attributes-1.1.3/test/mass_assignment_security/white_list_test.rb000066400000000000000000000007611255126777300276360ustar00rootroot00000000000000require 'test_helper' require 'active_model/mass_assignment_security' class WhiteListTest < ActiveModel::TestCase def setup @white_list = ActiveModel::MassAssignmentSecurity::WhiteList.new @included_key = 'first_name' @white_list += [ @included_key ] end test "deny? is false for included items" do assert_equal false, @white_list.deny?(@included_key) end test "deny? is true for non-included items" do assert_equal true, @white_list.deny?('admin') end end protected_attributes-1.1.3/test/mass_assignment_security_test.rb000066400000000000000000000115131255126777300254600ustar00rootroot00000000000000require 'test_helper' require 'active_model/mass_assignment_security' require 'models/mass_assignment_specific' class CustomSanitizer < ActiveModel::MassAssignmentSecurity::Sanitizer def process_removed_attributes(klass, attrs) raise StandardError end end class MassAssignmentSecurityTest < ActiveModel::TestCase def test_attribute_protection user = User.new expected = { "name" => "John Smith", "email" => "john@smith.com" } sanitized = user.sanitize_for_mass_assignment(expected.merge("admin" => true)) assert_equal expected, sanitized end def test_attribute_protection_when_role_is_nil user = User.new expected = { "name" => "John Smith", "email" => "john@smith.com" } sanitized = user.sanitize_for_mass_assignment(expected.merge("admin" => true), nil) assert_equal expected, sanitized end def test_only_moderator_role_attribute_accessible user = SpecialUser.new expected = { "name" => "John Smith", "email" => "john@smith.com" } sanitized = user.sanitize_for_mass_assignment(expected.merge("admin" => true), :moderator) assert_equal expected, sanitized sanitized = user.sanitize_for_mass_assignment({ "name" => "John Smith", "email" => "john@smith.com", "admin" => true }) assert_equal({}, sanitized) end def test_attributes_accessible user = Person.new expected = { "name" => "John Smith", "email" => "john@smith.com" } sanitized = user.sanitize_for_mass_assignment(expected.merge("admin" => true)) assert_equal expected, sanitized end def test_attributes_accessible_with_admin_role user = Person.new expected = { "name" => "John Smith", "email" => "john@smith.com", "admin" => true } sanitized = user.sanitize_for_mass_assignment(expected.merge("super_powers" => true), :admin) assert_equal expected, sanitized end def test_attributes_accessible_with_roles_given_as_array user = Account.new expected = { "name" => "John Smith", "email" => "john@smith.com" } sanitized = user.sanitize_for_mass_assignment(expected.merge("admin" => true)) assert_equal expected, sanitized end def test_attributes_accessible_with_admin_role_when_roles_given_as_array user = Account.new expected = { "name" => "John Smith", "email" => "john@smith.com", "admin" => true } sanitized = user.sanitize_for_mass_assignment(expected.merge("super_powers" => true), :admin) assert_equal expected, sanitized end def test_attributes_protected_by_default firm = Firm.new expected = { } sanitized = firm.sanitize_for_mass_assignment({ "type" => "Client" }) assert_equal expected, sanitized end def test_mass_assignment_protection_inheritance assert SpecialLoosePerson.accessible_attributes.blank? assert_equal Set.new(['credit_rating', 'administrator']), SpecialLoosePerson.protected_attributes assert SpecialLoosePerson.accessible_attributes.blank? assert_equal Set.new(['credit_rating']), SpecialLoosePerson.protected_attributes(:admin) assert LooseDescendant.accessible_attributes.blank? assert_equal Set.new(['credit_rating', 'administrator', 'phone_number']), LooseDescendant.protected_attributes assert LooseDescendantSecond.accessible_attributes.blank? assert_equal Set.new(['credit_rating', 'administrator', 'phone_number', 'name']), LooseDescendantSecond.protected_attributes, 'Running attr_protected twice in one class should merge the protections' assert((SpecialTightPerson.protected_attributes - SpecialTightPerson.attributes_protected_by_default).blank?) assert_equal Set.new(['name', 'address']), SpecialTightPerson.accessible_attributes assert((SpecialTightPerson.protected_attributes(:admin) - SpecialTightPerson.attributes_protected_by_default).blank?) assert_equal Set.new(['name', 'address', 'admin']), SpecialTightPerson.accessible_attributes(:admin) assert((TightDescendant.protected_attributes - TightDescendant.attributes_protected_by_default).blank?) assert_equal Set.new(['name', 'address', 'phone_number']), TightDescendant.accessible_attributes assert((TightDescendant.protected_attributes(:admin) - TightDescendant.attributes_protected_by_default).blank?) assert_equal Set.new(['name', 'address', 'admin', 'super_powers']), TightDescendant.accessible_attributes(:admin) end def test_mass_assignment_multiparameter_protector task = Task.new attributes = { "starting(1i)" => "2004", "starting(2i)" => "6", "starting(3i)" => "24" } sanitized = task.sanitize_for_mass_assignment(attributes) assert_equal sanitized, { } end def test_custom_sanitizer old_sanitizer = User._mass_assignment_sanitizer user = User.new User.mass_assignment_sanitizer = CustomSanitizer.new assert_raise StandardError do user.sanitize_for_mass_assignment("admin" => true) end ensure User.mass_assignment_sanitizer = old_sanitizer end end protected_attributes-1.1.3/test/models/000077500000000000000000000000001255126777300202345ustar00rootroot00000000000000protected_attributes-1.1.3/test/models/battle.rb000066400000000000000000000001711255126777300220330ustar00rootroot00000000000000class Battle < ActiveRecord::Base attr_accessible [] belongs_to :team belongs_to :battle, :polymorphic => true end protected_attributes-1.1.3/test/models/book.rb000066400000000000000000000000441255126777300215110ustar00rootroot00000000000000class Book < ActiveRecord::Base end protected_attributes-1.1.3/test/models/company.rb000066400000000000000000000004361255126777300222320ustar00rootroot00000000000000class AbstractCompany < ActiveRecord::Base self.abstract_class = true end class Company < AbstractCompany attr_protected :rating end class Firm < Company end class Corporation < Company attr_accessible :type, :name, :description end class SpecialCorporation < Corporation end protected_attributes-1.1.3/test/models/group.rb000066400000000000000000000003031255126777300217110ustar00rootroot00000000000000class Group < ActiveRecord::Base self.mass_assignment_sanitizer = :strict has_many :memberships, :dependent => :destroy has_many :members, :through => :memberships, :source => :pirate end protected_attributes-1.1.3/test/models/keyboard.rb000066400000000000000000000001421255126777300223560ustar00rootroot00000000000000class Keyboard < ActiveRecord::Base attr_accessible(nil) self.primary_key = 'key_number' end protected_attributes-1.1.3/test/models/mass_assignment_specific.rb000066400000000000000000000032411255126777300256210ustar00rootroot00000000000000class User include ActiveModel::MassAssignmentSecurity attr_protected :admin public :sanitize_for_mass_assignment end class SpecialUser include ActiveModel::MassAssignmentSecurity attr_accessible :name, :email, :as => :moderator public :sanitize_for_mass_assignment end class Person include ActiveModel::MassAssignmentSecurity attr_accessible :name, :email attr_accessible :name, :email, :admin, :as => :admin public :sanitize_for_mass_assignment end class Account include ActiveModel::MassAssignmentSecurity attr_accessible :name, :email, :as => [:default, :admin] attr_accessible :admin, :as => :admin public :sanitize_for_mass_assignment end class Firm include ActiveModel::MassAssignmentSecurity public :sanitize_for_mass_assignment def self.attributes_protected_by_default ["type"] end end class Task include ActiveModel::MassAssignmentSecurity attr_protected :starting public :sanitize_for_mass_assignment end class SpecialLoosePerson include ActiveModel::MassAssignmentSecurity attr_protected :credit_rating, :administrator attr_protected :credit_rating, :as => :admin end class LooseDescendant < SpecialLoosePerson attr_protected :phone_number end class LooseDescendantSecond< SpecialLoosePerson attr_protected :phone_number attr_protected :name end class SpecialTightPerson include ActiveModel::MassAssignmentSecurity attr_accessible :name, :address attr_accessible :name, :address, :admin, :as => :admin def self.attributes_protected_by_default ["mobile_number"] end end class TightDescendant < SpecialTightPerson attr_accessible :phone_number attr_accessible :super_powers, :as => :admin end protected_attributes-1.1.3/test/models/membership.rb000066400000000000000000000002251255126777300227130ustar00rootroot00000000000000class Membership < ActiveRecord::Base self.mass_assignment_sanitizer = :strict belongs_to :group belongs_to :pirate attr_accessible [] end protected_attributes-1.1.3/test/models/person.rb000066400000000000000000000036251255126777300220750ustar00rootroot00000000000000class LoosePerson < ActiveRecord::Base self.table_name = 'people' attr_protected :comments, :best_friend_id, :best_friend_of_id attr_protected :as => :admin has_one :best_friend, :class_name => 'LoosePerson', :foreign_key => :best_friend_id belongs_to :best_friend_of, :class_name => 'LoosePerson', :foreign_key => :best_friend_of_id has_many :best_friends, :class_name => 'LoosePerson', :foreign_key => :best_friend_id accepts_nested_attributes_for :best_friend, :best_friend_of, :best_friends end class TightPerson < ActiveRecord::Base self.table_name = 'people' attr_accessible :first_name, :gender attr_accessible :first_name, :gender, :comments, :as => :admin attr_accessible :best_friend_attributes, :best_friend_of_attributes, :best_friends_attributes attr_accessible :best_friend_attributes, :best_friend_of_attributes, :best_friends_attributes, :as => :admin has_one :best_friend, :class_name => 'TightPerson', :foreign_key => :best_friend_id belongs_to :best_friend_of, :class_name => 'TightPerson', :foreign_key => :best_friend_of_id has_many :best_friends, :class_name => 'TightPerson', :foreign_key => :best_friend_id accepts_nested_attributes_for :best_friend, :best_friend_of, :best_friends end class NestedPerson < ActiveRecord::Base self.table_name = 'people' attr_accessible :first_name, :best_friend_first_name, :best_friend_attributes attr_accessible :first_name, :gender, :comments, :as => :admin attr_accessible :best_friend_attributes, :best_friend_first_name, :as => :admin has_one :best_friend, :class_name => 'NestedPerson', :foreign_key => :best_friend_id accepts_nested_attributes_for :best_friend, :update_only => true, :reject_if => :all_blank def comments=(new_comments) raise RuntimeError end def best_friend_first_name=(new_name) assign_attributes({ :best_friend_attributes => { :first_name => new_name } }) end end protected_attributes-1.1.3/test/models/pirate.rb000066400000000000000000000002011255126777300220360ustar00rootroot00000000000000class Pirate < ActiveRecord::Base self.mass_assignment_sanitizer = :strict attr_accessible :name has_many :memberships end protected_attributes-1.1.3/test/models/subscriber.rb000066400000000000000000000002461255126777300227260ustar00rootroot00000000000000class Subscriber < ActiveRecord::Base attr_accessible(nil) self.primary_key = 'nick' has_many :subscriptions has_many :books, :through => :subscriptions end protected_attributes-1.1.3/test/models/task.rb000066400000000000000000000001061255126777300215200ustar00rootroot00000000000000class Task < ActiveRecord::Base def updated_at ending end end protected_attributes-1.1.3/test/models/team.rb000066400000000000000000000004451255126777300215120ustar00rootroot00000000000000class Team < ActiveRecord::Base has_many :battles has_many :wolf_battles, :through => :battles, :class_name => 'Wolf', :source => :battle, :source_type => 'Wolf' has_many :vampire_battles, :through => :battles, :class_name => 'Vampire', :source => :battle, :source_type => 'Vampire' end protected_attributes-1.1.3/test/models/vampire.rb000066400000000000000000000001631255126777300222240ustar00rootroot00000000000000class Vampire < ActiveRecord::Base has_many :battles, :as => :battle has_many :teams, :through => :battles end protected_attributes-1.1.3/test/models/wolf.rb000066400000000000000000000001601255126777300215250ustar00rootroot00000000000000class Wolf < ActiveRecord::Base has_many :battles, :as => :battle has_many :teams, :through => :battles end protected_attributes-1.1.3/test/test_helper.rb000066400000000000000000000002451255126777300216150ustar00rootroot00000000000000require 'bundler/setup' require 'minitest/autorun' require 'mocha/api' require 'rails' ActiveSupport.test_order = :random if ActiveSupport.respond_to?(:test_order)