From 73fba4faf13394540489063247a887a0d396af2f Mon Sep 17 00:00:00 2001
From: David Heinemeier Hansson <david@loudthinking.com>
Date: Sat, 23 Jun 2007 16:43:08 +0000
Subject: 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 +
 .../lib/action_controller/http_authentication.rb   | 94 ++++++++++++----------
 .../test/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
+    #   
+    #     def edit
+    #       render :text => "I'm only accessible if you know the password"
     #     end
-    # 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
-    #         else
-    #           request_http_basic_authentication
-    #         end
-    #       else
-    #         if session_authenticated?
-    #           @current_user = @account.users.find(session[:authenticated][:user_id])
+    #   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
-    #           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" }
-  end
+    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
-- 
cgit v1.2.3