# quixote.demo.authsession
#
# Application code for the Quixote session management demo,
# modified to demonstrate user authorization.
#
# Create a clone of session_demo.cgi called auth_demo.cgi.
# Edit it to publish 'quixote.demo.authsession'.

# 2002-11-16 JJD Experiment

import base64
from quixote import get_session_manager
from quixote.errors import AccessError, QueryError
from quixote.html import html_quote

_q_exports = ['login', 'logout', 'public', 'private']


# Typical stuff for any Quixote app.

template page_header (title):
    '''\
<html>
<head><title>%s</title></head>
<body>
<h1>%s</h1>
''' % (title, title)


template page_footer (request):
    '<p><font size="-1">\n'
    '<a href="./">Home</a> | <a href="public">Public</a> | <a href="private">Private</a>'
    if request.session and request.session.user:
        ' | <a href="logout">Log out</a>'
    '\n</font></p>'
    '</body></html>'
    

# We include the login form on two separate pages, so it's been factored
# out to a separate template.

template login_form ():
    '''
<form method="POST" action="login">
    <table>
    <tr><td align="left">User ID</td><td><input name="name" width="30"></td></tr>
    <tr><td align="left">Password</td><td><input name="password" type="password" width="30"></td></tr>
    <tr><td align="left"><input type="submit"></td><td></td></tr>
    </table>
</form>
'''
    

template _q_index (request):
    page_header("Quixote Authorization Demo")

    session = request.session

    # All Quixote sessions have the ability to track the user's identity
    # in session.user.  In this simple application, session.user is just
    # a string which the user enters directly into this form.

    if session.user is None:
        '''
<p>You may log in here, if you feel like it.</p>

'''
        login_form()
    else:
        ('<p>Hello, %s.  Good to see you.</p>\n' 
         % html_quote(session.user))

    '''
You can:
<ul>
'''
    '  <li><a href="public">View the public page</a>\n'
    if session.user:
    	'  <li><a href="private">View the private page</a>\n'
        '  <li><a href="logout">Log out</a>\n'
    else:
    	'  <li><a href="private">View the private page</a> (login will be required)\n'
    '</ul>\n'
    '</body></html>'


# The login() template has two purposes: to display a page with just a
# login form, and to process the login form submitted either from the
# index page or from login() itself.  This is a fairly common idiom in
# Quixote (just as it's a fairly common idiom with CGI scripts -- it's
# just cleaner with Quixote).

template login (request):
    page_header("Quixote Authorization Demo: Login")
    session = request.session

    # We seem to be processing the login form.
    if request.form:
        uid = request.form.get("name")
        pwd = request.form.get("password")
        
        # Check validity of user ID and password.
        # For this example, an authorized user is anybody with a
        # non-null user ID and a password identical to the ID.
        if uid and uid == pwd:
            session.user = uid
            '<p>Hello, %s.  Good to see you.</p>\n' % html_quote(uid)
        else:
            "<p>Invalid ID or password - try again</p>\n"
            login_form()

    # No form data to process, so generate the login form instead.  When
    # the user submits it, we'll return to this template and take the
    # above branch.
    else:
        '<p>Please log in:</p>\n'
        login_form()

    page_footer(request)


# logout() expires the current session, ie. removes it from the
# session manager and instructs the client to forget about the session
# cookie.  Then it invalidates the browser's login information.

template logout (request):
    session = request.session
    page_header("Quixote Authorization Demo: Logout")
    if session.user:
        '<p>Goodbye, %s.  See you around.</p>\n' % session.user
    '<p>You have logged out.</p>\n'
    page_footer(request)
    get_session_manager().expire_session(request)
    ### How to prevent the login dialog from popping up here? ###
    ### How to ensure that the logout is complete and permanent? ###
    request.response.set_header("WWW-Authenticate", "Basic realm=%s" % "QX_DEMO")
    request.response.set_status(401)

template public(request):
    page_header("Quixote Authorization Demo: Public Page")
    session = request.session
    '<p>This is public information. Enjoy.</p>'
    page_footer(request)

template private(request):
    page_header("Quixote Authorization Demo: Private Page")
    session = request.session
    if authorize(request):
        '<p>You are viewing private information, %s.</p>' % session.user
    else:
        request.response.set_header("WWW-Authenticate", "Basic realm=%s" % "QX_DEMO")
        request.response.set_status(401)
        "<p>Please log in!</p>"
    page_footer(request)

def authorize(request):
    result = 0
    session = request.session
    if session.user:
        # If user has already been authorized, do not check further
        result = 1
    else:
        # If user has not been authorized yet, invoke HTTP authorization.
        auth = request.get_header("authorization")
        if auth:
            # If authorization header is present, user has submitted uid & pwd
            auth = auth.split()
            if auth[0].lower() == "basic":
            # auth[1] is base64-encoded username:password
                uid_pwd = base64.decodestring(auth[1])
                (uid, pwd) = uid_pwd.split(":")
                # Check validity of user ID and password.
                # For this example, an authorized user is anybody with a
                # non-null user ID and a password identical to the ID.
                if uid and uid == pwd:
                    # Register authorized user in session
                    session.user = uid
                    result = 1
        else:
            # Absent or blank authorization header means user has not submitted
            # an ID and password yet.
            result = 0
    return result
