Introduce BaseSanitizer null sanitizer and controller-specific callbacks

This updates Devise's StrongParameter support to feature:

- A Null base sanitizer to support existing Rails 3.x installations that
  don't want to use StrongParameters yet
- A new, simpler API for ParameterSanitizer: #permit, #permit!, and #forbid
- Overrideable callbacks on a controller-basis, e.g. #create_sessions_params
  for passing the current scope's parameters through StrongParameters and
  a helper method, whitelisted_params, for rolling your own implementations
  of #create_x_params in your own controllers.
- Lots of tests!
This commit is contained in:
Drew Ulmer
2013-04-10 10:33:50 -05:00
parent 77203e3d97
commit d20fdf87b6
7 changed files with 190 additions and 86 deletions

View File

@@ -10,6 +10,7 @@ module Devise
autoload :FailureApp, 'devise/failure_app'
autoload :OmniAuth, 'devise/omniauth'
autoload :ParamFilter, 'devise/param_filter'
autoload :BaseSanitizer, 'devise/parameter_sanitizer'
autoload :ParameterSanitizer, 'devise/parameter_sanitizer'
autoload :TestHelpers, 'devise/test_helpers'
autoload :TimeInflector, 'devise/time_inflector'

View File

@@ -1,23 +1,42 @@
module Devise
class ParameterSanitizer
attr_reader :allowed_params
class BaseSanitizer
attr_reader :params, :resource_name, :allowed_params
# Return a list of parameter names permitted to be mass-assigned for the
# passed controller.
def permitted_params_for(controller_name)
allowed_params.fetch(key_for_controller_name(controller_name), [])
def initialize(resource_name, params)
@resource_name, @params = resource_name, params
@allowed_params = {}
end
def default_params
params.fetch(resource_name, {})
end
def sanitize_for(controller)
default_params
end
end
class ParameterSanitizer < BaseSanitizer
# Return the allowed parameters passed through the StrongParametesr
# require/permit step according to the allowed_params setup via
# #permit, #permit!, #forbid, and any defaults.
def sanitize_for(controller)
permitted_params = allowed_params.fetch(param_key(controller), []).to_a
params.require(resource_name).permit(permitted_params)
end
# Set up a new parameter sanitizer with a set of allowed parameters. This
# gets initialized on each request so that parameters may be augmented or
# changed as needed via before_filter.
def initialize
def initialize(resource_name, params)
super
@allowed_params = {
:confirmations_controller => [:email],
:passwords_controller => authentication_keys + [:password, :password_confirmation, :reset_password_token],
:registrations_controller => authentication_keys + [:password, :password_confirmation, :current_password],
:sessions_controller => authentication_keys + [:password],
:unlocks_controller => [:email]
:confirmations => [:email],
:passwords => auth_keys | [:password, :password_confirmation, :reset_password_token],
:registrations => auth_keys | [:password, :password_confirmation, :current_password],
:sessions => auth_keys | [:password],
:unlocks => [:email]
}
end
@@ -27,38 +46,41 @@ module Devise
# that when adding a new controller, use the full controller name
# (:confirmations_controller) and not the short names
# (:confirmation/:confirmations).
def permit_devise_param(controller_name, param_name)
@allowed_params[key_for_controller_name(controller_name)] << param_name
def permit(controller_name, *param_names)
@allowed_params[param_key(controller_name)] |= param_names
true
end
def permit!(controller_name, *param_names)
@allowed_params[param_key(controller_name)] = param_names
true
end
# Remove specific allowed parameter for a Devise controller. If the
# controller_name doesn't exist in allowed_params, it will be added to it
# as an empty array.
def remove_permitted_devise_param(controller_name, param_name)
@allowed_params[key_for_controller_name(controller_name)].delete(param_name)
def forbid(controller_name, *param_names)
@allowed_params[param_key(controller_name)] -= param_names
true
end
protected
def authentication_keys
def auth_keys
Array(::Devise.authentication_keys)
end
# Flexibly allow access to permitting/denying/checking parameters by
# controller name in the following key formats: :confirmations_controller,
# :confirmations, :confirmation
def key_for_controller_name(name)
if allowed_params.has_key?(name.to_sym)
name.to_sym
elsif allowed_params.has_key?(:"#{name}s_controller")
:"#{name}s_controller"
elsif allowed_params.has_key?(:"#{name}_controller")
:"#{name}_controller"
def param_key(controller_name)
k = controller_name.to_sym
if allowed_params.has_key?(k)
k
else
@allowed_params[name.to_sym] = []
name.to_sym
@allowed_params[k] = []
k
end
end
end