mongomodel 0.5.1 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +0 -3
- data/lib/mongomodel.rb +1 -6
- data/lib/mongomodel/concerns/attribute_methods/protected.rb +15 -19
- data/lib/mongomodel/vendor/active_model.rb +2 -0
- data/lib/mongomodel/vendor/active_model/mass_assignment_security.rb +353 -0
- data/lib/mongomodel/vendor/active_model/mass_assignment_security/permission_set.rb +40 -0
- data/lib/mongomodel/vendor/active_model/mass_assignment_security/sanitizer.rb +74 -0
- data/lib/mongomodel/version.rb +1 -1
- data/spec/mongomodel/concerns/attribute_methods/protected_spec.rb +68 -70
- metadata +6 -3
- data/gemfiles/rails-4-protected-attributes.gemfile +0 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b6303f96496ed1cb9c8c52316e02c07a7354f06e
|
4
|
+
data.tar.gz: 5ef0ee286e9a51dd9bb42390da710688eadde951
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d11971bf1bdef7946e7e54ba0c9912566e30008d64df721d1aff9aed17dc833d436a3156d337553d33e78711da0b8048bd3004c337bc0317efb05fdf5ba64326
|
7
|
+
data.tar.gz: a158854ae696e4a0bf4102835e6a6def055bce660f1141716f190e5ab576a79c2ca4793c42dc21d046d9b4d21df0c1e1d4dbc743dc47f4173692dd79ec00ff49
|
data/.travis.yml
CHANGED
@@ -15,7 +15,6 @@ gemfile:
|
|
15
15
|
- gemfiles/rails-4.0.gemfile
|
16
16
|
- gemfiles/rails-4.1.gemfile
|
17
17
|
- gemfiles/rails-4-observers.gemfile
|
18
|
-
- gemfiles/rails-4-protected-attributes.gemfile
|
19
18
|
- gemfiles/mongo_mapper.gemfile
|
20
19
|
- gemfiles/mongoid.gemfile
|
21
20
|
|
@@ -27,8 +26,6 @@ matrix:
|
|
27
26
|
gemfile: gemfiles/rails-4.1.gemfile
|
28
27
|
- rvm: 1.8.7
|
29
28
|
gemfile: gemfiles/rails-4-observers.gemfile
|
30
|
-
- rvm: 1.8.7
|
31
|
-
gemfile: gemfiles/rails-4-protected-attributes.gemfile
|
32
29
|
- rvm: 1.8.7
|
33
30
|
gemfile: gemfiles/mongoid.gemfile
|
34
31
|
|
data/lib/mongomodel.rb
CHANGED
@@ -6,6 +6,7 @@ require 'mongo'
|
|
6
6
|
require 'mongomodel/support/core_extensions'
|
7
7
|
require 'mongomodel/support/exceptions'
|
8
8
|
require 'mongomodel/log_subscriber'
|
9
|
+
require "mongomodel/vendor/active_model" unless defined?(ActiveModel::MassAssignmentSecurity)
|
9
10
|
|
10
11
|
require 'active_support/core_ext/module/attribute_accessors'
|
11
12
|
|
@@ -15,12 +16,6 @@ rescue LoadError
|
|
15
16
|
# Either ActiveModel < 4 or rails-observers gem is not available
|
16
17
|
end
|
17
18
|
|
18
|
-
begin
|
19
|
-
require "protected_attributes"
|
20
|
-
rescue LoadError
|
21
|
-
# Either ActiveModel < 4 or protected_attributes gem is not available
|
22
|
-
end
|
23
|
-
|
24
19
|
module MongoModel
|
25
20
|
autoload :VERSION, 'mongomodel/version'
|
26
21
|
|
@@ -3,29 +3,25 @@ module MongoModel
|
|
3
3
|
module Protected
|
4
4
|
extend ActiveSupport::Concern
|
5
5
|
|
6
|
-
|
7
|
-
include ActiveModel::MassAssignmentSecurity
|
6
|
+
include ActiveModel::MassAssignmentSecurity
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
module ClassMethods
|
9
|
+
def property(name, *args, &block)#:nodoc:
|
10
|
+
property = super(name, *args, &block)
|
12
11
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
end
|
12
|
+
attr_protected(name) if property.options[:protected]
|
13
|
+
attr_accessible(name) if property.options[:accessible]
|
14
|
+
|
15
|
+
property
|
18
16
|
end
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
17
|
+
end
|
18
|
+
|
19
|
+
def assign_attributes(attrs, options={})
|
20
|
+
if options[:without_protection]
|
21
|
+
super
|
22
|
+
else
|
23
|
+
super(sanitize_for_mass_assignment(attrs, options[:as] || :default))
|
26
24
|
end
|
27
|
-
elsif defined?(ActiveModel::DeprecatedMassAssignmentSecurity)
|
28
|
-
include ActiveModel::DeprecatedMassAssignmentSecurity
|
29
25
|
end
|
30
26
|
end
|
31
27
|
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
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module ActiveModel
|
4
|
+
module MassAssignmentSecurity
|
5
|
+
class PermissionSet < Set #:nodoc:
|
6
|
+
|
7
|
+
def +(values)
|
8
|
+
super(values.compact.map(&:to_s))
|
9
|
+
end
|
10
|
+
|
11
|
+
def include?(key)
|
12
|
+
super(remove_multiparameter_id(key))
|
13
|
+
end
|
14
|
+
|
15
|
+
def deny?(key)
|
16
|
+
raise NotImplementedError, "#deny?(key) supposed to be overwritten"
|
17
|
+
end
|
18
|
+
|
19
|
+
protected
|
20
|
+
|
21
|
+
def remove_multiparameter_id(key)
|
22
|
+
key.to_s.gsub(/\(.+/, '')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class WhiteList < PermissionSet #:nodoc:
|
27
|
+
|
28
|
+
def deny?(key)
|
29
|
+
!include?(key)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class BlackList < PermissionSet #:nodoc:
|
34
|
+
|
35
|
+
def deny?(key)
|
36
|
+
include?(key)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module ActiveModel
|
2
|
+
module MassAssignmentSecurity
|
3
|
+
class Sanitizer #:nodoc:
|
4
|
+
# Returns all attributes not denied by the authorizer.
|
5
|
+
def sanitize(klass, attributes, authorizer)
|
6
|
+
rejected = []
|
7
|
+
sanitized_attributes = attributes.reject do |key, value|
|
8
|
+
rejected << key if authorizer.deny?(key)
|
9
|
+
end
|
10
|
+
process_removed_attributes(klass, rejected) unless rejected.empty?
|
11
|
+
sanitized_attributes
|
12
|
+
end
|
13
|
+
|
14
|
+
protected
|
15
|
+
|
16
|
+
def process_removed_attributes(klass, attrs)
|
17
|
+
raise NotImplementedError, "#process_removed_attributes(attrs) suppose to be overwritten"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class LoggerSanitizer < Sanitizer #:nodoc:
|
22
|
+
def initialize(target)
|
23
|
+
@target = target
|
24
|
+
super()
|
25
|
+
end
|
26
|
+
|
27
|
+
def logger
|
28
|
+
@target.logger
|
29
|
+
end
|
30
|
+
|
31
|
+
def logger?
|
32
|
+
@target.respond_to?(:logger) && @target.logger
|
33
|
+
end
|
34
|
+
|
35
|
+
def backtrace
|
36
|
+
if defined? Rails
|
37
|
+
Rails.backtrace_cleaner.clean(caller)
|
38
|
+
else
|
39
|
+
caller
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def process_removed_attributes(klass, attrs)
|
44
|
+
if logger?
|
45
|
+
logger.warn do
|
46
|
+
"WARNING: Can't mass-assign protected attributes for #{klass.name}: #{attrs.join(', ')}\n" +
|
47
|
+
backtrace.map { |trace| "\t#{trace}" }.join("\n")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class StrictSanitizer < Sanitizer #:nodoc:
|
54
|
+
def initialize(target = nil)
|
55
|
+
super()
|
56
|
+
end
|
57
|
+
|
58
|
+
def process_removed_attributes(klass, attrs)
|
59
|
+
return if (attrs - insensitive_attributes).empty?
|
60
|
+
raise ActiveModel::MassAssignmentSecurity::Error.new(klass, attrs)
|
61
|
+
end
|
62
|
+
|
63
|
+
def insensitive_attributes
|
64
|
+
['id']
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class Error < StandardError #:nodoc:
|
69
|
+
def initialize(klass, attrs)
|
70
|
+
super("Can't mass-assign protected attributes for #{klass.name}: #{attrs.join(', ')}")
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
data/lib/mongomodel/version.rb
CHANGED
@@ -1,86 +1,84 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
module MongoModel
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
4
|
+
specs_for(Document, EmbeddedDocument) do
|
5
|
+
define_class(:TestDocument, described_class) do
|
6
|
+
property :foo, String
|
7
|
+
property :bar, String
|
8
|
+
end
|
9
|
+
|
10
|
+
subject { TestDocument.new }
|
11
|
+
|
12
|
+
describe "#attr_protected" do
|
13
|
+
before(:each) do
|
14
|
+
TestDocument.attr_protected :foo
|
9
15
|
end
|
10
16
|
|
11
|
-
|
17
|
+
it "disallows the attribute to be mass-assigned via attributes=" do
|
18
|
+
subject.attributes = { :foo => 'value of foo' }
|
19
|
+
subject.foo.should be_nil
|
20
|
+
end
|
12
21
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
end
|
17
|
-
|
18
|
-
it "disallows the attribute to be mass-assigned via attributes=" do
|
19
|
-
subject.attributes = { :foo => 'value of foo' }
|
20
|
-
subject.foo.should be_nil
|
21
|
-
end
|
22
|
-
|
23
|
-
it "does not disallow the attribute to be assigned individually" do
|
24
|
-
subject.foo = 'value of foo'
|
25
|
-
subject.foo.should == 'value of foo'
|
26
|
-
end
|
27
|
-
|
28
|
-
it "does not disallow other attributes to be mass-assigned via attributes=" do
|
29
|
-
subject.attributes = { :bar => 'value of bar' }
|
30
|
-
subject.bar.should == 'value of bar'
|
31
|
-
end
|
32
|
-
|
33
|
-
it "accepts multiple attributes" do
|
34
|
-
TestDocument.attr_protected :foo, :bar
|
35
|
-
|
36
|
-
subject.attributes = { :foo => 'value of foo', :bar => 'value of bar' }
|
37
|
-
subject.foo.should be_nil
|
38
|
-
subject.bar.should be_nil
|
39
|
-
end
|
22
|
+
it "does not disallow the attribute to be assigned individually" do
|
23
|
+
subject.foo = 'value of foo'
|
24
|
+
subject.foo.should == 'value of foo'
|
40
25
|
end
|
41
26
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
subject.foo.should == 'value of foo'
|
50
|
-
end
|
51
|
-
|
52
|
-
it "does not disallow other attributes to be mass-assigned via attributes=" do
|
53
|
-
subject.attributes = { :bar => 'value of bar' }
|
54
|
-
subject.bar.should be_nil
|
55
|
-
end
|
27
|
+
it "does not disallow other attributes to be mass-assigned via attributes=" do
|
28
|
+
subject.attributes = { :bar => 'value of bar' }
|
29
|
+
subject.bar.should == 'value of bar'
|
30
|
+
end
|
31
|
+
|
32
|
+
it "accepts multiple attributes" do
|
33
|
+
TestDocument.attr_protected :foo, :bar
|
56
34
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
35
|
+
subject.attributes = { :foo => 'value of foo', :bar => 'value of bar' }
|
36
|
+
subject.foo.should be_nil
|
37
|
+
subject.bar.should be_nil
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "#attr_accessible" do
|
42
|
+
before(:each) do
|
43
|
+
TestDocument.attr_accessible :foo
|
44
|
+
end
|
45
|
+
|
46
|
+
it "allows the attribute to be mass-assigned via attributes=" do
|
47
|
+
subject.attributes = { :foo => 'value of foo' }
|
48
|
+
subject.foo.should == 'value of foo'
|
49
|
+
end
|
50
|
+
|
51
|
+
it "does not disallow other attributes to be mass-assigned via attributes=" do
|
52
|
+
subject.attributes = { :bar => 'value of bar' }
|
53
|
+
subject.bar.should be_nil
|
54
|
+
end
|
55
|
+
|
56
|
+
it "does not disallow others attributes to be assigned individually" do
|
57
|
+
subject.bar = 'value of bar'
|
58
|
+
subject.bar.should == 'value of bar'
|
59
|
+
end
|
60
|
+
|
61
|
+
it "accepts multiple attributes" do
|
62
|
+
TestDocument.attr_accessible :foo, :bar
|
61
63
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
64
|
+
subject.attributes = { :foo => 'value of foo', :bar => 'value of bar' }
|
65
|
+
subject.foo.should == 'value of foo'
|
66
|
+
subject.bar.should == 'value of bar'
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "#property" do
|
71
|
+
context "with :protected option" do
|
72
|
+
it "makes the attribute protected" do
|
73
|
+
TestDocument.should_receive(:attr_protected).with(:baz)
|
74
|
+
TestDocument.property :baz, String, :protected => true
|
68
75
|
end
|
69
76
|
end
|
70
77
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
TestDocument.property :baz, String, :protected => true
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
context "with :accessible option" do
|
80
|
-
it "makes the attribute accessible" do
|
81
|
-
TestDocument.should_receive(:attr_accessible).with(:baz)
|
82
|
-
TestDocument.property :baz, String, :accessible => true
|
83
|
-
end
|
78
|
+
context "with :accessible option" do
|
79
|
+
it "makes the attribute accessible" do
|
80
|
+
TestDocument.should_receive(:attr_accessible).with(:baz)
|
81
|
+
TestDocument.property :baz, String, :accessible => true
|
84
82
|
end
|
85
83
|
end
|
86
84
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mongomodel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Pohlenz
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-02-
|
11
|
+
date: 2014-02-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -115,7 +115,6 @@ files:
|
|
115
115
|
- gemfiles/rails-3.1.gemfile
|
116
116
|
- gemfiles/rails-3.2.gemfile
|
117
117
|
- gemfiles/rails-4-observers.gemfile
|
118
|
-
- gemfiles/rails-4-protected-attributes.gemfile
|
119
118
|
- gemfiles/rails-4.0.gemfile
|
120
119
|
- gemfiles/rails-4.1.gemfile
|
121
120
|
- lib/mongomodel.rb
|
@@ -210,6 +209,10 @@ files:
|
|
210
209
|
- lib/mongomodel/support/types/symbol.rb
|
211
210
|
- lib/mongomodel/support/types/time.rb
|
212
211
|
- lib/mongomodel/tasks/database.rake
|
212
|
+
- lib/mongomodel/vendor/active_model.rb
|
213
|
+
- lib/mongomodel/vendor/active_model/mass_assignment_security.rb
|
214
|
+
- lib/mongomodel/vendor/active_model/mass_assignment_security/permission_set.rb
|
215
|
+
- lib/mongomodel/vendor/active_model/mass_assignment_security/sanitizer.rb
|
213
216
|
- lib/mongomodel/version.rb
|
214
217
|
- lib/rails/generators/mongo_model/config/config_generator.rb
|
215
218
|
- lib/rails/generators/mongo_model/config/templates/mongomodel.yml
|
@@ -1,11 +0,0 @@
|
|
1
|
-
source "https://rubygems.org"
|
2
|
-
|
3
|
-
gem "activesupport", :github => "rails/rails", :branch => "4-0-stable"
|
4
|
-
gem "activemodel", :github => "rails/rails", :branch => "4-0-stable"
|
5
|
-
gem "protected_attributes", :github => "rails/protected_attributes"
|
6
|
-
|
7
|
-
gem "bson_ext", "~> 1.8"
|
8
|
-
gem "tzinfo"
|
9
|
-
gem "rake"
|
10
|
-
|
11
|
-
gemspec :path => "../"
|