mongomodel 0.5.1 → 0.5.2
Sign up to get free protection for your applications and to get access to all the features.
- 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 => "../"
|