durable_parameters 0.2.3
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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +853 -0
- data/Rakefile +29 -0
- data/app/params/account_params.rb.example +38 -0
- data/app/params/application_params.rb +16 -0
- data/lib/durable_parameters/adapters/hanami.rb +138 -0
- data/lib/durable_parameters/adapters/rage.rb +124 -0
- data/lib/durable_parameters/adapters/rails.rb +280 -0
- data/lib/durable_parameters/adapters/sinatra.rb +91 -0
- data/lib/durable_parameters/core/application_params.rb +334 -0
- data/lib/durable_parameters/core/configuration.rb +83 -0
- data/lib/durable_parameters/core/forbidden_attributes_protection.rb +48 -0
- data/lib/durable_parameters/core/parameters.rb +643 -0
- data/lib/durable_parameters/core/params_registry.rb +110 -0
- data/lib/durable_parameters/core.rb +15 -0
- data/lib/durable_parameters/log_subscriber.rb +34 -0
- data/lib/durable_parameters/railtie.rb +65 -0
- data/lib/durable_parameters/version.rb +7 -0
- data/lib/durable_parameters.rb +41 -0
- data/lib/generators/rails/USAGE +12 -0
- data/lib/generators/rails/durable_parameters_controller_generator.rb +17 -0
- data/lib/generators/rails/templates/controller.rb +94 -0
- data/lib/legacy/action_controller/application_params.rb +235 -0
- data/lib/legacy/action_controller/parameters.rb +524 -0
- data/lib/legacy/action_controller/params_registry.rb +108 -0
- data/lib/legacy/active_model/forbidden_attributes_protection.rb +40 -0
- data/test/action_controller_required_params_test.rb +36 -0
- data/test/action_controller_tainted_params_test.rb +29 -0
- data/test/active_model_mass_assignment_taint_protection_test.rb +25 -0
- data/test/application_params_array_test.rb +245 -0
- data/test/application_params_edge_cases_test.rb +361 -0
- data/test/application_params_test.rb +893 -0
- data/test/controller_generator_test.rb +31 -0
- data/test/core_parameters_test.rb +2376 -0
- data/test/durable_parameters_test.rb +115 -0
- data/test/enhanced_error_messages_test.rb +120 -0
- data/test/gemfiles/Gemfile.rails-3.0.x +14 -0
- data/test/gemfiles/Gemfile.rails-3.1.x +14 -0
- data/test/gemfiles/Gemfile.rails-3.2.x +14 -0
- data/test/log_on_unpermitted_params_test.rb +49 -0
- data/test/metadata_validation_test.rb +294 -0
- data/test/multi_parameter_attributes_test.rb +38 -0
- data/test/parameters_core_methods_test.rb +503 -0
- data/test/parameters_integration_test.rb +553 -0
- data/test/parameters_permit_test.rb +491 -0
- data/test/parameters_require_test.rb +9 -0
- data/test/parameters_taint_test.rb +98 -0
- data/test/params_registry_concurrency_test.rb +422 -0
- data/test/params_registry_test.rb +112 -0
- data/test/permit_by_model_test.rb +227 -0
- data/test/raise_on_unpermitted_params_test.rb +32 -0
- data/test/test_helper.rb +38 -0
- data/test/transform_params_edge_cases_test.rb +526 -0
- data/test/transformation_test.rb +360 -0
- metadata +223 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'test_helper'
|
|
4
|
+
|
|
5
|
+
class DurableParametersTest < Minitest::Test
|
|
6
|
+
def test_top_level_aliases_are_defined
|
|
7
|
+
assert defined?(StrongParameters::Parameters)
|
|
8
|
+
assert defined?(StrongParameters::ApplicationParams)
|
|
9
|
+
assert defined?(StrongParameters::ParamsRegistry)
|
|
10
|
+
assert defined?(StrongParameters::ForbiddenAttributesProtection)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def test_aliases_point_to_core_classes
|
|
14
|
+
assert_equal StrongParameters::Core::Parameters, StrongParameters::Parameters
|
|
15
|
+
assert_equal StrongParameters::Core::ApplicationParams, StrongParameters::ApplicationParams
|
|
16
|
+
assert_equal StrongParameters::Core::ParamsRegistry, StrongParameters::ParamsRegistry
|
|
17
|
+
assert_equal StrongParameters::Core::ForbiddenAttributesProtection, StrongParameters::ForbiddenAttributesProtection
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_module_has_version
|
|
23
|
+
assert defined?(StrongParameters::VERSION)
|
|
24
|
+
assert StrongParameters::VERSION.is_a?(String)
|
|
25
|
+
assert_match(/\A\d+\.\d+\.\d+(\.\w+)?\z/, StrongParameters::VERSION)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def test_core_module_is_defined
|
|
29
|
+
assert defined?(StrongParameters::Core)
|
|
30
|
+
assert StrongParameters::Core.is_a?(Module)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def test_strong_parameters_is_module
|
|
34
|
+
assert StrongParameters.is_a?(Module)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def test_adapter_loading_rails
|
|
38
|
+
# Rails is loaded in test_helper, so railtie and log_subscriber should be required
|
|
39
|
+
# We can't easily test the require calls, but we can check if the adapters are available
|
|
40
|
+
# Since Rails is defined, the Rails adapter should be loaded
|
|
41
|
+
assert defined?(StrongParameters::Adapters::Rails)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def test_adapter_loading_sinatra
|
|
45
|
+
# Test that Sinatra adapter would be loaded if Sinatra was defined
|
|
46
|
+
# Since Sinatra is not defined in this test suite, we can't test the require
|
|
47
|
+
# But we can check that the adapter file exists
|
|
48
|
+
adapter_path = File.join(__dir__, '..', 'lib', 'durable_parameters', 'adapters', 'sinatra.rb')
|
|
49
|
+
assert File.exist?(adapter_path), "Sinatra adapter file should exist"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def test_adapter_loading_hanami
|
|
53
|
+
adapter_path = File.join(__dir__, '..', 'lib', 'durable_parameters', 'adapters', 'hanami.rb')
|
|
54
|
+
assert File.exist?(adapter_path), "Hanami adapter file should exist"
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def test_adapter_loading_rage
|
|
58
|
+
adapter_path = File.join(__dir__, '..', 'lib', 'durable_parameters', 'adapters', 'rage.rb')
|
|
59
|
+
assert File.exist?(adapter_path), "Rage adapter file should exist"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def test_aliases_are_not_redefined_if_already_defined
|
|
63
|
+
# Test that if StrongParameters::Parameters is already defined, it won't be redefined
|
|
64
|
+
original_value = StrongParameters::Parameters
|
|
65
|
+
# Simulate re-requiring (though in practice it's already required)
|
|
66
|
+
# Since the code uses unless defined?(Parameters), and Parameters is StrongParameters::Parameters
|
|
67
|
+
# It should not redefine if already defined
|
|
68
|
+
assert_equal original_value, StrongParameters::Parameters
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def test_adapters_module_is_defined
|
|
72
|
+
assert defined?(StrongParameters::Adapters)
|
|
73
|
+
assert StrongParameters::Adapters.is_a?(Module)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def test_rails_adapter_is_module
|
|
77
|
+
assert StrongParameters::Adapters::Rails.is_a?(Module)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def test_sinatra_adapter_file_exists_and_loadable
|
|
81
|
+
adapter_path = File.join(__dir__, '..', 'lib', 'durable_parameters', 'adapters', 'sinatra.rb')
|
|
82
|
+
assert File.exist?(adapter_path)
|
|
83
|
+
# Test that it can be required without error
|
|
84
|
+
assert require 'durable_parameters/adapters/sinatra'
|
|
85
|
+
assert defined?(StrongParameters::Adapters::Sinatra)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def test_hanami_adapter_file_exists_and_loadable
|
|
89
|
+
adapter_path = File.join(__dir__, '..', 'lib', 'durable_parameters', 'adapters', 'hanami.rb')
|
|
90
|
+
assert File.exist?(adapter_path)
|
|
91
|
+
# Test that it can be required without error
|
|
92
|
+
assert require 'durable_parameters/adapters/hanami'
|
|
93
|
+
assert defined?(StrongParameters::Adapters::Hanami)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def test_rage_adapter_file_exists_and_loadable
|
|
97
|
+
adapter_path = File.join(__dir__, '..', 'lib', 'durable_parameters', 'adapters', 'rage.rb')
|
|
98
|
+
assert File.exist?(adapter_path)
|
|
99
|
+
# Test that it can be required without error
|
|
100
|
+
assert require 'durable_parameters/adapters/rage'
|
|
101
|
+
assert defined?(StrongParameters::Adapters::Rage)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def test_version_is_not_empty
|
|
105
|
+
refute StrongParameters::VERSION.empty?
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def test_core_classes_are_accessible_via_aliases
|
|
109
|
+
# Test that the aliases work for instantiation or basic methods
|
|
110
|
+
assert StrongParameters::Parameters.new({}).is_a?(StrongParameters::Core::Parameters)
|
|
111
|
+
assert StrongParameters::ApplicationParams.is_a?(Class)
|
|
112
|
+
assert StrongParameters::ParamsRegistry.is_a?(Class)
|
|
113
|
+
assert StrongParameters::ForbiddenAttributesProtection.is_a?(Module)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'test_helper'
|
|
4
|
+
|
|
5
|
+
class EnhancedErrorMessagesTest < Minitest::Test
|
|
6
|
+
def setup
|
|
7
|
+
@params = StrongParameters::Core::Parameters.new(
|
|
8
|
+
usr: { name: 'John', email: 'john@example.com' },
|
|
9
|
+
account: { balance: 100 },
|
|
10
|
+
data: { value: 'test' }
|
|
11
|
+
)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def test_parameter_missing_includes_available_keys_in_error_message
|
|
15
|
+
error = assert_raises(StrongParameters::Core::ParameterMissing) do
|
|
16
|
+
@params.require(:missing_key)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
assert_match(/Available keys:/, error.message)
|
|
20
|
+
assert_match(/usr/, error.message)
|
|
21
|
+
assert_match(/account/, error.message)
|
|
22
|
+
assert_match(/data/, error.message)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def test_parameter_missing_suggests_similar_keys
|
|
26
|
+
error = assert_raises(StrongParameters::Core::ParameterMissing) do
|
|
27
|
+
@params.require(:user) # Similar to 'usr'
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
assert_match(/Did you mean\?/, error.message)
|
|
31
|
+
assert_match(/usr/, error.message)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def test_parameter_missing_suggests_keys_starting_with_same_letter
|
|
35
|
+
error = assert_raises(StrongParameters::Core::ParameterMissing) do
|
|
36
|
+
@params.require(:accounts) # Similar to 'account'
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
assert_match(/Did you mean\?/, error.message)
|
|
40
|
+
assert_match(/account/, error.message)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def test_parameter_missing_does_not_suggest_when_no_similar_keys_exist
|
|
44
|
+
error = assert_raises(StrongParameters::Core::ParameterMissing) do
|
|
45
|
+
@params.require(:xyz)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Should show available keys but not suggestions
|
|
49
|
+
assert_match(/Available keys:/, error.message)
|
|
50
|
+
refute_match(/Did you mean\?/, error.message)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def test_parameter_missing_with_empty_params_shows_helpful_message
|
|
54
|
+
empty_params = StrongParameters::Core::Parameters.new({})
|
|
55
|
+
|
|
56
|
+
error = assert_raises(StrongParameters::Core::ParameterMissing) do
|
|
57
|
+
empty_params.require(:user)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
assert_match(/param is missing or the value is empty: user/, error.message)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def test_parameter_missing_with_nested_params
|
|
64
|
+
params = StrongParameters::Core::Parameters.new(
|
|
65
|
+
user: { profile: { name: 'John' } }
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
user_params = params.require(:user)
|
|
69
|
+
|
|
70
|
+
error = assert_raises(StrongParameters::Core::ParameterMissing) do
|
|
71
|
+
user_params.require(:preferences)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
assert_match(/Available keys:/, error.message)
|
|
75
|
+
assert_match(/profile/, error.message)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def test_parameter_missing_stores_param_attribute
|
|
79
|
+
error = assert_raises(StrongParameters::Core::ParameterMissing) do
|
|
80
|
+
@params.require(:missing)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
assert_equal 'missing', error.param
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def test_parameter_missing_when_value_is_empty_hash
|
|
87
|
+
params = StrongParameters::Core::Parameters.new(user: {})
|
|
88
|
+
|
|
89
|
+
error = assert_raises(StrongParameters::Core::ParameterMissing) do
|
|
90
|
+
params.require(:user)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
assert_match(/param is missing or the value is empty: user/, error.message)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def test_parameter_missing_when_value_is_empty_array
|
|
97
|
+
params = StrongParameters::Core::Parameters.new(tags: [])
|
|
98
|
+
|
|
99
|
+
error = assert_raises(StrongParameters::Core::ParameterMissing) do
|
|
100
|
+
params.require(:tags)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
assert_match(/param is missing or the value is empty: tags/, error.message)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def test_parameter_missing_suggestion_with_partial_match
|
|
107
|
+
params = StrongParameters::Core::Parameters.new(
|
|
108
|
+
user_profile: { name: 'John' },
|
|
109
|
+
user_settings: { theme: 'dark' }
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
error = assert_raises(StrongParameters::Core::ParameterMissing) do
|
|
113
|
+
params.require(:user)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
assert_match(/Did you mean\?/, error.message)
|
|
117
|
+
# Should suggest keys containing 'user'
|
|
118
|
+
assert_match(/user_profile|user_settings/, error.message)
|
|
119
|
+
end
|
|
120
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
source 'http://rubygems.org'
|
|
2
|
+
gemspec :path => "./../.."
|
|
3
|
+
|
|
4
|
+
gem "actionpack", "~> 3.0.0"
|
|
5
|
+
gem "railties", "~> 3.0.0"
|
|
6
|
+
gem "activemodel", "~> 3.0.0"
|
|
7
|
+
|
|
8
|
+
if RUBY_VERSION < '1.9.3'
|
|
9
|
+
gem 'rake', '~> 10.0'
|
|
10
|
+
gem 'i18n', '~> 0.6.11'
|
|
11
|
+
gem 'rdoc', '~> 4.2.2'
|
|
12
|
+
else
|
|
13
|
+
gem 'rake'
|
|
14
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
source 'http://rubygems.org'
|
|
2
|
+
gemspec :path => "./../.."
|
|
3
|
+
|
|
4
|
+
gem "actionpack", "~> 3.1.0"
|
|
5
|
+
gem "railties", "~> 3.1.0"
|
|
6
|
+
gem "activemodel", "~> 3.1.0"
|
|
7
|
+
|
|
8
|
+
if RUBY_VERSION < '1.9.3'
|
|
9
|
+
gem 'rake', '~> 10.0'
|
|
10
|
+
gem 'i18n', '~> 0.6.11'
|
|
11
|
+
gem 'rack-cache', '< 1.3'
|
|
12
|
+
else
|
|
13
|
+
gem 'rake'
|
|
14
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
source 'http://rubygems.org'
|
|
2
|
+
gemspec :path => "./../.."
|
|
3
|
+
|
|
4
|
+
gem "actionpack", "~> 3.2.0"
|
|
5
|
+
gem "railties", "~> 3.2.0"
|
|
6
|
+
gem "activemodel", "~> 3.2.0"
|
|
7
|
+
|
|
8
|
+
if RUBY_VERSION < '1.9.3'
|
|
9
|
+
gem 'rake', '~> 10.0'
|
|
10
|
+
gem 'i18n', '~> 0.6.11'
|
|
11
|
+
gem 'rack-cache', '< 1.3'
|
|
12
|
+
else
|
|
13
|
+
gem 'rake'
|
|
14
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
class LogOnUnpermittedParamsTest < Minitest::Test
|
|
4
|
+
def setup
|
|
5
|
+
ActionController::Parameters.action_on_unpermitted_parameters = :log
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def teardown
|
|
9
|
+
ActionController::Parameters.action_on_unpermitted_parameters = false
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def test_logs_on_unexpected_params
|
|
13
|
+
params = ActionController::Parameters.new({
|
|
14
|
+
:book => { :pages => 65 },
|
|
15
|
+
:fishing => "Turnips"
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
assert_logged("Unpermitted parameters: fishing") do
|
|
19
|
+
params.permit(:book => [:pages])
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def test_logs_on_unexpected_nested_params
|
|
24
|
+
params = ActionController::Parameters.new({
|
|
25
|
+
:book => { :pages => 65, :title => "Green Cats and where to find then." }
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
assert_logged("Unpermitted parameters: title") do
|
|
29
|
+
params.permit(:book => [:pages])
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def assert_logged(message)
|
|
36
|
+
old_logger = ActionController::Base.logger
|
|
37
|
+
log = StringIO.new
|
|
38
|
+
ActionController::Base.logger = Logger.new(log)
|
|
39
|
+
|
|
40
|
+
begin
|
|
41
|
+
yield
|
|
42
|
+
|
|
43
|
+
log.rewind
|
|
44
|
+
assert_match message, log.read
|
|
45
|
+
ensure
|
|
46
|
+
ActionController::Base.logger = old_logger
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
class MetadataValidationTest < Minitest::Test
|
|
4
|
+
def setup
|
|
5
|
+
ActionController::ParamsRegistry.clear!
|
|
6
|
+
|
|
7
|
+
@basic_params_class = Class.new(ActionController::ApplicationParams) do
|
|
8
|
+
def self.name
|
|
9
|
+
'BasicParams'
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
allow :name
|
|
13
|
+
allow :email
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
@params_with_metadata = Class.new(ActionController::ApplicationParams) do
|
|
17
|
+
def self.name
|
|
18
|
+
'ParamsWithMetadata'
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
allow :name
|
|
22
|
+
allow :email
|
|
23
|
+
metadata :ip_address, :user_agent, :session_id
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
ActionController::ParamsRegistry.register('Basic', @basic_params_class)
|
|
27
|
+
ActionController::ParamsRegistry.register('WithMetadata', @params_with_metadata)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def teardown
|
|
31
|
+
ActionController::ParamsRegistry.clear!
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Test current_user is always allowed without declaration
|
|
35
|
+
def test_current_user_always_allowed_without_declaration
|
|
36
|
+
params = ActionController::Parameters.new(
|
|
37
|
+
basic: {
|
|
38
|
+
name: 'Test User',
|
|
39
|
+
email: 'test@example.com'
|
|
40
|
+
}
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
# Should not raise even though current_user not declared
|
|
44
|
+
permitted = params.require(:basic).transform_params(current_user: Object.new)
|
|
45
|
+
|
|
46
|
+
assert_equal 'Test User', permitted[:name]
|
|
47
|
+
assert_equal 'test@example.com', permitted[:email]
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Test metadata_allowed? method
|
|
51
|
+
def test_metadata_allowed_returns_true_for_current_user
|
|
52
|
+
assert @basic_params_class.metadata_allowed?(:current_user)
|
|
53
|
+
assert @basic_params_class.metadata_allowed?('current_user')
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def test_metadata_allowed_returns_false_for_undeclared_keys
|
|
57
|
+
assert !@basic_params_class.metadata_allowed?(:ip_address)
|
|
58
|
+
assert !@basic_params_class.metadata_allowed?(:random_key)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def test_metadata_allowed_returns_true_for_declared_keys
|
|
62
|
+
assert @params_with_metadata.metadata_allowed?(:ip_address)
|
|
63
|
+
assert @params_with_metadata.metadata_allowed?('user_agent')
|
|
64
|
+
assert @params_with_metadata.metadata_allowed?(:session_id)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Test multiple metadata keys
|
|
68
|
+
def test_multiple_metadata_keys_can_be_passed
|
|
69
|
+
params = ActionController::Parameters.new(
|
|
70
|
+
with_metadata: {
|
|
71
|
+
name: 'Test User',
|
|
72
|
+
email: 'test@example.com'
|
|
73
|
+
}
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# Should not raise with all declared metadata
|
|
77
|
+
permitted = params.require(:with_metadata).transform_params(
|
|
78
|
+
current_user: Object.new,
|
|
79
|
+
ip_address: '192.168.1.1',
|
|
80
|
+
user_agent: 'Mozilla/5.0',
|
|
81
|
+
session_id: 'abc123'
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
assert_equal 'Test User', permitted[:name]
|
|
85
|
+
assert_equal 'test@example.com', permitted[:email]
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Test error messages
|
|
89
|
+
def test_error_message_includes_undeclared_key
|
|
90
|
+
params = ActionController::Parameters.new(
|
|
91
|
+
basic: { name: 'Test' }
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
error = assert_raises(ArgumentError) do
|
|
95
|
+
params.require(:basic).transform_params(ip_address: '127.0.0.1')
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
assert_includes error.message, 'ip_address'
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def test_error_message_includes_params_class_name
|
|
102
|
+
params = ActionController::Parameters.new(
|
|
103
|
+
basic: { name: 'Test' }
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
error = assert_raises(ArgumentError) do
|
|
107
|
+
params.require(:basic).transform_params(ip_address: '127.0.0.1')
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
assert_includes error.message, 'BasicParams'
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def test_error_message_includes_declaration_hint
|
|
114
|
+
params = ActionController::Parameters.new(
|
|
115
|
+
basic: { name: 'Test' }
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
error = assert_raises(ArgumentError) do
|
|
119
|
+
params.require(:basic).transform_params(ip_address: '127.0.0.1')
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
assert_includes error.message, 'metadata :ip_address'
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def test_error_message_mentions_current_user_is_always_allowed
|
|
126
|
+
params = ActionController::Parameters.new(
|
|
127
|
+
basic: { name: 'Test' }
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
error = assert_raises(ArgumentError) do
|
|
131
|
+
params.require(:basic).transform_params(ip_address: '127.0.0.1')
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
assert_includes error.message, 'current_user is always allowed'
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Test multiple undeclared keys
|
|
138
|
+
def test_multiple_undeclared_keys_all_mentioned_in_error
|
|
139
|
+
params = ActionController::Parameters.new(
|
|
140
|
+
basic: { name: 'Test' }
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
error = assert_raises(ArgumentError) do
|
|
144
|
+
params.require(:basic).transform_params(
|
|
145
|
+
ip_address: '127.0.0.1',
|
|
146
|
+
user_agent: 'Mozilla',
|
|
147
|
+
device_id: 'xyz'
|
|
148
|
+
)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
assert_includes error.message, 'ip_address'
|
|
152
|
+
assert_includes error.message, 'user_agent'
|
|
153
|
+
assert_includes error.message, 'device_id'
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Test metadata doesn't affect the actual parameter filtering
|
|
157
|
+
def test_metadata_keys_dont_affect_parameter_filtering
|
|
158
|
+
params = ActionController::Parameters.new(
|
|
159
|
+
with_metadata: {
|
|
160
|
+
name: 'Test User',
|
|
161
|
+
email: 'test@example.com',
|
|
162
|
+
ip_address: 'should not be in result',
|
|
163
|
+
user_agent: 'should not be in result'
|
|
164
|
+
}
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
permitted = params.require(:with_metadata).transform_params(
|
|
168
|
+
current_user: Object.new,
|
|
169
|
+
ip_address: '192.168.1.1',
|
|
170
|
+
user_agent: 'Mozilla/5.0'
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
assert_equal 'Test User', permitted[:name]
|
|
174
|
+
assert_equal 'test@example.com', permitted[:email]
|
|
175
|
+
# These should not be in the permitted params because they're metadata, not allowed attributes
|
|
176
|
+
assert_nil permitted[:ip_address]
|
|
177
|
+
assert_nil permitted[:user_agent]
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Test inheritance of metadata declarations
|
|
181
|
+
def test_metadata_inherited_from_parent
|
|
182
|
+
parent_class = Class.new(ActionController::ApplicationParams) do
|
|
183
|
+
def self.name
|
|
184
|
+
'ParentParams'
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
allow :name
|
|
188
|
+
metadata :ip_address
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
child_class = Class.new(parent_class) do
|
|
192
|
+
def self.name
|
|
193
|
+
'ChildParams'
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
allow :email
|
|
197
|
+
metadata :session_id
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# Child should have parent's metadata
|
|
201
|
+
assert child_class.metadata_allowed?(:ip_address)
|
|
202
|
+
# Child should have its own metadata
|
|
203
|
+
assert child_class.metadata_allowed?(:session_id)
|
|
204
|
+
# Both should allow current_user
|
|
205
|
+
assert child_class.metadata_allowed?(:current_user)
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Test empty metadata
|
|
209
|
+
def test_empty_metadata_set_by_default
|
|
210
|
+
assert_equal Set.new, @basic_params_class.allowed_metadata
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# Test metadata method accepts multiple keys at once
|
|
214
|
+
def test_metadata_method_accepts_multiple_keys
|
|
215
|
+
test_class = Class.new(ActionController::ApplicationParams) do
|
|
216
|
+
metadata :key1, :key2, :key3
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
assert test_class.metadata_allowed?(:key1)
|
|
220
|
+
assert test_class.metadata_allowed?(:key2)
|
|
221
|
+
assert test_class.metadata_allowed?(:key3)
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# Test metadata with string keys
|
|
225
|
+
def test_metadata_works_with_string_keys
|
|
226
|
+
test_class = Class.new(ActionController::ApplicationParams) do
|
|
227
|
+
metadata 'string_key'
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
assert test_class.metadata_allowed?(:string_key)
|
|
231
|
+
assert test_class.metadata_allowed?('string_key')
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# Test that action and additional_attrs are not treated as metadata
|
|
235
|
+
def test_action_option_not_treated_as_metadata
|
|
236
|
+
params = ActionController::Parameters.new(
|
|
237
|
+
basic: { name: 'Test' }
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
# Should not raise even though action not declared as metadata
|
|
241
|
+
permitted = params.require(:basic).transform_params(action: :create)
|
|
242
|
+
assert_equal 'Test', permitted[:name]
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
def test_additional_attrs_option_not_treated_as_metadata
|
|
246
|
+
params = ActionController::Parameters.new(
|
|
247
|
+
basic: { name: 'Test' }
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
# Should not raise even though additional_attrs not declared as metadata
|
|
251
|
+
permitted = params.require(:basic).transform_params(additional_attrs: [:other])
|
|
252
|
+
assert_equal 'Test', permitted[:name]
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
def test_action_and_additional_attrs_with_current_user_all_work
|
|
256
|
+
params = ActionController::Parameters.new(
|
|
257
|
+
basic: { name: 'Test' }
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
# Should not raise with all non-metadata options
|
|
261
|
+
permitted = params.require(:basic).transform_params(
|
|
262
|
+
action: :create,
|
|
263
|
+
additional_attrs: [:other],
|
|
264
|
+
current_user: Object.new
|
|
265
|
+
)
|
|
266
|
+
assert_equal 'Test', permitted[:name]
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
# Test nil and empty values
|
|
270
|
+
def test_nil_metadata_values_allowed
|
|
271
|
+
params = ActionController::Parameters.new(
|
|
272
|
+
with_metadata: { name: 'Test' }
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
# Should not raise with nil metadata value
|
|
276
|
+
permitted = params.require(:with_metadata).transform_params(
|
|
277
|
+
current_user: nil,
|
|
278
|
+
ip_address: nil
|
|
279
|
+
)
|
|
280
|
+
assert_equal 'Test', permitted[:name]
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
def test_empty_string_metadata_values_allowed
|
|
284
|
+
params = ActionController::Parameters.new(
|
|
285
|
+
with_metadata: { name: 'Test' }
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
# Should not raise with empty string metadata value
|
|
289
|
+
permitted = params.require(:with_metadata).transform_params(
|
|
290
|
+
ip_address: ''
|
|
291
|
+
)
|
|
292
|
+
assert_equal 'Test', permitted[:name]
|
|
293
|
+
end
|
|
294
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
class MultiParameterAttributesTest < Minitest::Test
|
|
4
|
+
def test_permitted_multi_parameter_attribute_keys
|
|
5
|
+
params = ActionController::Parameters.new({
|
|
6
|
+
:book => {
|
|
7
|
+
"shipped_at(1i)" => "2012",
|
|
8
|
+
"shipped_at(2i)" => "3",
|
|
9
|
+
"shipped_at(3i)" => "25",
|
|
10
|
+
"shipped_at(4i)" => "10",
|
|
11
|
+
"shipped_at(5i)" => "15",
|
|
12
|
+
"published_at(1i)" => "1999",
|
|
13
|
+
"published_at(2i)" => "2",
|
|
14
|
+
"published_at(3i)" => "5",
|
|
15
|
+
"price(1)" => "R$",
|
|
16
|
+
"price(2f)" => "2.02"
|
|
17
|
+
}
|
|
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
|
+
|