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 #:nodoc: # 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