diff options
author | José Valim <jose.valim@gmail.com> | 2010-01-21 16:50:11 +0100 |
---|---|---|
committer | José Valim <jose.valim@gmail.com> | 2010-01-21 16:52:49 +0100 |
commit | fc4f237864541f5012f9b8cc8e0ec81960377e55 (patch) | |
tree | af89be8dd26fe96044d4b9114f1cea1a4e6910cc /actionpack | |
parent | 04063393f9392b83cf5ccd0a16f217cc7261e15c (diff) | |
download | rails-fc4f237864541f5012f9b8cc8e0ec81960377e55.tar.gz rails-fc4f237864541f5012f9b8cc8e0ec81960377e55.tar.bz2 rails-fc4f237864541f5012f9b8cc8e0ec81960377e55.zip |
Make filter parameters based on request, so they can be modified for anything in the middleware stack.
Diffstat (limited to 'actionpack')
-rw-r--r-- | actionpack/lib/action_controller/base.rb | 6 | ||||
-rw-r--r-- | actionpack/lib/action_controller/railties/subscriber.rb | 6 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/http/filter_parameters.rb | 128 | ||||
-rw-r--r-- | actionpack/test/controller/base_test.rb | 21 | ||||
-rw-r--r-- | actionpack/test/controller/subscriber_test.rb | 5 | ||||
-rw-r--r-- | actionpack/test/dispatch/request_test.rb | 39 |
6 files changed, 109 insertions, 96 deletions
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index cf66e99fa5..a66aafd80e 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -74,8 +74,12 @@ module ActionController end # This method has been moved to ActionDispatch::Request.filter_parameters - def self.filter_parameter_logging(*) + def self.filter_parameter_logging(*args, &block) ActiveSupport::Deprecation.warn("Setting filter_parameter_logging in ActionController is deprecated and has no longer effect, please set 'config.filter_parameters' in config/application.rb instead", caller) + filter = Rails.application.config.filter_parameters + filter.concat(args) + filter << block if block + filter end def _normalize_options(action=nil, options={}, &blk) diff --git a/actionpack/lib/action_controller/railties/subscriber.rb b/actionpack/lib/action_controller/railties/subscriber.rb index d257d6ac2c..1f0e6bf51a 100644 --- a/actionpack/lib/action_controller/railties/subscriber.rb +++ b/actionpack/lib/action_controller/railties/subscriber.rb @@ -1,10 +1,14 @@ module ActionController module Railties class Subscriber < Rails::Subscriber + INTERNAL_PARAMS = %w(controller action format _method only_path) + def start_processing(event) payload = event.payload + params = payload[:params].except(*INTERNAL_PARAMS) + info " Processing by #{payload[:controller]}##{payload[:action]} as #{payload[:formats].first.to_s.upcase}" - info " Parameters: #{payload[:params].inspect}" unless payload[:params].blank? + info " Parameters: #{params.inspect}" unless params.empty? end def process_action(event) diff --git a/actionpack/lib/action_dispatch/http/filter_parameters.rb b/actionpack/lib/action_dispatch/http/filter_parameters.rb index 0f4afb01d9..1958e1668d 100644 --- a/actionpack/lib/action_dispatch/http/filter_parameters.rb +++ b/actionpack/lib/action_dispatch/http/filter_parameters.rb @@ -1,69 +1,30 @@ +require 'active_support/core_ext/object/blank' require 'active_support/core_ext/hash/keys' module ActionDispatch module Http + # Allows you to specify sensitive parameters which will be replaced from + # the request log by looking in all subhashes of the param hash for keys + # to filter. If a block is given, each key and value of the parameter + # hash and all subhashes is passed to it, the value or key can be replaced + # using String#replace or similar method. + # + # Examples: + # + # env["action_dispatch.parameter_filter"] = [:password] + # => replaces the value to all keys matching /password/i with "[FILTERED]" + # + # env["action_dispatch.parameter_filter"] = [:foo, "bar"] + # => replaces the value to all keys matching /foo|bar/i with "[FILTERED]" + # + # env["action_dispatch.parameter_filter"] = lambda do |k,v| + # v.reverse! if k =~ /secret/i + # end + # => reverses the value to all keys matching /secret/i + # module FilterParameters extend ActiveSupport::Concern - INTERNAL_PARAMS = %w(controller action format _method only_path) - - module ClassMethods - # Specify sensitive parameters which will be replaced from the request log. - # Filters parameters that have any of the arguments as a substring. - # Looks in all subhashes of the param hash for keys to filter. - # If a block is given, each key and value of the parameter hash and all - # subhashes is passed to it, the value or key - # can be replaced using String#replace or similar method. - # - # Examples: - # - # ActionDispatch::Request.filter_parameters :password - # => replaces the value to all keys matching /password/i with "[FILTERED]" - # - # ActionDispatch::Request.filter_parameters :foo, "bar" - # => replaces the value to all keys matching /foo|bar/i with "[FILTERED]" - # - # ActionDispatch::Request.filter_parameters do |k,v| - # v.reverse! if k =~ /secret/i - # end - # => reverses the value to all keys matching /secret/i - # - # ActionDispatch::Request.filter_parameters(:foo, "bar") do |k,v| - # v.reverse! if k =~ /secret/i - # end - # => reverses the value to all keys matching /secret/i, and - # replaces the value to all keys matching /foo|bar/i with "[FILTERED]" - def filter_parameters(*filter_words, &block) - raise "You must filter at least one word" if filter_words.empty? - - parameter_filter = Regexp.new(filter_words.join('|'), true) - - define_method(:process_parameter_filter) do |original_params| - filtered_params = {} - - original_params.each do |key, value| - if key =~ parameter_filter - value = '[FILTERED]' - elsif value.is_a?(Hash) - value = process_parameter_filter(value) - elsif value.is_a?(Array) - value = value.map { |i| process_parameter_filter(i) } - elsif block_given? - key = key.dup - value = value.dup if value.duplicable? - yield key, value - end - - filtered_params[key] = value - end - - filtered_params.except!(*INTERNAL_PARAMS) - end - - protected :process_parameter_filter - end - end - # Return a hash of parameters with all sensitive data replaced. def filtered_parameters @filtered_parameters ||= process_parameter_filter(parameters) @@ -71,7 +32,6 @@ module ActionDispatch alias :fitered_params :filtered_parameters # Return a hash of request.env with all sensitive data replaced. - # TODO Josh should white list env to remove stuff like rack.input and rack.errors def filtered_env filtered_env = @env.dup filtered_env.each do |key, value| @@ -86,8 +46,52 @@ module ActionDispatch protected - def process_parameter_filter(original_parameters) - original_parameters.except(*INTERNAL_PARAMS) + def compile_parameter_filter #:nodoc: + strings, regexps, blocks = [], [], [] + + Array(@env["action_dispatch.parameter_filter"]).each do |item| + case item + when NilClass + when Proc + blocks << item + when Regexp + regexps << item + else + strings << item.to_s + end + end + + regexps << Regexp.new(strings.join('|'), true) unless strings.empty? + [regexps, blocks] + end + + def filtering_parameters? #:nodoc: + @env["action_dispatch.parameter_filter"].present? + end + + def process_parameter_filter(original_params) #:nodoc: + return original_params.dup unless filtering_parameters? + + filtered_params = {} + regexps, blocks = compile_parameter_filter + + original_params.each do |key, value| + if regexps.find { |r| key =~ r } + value = '[FILTERED]' + elsif value.is_a?(Hash) + value = process_parameter_filter(value) + elsif value.is_a?(Array) + value = value.map { |i| process_parameter_filter(i) } + elsif blocks.present? + key = key.dup + value = value.dup if value.duplicable? + blocks.each { |b| b.call(key, value) } + end + + filtered_params[key] = value + end + + filtered_params end end end diff --git a/actionpack/test/controller/base_test.rb b/actionpack/test/controller/base_test.rb index 1510a6a7e0..4fcfbacf4e 100644 --- a/actionpack/test/controller/base_test.rb +++ b/actionpack/test/controller/base_test.rb @@ -2,6 +2,9 @@ require 'abstract_unit' require 'logger' require 'pp' # require 'pp' early to prevent hidden_methods from not picking up the pretty-print methods until too late +module Rails +end + # Provide some controller to run the tests on. module Submodule class ContainedEmptyController < ActionController::Base @@ -63,7 +66,7 @@ class DefaultUrlOptionsController < ActionController::Base end end -class ControllerClassTests < Test::Unit::TestCase +class ControllerClassTests < ActiveSupport::TestCase def test_controller_path assert_equal 'empty', EmptyController.controller_path assert_equal EmptyController.controller_path, EmptyController.new.controller_path @@ -74,7 +77,21 @@ class ControllerClassTests < Test::Unit::TestCase def test_controller_name assert_equal 'empty', EmptyController.controller_name assert_equal 'contained_empty', Submodule::ContainedEmptyController.controller_name - end + end + + def test_filter_parameter_logging + parameters = [] + config = mock(:config => mock(:filter_parameters => parameters)) + Rails.expects(:application).returns(config) + + assert_deprecated do + Class.new(ActionController::Base) do + filter_parameter_logging :password + end + end + + assert_equal [:password], parameters + end end class ControllerInstanceTests < Test::Unit::TestCase diff --git a/actionpack/test/controller/subscriber_test.rb b/actionpack/test/controller/subscriber_test.rb index bd0c83413c..152a0d0c04 100644 --- a/actionpack/test/controller/subscriber_test.rb +++ b/actionpack/test/controller/subscriber_test.rb @@ -97,8 +97,7 @@ class ACSubscriberTest < ActionController::TestCase end def test_process_action_with_filter_parameters - ActionDispatch::Request.class_eval "alias :safe_process_parameter_filter :process_parameter_filter" - ActionDispatch::Request.filter_parameters(:lifo, :amount) + @request.env["action_dispatch.parameter_filter"] = [:lifo, :amount] get :show, :lifo => 'Pratik', :amount => '420', :step => '1' wait @@ -107,8 +106,6 @@ class ACSubscriberTest < ActionController::TestCase assert_match /"amount"=>"\[FILTERED\]"/, params assert_match /"lifo"=>"\[FILTERED\]"/, params assert_match /"step"=>"1"/, params - ensure - ActionDispatch::Request.class_eval "alias :process_parameter_filter :safe_process_parameter_filter" end def test_redirect_to diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb index c5a75f5d21..2b5c19361a 100644 --- a/actionpack/test/dispatch/request_test.rb +++ b/actionpack/test/dispatch/request_test.rb @@ -454,18 +454,7 @@ class RequestTest < ActiveSupport::TestCase assert_equal Mime::XML, request.negotiate_mime([Mime::XML, Mime::CSV]) end - class FilterRequest < ActionDispatch::Request - end - - test "filter_parameters raises error without arguments" do - assert_raises RuntimeError do - FilterRequest.filter_parameters - end - end - test "process parameter filter" do - request = FilterRequest.new({}) - test_hashes = [ [{'foo'=>'bar'},{'foo'=>'bar'},%w'food'], [{'foo'=>'bar'},{'foo'=>'[FILTERED]'},%w'foo'], @@ -473,18 +462,18 @@ class RequestTest < ActiveSupport::TestCase [{'foo'=>'bar', 'baz'=>'foo'},{'foo'=>'[FILTERED]', 'baz'=>'[FILTERED]'},%w'foo baz'], [{'bar'=>{'foo'=>'bar','bar'=>'foo'}},{'bar'=>{'foo'=>'[FILTERED]','bar'=>'foo'}},%w'fo'], [{'foo'=>{'foo'=>'bar','bar'=>'foo'}},{'foo'=>'[FILTERED]'},%w'f banana'], - [{'baz'=>[{'foo'=>'baz'}]}, {'baz'=>[{'foo'=>'[FILTERED]'}]}, %w(foo)]] + [{'baz'=>[{'foo'=>'baz'}]}, {'baz'=>[{'foo'=>'[FILTERED]'}]}, [/foo/]]] test_hashes.each do |before_filter, after_filter, filter_words| - FilterRequest.filter_parameters(*filter_words) + request = stub_request('action_dispatch.parameter_filter' => filter_words) assert_equal after_filter, request.send(:process_parameter_filter, before_filter) - filter_words.push('blah') - - FilterRequest.filter_parameters(*filter_words) do |key, value| + filter_words << 'blah' + filter_words << lambda { |key, value| value.reverse! if key =~ /bargain/ - end + } + request = stub_request('action_dispatch.parameter_filter' => filter_words) before_filter['barg'] = {'bargain'=>'gain', 'blah'=>'bar', 'bar'=>{'bargain'=>{'blah'=>'foo'}}} after_filter['barg'] = {'bargain'=>'niag', 'blah'=>'[FILTERED]', 'bar'=>{'bargain'=>{'blah'=>'[FILTERED]'}}} @@ -493,10 +482,9 @@ class RequestTest < ActiveSupport::TestCase end test "filtered_parameters returns params filtered" do - FilterRequest.filter_parameters(:lifo, :amount) - - request = FilterRequest.new('action_dispatch.request.parameters' => - { 'lifo' => 'Pratik', 'amount' => '420', 'step' => '1' }) + request = stub_request('action_dispatch.request.parameters' => + { 'lifo' => 'Pratik', 'amount' => '420', 'step' => '1' }, + 'action_dispatch.parameter_filter' => [:lifo, :amount]) params = request.filtered_parameters assert_equal "[FILTERED]", params["lifo"] @@ -505,12 +493,11 @@ class RequestTest < ActiveSupport::TestCase end test "filtered_env filters env as a whole" do - FilterRequest.filter_parameters(:lifo, :amount) - - request = FilterRequest.new('action_dispatch.request.parameters' => - { 'amount' => '420', 'step' => '1' }, "RAW_POST_DATA" => "yada yada") + request = stub_request('action_dispatch.request.parameters' => + { 'amount' => '420', 'step' => '1' }, "RAW_POST_DATA" => "yada yada", + 'action_dispatch.parameter_filter' => [:lifo, :amount]) - request = FilterRequest.new(request.filtered_env) + request = stub_request(request.filtered_env) assert_equal "[FILTERED]", request.raw_post assert_equal "[FILTERED]", request.params["amount"] |