module Merb::AuthenticationMixin
  
  # Attempts to authenticate the user via HTTP Basic authentication. Takes a
  # block with the username and password, if the block yields false the
  # authentication is not accepted and :halt is thrown.
  #
  # If no block is passed, +basic_authentication+, the +request+ and +authenticate+
  # methods can be chained. These can be used to independently request authentication
  # or confirm it, if more control is desired.
  #
  # ==== Parameters
  # realm<~to_s>:: The realm to authenticate against. Defaults to 'Application'.
  # &authenticator:: A block to check if the authentication is valid.
  #
  # ==== Examples
  #     class Application < Merb::Controller
  #     
  #       before :authenticate
  #     
  #       protected
  #     
  #       def authenticate
  #         basic_authentication("My App") do |username, password|
  #           password == "secret"
  #         end
  #       end
  #     
  #     end
  #
  #     class Application < Merb::Controller
  #     
  #       before :authenticate
  #     
  #       def authenticate
  #         user = basic_authentication.authenticate do |username, password|
  #           User.authenticate(username, password)
  #         end
  #     
  #         if user
  #           @current_user = user
  #         else
  #           basic_authentication.request
  #         end
  #       end
  #     
  #     end
  #
  #---
  # @public
  def basic_authentication(realm = "Application", &authenticator)
    BasicAuthentication.new(self, realm, &authenticator)
  end
  
  class BasicAuthentication
    # So we can have access to the status codes
    include Merb::ControllerExceptions

    def initialize(controller, realm = "Application", &authenticator)
      @controller = controller
      @realm = realm
      authenticate_or_request(&authenticator) if authenticator
    end

    def authenticate(&authenticator)
      auth = Rack::Auth::Basic::Request.new(@controller.request.env)

      if auth.provided? and auth.basic?
        authenticator.call(*auth.credentials)
      else
        false
      end
    end

    def request
      @controller.headers['WWW-Authenticate'] = 'Basic realm="%s"' % @realm
      throw :halt, @controller.render("HTTP Basic: Access denied.\n", :status => Unauthorized.status, :layout => false)
    end
    
    protected
    
    def authenticate_or_request(&authenticator)
      authenticate(&authenticator) || request
    end
    
  end

end
