#371 Strong Parameters pro
The strong_parameters gem is an improvement over attr_accessible to securely handle mass assignment even when you have complex authorization logic. The functionality will likely be added to Rails 4 so it is a good idea to learn how it works.
- Download:
- source codeProject Files in Zip (74.9 KB)
- mp4Full Size H.264 Video (30.1 MB)
- m4vSmaller H.264 Video (15.2 MB)
- webmFull Size VP8 Video (17.7 MB)
- ogvFull Size Theora Video (35.5 MB)
Resources
- Strong Parameters
- Yehuda Katz on Improving Mass Assignment
- Episode 26: Hackers Love Mass Assignment (revised)
Gemfile
gem 'strong_parameters'
config/application.rb
# this line should be removed to disable attr_accessible requirement # config.active_record.whitelist_attributes = true
config/initializers/strong_parameters.rb
ActiveRecord::Base.send(:include, ActiveModel::ForbiddenAttributesProtection)
rails console
Topic.new(sticky: true) params = ActionController::Parameters.new(sticky: true) Topic.new(params) params.to_hash
models/permitted_params.rb
class PermittedParams < Struct.new(:params, :user) def topic params.require(:topic).permit(*topic_attributes) end def topic_attributes if user && user.admin? [:name, :sticky] else [:name] end end end
application_controller.rb
def permitted_params @permitted_params ||= PermittedParams.new(params, current_user) end helper_method :permitted_params
topics_controller.rb
Topic.new(permitted_params.topic) ... @topic.update_attributes(permitted_params.topic)
topics/_form.html.erb
<% if permitted_params.topic_attributes.include? :sticky %>
Nested Attributes
The strong_parameters
gem has some support for nested attributes, but the solution wasn't clean enough for me to show in the episode. Here are some notes about it:
In the example app, when creating a topic, it is possible to create a post at the same time. This currently will not work because posts_attributes
is not permitted to be set. Restricting which parameters can be set on a post is a little tricky. Here's the working code I ended up with.
ruby
class PermittedParams < Struct.new(:params, :user) def topic params.require(:topic).permit(*topic_attributes) end def topic_attributes [:name].tap do |attributes| attributes << :sticky if user && user.admin? attributes << {posts_attributes: {:"0" => post_attributes}} end end def post_attributes [:content].tap do |attributes| attributes << :inline_images if user && user.admin? end end end
This works because I'm just creating one new post, however it is not dynamic enough to work with multiple records. Maybe if strong_parameters
allowed a regular expression or a proc to be passed in as a key that would be doable.