How to enable Strong Parameters protection for non-ActiveRecord models?
I'm currently migrating our Rails app to use Strong Parameters mass-assignment protection instead of model-centric protection, which is used in Rails 3.2.x by default.
Migration of ActiveRecord models is easy and well documented. However I want to enable that kind of protection even for our not persisted models.
So lets have kind of hypothetical model 'CarForm' with properties 'driver', 'manufacturer', and 'color'.
class CarForm
# It quacks like ActiveModel...
extend ActiveModel::Naming
include ActiveModel::Conversion
include ActiveModel::Validations
attr_accessor :driver, :manufacturer, :color
validates :driver, :manufacturer, :color, presence: true
def initialize(values = {})
values.each do |k, v|
send("#{k}=", v)
end
end
def persisted?
false
end
end
Readme for Strong Parameters guide us to use the include ActiveModel::ForbiddenAttributesProtection
in the model. So we just include that and we are done, right? Nope. This module was designed to work with ActiveRecord with such methods like assign_attributes
and initialize
where the parameters are filtered through sanitize_for_mass_assignment
. So we need to add this method to our model:
def assign_attributes(values, options = {})
sanitize_for_mass_assignment(values, options[:as]).each do |k, v|
send("#{k}=", v)
end
end
And we can rewrite initialize
like this:
def initialize(values = {})
assign_attributes(values)
end
However when we try to run this code we got the following error:
NoMethodError: super: no superclass method 'sanitize_for_mass_assignment' for #<CarForm:0x000000078aea38>
We missing just one thing the mass assignment protection itself! We need to include one more mixin ActiveModel::MassAssignmentSecurity
(which is also part of the ActiveRecord
by default). This module contains sanitize_for_mass_assignment
method as well. So we need to load this module before the ActiveModel::ForbiddenAttributesProtection
mixin to act as superclass.
And now we finally can call:
params = ActionController::Parameters.new({
'car_form' => {
'driver' => 'Alfred',
'color' => '#000000',
'manufacturer' => 'Rolls-Royce'
}
})
sanitized_params = params.require(:car_form).permit(:driver, :color, :manufacturer)
form = CarForm.new(sanitized_params)
Below you can see the modified class:
class CarForm
# It quacks like ActiveModel...
extend ActiveModel::Naming
include ActiveModel::Conversion
include ActiveModel::Validations
# Mass-assignment protection using Strong Params
include ActiveModel::MassAssignmentSecurity
include ActiveModel::ForbiddenAttributesProtection
attr_accessor :driver, :manufacturer, :color
validates :driver, :manufacturer, :color, presence: true
def initialize(values = {})
assign_attributes(values)
end
def assign_attributes(values, options = {})
sanitize_for_mass_assignment(values, options[:as]).each do |k, v|
send("#{k}=", v)
end
end
def persisted?
false
end
end