posted on: 2009-10-27 18:48:38
I took the cherrypy wsgi server and setup a basic and digest authentication scheme. Works great.

>This is a very basic setup and I doubt it is much different then the one that comes with python. I start with cherrypy wsgi server, not the whole server. Then I parse the wsgi environ and perform http authentication.

Here is the code

#!/usr/bin/env python
"""
This file is minimal techniques to get both types of athentication through a simple wsgi server
mos likely this should be delegated to classes, but I have not done that since all I wanted to 
make is a simple example, that uses both types of authentication.

I started with the example in CherryPy WSGI stand alone server.

"""
import CherryPyWSGI as cwsgi
import base64
from hashlib import md5


def my_crazy_app(environ, start_response):
    """Root directory no authenticaion"""
    status = '200 OK'
    response_headers = [('Content-type','text/plain')]
    start_response(status, response_headers)
    return ['Hello world!\n']

def my_basic_app(environ, start_response):
    """This basic authenticated file"""
    status = '200 OK'
    response_headers = [('Content-type','text/html')]
    
    start_response(status, response_headers)
    return ['<html><body><p>This is there the webpages go</p></body></html>']

def authenticate_basic_app(environ,start_response):
    """Performs the authentcation"""
    auth_response = environ.get("HTTP_AUTHORIZATION","none:none")
    name,passwd = getBasicCredentials(auth_response)
    if passwd==getBasicPassword(name):
        ret_value = my_basic_app(environ,start_response)
        print passwd, getBasicPassword(name)
    else:
        print passwd, getBasicPassword(name)
        ret_value = basicAuthenticationFailed(environ,start_response)
    return ret_value
    
    
def getBasicCredentials(both):
    """Parses the HTTP_AUTHORIZATION item for basic credentials"""
    encd = both.split()[-1]
    values = base64.b64decode(encd).split(':')
    if len(values)==2:
        return values[0],values[1]
    return "none","none"
    
def getBasicPassword(user):
    """Dummy function for getting a pasword"""
    return "dogs"
    
def basicAuthenticationFailed(environ,start_response):
    """Failed authentication"""
    status = '401 Unauthorized'
    response_headers = [('WWW-Authenticate','basic realm="Secure Area"'),('Content-type','text/html')]
    start_response(status,response_headers)
    return ["<html><body>Authorization has failed</body></html>"]
    
    
    
    
def authenticate_digest_app(environ,start_response):
    """This begins the digest authentication process"""
    auth_response = environ.get("HTTP_AUTHORIZATION","none")
    data = getDigestCredentials(auth_response)
    data['method'] = environ.get('REQUEST_METHOD')
    if getDigestResponse(data):
        ret_value = my_digest_app(environ,start_response)
    else:
        ret_value = digestAuthenticationFailed(environ,start_response)
    return ret_value

def getDigestCredentials(auth_response):
    """Parses the return values from 'HTTP_AUHORIZATION' string"""
    data = {}    
    for item in auth_response.split(','):
        part = item.find("=")
        if part>0:
            data[item[:part].strip()] = item[part+1:].strip("\"")
        
    return data
    
def getDigestResponse(data):
    """Creates the hash values that are returned"""
    
    #first section could probably be stored in a data base
    user = data.get("Digest username")
    realm = data.get("realm")
    valueA = md5()
    valueA.update('%s:%s:%s' % (user, realm, 'secret'))
    hashA = valueA.hexdigest()
    
    #this section will change on request
    nonce = data.get("nonce")
    uri = data.get("uri")
    method = data.get('method')
    valueB = md5()
    valueB.update("%s:%s"%(method,uri))
    hashB = valueB.hexdigest()
    
    value = md5()
    value.update("%s:%s:%s"%(hashA,nonce,hashB))
    
    return data.get("response")==value.hexdigest()
    
    

def digestAuthenticationFailed(environ,start_response):
    """Failed digest request"""
    status = '401 Unauthorized'
    response_headers = [('WWW-Authenticate','Digest realm="Secure Area" nonce="two"'),('Content-type','text/html')]
    start_response(status,response_headers)
    return ["<html><body>Authorization has failed</body></html>"]
    
def my_digest_app(environ, start_response):
    """Success"""
    status = '200 OK'
    response_headers = [('Content-type','text/html')]
    
    start_response(status, response_headers)
    return ['<html><body><p>Yo Yo, digest stylie.</p></body></html>']
    

"""

Creates a cherrypy wsgi server with three different locations, / ... no authentication
/basic... basic authentication
/digest ... digest authentication

"""
d = cwsgi.WSGIPathInfoDispatcher({'/': my_crazy_app, '/basic': authenticate_basic_app, '/digest':authenticate_digest_app})
server = cwsgi.CherryPyWSGIServer(('0.0.0.0', 8080), d)


if __name__ == '__main__':
    try:
        server.start()
    except KeyboardInterrupt:
        server.stop()




I got the http info from wikapedia, here

and I found some other info that I might add when I find it again.

Comments

Name: