From 73fba4faf13394540489063247a887a0d396af2f Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sat, 23 Jun 2007 16:43:08 +0000 Subject: [PATCH] Fixed that HTTP authentication should work if the header is called REDIRECT_X_HTTP_AUTHORIZATION as well (closes #6754) [mislaw] git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@7091 5ecf4fe2-1ee6-0310-87b1-e25e094e27de --- actionpack/CHANGELOG | 2 + .../action_controller/http_authentication.rb | 94 ++++++++++--------- .../controller/http_authentication_test.rb | 56 ++++++----- 3 files changed, 87 insertions(+), 65 deletions(-) diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index bff0c2868e..16c9b0add8 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,5 +1,7 @@ *SVN* +* Fixed that HTTP authentication should work if the header is called REDIRECT_X_HTTP_AUTHORIZATION as well #6754 [mislaw] + * Don't mistakenly interpret the request uri as the query string. #8731 [lifofifo, Jeremy Kemper] * Make ActionView#view_paths an attr_accessor for real this time. Also, don't perform an unnecessary #compact on the @view_paths array in #initialize. Closes #8582 [dasil003, julik, rick] diff --git a/actionpack/lib/action_controller/http_authentication.rb b/actionpack/lib/action_controller/http_authentication.rb index fbc34d1f2e..78f41eb503 100644 --- a/actionpack/lib/action_controller/http_authentication.rb +++ b/actionpack/lib/action_controller/http_authentication.rb @@ -6,56 +6,56 @@ module ActionController # # Simple Basic example: # - # class PostsController < ApplicationController - # USER_NAME, PASSWORD = "dhh", "secret" - # - # before_filter :authenticate, :except => [ :index ] - # - # def index - # render :text => "Everyone can see me!" - # end - # - # def edit - # render :text => "I'm only accessible if you know the password" - # end - # - # private - # def authenticate - # authenticate_or_request_with_http_basic do |user_name, password| - # user_name == USER_NAME && password == PASSWORD - # end + # class PostsController < ApplicationController + # USER_NAME, PASSWORD = "dhh", "secret" + # + # before_filter :authenticate, :except => [ :index ] + # + # def index + # render :text => "Everyone can see me!" # end - # end + # + # def edit + # render :text => "I'm only accessible if you know the password" + # end + # + # private + # def authenticate + # authenticate_or_request_with_http_basic do |user_name, password| + # user_name == USER_NAME && password == PASSWORD + # end + # end + # end # # # Here is a more advanced Basic example where only Atom feeds and the XML API is protected by HTTP authentication, # the regular HTML interface is protected by a session approach: # - # class ApplicationController < ActionController::Base - # before_filter :set_account, :authenticate - # - # protected - # def set_account - # @account = Account.find_by_url_name(request.subdomains.first) - # end - # - # def authenticate - # case request.format - # when Mime::XML, Mime::ATOM - # if user = authenticate_with_http_basic { |u, p| @account.users.authenticate(u, p) } - # @current_user = user + # class ApplicationController < ActionController::Base + # before_filter :set_account, :authenticate + # + # protected + # def set_account + # @account = Account.find_by_url_name(request.subdomains.first) + # end + # + # def authenticate + # case request.format + # when Mime::XML, Mime::ATOM + # if user = authenticate_with_http_basic { |u, p| @account.users.authenticate(u, p) } + # @current_user = user + # else + # request_http_basic_authentication + # end # else - # request_http_basic_authentication - # end - # else - # if session_authenticated? - # @current_user = @account.users.find(session[:authenticated][:user_id]) - # else - # redirect_to(login_url) and return false + # if session_authenticated? + # @current_user = @account.users.find(session[:authenticated][:user_id]) + # else + # redirect_to(login_url) and return false + # end # end # end - # end - # end + # end # # # In your integration tests, you can do something like this: @@ -68,6 +68,13 @@ module ActionController # # assert_equal 200, status # end + # + # + # On shared hosts, Apache sometimes doesn't pass authentication headers to + # FCGI instances. If your environment matches this description and you cannot + # authenticate, try this rule in public/.htaccess (replace the plain one): + # + # RewriteRule ^(.*)$ dispatch.fcgi [E=X-HTTP_AUTHORIZATION:%{HTTP:Authorization},QSA,L] module Basic extend self @@ -100,11 +107,12 @@ module ActionController def authorization(request) request.env['HTTP_AUTHORIZATION'] || request.env['X-HTTP_AUTHORIZATION'] || - request.env['X_HTTP_AUTHORIZATION'] + request.env['X_HTTP_AUTHORIZATION'] || + request.env['REDIRECT_X_HTTP_AUTHORIZATION'] end def decode_credentials(request) - Base64.decode64(authorization(request).split.last) + Base64.decode64(authorization(request).split.last || '') end def encode_credentials(user_name, password) diff --git a/actionpack/test/controller/http_authentication_test.rb b/actionpack/test/controller/http_authentication_test.rb index e08bc7b94b..6f7b31a41b 100644 --- a/actionpack/test/controller/http_authentication_test.rb +++ b/actionpack/test/controller/http_authentication_test.rb @@ -3,35 +3,42 @@ require File.dirname(__FILE__) + '/../abstract_unit' class HttpBasicAuthenticationTest < Test::Unit::TestCase include ActionController::HttpAuthentication::Basic + class DummyController + attr_accessor :headers, :renders, :request + + def initialize + @headers, @renders = {}, [] + @request = ActionController::TestRequest.new + end + + def render(options) + self.renders << options + end + end + def setup - @controller = Class.new do - attr_accessor :headers, :renders - - def initialize - @headers, @renders = {}, [] - end - - def request - Class.new do - def env - { 'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Basic.encode_credentials("dhh", "secret") } - end - end.new - end - - def render(options) - self.renders << options - end - end.new + @controller = DummyController.new + @credentials = ActionController::HttpAuthentication::Basic.encode_credentials("dhh", "secret") end def test_successful_authentication - assert authenticate(@controller) { |user_name, password| user_name == "dhh" && password == "secret" } + login = Proc.new { |user_name, password| user_name == "dhh" && password == "secret" } + set_headers + assert authenticate(@controller, &login) + + set_headers '' + assert_nothing_raised do + assert !authenticate(@controller, &login) + end + + set_headers nil + set_headers @credentials, 'REDIRECT_X_HTTP_AUTHORIZATION' + assert authenticate(@controller, &login) end - def test_failing_authentication - assert !authenticate(@controller) { |user_name, password| user_name == "dhh" && password == "secret!!" } + set_headers + assert !authenticate(@controller) { |user_name, password| user_name == "dhh" && password == "incorrect" } end def test_authentication_request @@ -39,4 +46,9 @@ class HttpBasicAuthenticationTest < Test::Unit::TestCase assert_equal 'Basic realm="Megaglobalapp"', @controller.headers["WWW-Authenticate"] assert_equal :unauthorized, @controller.renders.first[:status] end + + private + def set_headers(value = @credentials, name = 'HTTP_AUTHORIZATION') + @controller.request.env[name] = value + end end