strong_parameters 0.2.0 → 0.2.1

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.
@@ -0,0 +1,129 @@
1
+ [![Travis CI](https://secure.travis-ci.org/rails/strong_parameters.png)](http://travis-ci.org/rails/strong_parameters) [![Gem Version](https://badge.fury.io/rb/strong_parameters.png)](http://badge.fury.io/rb/strong_parameters)
2
+ # Strong Parameters
3
+
4
+ With this plugin Action Controller parameters are forbidden to be used in Active Model mass assignments until they have been whitelisted. This means you'll have to make a conscious choice about which attributes to allow for mass updating and thus prevent accidentally exposing that which shouldn't be exposed.
5
+
6
+ In addition, parameters can be marked as required and flow through a predefined raise/rescue flow to end up as a 400 Bad Request with no effort.
7
+
8
+ ``` ruby
9
+ class PeopleController < ActionController::Base
10
+ # This will raise an ActiveModel::ForbiddenAttributes exception because it's using mass assignment
11
+ # without an explicit permit step.
12
+ def create
13
+ Person.create(params[:person])
14
+ end
15
+
16
+ # This will pass with flying colors as long as there's a person key in the parameters, otherwise
17
+ # it'll raise a ActionController::MissingParameter exception, which will get caught by
18
+ # ActionController::Base and turned into that 400 Bad Request reply.
19
+ def update
20
+ person = current_account.people.find(params[:id])
21
+ person.update_attributes!(person_params)
22
+ redirect_to person
23
+ end
24
+
25
+ private
26
+ # Using a private method to encapsulate the permissible parameters is just a good pattern
27
+ # since you'll be able to reuse the same permit list between create and update. Also, you
28
+ # can specialize this method with per-user checking of permissible attributes.
29
+ def person_params
30
+ params.require(:person).permit(:name, :age)
31
+ end
32
+ end
33
+ ```
34
+
35
+ ## Permitted Scalar Values
36
+
37
+ Given
38
+
39
+ ``` ruby
40
+ params.permit(:id)
41
+ ```
42
+
43
+ the key `:id` will pass the whitelisting if it appears in `params` and it has a permitted scalar value associated. Otherwise the key is going to be filtered out, so arrays, hashes, or any other objects cannot be injected.
44
+
45
+ The permitted scalar types are `String`, `Symbol`, `NilClass`, `Numeric`, `TrueClass`, `FalseClass`, `Date`, `Time`, `DateTime`, `StringIO`, `IO`, `ActionDispatch::Http::UploadedFile` and `Rack::Test::UploadedFile`.
46
+
47
+ To declare that the value in `params` must be an array of permitted scalar values map the key to an empty array:
48
+
49
+ ``` ruby
50
+ params.permit(:id => [])
51
+ ```
52
+
53
+ To whitelist an entire hash of parameters, the `permit!` method can be used
54
+
55
+ ``` ruby
56
+ params.require(:log_entry).permit!
57
+ ```
58
+
59
+ This will mark the `:log_entry` parameters hash and any subhash of it permitted. Extreme care should be taken when using `permit!` as it will allow all current and future model attributes to be mass-assigned.
60
+
61
+ ## Nested Parameters
62
+
63
+ You can also use permit on nested parameters, like:
64
+
65
+ ``` ruby
66
+ params.permit(:name, {:emails => []}, :friends => [ :name, { :family => [ :name ], :hobbies => [] }])
67
+ ```
68
+
69
+ This declaration whitelists the `name`, `emails` and `friends` attributes. It is expected that `emails` will be an array of permitted scalar values and that `friends` will be an array of resources with specific attributes : they should have a `name` attribute (any permitted scalar values allowed), a `hobbies` attribute as an array of permitted scalar values, and a `family` attribute which is restricted to having a `name` (any permitted scalar values allowed, too).
70
+
71
+ Thanks to Nick Kallen for the permit idea!
72
+
73
+ ## Handling of Unpermitted Keys
74
+
75
+ By default parameter keys that are not explicitly permitted will be logged in the development and test environment. In other environments these parameters will simply be filtered out and ignored.
76
+
77
+ Additionally, this behaviour can be changed by changing the `config.action_controller.action_on_unpermitted_parameters` property in your environment files. If set to `:log` the unpermitted attributes will be logged, if set to `:raise` an exception will be raised.
78
+
79
+ ## Use Outside of Controllers
80
+
81
+ While Strong Parameters will enforce permitted and required values in your application controllers, keep in mind
82
+ that you will need to sanitize untrusted data used for mass assignment when in use outside of controllers.
83
+
84
+ For example, if you retrieve JSON data from a third party API call and pass the unchecked parsed result on to
85
+ `Model.create`, undesired mass assignments could take place. You can alleviate this risk by slicing the hash data,
86
+ or wrapping the data in a new instance of `ActionController::Parameters` and declaring permissions the same as
87
+ you would in a controller. For example:
88
+
89
+ ``` ruby
90
+ raw_parameters = { :email => "john@example.com", :name => "John", :admin => true }
91
+ parameters = ActionController::Parameters.new(raw_parameters)
92
+ user = User.create(parameters.permit(:name, :email))
93
+ ```
94
+
95
+ ## Installation
96
+
97
+ In Gemfile:
98
+
99
+ ``` ruby
100
+ gem 'strong_parameters'
101
+ ```
102
+
103
+ and then run `bundle`. To activate the strong parameters, you need to include this module in
104
+ every model you want protected.
105
+
106
+ ``` ruby
107
+ class Post < ActiveRecord::Base
108
+ include ActiveModel::ForbiddenAttributesProtection
109
+ end
110
+ ```
111
+
112
+ Alternatively, you can protect all Active Record resources by default by creating an initializer and pasting the line:
113
+
114
+ ``` ruby
115
+ ActiveRecord::Base.send(:include, ActiveModel::ForbiddenAttributesProtection)
116
+ ```
117
+
118
+ If you want to now disable the default whitelisting that occurs in Rails 3.2, change the `config.active_record.whitelist_attributes` property in your `config/application.rb`:
119
+
120
+ ``` ruby
121
+ config.active_record.whitelist_attributes = false
122
+ ```
123
+
124
+ This will allow you to remove / not have to use `attr_accessible` and do mass assignment inside your code and tests.
125
+
126
+ ## Compatibility
127
+
128
+ This plugin is only fully compatible with Rails versions 3.0, 3.1 and 3.2 but not 4.0+, as it is part of Rails Core in 4.0.
129
+ An unofficial Rails 2 version is [strong_parameters_rails2](https://github.com/grosser/strong_parameters/tree/rails2).
data/Rakefile CHANGED
@@ -12,7 +12,8 @@ RDoc::Task.new(:rdoc) do |rdoc|
12
12
  rdoc.rdoc_dir = 'rdoc'
13
13
  rdoc.title = 'StrongParameters'
14
14
  rdoc.options << '--line-numbers'
15
- rdoc.rdoc_files.include('README.rdoc')
15
+ rdoc_main = 'README.md'
16
+ rdoc.rdoc_files.include('README.md')
16
17
  rdoc.rdoc_files.include('lib/**/*.rb')
17
18
  end
18
19
 
@@ -143,6 +143,7 @@ module ActionController
143
143
  StringIO,
144
144
  IO,
145
145
  ActionDispatch::Http::UploadedFile,
146
+ Rack::Test::UploadedFile,
146
147
  ]
147
148
 
148
149
  def permitted_scalar?(value)
@@ -167,9 +168,9 @@ module ActionController
167
168
  end
168
169
  end
169
170
 
170
- def array_of_permitted_scalars_filter(params, key)
171
- if has_key?(key) && array_of_permitted_scalars?(self[key])
172
- params[key] = self[key]
171
+ def array_of_permitted_scalars_filter(params, key, hash = self)
172
+ if hash.has_key?(key) && array_of_permitted_scalars?(hash[key])
173
+ params[key] = hash[key]
173
174
  end
174
175
  end
175
176
 
@@ -185,10 +186,12 @@ module ActionController
185
186
  array_of_permitted_scalars_filter(params, key)
186
187
  else
187
188
  # Declaration {:user => :name} or {:user => [:name, :age, {:adress => ...}]}.
188
- params[key] = each_element(value) do |element|
189
+ params[key] = each_element(value) do |element, index|
189
190
  if element.is_a?(Hash)
190
191
  element = self.class.new(element) unless element.respond_to?(:permit)
191
192
  element.permit(*Array.wrap(filter[key]))
193
+ elsif filter[key].is_a?(Hash) && filter[key][index] == []
194
+ array_of_permitted_scalars_filter(params, index, value)
192
195
  end
193
196
  end
194
197
  end
@@ -201,7 +204,7 @@ module ActionController
201
204
  # fields_for on an array of records uses numeric hash keys.
202
205
  elsif value.is_a?(Hash) && value.keys.all? { |k| k =~ /\A-?\d+\z/ }
203
206
  hash = value.class.new
204
- value.each { |k,v| hash[k] = yield v }
207
+ value.each { |k,v| hash[k] = yield(v, k) }
205
208
  hash
206
209
  else
207
210
  yield value
@@ -215,8 +218,9 @@ module ActionController
215
218
 
216
219
  if unpermitted_keys.any?
217
220
  case self.class.action_on_unpermitted_parameters
218
- when :log
219
- ActionController::Base.logger.debug "Unpermitted parameters: #{unpermitted_keys.join(", ")}"
221
+ when :log
222
+ name = "unpermitted_parameters.action_controller"
223
+ ActiveSupport::Notifications.instrument(name, :keys => unpermitted_keys)
220
224
  when :raise
221
225
  raise ActionController::UnpermittedParameters.new(unpermitted_keys)
222
226
  end
@@ -1,3 +1,4 @@
1
1
  require 'action_controller/parameters'
2
2
  require 'active_model/forbidden_attributes_protection'
3
3
  require 'strong_parameters/railtie'
4
+ require 'strong_parameters/log_subscriber'
@@ -0,0 +1,14 @@
1
+ module StrongParameters
2
+ class LogSubscriber < ActiveSupport::LogSubscriber
3
+ def unpermitted_parameters(event)
4
+ unpermitted_keys = event.payload[:keys]
5
+ debug("Unpermitted parameters: #{unpermitted_keys.join(", ")}")
6
+ end
7
+
8
+ def logger
9
+ ActionController::Base.logger
10
+ end
11
+ end
12
+ end
13
+
14
+ StrongParameters::LogSubscriber.attach_to :action_controller
@@ -1,3 +1,3 @@
1
1
  module StrongParameters
2
- VERSION = "0.2.0"
2
+ VERSION = "0.2.1"
3
3
  end
@@ -8,7 +8,6 @@ class StrongParametersControllerGeneratorTest < Rails::Generators::TestCase
8
8
  setup :prepare_destination
9
9
 
10
10
  def test_controller_content
11
- Rails.stubs(:application).returns(nil)
12
11
  run_generator
13
12
 
14
13
  assert_file "app/controllers/users_controller.rb" do |content|
@@ -30,9 +30,6 @@ GEM
30
30
  abstract (>= 1.0.0)
31
31
  i18n (0.5.0)
32
32
  json (1.7.5)
33
- metaclass (0.0.1)
34
- mocha (0.12.7)
35
- metaclass (~> 0.0.1)
36
33
  rack (1.2.5)
37
34
  rack-mount (0.6.14)
38
35
  rack (>= 1.0.0)
@@ -56,7 +53,6 @@ PLATFORMS
56
53
  DEPENDENCIES
57
54
  actionpack (~> 3.0.0)
58
55
  activemodel (~> 3.0.0)
59
- mocha (~> 0.12.0)
60
56
  railties (~> 3.0.0)
61
57
  rake
62
58
  strong_parameters!
@@ -27,7 +27,7 @@ class NestedParametersTest < ActiveSupport::TestCase
27
27
  values += [0, 1.0, 2**128, BigDecimal.new('1')]
28
28
  values += [true, false]
29
29
  values += [Date.today, Time.now, DateTime.now]
30
- values += [StringIO.new, STDOUT, ActionDispatch::Http::UploadedFile.new(:tempfile => __FILE__)]
30
+ values += [StringIO.new, STDOUT, ActionDispatch::Http::UploadedFile.new(:tempfile => __FILE__), Rack::Test::UploadedFile.new(__FILE__)]
31
31
 
32
32
  values.each do |value|
33
33
  params = ActionController::Parameters.new(:id => value)
@@ -291,4 +291,22 @@ class NestedParametersTest < ActiveSupport::TestCase
291
291
 
292
292
  assert_filtered_out permitted[:book][:authors_attributes]['-1'], :age_of_death
293
293
  end
294
+
295
+ test "fields_for_style_nested_params with nested arrays" do
296
+ params = ActionController::Parameters.new({
297
+ :book => {
298
+ :authors_attributes => {
299
+ :'0' => ['William Shakespeare', '52'],
300
+ :'1' => ['Unattributed Assistant']
301
+ }
302
+ }
303
+ })
304
+ permitted = params.permit :book => { :authors_attributes => { :'0' => [], :'1' => [] } }
305
+
306
+ assert_not_nil permitted[:book][:authors_attributes]['0']
307
+ assert_not_nil permitted[:book][:authors_attributes]['1']
308
+ assert_nil permitted[:book][:authors_attributes]['2']
309
+ assert_equal 'William Shakespeare', permitted[:book][:authors_attributes]['0'][0]
310
+ assert_equal 'Unattributed Assistant', permitted[:book][:authors_attributes]['1'][0]
311
+ end
294
312
  end
@@ -10,7 +10,6 @@ Rails.application = FakeApplication
10
10
  Rails.configuration.action_controller = ActiveSupport::OrderedOptions.new
11
11
 
12
12
  require 'strong_parameters'
13
- require 'mocha'
14
13
 
15
14
  module ActionController
16
15
  SharedTestRoutes = ActionDispatch::Routing::RouteSet.new
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: strong_parameters
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-02-18 00:00:00.000000000 Z
12
+ date: 2013-05-08 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: actionpack
@@ -75,22 +75,6 @@ dependencies:
75
75
  - - ! '>='
76
76
  - !ruby/object:Gem::Version
77
77
  version: '0'
78
- - !ruby/object:Gem::Dependency
79
- name: mocha
80
- requirement: !ruby/object:Gem::Requirement
81
- none: false
82
- requirements:
83
- - - ~>
84
- - !ruby/object:Gem::Version
85
- version: 0.12.0
86
- type: :development
87
- prerelease: false
88
- version_requirements: !ruby/object:Gem::Requirement
89
- none: false
90
- requirements:
91
- - - ~>
92
- - !ruby/object:Gem::Version
93
- version: 0.12.0
94
78
  description:
95
79
  email:
96
80
  - david@heinemeierhansson.com
@@ -103,12 +87,13 @@ files:
103
87
  - lib/generators/rails/strong_parameters_controller_generator.rb
104
88
  - lib/generators/rails/templates/controller.rb
105
89
  - lib/generators/rails/USAGE
90
+ - lib/strong_parameters/log_subscriber.rb
106
91
  - lib/strong_parameters/railtie.rb
107
92
  - lib/strong_parameters/version.rb
108
93
  - lib/strong_parameters.rb
109
94
  - MIT-LICENSE
110
95
  - Rakefile
111
- - README.rdoc
96
+ - README.md
112
97
  - test/action_controller_required_params_test.rb
113
98
  - test/action_controller_tainted_params_test.rb
114
99
  - test/active_model_mass_assignment_taint_protection_test.rb
@@ -1,102 +0,0 @@
1
- = Strong Parameters
2
-
3
- With this plugin Action Controller parameters are forbidden to be used in Active Model mass assignments until they have been whitelisted. This means you'll have to make a conscious choice about which attributes to allow for mass updating and thus prevent accidentally exposing that which shouldn't be exposed.
4
-
5
- In addition, parameters can be marked as required and flow through a predefined raise/rescue flow to end up as a 400 Bad Request with no effort.
6
-
7
- class PeopleController < ActionController::Base
8
- # This will raise an ActiveModel::ForbiddenAttributes exception because it's using mass assignment
9
- # without an explicit permit step.
10
- def create
11
- Person.create(params[:person])
12
- end
13
-
14
- # This will pass with flying colors as long as there's a person key in the parameters, otherwise
15
- # it'll raise a ActionController::MissingParameter exception, which will get caught by
16
- # ActionController::Base and turned into that 400 Bad Request reply.
17
- def update
18
- person = current_account.people.find(params[:id])
19
- person.update_attributes!(person_params)
20
- redirect_to person
21
- end
22
-
23
- private
24
- # Using a private method to encapsulate the permissible parameters is just a good pattern
25
- # since you'll be able to reuse the same permit list between create and update. Also, you
26
- # can specialize this method with per-user checking of permissible attributes.
27
- def person_params
28
- params.require(:person).permit(:name, :age)
29
- end
30
- end
31
-
32
- == Permitted Scalar Values
33
-
34
- Given
35
-
36
- params.permit(:id)
37
-
38
- the key +:id+ will pass the whitelisting if it appears in +params+ and it has a permitted scalar value associated. Otherwise the key is going to be filtered out, so arrays, hashes, or any other objects cannot be injected.
39
-
40
- The permitted scalar types are +String+, +Symbol+, +NilClass+, +Numeric+, +TrueClass+, +FalseClass+, +Date+, +Time+, +DateTime+, +StringIO+, +IO+, and +ActionDispatch::Http::UploadedFile+.
41
-
42
- To declare that the value in +params+ must be an array of permitted scalar values map the key to an empty array:
43
-
44
- params.permit(:id => [])
45
-
46
- == Nested Parameters
47
-
48
- You can also use permit on nested parameters, like:
49
-
50
- params.permit(:name, {:emails => []}, :friends => [ :name, { :family => [ :name ], :hobbies => [] }])
51
-
52
- This declaration whitelists the +name+, +emails+ and +friends+ attributes. It is expected that +emails+ will be an array of permitted scalar values and that +friends+ will be an array of resources with specific attributes : they should have a +name+ attribute (any permitted scalar values allowed), a +hobbies+ attribute as an array of permitted scalar values, and a +family+ attribute which is restricted to having a +name+ (any permitted scalar values allowed, too).
53
-
54
- Thanks to Nick Kallen for the permit idea!
55
-
56
- == Handling of Unpermitted Keys
57
-
58
- By default parameter keys that are not explicitly permitted will be logged in the development and test environment. In other environments these parameters will simply be filtered out and ignored.
59
-
60
- Additionally, this behaviour can be changed by changing the +config.action_controller.action_on_unpermitted_parameters+ property in your environment files. If set to +:log+ the unpermitted attributes will be logged, if set to +:raise+ an exception will be raised.
61
-
62
- == Use Outside of Controllers
63
-
64
- While Strong Parameters will enforce permitted and required values in your application controllers, keep in mind
65
- that you will need to sanitize untrusted data used for mass assignment when in use outside of controllers.
66
-
67
- For example, if you retrieve JSON data from a third party API call and pass the unchecked parsed result on to
68
- +Model.create+, undesired mass assignments could take place. You can alleviate this risk by slicing the hash data,
69
- or wrapping the data in a new instance of +ActionController::Parameters+ and declaring permissions the same as
70
- you would in a controller. For example:
71
-
72
- raw_parameters = { :email => "john@example.com", :name => "John", :admin => true }
73
- parameters = ActionController::Parameters.new(raw_parameters)
74
- user = User.create(parameters.permit(:name, :email))
75
-
76
- == Installation
77
-
78
- In Gemfile:
79
-
80
- gem 'strong_parameters'
81
-
82
- and then run `bundle`. To activate the strong parameters, you need to include this module in
83
- every model you want protected.
84
-
85
- class Post < ActiveRecord::Base
86
- include ActiveModel::ForbiddenAttributesProtection
87
- end
88
-
89
- Alternatively, you can protect all Active Record resources by default by creating an initializer and pasting the line:
90
-
91
- ActiveRecord::Base.send(:include, ActiveModel::ForbiddenAttributesProtection)
92
-
93
- If you want to now disable the default whitelisting that occurs in later versions of Rails, change the +config.active_record.whitelist_attributes+ property in your +config/application.rb+:
94
-
95
- config.active_record.whitelist_attributes = false
96
-
97
- This will allow you to remove / not have to use +attr_accessible+ and do mass assignment inside your code and tests.
98
-
99
- == Compatibility
100
-
101
- This plugin is only fully compatible with Rails versions 3.0, 3.1 and 3.2 but not 4.0+, as it is part of Rails Core in 4.0.
102
- An unofficial Rails 2 version is {strong_parameters_rails2}[https://github.com/grosser/strong_parameters/tree/rails2].