diff options
Diffstat (limited to 'actionpack/test/controller')
8 files changed, 358 insertions, 27 deletions
diff --git a/actionpack/test/controller/http_basic_authentication_test.rb b/actionpack/test/controller/http_basic_authentication_test.rb index 0a5e5402b9..adcf259317 100644 --- a/actionpack/test/controller/http_basic_authentication_test.rb +++ b/actionpack/test/controller/http_basic_authentication_test.rb @@ -5,6 +5,7 @@ class HttpBasicAuthenticationTest < ActionController::TestCase before_action :authenticate, only: :index before_action :authenticate_with_request, only: :display before_action :authenticate_long_credentials, only: :show + before_action :auth_with_special_chars, only: :special_creds http_basic_authenticate_with :name => "David", :password => "Goliath", :only => :search @@ -20,6 +21,10 @@ class HttpBasicAuthenticationTest < ActionController::TestCase render plain: 'Only for loooooong credentials' end + def special_creds + render plain: 'Only for special credentials' + end + def search render plain: 'All inline' end @@ -40,6 +45,12 @@ class HttpBasicAuthenticationTest < ActionController::TestCase end end + def auth_with_special_chars + authenticate_or_request_with_http_basic do |username, password| + username == 'login!@#$%^&*()_+{}[];"\',./<>?`~ \n\r\t' && password == 'pwd:!@#$%^&*()_+{}[];"\',./<>?`~ \n\r\t' + end + end + def authenticate_long_credentials authenticate_or_request_with_http_basic do |username, password| username == '1234567890123456789012345678901234567890' && password == '1234567890123456789012345678901234567890' @@ -100,7 +111,7 @@ class HttpBasicAuthenticationTest < ActionController::TestCase assert_no_match(/\n/, result) end - test "succesful authentication with uppercase authorization scheme" do + test "successful authentication with uppercase authorization scheme" do @request.env['HTTP_AUTHORIZATION'] = "BASIC #{::Base64.encode64("lifo:world")}" get :index @@ -133,6 +144,14 @@ class HttpBasicAuthenticationTest < ActionController::TestCase assert_equal 'Definitely Maybe', @response.body end + test "authentication request with valid credential special chars" do + @request.env['HTTP_AUTHORIZATION'] = encode_credentials('login!@#$%^&*()_+{}[];"\',./<>?`~ \n\r\t', 'pwd:!@#$%^&*()_+{}[];"\',./<>?`~ \n\r\t') + get :special_creds + + assert_response :success + assert_equal 'Only for special credentials', @response.body + end + test "authenticate with class method" do @request.env['HTTP_AUTHORIZATION'] = encode_credentials('David', 'Goliath') get :search diff --git a/actionpack/test/controller/metal/renderers_test.rb b/actionpack/test/controller/metal/renderers_test.rb new file mode 100644 index 0000000000..007866a559 --- /dev/null +++ b/actionpack/test/controller/metal/renderers_test.rb @@ -0,0 +1,42 @@ +require 'abstract_unit' +require 'active_support/core_ext/hash/conversions' + +class MetalRenderingJsonController < MetalRenderingController + class Model + def to_json(options = {}) + { a: 'b' }.to_json(options) + end + + def to_xml(options = {}) + { a: 'b' }.to_xml(options) + end + end + + use_renderers :json + + def one + render json: Model.new + end + + def two + render xml: Model.new + end +end + +class RenderersMetalTest < ActionController::TestCase + tests MetalRenderingJsonController + + def test_render_json + get :one + assert_response :success + assert_equal({ a: 'b' }.to_json, @response.body) + assert_equal 'application/json', @response.content_type + end + + def test_render_xml + get :two + assert_response :success + assert_equal(" ", @response.body) + assert_equal 'text/plain', @response.content_type + end +end diff --git a/actionpack/test/controller/parameters/accessors_test.rb b/actionpack/test/controller/parameters/accessors_test.rb index 97875c3cbb..a8f4d877a6 100644 --- a/actionpack/test/controller/parameters/accessors_test.rb +++ b/actionpack/test/controller/parameters/accessors_test.rb @@ -27,6 +27,12 @@ class ParametersAccessorsTest < ActiveSupport::TestCase assert_not @params[:person][:name].permitted? end + test "as_json returns the JSON representation of the parameters hash" do + assert_not @params.as_json.key? "parameters" + assert_not @params.as_json.key? "permitted" + assert @params.as_json.key? "person" + end + test "each carries permitted status" do @params.permit! @params.each { |key, value| assert(value.permitted?) if key == "person" } diff --git a/actionpack/test/controller/parameters/parameters_permit_test.rb b/actionpack/test/controller/parameters/parameters_permit_test.rb index f23aa599c1..3299f2d9d0 100644 --- a/actionpack/test/controller/parameters/parameters_permit_test.rb +++ b/actionpack/test/controller/parameters/parameters_permit_test.rb @@ -294,8 +294,16 @@ class ParametersPermitTest < ActiveSupport::TestCase end test "to_unsafe_h returns unfiltered params" do - assert @params.to_h.is_a? ActiveSupport::HashWithIndifferentAccess - assert_not @params.to_h.is_a? ActionController::Parameters + assert @params.to_unsafe_h.is_a? ActiveSupport::HashWithIndifferentAccess + assert_not @params.to_unsafe_h.is_a? ActionController::Parameters + end + + test "to_unsafe_h returns unfiltered params even after accessing few keys" do + params = ActionController::Parameters.new("f"=>{"language_facet"=>["Tibetan"]}) + expected = {"f"=>{"language_facet"=>["Tibetan"]}} + + assert params['f'].is_a? ActionController::Parameters + assert_equal expected, params.to_unsafe_h end test "to_h only deep dups Ruby collections" do @@ -325,4 +333,10 @@ class ParametersPermitTest < ActiveSupport::TestCase assert_equal({ 'companies' => [ company, :acme ] }, params.to_unsafe_h) assert_not company.dupped end + + test "include? returns true when the key is present" do + assert @params.include? :person + assert @params.include? 'person' + assert_not @params.include? :gorilla + end end diff --git a/actionpack/test/controller/render_other_test.rb b/actionpack/test/controller/render_other_test.rb deleted file mode 100644 index 1f5215ac55..0000000000 --- a/actionpack/test/controller/render_other_test.rb +++ /dev/null @@ -1,24 +0,0 @@ -require 'abstract_unit' - - -class RenderOtherTest < ActionController::TestCase - class TestController < ActionController::Base - def render_simon_says - render :simon => "foo" - end - end - - tests TestController - - def test_using_custom_render_option - ActionController.add_renderer :simon do |says, options| - self.content_type = Mime[:text] - self.response_body = "Simon says: #{says}" - end - - get :render_simon_says - assert_equal "Simon says: foo", @response.body - ensure - ActionController.remove_renderer :simon - end -end diff --git a/actionpack/test/controller/renderers_test.rb b/actionpack/test/controller/renderers_test.rb new file mode 100644 index 0000000000..e6c2e4636e --- /dev/null +++ b/actionpack/test/controller/renderers_test.rb @@ -0,0 +1,90 @@ +require 'abstract_unit' +require 'controller/fake_models' +require 'active_support/logger' + +class RenderersTest < ActionController::TestCase + class XmlRenderable + def to_xml(options) + options[:root] ||= "i-am-xml" + "<#{options[:root]}/>" + end + end + class JsonRenderable + def as_json(options={}) + hash = { :a => :b, :c => :d, :e => :f } + hash.except!(*options[:except]) if options[:except] + hash + end + + def to_json(options = {}) + super :except => [:c, :e] + end + end + class CsvRenderable + def to_csv + "c,s,v" + end + end + class TestController < ActionController::Base + + def render_simon_says + render :simon => "foo" + end + + def respond_to_mime + respond_to do |type| + type.json do + render json: JsonRenderable.new + end + type.js { render json: 'JS', callback: 'alert' } + type.csv { render csv: CsvRenderable.new } + type.xml { render xml: XmlRenderable.new } + type.html { render body: "HTML" } + type.rss { render body: "RSS" } + type.all { render body: "Nothing" } + type.any(:js, :xml) { render body: "Either JS or XML" } + end + end + end + + tests TestController + + def setup + # enable a logger so that (e.g.) the benchmarking stuff runs, so we can get + # a more accurate simulation of what happens in "real life". + super + @controller.logger = ActiveSupport::Logger.new(nil) + end + + def test_using_custom_render_option + ActionController.add_renderer :simon do |says, options| + self.content_type = Mime[:text] + self.response_body = "Simon says: #{says}" + end + + get :render_simon_says + assert_equal "Simon says: foo", @response.body + ensure + ActionController.remove_renderer :simon + end + + def test_raises_missing_template_no_renderer + assert_raise ActionView::MissingTemplate do + get :respond_to_mime, format: 'csv' + end + assert_equal Mime[:csv], @response.content_type + assert_equal "", @response.body + end + + def test_adding_csv_rendering_via_renderers_add + ActionController::Renderers.add :csv do |value, options| + send_data value.to_csv, type: Mime[:csv] + end + @request.accept = "text/csv" + get :respond_to_mime, format: 'csv' + assert_equal Mime[:csv], @response.content_type + assert_equal "c,s,v", @response.body + ensure + ActionController::Renderers.remove :csv + end +end diff --git a/actionpack/test/controller/request_forgery_protection_test.rb b/actionpack/test/controller/request_forgery_protection_test.rb index 87a8ed3dc9..1984ad8825 100644 --- a/actionpack/test/controller/request_forgery_protection_test.rb +++ b/actionpack/test/controller/request_forgery_protection_test.rb @@ -128,6 +128,23 @@ class CustomAuthenticityParamController < RequestForgeryProtectionControllerUsin end end +class PerFormTokensController < ActionController::Base + protect_from_forgery :with => :exception + self.per_form_csrf_tokens = true + + def index + render inline: "<%= form_tag (params[:form_path] || '/per_form_tokens/post_one'), method: (params[:form_method] || :post) %>" + end + + def post_one + render plain: '' + end + + def post_two + render plain: '' + end +end + # common test methods module RequestForgeryProtectionTests def setup @@ -623,3 +640,158 @@ class CustomAuthenticityParamControllerTest < ActionController::TestCase end end end + +class PerFormTokensControllerTest < ActionController::TestCase + def test_per_form_token_is_same_size_as_global_token + get :index + expected = ActionController::RequestForgeryProtection::AUTHENTICITY_TOKEN_LENGTH + actual = @controller.send(:per_form_csrf_token, session, '/path', 'post').size + assert_equal expected, actual + end + + def test_accepts_token_for_correct_path_and_method + get :index + + form_token = nil + assert_select 'input[name=custom_authenticity_token]' do |elts| + form_token = elts.first['value'] + assert_not_nil form_token + end + + actual = @controller.send(:unmask_token, Base64.strict_decode64(form_token)) + expected = @controller.send(:per_form_csrf_token, session, '/per_form_tokens/post_one', 'post') + assert_equal expected, actual + + # This is required because PATH_INFO isn't reset between requests. + @request.env['PATH_INFO'] = '/per_form_tokens/post_one' + assert_nothing_raised do + post :post_one, params: {custom_authenticity_token: form_token} + end + assert_response :success + end + + def test_rejects_token_for_incorrect_path + get :index + + form_token = nil + assert_select 'input[name=custom_authenticity_token]' do |elts| + form_token = elts.first['value'] + assert_not_nil form_token + end + + actual = @controller.send(:unmask_token, Base64.strict_decode64(form_token)) + expected = @controller.send(:per_form_csrf_token, session, '/per_form_tokens/post_one', 'post') + assert_equal expected, actual + + # This is required because PATH_INFO isn't reset between requests. + @request.env['PATH_INFO'] = '/per_form_tokens/post_two' + assert_raises(ActionController::InvalidAuthenticityToken) do + post :post_two, params: {custom_authenticity_token: form_token} + end + end + + def test_rejects_token_for_incorrect_method + get :index + + form_token = nil + assert_select 'input[name=custom_authenticity_token]' do |elts| + form_token = elts.first['value'] + assert_not_nil form_token + end + + actual = @controller.send(:unmask_token, Base64.strict_decode64(form_token)) + expected = @controller.send(:per_form_csrf_token, session, '/per_form_tokens/post_one', 'post') + assert_equal expected, actual + + # This is required because PATH_INFO isn't reset between requests. + @request.env['PATH_INFO'] = '/per_form_tokens/post_one' + assert_raises(ActionController::InvalidAuthenticityToken) do + patch :post_one, params: {custom_authenticity_token: form_token} + end + end + + def test_accepts_global_csrf_token + get :index + + token = @controller.send(:form_authenticity_token) + + # This is required because PATH_INFO isn't reset between requests. + @request.env['PATH_INFO'] = '/per_form_tokens/post_one' + assert_nothing_raised do + post :post_one, params: {custom_authenticity_token: token} + end + assert_response :success + end + + def test_ignores_params + get :index, params: {form_path: '/per_form_tokens/post_one?foo=bar'} + + form_token = nil + assert_select 'input[name=custom_authenticity_token]' do |elts| + form_token = elts.first['value'] + assert_not_nil form_token + end + + actual = @controller.send(:unmask_token, Base64.strict_decode64(form_token)) + expected = @controller.send(:per_form_csrf_token, session, '/per_form_tokens/post_one', 'post') + assert_equal expected, actual + + # This is required because PATH_INFO isn't reset between requests. + @request.env['PATH_INFO'] = '/per_form_tokens/post_one?foo=baz' + assert_nothing_raised do + post :post_one, params: {custom_authenticity_token: form_token, baz: 'foo'} + end + assert_response :success + end + + def test_ignores_trailing_slash_during_generation + get :index, params: {form_path: '/per_form_tokens/post_one/'} + + form_token = nil + assert_select 'input[name=custom_authenticity_token]' do |elts| + form_token = elts.first['value'] + assert_not_nil form_token + end + + # This is required because PATH_INFO isn't reset between requests. + @request.env['PATH_INFO'] = '/per_form_tokens/post_one' + assert_nothing_raised do + post :post_one, params: {custom_authenticity_token: form_token} + end + assert_response :success + end + + def test_ignores_trailing_slash_during_validation + get :index + + form_token = nil + assert_select 'input[name=custom_authenticity_token]' do |elts| + form_token = elts.first['value'] + assert_not_nil form_token + end + + # This is required because PATH_INFO isn't reset between requests. + @request.env['PATH_INFO'] = '/per_form_tokens/post_one/' + assert_nothing_raised do + post :post_one, params: {custom_authenticity_token: form_token} + end + assert_response :success + end + + def test_method_is_case_insensitive + get :index, params: {form_method: "POST"} + + form_token = nil + assert_select 'input[name=custom_authenticity_token]' do |elts| + form_token = elts.first['value'] + assert_not_nil form_token + end + + # This is required because PATH_INFO isn't reset between requests. + @request.env['PATH_INFO'] = '/per_form_tokens/post_one/' + assert_nothing_raised do + post :post_one, params: {custom_authenticity_token: form_token} + end + assert_response :success + end +end diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index a39fede5b9..178cd41922 100644 --- a/actionpack/test/controller/routing_test.rb +++ b/actionpack/test/controller/routing_test.rb @@ -94,6 +94,18 @@ class LegacyRouteSetTests < ActiveSupport::TestCase assert_equal({"artist"=>"journey", "song"=>"faithfully"}, hash) end + def test_symbols_with_dashes_reversed + rs.draw do + get ':artist(/omg-:song)', :to => lambda { |env| + resp = ActiveSupport::JSON.encode ActionDispatch::Request.new(env).path_parameters + [200, {}, [resp]] + } + end + + hash = ActiveSupport::JSON.decode get(URI('http://example.org/journey/omg-faithfully')) + assert_equal({"artist"=>"journey", "song"=>"faithfully"}, hash) + end + def test_id_with_dash rs.draw do get '/journey/:id', :to => lambda { |env| |