Skip to content

Commit c0b2c93

Browse files
committed
Support Strong Parameters
Add support for handling [attribute sanitization][] through [Strong Parameters][]-compliant classes like [ActionController::Parameters][]. The implementation draws inspiration from built-in `rails` cases like [ActiveModel::ForbiddenAttributesProtection][]. To test this behavior, add the `StrongParameters` class to implement `#permitted?`, `#permit!`, and `#to_hash`. [attribute sanitization]: https://guides.rubyonrails.org/active_model_basics.html#attribute-assignment [Strong Parameters]: https://edgeapi.rubyonrails.org/classes/ActionController/StrongParameters.html [ActionController::Parameters]: https://edgeapi.rubyonrails.org/classes/ActionController/Parameters.html#constant-EMPTY_HASH [ActiveModel::ForbiddenAttributesProtection]: https://github.com/rails/rails/blob/v8.0.0.1/activemodel/lib/active_model/forbidden_attributes_protection.rb#L23-L30
1 parent 9c8a2ee commit c0b2c93

File tree

7 files changed

+69
-1
lines changed

7 files changed

+69
-1
lines changed

lib/active_resource.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ module ActiveResource
3939
autoload :Callbacks
4040
autoload :Connection
4141
autoload :CustomMethods
42+
autoload :ForbiddenAttributesProtection
4243
autoload :Formats
4344
autoload :HttpMock
4445
autoload :Schema

lib/active_resource/base.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1041,6 +1041,7 @@ def all(*args)
10411041
end
10421042

10431043
def where(clauses = {})
1044+
clauses = sanitize_for_mass_assignment(clauses)
10441045
raise ArgumentError, "expected a clauses Hash, got #{clauses.inspect}" unless clauses.is_a? Hash
10451046
find(:all, params: clauses)
10461047
end
@@ -1471,7 +1472,7 @@ def load(attributes, remove_root = false, persisted = false)
14711472
raise ArgumentError, "expected attributes to be able to convert to Hash, got #{attributes.inspect}"
14721473
end
14731474

1474-
attributes = attributes.to_hash
1475+
attributes = sanitize_for_mass_assignment(attributes).to_hash
14751476
@prefix_options, attributes = split_options(attributes)
14761477

14771478
if attributes.keys.size == 1
@@ -1720,7 +1721,9 @@ def method_missing(method_symbol, *arguments) # :nodoc:
17201721
class Base
17211722
extend ActiveModel::Naming
17221723
extend ActiveResource::Associations
1724+
extend ForbiddenAttributesProtection
17231725

1726+
include ForbiddenAttributesProtection
17241727
include Callbacks, CustomMethods, Validations
17251728
include ActiveModel::Conversion
17261729
include ActiveModel::Serializers::JSON
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# frozen_string_literal: true
2+
3+
require "active_model/forbidden_attributes_protection"
4+
5+
module ActiveResource
6+
class ForbiddenAttributesError < ActiveModel::ForbiddenAttributesError
7+
end
8+
9+
module ForbiddenAttributesProtection
10+
include ActiveModel::ForbiddenAttributesProtection
11+
12+
private
13+
def sanitize_for_mass_assignment(attributes)
14+
super
15+
rescue ActiveModel::ForbiddenAttributesError
16+
raise ForbiddenAttributesError
17+
end
18+
end
19+
end

test/abstract_unit.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
require "active_support"
1212
require "active_support/test_case"
1313
require "setter_trap"
14+
require "strong_parameters"
1415
require "active_support/logger"
1516
require "base64"
1617

test/cases/base/load_test.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,16 @@ def test_load_object_with_implicit_conversion_to_hash
127127
assert_equal @matz.stringify_keys, @person.load(FakeParameters.new(@matz)).attributes
128128
end
129129

130+
def test_load_object_with_unpermitted_strong_parameters
131+
params = StrongParameters.new(@matz)
132+
assert_raises(ActiveResource::ForbiddenAttributesError) { @person.load(params) }
133+
end
134+
135+
def test_load_object_with_permitted_strong_parameters
136+
params = StrongParameters.new(@matz).tap(&:permit!)
137+
assert_equal @matz.stringify_keys, @person.load(params).attributes
138+
end
139+
130140
def test_after_load_attributes_are_accessible
131141
assert_equal Hash.new, @person.attributes
132142
assert_equal @matz.stringify_keys, @person.load(@matz).attributes

test/cases/finder_test.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,19 @@ def test_where_with_clauses
6363
assert_kind_of StreetAddress, addresses.first
6464
end
6565

66+
def test_where_clause_with_unpermitted_params
67+
params = StrongParameters.new(person_id: "1")
68+
error = assert_raises(ActiveResource::ForbiddenAttributesError) { StreetAddress.where(params) }
69+
assert_kind_of ActiveModel::ForbiddenAttributesError, error
70+
end
71+
72+
def test_where_clause_with_permitted_params
73+
params = StrongParameters.new(person_id: "1").tap(&:permit!)
74+
addresses = StreetAddress.where(params)
75+
assert_equal 1, addresses.size
76+
assert_kind_of StreetAddress, addresses.first
77+
end
78+
6679
def test_where_with_clause_in
6780
ActiveResource::HttpMock.respond_to { |m| m.get "/people.json?id%5B%5D=2", {}, @people_david }
6881
people = Person.where(id: [2])

test/strong_parameters.rb

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# frozen_string_literal: true
2+
3+
class StrongParameters
4+
def initialize(parameters = {})
5+
@parameters = parameters
6+
@permitted = false
7+
end
8+
9+
def permitted?
10+
@permitted
11+
end
12+
13+
def permit!
14+
@permitted = true
15+
end
16+
17+
def to_hash
18+
@parameters.to_hash
19+
end
20+
alias to_h to_hash
21+
end

0 commit comments

Comments
 (0)