Update: It seems like I reinvented Hash#slice which is already in the Rails API
recently less everything blogged about the need to secure your code against mass-assignment through forms.
Easy to hack! (As taken from the Less Everything blog)
class Users < ApplicationController def create @user = User.create params[:user] end end
This is actually very old news, less everything caught some attention by threatening to hack people.
As they point out, there are two ways to prevent this problem. One is to use attr_protected or attr_accessible in the model. By the way, always use attr_accessible!
The other way to secure your code is to select the correct input in the controller. The code they showed for doing this was:
class Users < ApplicationController def create @user = User.new @user.login = params[:user][:login] @user.password = params[:user][:password] @user.password_confirmation = params[:user][:password_confirmation] @user.save #user is created, but the is_admin flag is not set end end
It is suggested that you are stuck with writing 5x the code to be more secure. But obviously there is a pattern here, so lets abstract it out. All we really need to know is which paramaters to sign over
class Users < ApplicationController def create @user = User.create( params.split_off(:login, :password, :password_confirmation) ) end end
There, nice in DRY. Here is the helper:
class Hash def split_off(*keys) h = {} keys.each { |key| h[key] = self[key] } h end
Destructive version:
def split_off!(*keys)
h = {}
keys.each { |key| h[key] = delete key }
h
end
end
allow a different action (with a block) if there is no value for the key:
class Hash def split_off(*keys) h = {} if block_given? keys.each do |key| val = self[key] h[key] = val.nil? ? yield(key) : val end else keys.each { |key| h[key] = self[key] } end h end def split_off!(*keys) h = {} if block_given? keys.each do |key| val = delete(key) h[key] = val.nil? ? yield(key) : val end else keys.each { |key| h[key] = delete(key) } end h end end