protected_attributes 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/.gitignore +17 -0
  2. data/.travis.yml +17 -0
  3. data/Gemfile +7 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +111 -0
  6. data/Rakefile +11 -0
  7. data/lib/action_controller/accessible_params_wrapper.rb +29 -0
  8. data/lib/active_model/mass_assignment_security.rb +353 -0
  9. data/lib/active_model/mass_assignment_security/permission_set.rb +40 -0
  10. data/lib/active_model/mass_assignment_security/sanitizer.rb +74 -0
  11. data/lib/active_record/mass_assignment_security.rb +23 -0
  12. data/lib/active_record/mass_assignment_security/associations.rb +116 -0
  13. data/lib/active_record/mass_assignment_security/attribute_assignment.rb +88 -0
  14. data/lib/active_record/mass_assignment_security/core.rb +27 -0
  15. data/lib/active_record/mass_assignment_security/inheritance.rb +18 -0
  16. data/lib/active_record/mass_assignment_security/nested_attributes.rb +148 -0
  17. data/lib/active_record/mass_assignment_security/persistence.rb +81 -0
  18. data/lib/active_record/mass_assignment_security/reflection.rb +9 -0
  19. data/lib/active_record/mass_assignment_security/relation.rb +47 -0
  20. data/lib/active_record/mass_assignment_security/validations.rb +24 -0
  21. data/lib/protected_attributes.rb +14 -0
  22. data/lib/protected_attributes/railtie.rb +18 -0
  23. data/lib/protected_attributes/version.rb +3 -0
  24. data/protected_attributes.gemspec +26 -0
  25. data/test/abstract_unit.rb +156 -0
  26. data/test/accessible_params_wrapper_test.rb +76 -0
  27. data/test/ar_helper.rb +67 -0
  28. data/test/attribute_sanitization_test.rb +929 -0
  29. data/test/mass_assignment_security/black_list_test.rb +20 -0
  30. data/test/mass_assignment_security/permission_set_test.rb +36 -0
  31. data/test/mass_assignment_security/sanitizer_test.rb +50 -0
  32. data/test/mass_assignment_security/white_list_test.rb +19 -0
  33. data/test/mass_assignment_security_test.rb +118 -0
  34. data/test/models/company.rb +105 -0
  35. data/test/models/keyboard.rb +3 -0
  36. data/test/models/mass_assignment_specific.rb +76 -0
  37. data/test/models/person.rb +82 -0
  38. data/test/models/subscriber.rb +5 -0
  39. data/test/models/task.rb +5 -0
  40. data/test/test_helper.rb +3 -0
  41. metadata +199 -0
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.travis.yml ADDED
@@ -0,0 +1,17 @@
1
+ language: ruby
2
+ before_install:
3
+ - gem install bundler
4
+ rvm:
5
+ - 1.9.3
6
+ notifications:
7
+ email: false
8
+ irc:
9
+ on_success: change
10
+ on_failure: always
11
+ channels:
12
+ - "irc.freenode.org#rails-contrib"
13
+ campfire:
14
+ on_success: change
15
+ on_failure: always
16
+ rooms:
17
+ - secure: "CGWvthGkBKNnTnk9YSmf9AXKoiRI33fCl5D3jU4nx3cOPu6kv2R9nMjt9EAo\nOuS4Q85qNSf4VNQ2cUPNiNYSWQ+XiTfivKvDUw/QW9r1FejYyeWarMsSBWA+\n0fADjF1M2dkDIVLgYPfwoXEv7l+j654F1KLKB69F0F/netwP9CQ="
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in protected_attributes.gemspec
4
+ gem 'rails', github: 'rails/rails', branch: 'master'
5
+ gem 'activerecord-deprecated_finders', github: 'rails/activerecord-deprecated_finders', branch: 'master'
6
+
7
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Guillermo Iguaran
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,111 @@
1
+ # ProtectedAttributes
2
+
3
+ Protect attributes from mass-assignment in ActiveRecord models.
4
+
5
+ This plugin adds `attr_accessible` and `attr_protected` in your models.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'protected_attributes'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install protected_attributes
20
+
21
+ ## Usage
22
+
23
+ Mass assignment security provides an interface for protecting attributes from end-user assignment. This plugin provides two class methods in your Active Record class to control access to your attributes. The `attr_protected` method takes a list of attributes that will not be accessible for mass-assignment.
24
+
25
+ For example:
26
+
27
+ attr_protected :admin
28
+
29
+ `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.
30
+
31
+ attr_protected :last_login, :as => :admin
32
+
33
+ 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 accessible. All other attributes will be protected. This way you won’t forget to protect attributes when adding new ones in the course of development. Here is an example:
34
+
35
+ attr_accessible :name
36
+ attr_accessible :name, :is_admin, :as => :admin
37
+
38
+ If you want to set a protected attribute, you will to have to assign it individually:
39
+
40
+ params[:user] # => {:name => "owned", :admin => true}
41
+ @user = User.new(params[:user])
42
+ @user.admin # => false, not mass-assigned
43
+ @user.admin = true
44
+ @user.admin # => true
45
+
46
+ 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.
47
+ You can also bypass mass-assignment security by using the `:without_protection` option. Here is an example:
48
+
49
+ @user = User.new
50
+
51
+ @user.assign_attributes({ :name => 'Josh', :is_admin => true })
52
+ @user.name # => Josh
53
+ @user.is_admin # => false
54
+
55
+ @user.assign_attributes({ :name => 'Josh', :is_admin => true }, :as => :admin)
56
+ @user.name # => Josh
57
+ @user.is_admin # => true
58
+
59
+ @user.assign_attributes({ :name => 'Josh', :is_admin => true }, :without_protection => true)
60
+ @user.name # => Josh
61
+ @user.is_admin # => true
62
+
63
+ 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:
64
+
65
+ @user = User.new({ :name => 'Sebastian', :is_admin => true }, :as => :admin)
66
+ @user.name # => Sebastian
67
+ @user.is_admin # => true
68
+
69
+ @user = User.create({ :name => 'Sebastian', :is_admin => true }, :without_protection => true)
70
+ @user.name # => Sebastian
71
+ @user.is_admin # => true
72
+
73
+ A more paranoid technique to protect your whole project would be to enforce that all models define their accessible attributes.
74
+ This can be easily achieved with a very simple application config option of:
75
+
76
+ config.active_record.whitelist_attributes = true
77
+
78
+ This will create an empty whitelist of attributes available for mass-assignment for all models in your app.
79
+ As such, your models will need to explicitly whitelist or blacklist accessible parameters by using an `attr_accessible` or `attr_protected` declaration. This technique is best applied at the start of a new project. However, for an existing project with a thorough set of functional tests, it should be straightforward and relatively quick to use this application config option; run your tests, and expose each attribute (via `attr_accessible` or `attr_protected`), as dictated by your failing test.
80
+
81
+ 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.
82
+
83
+ For example, a logged-in user may need to assign additional attributes depending on their role:
84
+
85
+ class AccountsController < ApplicationController
86
+ include ActiveModel::MassAssignmentSecurity
87
+
88
+ attr_accessible :first_name, :last_name
89
+ attr_accessible :first_name, :last_name, :plan_id, :as => :admin
90
+
91
+ def update
92
+ ...
93
+ @account.update_attributes(account_params)
94
+ ...
95
+ end
96
+
97
+ protected
98
+
99
+ def account_params
100
+ role = admin ? :admin : :default
101
+ sanitize_for_mass_assignment(params[:account], role)
102
+ end
103
+ end
104
+
105
+ ## Contributing
106
+
107
+ 1. Fork it
108
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
109
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
110
+ 4. Push to the branch (`git push origin my-new-feature`)
111
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require 'rake/testtask'
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.libs = ["test"]
7
+ t.pattern = "test/**/*_test.rb"
8
+ t.ruby_opts = ['-w']
9
+ end
10
+
11
+ task :default => :test
@@ -0,0 +1,29 @@
1
+ require 'active_support/concern'
2
+ require 'action_controller'
3
+ require 'action_controller/metal/params_wrapper'
4
+
5
+ module ActionController
6
+ module ParamsWrapper
7
+ class Options # :nodoc:
8
+ def include
9
+ return super if @include_set
10
+
11
+ m = model
12
+ synchronize do
13
+ return super if @include_set
14
+
15
+ @include_set = true
16
+
17
+ unless super || exclude
18
+
19
+ if m.respond_to?(:accessible_attributes) && m.accessible_attributes(:default).present?
20
+ self.include = m.accessible_attributes(:default).to_a
21
+ elsif m.respond_to?(:attribute_names) && m.attribute_names.any?
22
+ self.include = m.attribute_names
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,353 @@
1
+ require 'active_support/concern'
2
+ require 'active_support/core_ext/class/attribute'
3
+ require 'active_support/core_ext/string/inflections'
4
+ require 'active_model'
5
+ require 'active_model/mass_assignment_security/permission_set'
6
+ require 'active_model/mass_assignment_security/sanitizer'
7
+
8
+ module ActiveModel
9
+ # == Active Model Mass-Assignment Security
10
+ #
11
+ # Mass assignment security provides an interface for protecting attributes
12
+ # from end-user assignment. For more complex permissions, mass assignment
13
+ # security may be handled outside the model by extending a non-ActiveRecord
14
+ # class, such as a controller, with this behavior.
15
+ #
16
+ # For example, a logged in user may need to assign additional attributes
17
+ # depending on their role:
18
+ #
19
+ # class AccountsController < ApplicationController
20
+ # include ActiveModel::MassAssignmentSecurity
21
+ #
22
+ # attr_accessible :first_name, :last_name
23
+ # attr_accessible :first_name, :last_name, :plan_id, as: :admin
24
+ #
25
+ # def update
26
+ # ...
27
+ # @account.update_attributes(account_params)
28
+ # ...
29
+ # end
30
+ #
31
+ # protected
32
+ #
33
+ # def account_params
34
+ # role = admin ? :admin : :default
35
+ # sanitize_for_mass_assignment(params[:account], role)
36
+ # end
37
+ #
38
+ # end
39
+ #
40
+ # === Configuration options
41
+ #
42
+ # * <tt>mass_assignment_sanitizer</tt> - Defines sanitize method. Possible
43
+ # values are:
44
+ # * <tt>:logger</tt> (default) - writes filtered attributes to logger
45
+ # * <tt>:strict</tt> - raise <tt>ActiveModel::MassAssignmentSecurity::Error</tt>
46
+ # on any protected attribute update.
47
+ #
48
+ # You can specify your own sanitizer object eg. <tt>MySanitizer.new</tt>.
49
+ # See <tt>ActiveModel::MassAssignmentSecurity::LoggerSanitizer</tt> for
50
+ # example implementation.
51
+ module MassAssignmentSecurity
52
+ extend ActiveSupport::Concern
53
+
54
+ included do
55
+ class_attribute :_accessible_attributes, instance_writer: false
56
+ class_attribute :_protected_attributes, instance_writer: false
57
+ class_attribute :_active_authorizer, instance_writer: false
58
+
59
+ class_attribute :_mass_assignment_sanitizer, instance_writer: false
60
+ self.mass_assignment_sanitizer = :logger
61
+ end
62
+
63
+ module ClassMethods
64
+ # Attributes named in this macro are protected from mass-assignment
65
+ # whenever attributes are sanitized before assignment. A role for the
66
+ # attributes is optional, if no role is provided then <tt>:default</tt>
67
+ # is used. A role can be defined by using the <tt>:as</tt> option with a
68
+ # symbol or an array of symbols as the value.
69
+ #
70
+ # Mass-assignment to these attributes will simply be ignored, to assign
71
+ # to them you can use direct writer methods. This is meant to protect
72
+ # sensitive attributes from being overwritten by malicious users
73
+ # tampering with URLs or forms.
74
+ #
75
+ # class Customer
76
+ # include ActiveModel::MassAssignmentSecurity
77
+ #
78
+ # attr_accessor :name, :email, :logins_count
79
+ #
80
+ # attr_protected :logins_count
81
+ # # Suppose that admin can not change email for customer
82
+ # attr_protected :logins_count, :email, as: :admin
83
+ #
84
+ # def assign_attributes(values, options = {})
85
+ # sanitize_for_mass_assignment(values, options[:as]).each do |k, v|
86
+ # send("#{k}=", v)
87
+ # end
88
+ # end
89
+ # end
90
+ #
91
+ # When using the <tt>:default</tt> role:
92
+ #
93
+ # customer = Customer.new
94
+ # customer.assign_attributes({ name: 'David', email: 'a@b.com', logins_count: 5 }, as: :default)
95
+ # customer.name # => "David"
96
+ # customer.email # => "a@b.com"
97
+ # customer.logins_count # => nil
98
+ #
99
+ # And using the <tt>:admin</tt> role:
100
+ #
101
+ # customer = Customer.new
102
+ # customer.assign_attributes({ name: 'David', email: 'a@b.com', logins_count: 5}, as: :admin)
103
+ # customer.name # => "David"
104
+ # customer.email # => nil
105
+ # customer.logins_count # => nil
106
+ #
107
+ # customer.email = 'c@d.com'
108
+ # customer.email # => "c@d.com"
109
+ #
110
+ # To start from an all-closed default and enable attributes as needed,
111
+ # have a look at +attr_accessible+.
112
+ #
113
+ # Note that using <tt>Hash#except</tt> or <tt>Hash#slice</tt> in place of
114
+ # +attr_protected+ to sanitize attributes provides basically the same
115
+ # functionality, but it makes a bit tricky to deal with nested attributes.
116
+ def attr_protected(*args)
117
+ options = args.extract_options!
118
+ role = options[:as] || :default
119
+
120
+ self._protected_attributes = protected_attributes_configs.dup
121
+
122
+ Array(role).each do |name|
123
+ self._protected_attributes[name] = self.protected_attributes(name) + args
124
+ end
125
+
126
+ self._active_authorizer = self._protected_attributes
127
+ end
128
+
129
+ # Specifies a white list of model attributes that can be set via
130
+ # mass-assignment.
131
+ #
132
+ # Like +attr_protected+, a role for the attributes is optional,
133
+ # if no role is provided then <tt>:default</tt> is used. A role can be
134
+ # defined by using the <tt>:as</tt> option with a symbol or an array of
135
+ # symbols as the value.
136
+ #
137
+ # This is the opposite of the +attr_protected+ macro: Mass-assignment
138
+ # will only set attributes in this list, to assign to the rest of
139
+ # attributes you can use direct writer methods. This is meant to protect
140
+ # sensitive attributes from being overwritten by malicious users
141
+ # tampering with URLs or forms. If you'd rather start from an all-open
142
+ # default and restrict attributes as needed, have a look at
143
+ # +attr_protected+.
144
+ #
145
+ # class Customer
146
+ # include ActiveModel::MassAssignmentSecurity
147
+ #
148
+ # attr_accessor :name, :credit_rating
149
+ #
150
+ # # Both admin and default user can change name of a customer
151
+ # attr_accessible :name, as: [:admin, :default]
152
+ # # Only admin can change credit rating of a customer
153
+ # attr_accessible :credit_rating, as: :admin
154
+ #
155
+ # def assign_attributes(values, options = {})
156
+ # sanitize_for_mass_assignment(values, options[:as]).each do |k, v|
157
+ # send("#{k}=", v)
158
+ # end
159
+ # end
160
+ # end
161
+ #
162
+ # When using the <tt>:default</tt> role:
163
+ #
164
+ # customer = Customer.new
165
+ # customer.assign_attributes({ name: 'David', credit_rating: 'Excellent', last_login: 1.day.ago }, as: :default)
166
+ # customer.name # => "David"
167
+ # customer.credit_rating # => nil
168
+ #
169
+ # customer.credit_rating = 'Average'
170
+ # customer.credit_rating # => "Average"
171
+ #
172
+ # And using the <tt>:admin</tt> role:
173
+ #
174
+ # customer = Customer.new
175
+ # customer.assign_attributes({ name: 'David', credit_rating: 'Excellent', last_login: 1.day.ago }, as: :admin)
176
+ # customer.name # => "David"
177
+ # customer.credit_rating # => "Excellent"
178
+ #
179
+ # Note that using <tt>Hash#except</tt> or <tt>Hash#slice</tt> in place of
180
+ # +attr_accessible+ to sanitize attributes provides basically the same
181
+ # functionality, but it makes a bit tricky to deal with nested attributes.
182
+ def attr_accessible(*args)
183
+ options = args.extract_options!
184
+ role = options[:as] || :default
185
+
186
+ self._accessible_attributes = accessible_attributes_configs.dup
187
+
188
+ Array(role).each do |name|
189
+ self._accessible_attributes[name] = self.accessible_attributes(name) + args
190
+ end
191
+
192
+ self._active_authorizer = self._accessible_attributes
193
+ end
194
+
195
+ # Returns an instance of <tt>ActiveModel::MassAssignmentSecurity::BlackList</tt>
196
+ # with the attributes protected by #attr_protected method. If no +role+
197
+ # is provided, then <tt>:default</tt> is used.
198
+ #
199
+ # class Customer
200
+ # include ActiveModel::MassAssignmentSecurity
201
+ #
202
+ # attr_accessor :name, :email, :logins_count
203
+ #
204
+ # attr_protected :logins_count
205
+ # attr_protected :logins_count, :email, as: :admin
206
+ # end
207
+ #
208
+ # Customer.protected_attributes
209
+ # # => #<ActiveModel::MassAssignmentSecurity::BlackList: {"logins_count"}>
210
+ #
211
+ # Customer.protected_attributes(:default)
212
+ # # => #<ActiveModel::MassAssignmentSecurity::BlackList: {"logins_count"}>
213
+ #
214
+ # Customer.protected_attributes(:admin)
215
+ # # => #<ActiveModel::MassAssignmentSecurity::BlackList: {"logins_count", "email"}>
216
+ def protected_attributes(role = :default)
217
+ protected_attributes_configs[role]
218
+ end
219
+
220
+ # Returns an instance of <tt>ActiveModel::MassAssignmentSecurity::WhiteList</tt>
221
+ # with the attributes protected by #attr_accessible method. If no +role+
222
+ # is provided, then <tt>:default</tt> is used.
223
+ #
224
+ # class Customer
225
+ # include ActiveModel::MassAssignmentSecurity
226
+ #
227
+ # attr_accessor :name, :credit_rating
228
+ #
229
+ # attr_accessible :name, as: [:admin, :default]
230
+ # attr_accessible :credit_rating, as: :admin
231
+ # end
232
+ #
233
+ # Customer.accessible_attributes
234
+ # # => #<ActiveModel::MassAssignmentSecurity::WhiteList: {"name"}>
235
+ #
236
+ # Customer.accessible_attributes(:default)
237
+ # # => #<ActiveModel::MassAssignmentSecurity::WhiteList: {"name"}>
238
+ #
239
+ # Customer.accessible_attributes(:admin)
240
+ # # => #<ActiveModel::MassAssignmentSecurity::WhiteList: {"name", "credit_rating"}>
241
+ def accessible_attributes(role = :default)
242
+ accessible_attributes_configs[role]
243
+ end
244
+
245
+ # Returns a hash with the protected attributes (by #attr_accessible or
246
+ # #attr_protected) per role.
247
+ #
248
+ # class Customer
249
+ # include ActiveModel::MassAssignmentSecurity
250
+ #
251
+ # attr_accessor :name, :credit_rating
252
+ #
253
+ # attr_accessible :name, as: [:admin, :default]
254
+ # attr_accessible :credit_rating, as: :admin
255
+ # end
256
+ #
257
+ # Customer.active_authorizers
258
+ # # => {
259
+ # # :admin=> #<ActiveModel::MassAssignmentSecurity::WhiteList: {"name", "credit_rating"}>,
260
+ # # :default=>#<ActiveModel::MassAssignmentSecurity::WhiteList: {"name"}>
261
+ # #  }
262
+ def active_authorizers
263
+ self._active_authorizer ||= protected_attributes_configs
264
+ end
265
+ alias active_authorizer active_authorizers
266
+
267
+ # Returns an empty array by default. You can still override this to define
268
+ # the default attributes protected by #attr_protected method.
269
+ #
270
+ # class Customer
271
+ # include ActiveModel::MassAssignmentSecurity
272
+ #
273
+ # def self.attributes_protected_by_default
274
+ # [:name]
275
+ # end
276
+ # end
277
+ #
278
+ # Customer.protected_attributes
279
+ # # => #<ActiveModel::MassAssignmentSecurity::BlackList: {:name}>
280
+ def attributes_protected_by_default
281
+ []
282
+ end
283
+
284
+ # Defines sanitize method.
285
+ #
286
+ # class Customer
287
+ # include ActiveModel::MassAssignmentSecurity
288
+ #
289
+ # attr_accessor :name
290
+ #
291
+ # attr_protected :name
292
+ #
293
+ # def assign_attributes(values)
294
+ # sanitize_for_mass_assignment(values).each do |k, v|
295
+ # send("#{k}=", v)
296
+ # end
297
+ # end
298
+ # end
299
+ #
300
+ # # See ActiveModel::MassAssignmentSecurity::StrictSanitizer for more information.
301
+ # Customer.mass_assignment_sanitizer = :strict
302
+ #
303
+ # customer = Customer.new
304
+ # customer.assign_attributes(name: 'David')
305
+ # # => ActiveModel::MassAssignmentSecurity::Error: Can't mass-assign protected attributes for Customer: name
306
+ #
307
+ # Also, you can specify your own sanitizer object.
308
+ #
309
+ # class CustomSanitizer < ActiveModel::MassAssignmentSecurity::Sanitizer
310
+ # def process_removed_attributes(klass, attrs)
311
+ # raise StandardError
312
+ # end
313
+ # end
314
+ #
315
+ # Customer.mass_assignment_sanitizer = CustomSanitizer.new
316
+ #
317
+ # customer = Customer.new
318
+ # customer.assign_attributes(name: 'David')
319
+ # # => StandardError: StandardError
320
+ def mass_assignment_sanitizer=(value)
321
+ self._mass_assignment_sanitizer = if value.is_a?(Symbol)
322
+ const_get(:"#{value.to_s.camelize}Sanitizer").new(self)
323
+ else
324
+ value
325
+ end
326
+ end
327
+
328
+ private
329
+
330
+ def protected_attributes_configs
331
+ self._protected_attributes ||= begin
332
+ Hash.new { |h,k| h[k] = BlackList.new(attributes_protected_by_default) }
333
+ end
334
+ end
335
+
336
+ def accessible_attributes_configs
337
+ self._accessible_attributes ||= begin
338
+ Hash.new { |h,k| h[k] = WhiteList.new }
339
+ end
340
+ end
341
+ end
342
+
343
+ protected
344
+
345
+ def sanitize_for_mass_assignment(attributes, role = nil) #:nodoc:
346
+ _mass_assignment_sanitizer.sanitize(self.class, attributes, mass_assignment_authorizer(role))
347
+ end
348
+
349
+ def mass_assignment_authorizer(role) #:nodoc:
350
+ self.class.active_authorizer[role || :default]
351
+ end
352
+ end
353
+ end