strongly_typed_parameters 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ require 'action_controller/parameters'
2
+ require 'active_model/forbidden_attributes_protection'
3
+ require 'strongly_typed_parameters/railtie'
4
+ require 'strongly_typed_parameters/boolean'
@@ -0,0 +1,12 @@
1
+ #marker module to identify boolean objects.
2
+
3
+ module Boolean
4
+ end
5
+
6
+ class TrueClass
7
+ include Boolean
8
+ end
9
+
10
+ class FalseClass
11
+ include Boolean
12
+ end
@@ -0,0 +1,17 @@
1
+ require 'rails/railtie'
2
+
3
+ module StronglyTypedParameters
4
+ class Railtie < ::Rails::Railtie
5
+ if config.respond_to?(:app_generators)
6
+ config.app_generators.scaffold_controller = :strongly_typed_parameters_controller
7
+ else
8
+ config.generators.scaffold_controller = :strongly_typed_parameters_controller
9
+ end
10
+
11
+ initializer "strong_parameters.config", :before => "action_controller.set_configs" do |app|
12
+ ActionController::Parameters.action_on_unpermitted_parameters = app.config.action_controller.delete(:action_on_unpermitted_parameters) do
13
+ (Rails.env.test? || Rails.env.development?) ? :log : false
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ module StronglyTypedParameters
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,30 @@
1
+ require 'test_helper'
2
+
3
+ class BooksController < ActionController::Base
4
+ def create
5
+ params.require(:book).require(:name)
6
+ head :ok
7
+ end
8
+ end
9
+
10
+ class ActionControllerRequiredParamsTest < ActionController::TestCase
11
+ tests BooksController
12
+
13
+ test "missing required parameters will raise exception" do
14
+ post :create, { :magazine => { :name => "Mjallo!" } }
15
+ assert_response :bad_request
16
+
17
+ post :create, { :book => { :title => "Mjallo!" } }
18
+ assert_response :bad_request
19
+ end
20
+
21
+ test "required parameters that are present will not raise" do
22
+ post :create, { :book => { :name => "Mjallo!" } }
23
+ assert_response :ok
24
+ end
25
+
26
+ test "missing parameters will be mentioned in the return" do
27
+ post :create, { :magazine => { :name => "Mjallo!" } }
28
+ assert_equal "Required parameter missing: book", response.body
29
+ end
30
+ end
@@ -0,0 +1,25 @@
1
+ require 'test_helper'
2
+
3
+ class PeopleController < ActionController::Base
4
+ def create
5
+ render :text => params[:person].permitted? ? "untainted" : "tainted"
6
+ end
7
+
8
+ def create_with_permit
9
+ render :text => params[:person].permit(:name).permitted? ? "untainted" : "tainted"
10
+ end
11
+ end
12
+
13
+ class ActionControllerTaintedParamsTest < ActionController::TestCase
14
+ tests PeopleController
15
+
16
+ test "parameters are tainted" do
17
+ post :create, { :person => { :name => "Mjallo!" } }
18
+ assert_equal "tainted", response.body
19
+ end
20
+
21
+ test "parameters can be permitted and are then not tainted" do
22
+ post :create_with_permit, { :person => { :name => "Mjallo!" } }
23
+ assert_equal "untainted", response.body
24
+ end
25
+ end
@@ -0,0 +1,30 @@
1
+ require 'test_helper'
2
+
3
+ class Person
4
+ include ActiveModel::MassAssignmentSecurity
5
+ include ActiveModel::ForbiddenAttributesProtection
6
+
7
+ public :sanitize_for_mass_assignment
8
+ end
9
+
10
+ class ActiveModelMassUpdateProtectionTest < ActiveSupport::TestCase
11
+ test "forbidden attributes cannot be used for mass updating" do
12
+ assert_raises(ActiveModel::ForbiddenAttributes) do
13
+ Person.new.sanitize_for_mass_assignment(ActionController::Parameters.new(:a => "b"))
14
+ end
15
+ end
16
+
17
+ test "permitted attributes can be used for mass updating" do
18
+ assert_nothing_raised do
19
+ assert_equal({ "a" => "b" },
20
+ Person.new.sanitize_for_mass_assignment(ActionController::Parameters.new(:a => "b").permit(:a)))
21
+ end
22
+ end
23
+
24
+ test "regular attributes should still be allowed" do
25
+ assert_nothing_raised do
26
+ assert_equal({ :a => "b" },
27
+ Person.new.sanitize_for_mass_assignment(:a => "b"))
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,28 @@
1
+ require 'test_helper'
2
+ require 'action_controller/parameters'
3
+
4
+ class Column
5
+ attr_accessor :name
6
+ attr_accessor :klass
7
+ def initialize(hsh)
8
+ hsh.each do |name, klass|
9
+ @name = name
10
+ @klass = klass
11
+ end
12
+ end
13
+ end
14
+
15
+ class User
16
+ def self.columns
17
+ [Column.new("id" => Fixnum), Column.new("name" => String)]
18
+ end
19
+ end
20
+
21
+ class ActiveModelSmartTypeDefaultingTest < ActiveSupport::TestCase
22
+ test "if no types are given but the parent object shares a name with a model, attribute types are used" do
23
+ params = ActionController::Parameters.new(:user => [:id => 1234])
24
+ permitted = params.permit(:user => [:id, :name])
25
+ assert_equal permitted[:user][0][:id], 1234
26
+ assert_nil permitted[:user][0][:name]
27
+ end
28
+ end
@@ -0,0 +1,38 @@
1
+ #FIXME: This errors due to Mocha.
2
+
3
+ =begin
4
+
5
+ require 'rails/generators/test_case'
6
+ require 'generators/rails/strong_parameters_controller_generator'
7
+
8
+ class StrongParametersControllerGeneratorTest < Rails::Generators::TestCase
9
+ tests Rails::Generators::StrongParametersControllerGenerator
10
+ arguments %w(User name:string age:integer --orm=none)
11
+ destination File.expand_path("../tmp", File.dirname(__FILE__))
12
+ setup :prepare_destination
13
+
14
+ def test_controller_content
15
+ Rails.stubs(:application).returns(nil)
16
+ run_generator
17
+
18
+ assert_file "app/controllers/users_controller.rb" do |content|
19
+
20
+ assert_instance_method :create, content do |m|
21
+ assert_match '@user = User.new(user_params)', m
22
+ assert_match '@user.save', m
23
+ assert_match '@user.errors', m
24
+ end
25
+
26
+ assert_instance_method :update, content do |m|
27
+ assert_match '@user = User.find(params[:id])', m
28
+ assert_match '@user.update_attributes(user_params)', m
29
+ assert_match '@user.errors', m
30
+ end
31
+
32
+ assert_match 'def user_params', content
33
+ assert_match 'params.require(:user).permit(:age, :name)', content
34
+ end
35
+ end
36
+ end
37
+
38
+ =end
@@ -0,0 +1,6 @@
1
+ source :rubygems
2
+ gemspec :path => "./../.."
3
+
4
+ gem "actionpack", "~> 3.0.0"
5
+ gem "railties", "~> 3.0.0"
6
+ gem "activemodel", "~> 3.0.0"
@@ -0,0 +1,62 @@
1
+ PATH
2
+ remote: /Users/mgrosser/code/tools/strong_parameters
3
+ specs:
4
+ strong_parameters (0.1.6.dev)
5
+ actionpack (~> 3.0)
6
+ activemodel (~> 3.0)
7
+ railties (~> 3.0)
8
+
9
+ GEM
10
+ remote: http://rubygems.org/
11
+ specs:
12
+ abstract (1.0.0)
13
+ actionpack (3.0.17)
14
+ activemodel (= 3.0.17)
15
+ activesupport (= 3.0.17)
16
+ builder (~> 2.1.2)
17
+ erubis (~> 2.6.6)
18
+ i18n (~> 0.5.0)
19
+ rack (~> 1.2.5)
20
+ rack-mount (~> 0.6.14)
21
+ rack-test (~> 0.5.7)
22
+ tzinfo (~> 0.3.23)
23
+ activemodel (3.0.17)
24
+ activesupport (= 3.0.17)
25
+ builder (~> 2.1.2)
26
+ i18n (~> 0.5.0)
27
+ activesupport (3.0.17)
28
+ builder (2.1.2)
29
+ erubis (2.6.6)
30
+ abstract (>= 1.0.0)
31
+ i18n (0.5.0)
32
+ json (1.7.5)
33
+ metaclass (0.0.1)
34
+ mocha (0.12.7)
35
+ metaclass (~> 0.0.1)
36
+ rack (1.2.5)
37
+ rack-mount (0.6.14)
38
+ rack (>= 1.0.0)
39
+ rack-test (0.5.7)
40
+ rack (>= 1.0)
41
+ railties (3.0.17)
42
+ actionpack (= 3.0.17)
43
+ activesupport (= 3.0.17)
44
+ rake (>= 0.8.7)
45
+ rdoc (~> 3.4)
46
+ thor (~> 0.14.4)
47
+ rake (10.0.1)
48
+ rdoc (3.12)
49
+ json (~> 1.4)
50
+ thor (0.14.6)
51
+ tzinfo (0.3.35)
52
+
53
+ PLATFORMS
54
+ ruby
55
+
56
+ DEPENDENCIES
57
+ actionpack (~> 3.0.0)
58
+ activemodel (~> 3.0.0)
59
+ mocha (~> 0.12.0)
60
+ railties (~> 3.0.0)
61
+ rake
62
+ strong_parameters!
@@ -0,0 +1,6 @@
1
+ source :rubygems
2
+ gemspec :path => "./../.."
3
+
4
+ gem "actionpack", "~> 3.1.0"
5
+ gem "railties", "~> 3.1.0"
6
+ gem "activemodel", "~> 3.1.0"
@@ -0,0 +1,6 @@
1
+ source :rubygems
2
+ gemspec :path => "./../.."
3
+
4
+ gem "actionpack", "~> 3.2.0"
5
+ gem "railties", "~> 3.2.0"
6
+ gem "activemodel", "~> 3.2.0"
@@ -0,0 +1,50 @@
1
+ require 'test_helper'
2
+ require 'action_controller/parameters'
3
+
4
+ class LogOnUnpermittedParamsTest < ActiveSupport::TestCase
5
+ def setup
6
+ ActionController::Parameters.action_on_unpermitted_parameters = :log
7
+ end
8
+
9
+ def teardown
10
+ ActionController::Parameters.action_on_unpermitted_parameters = false
11
+ end
12
+
13
+ test "logs on unexpected params" do
14
+ params = ActionController::Parameters.new({
15
+ :book => { :pages => 65 },
16
+ :fishing => "Turnips"
17
+ })
18
+
19
+ assert_logged("Unpermitted parameters: fishing") do
20
+ params.permit(:book => [:pages])
21
+ end
22
+ end
23
+
24
+ test "logs on unexpected nested params" do
25
+ params = ActionController::Parameters.new({
26
+ :book => { :pages => 65, :title => "Green Cats and where to find then." }
27
+ })
28
+
29
+ assert_logged("Unpermitted parameters: title") do
30
+ params.permit(:book => [:pages => Numeric])
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def assert_logged(message)
37
+ old_logger = ActionController::Base.logger
38
+ log = StringIO.new
39
+ ActionController::Base.logger = Logger.new(log)
40
+
41
+ begin
42
+ yield
43
+
44
+ log.rewind
45
+ assert_match message, log.read
46
+ ensure
47
+ ActionController::Base.logger = old_logger
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,38 @@
1
+ require 'test_helper'
2
+ require 'action_controller/parameters'
3
+
4
+ class MultiParameterAttributesTest < ActiveSupport::TestCase
5
+ test "permitted multi-parameter attribute keys" do
6
+ params = ActionController::Parameters.new({
7
+ :book => {
8
+ "shipped_at(1i)" => "2012",
9
+ "shipped_at(2i)" => "3",
10
+ "shipped_at(3i)" => "25",
11
+ "shipped_at(4i)" => "10",
12
+ "shipped_at(5i)" => "15",
13
+ "published_at(1i)" => "1999",
14
+ "published_at(2i)" => "2",
15
+ "published_at(3i)" => "5",
16
+ "price(1)" => "R$",
17
+ "price(2f)" => "2.02"
18
+ }
19
+ })
20
+ permitted = params.permit :book => [ :shipped_at, :price ]
21
+
22
+ assert permitted.permitted?
23
+
24
+ assert_equal "2012", permitted[:book]["shipped_at(1i)"]
25
+ assert_equal "3", permitted[:book]["shipped_at(2i)"]
26
+ assert_equal "25", permitted[:book]["shipped_at(3i)"]
27
+ assert_equal "10", permitted[:book]["shipped_at(4i)"]
28
+ assert_equal "15", permitted[:book]["shipped_at(5i)"]
29
+
30
+ assert_equal "R$", permitted[:book]["price(1)"]
31
+ assert_equal "2.02", permitted[:book]["price(2f)"]
32
+
33
+ assert_nil permitted[:book]["published_at(1i)"]
34
+ assert_nil permitted[:book]["published_at(2i)"]
35
+ assert_nil permitted[:book]["published_at(3i)"]
36
+ end
37
+ end
38
+
@@ -0,0 +1,264 @@
1
+ require 'test_helper'
2
+ require 'action_controller/parameters'
3
+ require 'action_dispatch/http/upload'
4
+
5
+ class NestedParametersTest < ActiveSupport::TestCase
6
+ def assert_filtered_out(params, key)
7
+ assert !params.has_key?(key), "key #{key.inspect} has not been filtered out"
8
+ end
9
+
10
+ #
11
+ # --- Basic interface --------------------------------------------------------
12
+ #
13
+
14
+ # --- nothing ----------------------------------------------------------------
15
+
16
+ test 'if nothing is permitted, the hash becomes empty' do
17
+ params = ActionController::Parameters.new(:id => '1234')
18
+ permitted = params.permit
19
+ permitted.permitted?
20
+ permitted.empty?
21
+ end
22
+
23
+ # --- key --------------------------------------------------------------------
24
+
25
+ test 'key: unexpected types are filtered out' do
26
+ params = ActionController::Parameters.new(:id => 1234, :token => 0)
27
+ permitted = params.permit(:id => Numeric, :token => String)
28
+ assert_equal 1234, permitted[:id]
29
+ assert_filtered_out permitted, :token
30
+ end
31
+
32
+ test 'key: unknown keys are filtered out' do
33
+ params = ActionController::Parameters.new(:id => '1234', :injected => 'injected')
34
+ permitted = params.permit(:id)
35
+ assert_equal '1234', permitted[:id]
36
+ assert_filtered_out permitted, :injected
37
+ end
38
+
39
+ test 'key: arrays are filtered out' do
40
+ [[], [1], ['1']].each do |array|
41
+ params = ActionController::Parameters.new(:id => array)
42
+ permitted = params.permit(:id)
43
+ assert_filtered_out permitted, :id
44
+
45
+ %w(i f).each do |suffix|
46
+ params = ActionController::Parameters.new("foo(000#{suffix})" => array)
47
+ permitted = params.permit(:foo)
48
+ assert_filtered_out permitted, "foo(000#{suffix})"
49
+ end
50
+ end
51
+ end
52
+
53
+ test 'key: hashes are filtered out' do
54
+ [{}, {:foo => 1}, {:foo => 'bar'}].each do |hash|
55
+ params = ActionController::Parameters.new(:id => hash)
56
+ permitted = params.permit(:id)
57
+ assert_filtered_out permitted, :id
58
+
59
+ %w(i f).each do |suffix|
60
+ params = ActionController::Parameters.new("foo(000#{suffix})" => hash)
61
+ permitted = params.permit(:foo)
62
+ assert_filtered_out permitted, "foo(000#{suffix})"
63
+ end
64
+ end
65
+ end
66
+
67
+ test 'key: non-permitted scalar values are filtered out' do
68
+ params = ActionController::Parameters.new(:id => Object.new)
69
+ permitted = params.permit(:id)
70
+ assert_filtered_out permitted, :id
71
+
72
+ %w(i f).each do |suffix|
73
+ params = ActionController::Parameters.new("foo(000#{suffix})" => Object.new)
74
+ permitted = params.permit(:foo)
75
+ assert_filtered_out permitted, "foo(000#{suffix})"
76
+ end
77
+ end
78
+
79
+ test 'key: Boolean matches only true and false' do
80
+ params = ActionController::Parameters.new(:happy => true)
81
+ permitted = params.permit(:happy => Boolean)
82
+ assert_equal true, permitted[:happy]
83
+
84
+ params = ActionController::Parameters.new(:happy => false)
85
+ permitted = params.permit(:happy => Boolean)
86
+ assert_equal false, permitted[:happy]
87
+
88
+ params = ActionController::Parameters.new(:happy => Object.new)
89
+ permitted = params.permit(:happy => Boolean)
90
+ assert_filtered_out permitted, :happy
91
+
92
+ end
93
+
94
+ test 'key: it is not assigned if not present in params' do
95
+ params = ActionController::Parameters.new(:name => 'Joe')
96
+ permitted = params.permit(:id)
97
+ assert !permitted.has_key?(:id)
98
+ end
99
+
100
+ #
101
+ # --- Nesting ----------------------------------------------------------------
102
+ #
103
+
104
+ test "permitted nested parameters" do
105
+ params = ActionController::Parameters.new({
106
+ :book => {
107
+ :title => "Romeo and Juliet",
108
+ :authors => [{
109
+ :name => "William Shakespeare",
110
+ :born => "1564-04-26"
111
+ }, {
112
+ :name => "Christopher Marlowe"
113
+ }, {
114
+ :name => %w(malicious injected names)
115
+ }],
116
+ :details => {
117
+ :pages => 200,
118
+ :genre => "Tragedy"
119
+ }
120
+ },
121
+ :magazine => "Mjallo!"
122
+ })
123
+
124
+ permitted = params.permit :book => [ :title, { :authors => [ :name ] }, { :details => {:pages => Numeric} } ]
125
+
126
+ assert permitted.permitted?
127
+ assert_equal "Romeo and Juliet", permitted[:book][:title]
128
+ assert_equal "William Shakespeare", permitted[:book][:authors][0][:name]
129
+ assert_equal "Christopher Marlowe", permitted[:book][:authors][1][:name]
130
+ assert_equal 200, permitted[:book][:details][:pages]
131
+
132
+ assert_filtered_out permitted[:book][:authors][2], :name
133
+
134
+ assert_filtered_out permitted, :magazine
135
+ assert_filtered_out permitted[:book][:details], :genre
136
+ assert_filtered_out permitted[:book][:authors][0], :born
137
+ end
138
+
139
+ test "permitted nested parameters with a string or a symbol as a key" do
140
+ params = ActionController::Parameters.new({
141
+ :book => {
142
+ 'authors' => [
143
+ { :name => "William Shakespeare", :born => "1564-04-26" },
144
+ { :name => "Christopher Marlowe" }
145
+ ]
146
+ }
147
+ })
148
+
149
+ permitted = params.permit :book => [ { 'authors' => [ :name ] } ]
150
+
151
+ assert_equal "William Shakespeare", permitted[:book]['authors'][0][:name]
152
+ assert_equal "William Shakespeare", permitted[:book][:authors][0][:name]
153
+ assert_equal "Christopher Marlowe", permitted[:book]['authors'][1][:name]
154
+ assert_equal "Christopher Marlowe", permitted[:book][:authors][1][:name]
155
+
156
+ permitted = params.permit :book => [ { :authors => [ :name ] } ]
157
+
158
+ assert_equal "William Shakespeare", permitted[:book]['authors'][0][:name]
159
+ assert_equal "William Shakespeare", permitted[:book][:authors][0][:name]
160
+ assert_equal "Christopher Marlowe", permitted[:book]['authors'][1][:name]
161
+ assert_equal "Christopher Marlowe", permitted[:book][:authors][1][:name]
162
+ end
163
+
164
+ test "nested arrays with strings" do
165
+ params = ActionController::Parameters.new({
166
+ :book => {
167
+ :genres => ["Tragedy"]
168
+ }
169
+ })
170
+
171
+ permitted = params.permit :book => {:genres => [String]}
172
+ assert_equal ["Tragedy"], permitted[:book][:genres]
173
+ end
174
+
175
+ test "permit may specify symbols or strings" do
176
+ params = ActionController::Parameters.new({
177
+ :book => {
178
+ :title => "Romeo and Juliet",
179
+ :author => "William Shakespeare"
180
+ },
181
+ :magazine => "Shakespeare Today"
182
+ })
183
+
184
+ permitted = params.permit({ :book => ["title", :author] }, "magazine")
185
+ assert_equal "Romeo and Juliet", permitted[:book][:title]
186
+ assert_equal "William Shakespeare", permitted[:book][:author]
187
+ assert_equal "Shakespeare Today", permitted[:magazine]
188
+ end
189
+
190
+ test "nested array with strings that should be hashes" do
191
+ params = ActionController::Parameters.new({
192
+ :book => {
193
+ :genres => ["Tragedy"]
194
+ }
195
+ })
196
+
197
+ permitted = params.permit :book => { :genres => :type }
198
+ assert permitted[:book][:genres].empty?
199
+ end
200
+
201
+ test "nested array with strings that should be hashes and additional values" do
202
+ params = ActionController::Parameters.new({
203
+ :book => {
204
+ :title => "Romeo and Juliet",
205
+ :genres => ["Tragedy"]
206
+ }
207
+ })
208
+
209
+ permitted = params.permit :book => [ :title, { :genres => :type } ]
210
+ assert_equal "Romeo and Juliet", permitted[:book][:title]
211
+ assert permitted[:book][:genres].empty?
212
+ end
213
+
214
+ test "nested string that should be a hash" do
215
+ params = ActionController::Parameters.new({
216
+ :book => {
217
+ :genre => "Tragedy"
218
+ }
219
+ })
220
+
221
+ permitted = params.permit :book => { :genre => :type }
222
+ assert_nil permitted[:book][:genre]
223
+ end
224
+
225
+ test "fields_for_style_nested_params" do
226
+ params = ActionController::Parameters.new({
227
+ :book => {
228
+ :authors_attributes => {
229
+ :'0' => { :name => 'William Shakespeare', :age_of_death => '52' },
230
+ :'1' => { :name => 'Unattributed Assistant' },
231
+ :'2' => { :name => %w(injected names)}
232
+ }
233
+ }
234
+ })
235
+ permitted = params.permit :book => { :authors_attributes => [ :name ] }
236
+
237
+ assert_not_nil permitted[:book][:authors_attributes]['0']
238
+ assert_not_nil permitted[:book][:authors_attributes]['1']
239
+ assert permitted[:book][:authors_attributes]['2'].empty?
240
+ assert_equal 'William Shakespeare', permitted[:book][:authors_attributes]['0'][:name]
241
+ assert_equal 'Unattributed Assistant', permitted[:book][:authors_attributes]['1'][:name]
242
+
243
+ assert_filtered_out permitted[:book][:authors_attributes]['0'], :age_of_death
244
+ end
245
+
246
+ test "fields_for_style_nested_params with negative numbers" do
247
+ params = ActionController::Parameters.new({
248
+ :book => {
249
+ :authors_attributes => {
250
+ :'-1' => { :name => 'William Shakespeare', :age_of_death => '52' },
251
+ :'-2' => { :name => 'Unattributed Assistant' }
252
+ }
253
+ }
254
+ })
255
+ permitted = params.permit :book => { :authors_attributes => [:name] }
256
+
257
+ assert_not_nil permitted[:book][:authors_attributes]['-1']
258
+ assert_not_nil permitted[:book][:authors_attributes]['-2']
259
+ assert_equal 'William Shakespeare', permitted[:book][:authors_attributes]['-1'][:name]
260
+ assert_equal 'Unattributed Assistant', permitted[:book][:authors_attributes]['-2'][:name]
261
+
262
+ assert_filtered_out permitted[:book][:authors_attributes]['-1'], :age_of_death
263
+ end
264
+ end