"""$URL: svn+ssh://svn.mems-exchange.org/repos/trunk/quixote/demo/altdemo.py $ $Id: altdemo.py *** 25635 2004-11-18 00:11:51Z nascheme $ An alternative Quixote demo, showing also a way to do form-based authentication and page access control. This version is contained in a single module and does not use PTL. The easiest way to run this demo is to use the simple HTTP server included with Quixote. For example: $ server/simple_server.py --factory quixote.demo.altdemo.create_publisher The server listens on localhost:8080 by default. Debug and error output will be sent to the terminal. If you have installed durus, you can run the same demo, except with persistent sessions stored in a durus database, by running: $ server/simple_server.py --factory quixote.demo.altdemo.create_durus_publisher """ from quixote import get_request, get_user, get_session, get_session_manager, \ get_field, redirect from quixote.directory import Directory, AccessControlled from quixote.html import href, htmltext from quixote.errors import AccessError from quixote.publish import SessionPublisher from quixote.session import Session, SessionManager from quixote.util import dump_request # def format_page(title, content): request = htmltext( '
' '

Request:

%s
') % dump_request() return htmltext( '%(title)s' '' '%(content)s%(request)s') % locals() def format_restricted_page(title, content): sessions = get_session_manager().items() if sessions: sessions.sort() content += htmltext('' '' '' '' '' '') this_session = get_session() for index, (id, session) in enumerate(sessions): if session is this_session: formatted_id = htmltext( '%s' % id) else: formatted_id = id content += htmltext( '' % ( index, formatted_id, session.user or htmltext("None"), session.num_requests)) content += htmltext('
SessionUserNumber of Requests
%s%s%s%d
') return format_page(title, content) # class RootDirectory(AccessControlled, Directory): _q_exports = ['', 'restricted', 'login', 'logout'] def _q_access(self): if get_request().get_header('PATH_INFO')[1:] in ('', 'restricted',): if not get_user(): raise NotLoggedInError() def _q_index(self): content = htmltext('') content += htmltext('

%s

' % 'index') content += htmltext('

%s

' % href('restricted', 'restricted')) if not get_user(): content += htmltext('

%s

' % href('login', 'login')) else: content += htmltext('

%s - logged in as %s

' % ( href('logout', 'logout'), get_user())) return format_page("Quixote Demo", content) def restricted(self): content = htmltext('') content += htmltext('

%s

' % href('..','index')) content += htmltext('

%s

' % 'restricted') content += htmltext('

%s - logged in as %s

' % ( href('logout', 'logout'), get_user())) return format_restricted_page("Quixote Demo - Restricted Page", content) def login(self): content = htmltext('') content += htmltext('

%s

' % href('..','index')) def authenticate(name,passwd): # customize check if not name or not passwd: return False return True if not get_user(): if authenticate(get_field("name"), get_field('passwd')): session = get_session() session.set_user(get_field("name")) # This is the important part. print 'login user pathinfo', get_user(), get_request().get_header('PATH_INFO') print 'login sessions isdirty', get_session_manager().items(), get_session().is_dirty() if get_user(): # may have already been logged in content += htmltext( '

You are logged in as %s.

') % get_user() content += htmltext('

%s

' % href('logout', 'logout')) # If path_info is _not_ /login itself path_info = get_request().get_header('PATH_INFO') if path_info and path_info.find('/login') < 0: return redirect( path_info or '/' ) else: content += htmltext( '

Please enter your name here:

\n' '
' 'name
' 'passwd
' '' '
' % (get_request().get_header('PATH_INFO'))) return format_page("Quixote Demo: Login", content) def logout(self): content = htmltext('') content += htmltext('

%s

' % href('..','index')) if get_user(): content += htmltext('

You are now logged out. Goodbye, %s.

')%( get_user() ) else: content += htmltext('

You are already logged out.

') content += htmltext('

%s

' % href('login', 'login')) get_session_manager().expire_session() # This is the important part. # If path_info is _not_ /logout itself path_info = get_request().get_header('PATH_INFO') if path_info and path_info.find('/logout') < 0: return redirect( path_info or '/' ) return format_page("Quixote Demo: Logout", content) # class DemoSession(Session): def __init__(self, id): Session.__init__(self, id) self.num_requests = 0 def start_request(self): """ This is called from the main object publishing loop whenever we start processing a new request. Obviously, this is a good place to track the number of requests made. (If we were interested in the number of *successful* requests made, then we could override finish_request(), which is called by the publisher at the end of each successful request.) """ Session.start_request(self) self.num_requests += 1 def has_info(self): """ Overriding has_info() is essential but non-obvious. The session manager uses has_info() to know if it should hang on to a session object or not: if a session is "dirty", then it must be saved. This prevents saving sessions that don't need to be saved, which is especially important as a defensive measure against clients that don't handle cookies: without it, we might create and store a new session object for every request made by such clients. With has_info(), we create the new session object every time, but throw it away unsaved as soon as the request is complete. (Of course, if you write your session class such that has_info() always returns true after a request has been processed, you're back to the original problem -- and in fact, this class *has* been written that way, because num_requests is incremented on every request, which makes has_info() return true, which makes SessionManager always store the session object. In a real application, think carefully before putting data in a session object that causes has_info() to return true.) """ return (self.num_requests > 0) or Session.has_info(self) is_dirty = has_info # class NotLoggedInError(AccessError): status_code = 401 # Unauthorized title = "Not logged in" description = "You must be logged in to access this resource." def __init__(self, public_msg=None, private_msg=None): if not public_msg: public_msg = self.description if not private_msg: private_msg = self.title AccessError.__init__(self, public_msg, private_msg) # class DemoSessionPublisher(SessionPublisher): def try_publish(self, request): try: return SessionPublisher.try_publish(self, request) except NotLoggedInError, e: # Just return the login page, but without redirecting # to "stay within" current request/url return self.root_directory.login() def finish_successful_request(self): print 'finish_successful_request' print '%s - %s - %s' %(get_request().get_url(), get_session(), get_user()) return SessionPublisher.finish_successful_request(self) def finish_interrupted_request(self, exc): print 'finish_interrupted_request' print '%s - %s - %s' %(get_request().get_url(), get_session(), get_user()) return SessionPublisher.finish_interrupted_request(self, exc) def finish_failed_request(self): print 'finish_failed_request' print '%s - %s - %s' %(get_request().get_url(), get_session(), get_user()) return SessionPublisher.finish_failed_request(self) def create_publisher(): return DemoSessionPublisher(RootDirectory(), SessionManager(session_class=DemoSession), display_exceptions='plain') try: # If durus is installed, define a create_durus_publisher() that # uses a durus database to store persistent sessions. import os, tempfile from durus.persistent import Persistent from durus.persistent_dict import PersistentDict from durus.file_storage import FileStorage from durus.connection import Connection connection = None # set in create_durus_publisher() class PersistentSession(DemoSession, Persistent): pass class PersistentSessionManager(SessionManager, Persistent): def __init__(self): sessions = PersistentDict() SessionManager.__init__(self, session_class=PersistentSession, session_mapping=sessions) def forget_changes(self, session): print 'abort changes', get_session() connection.abort() def commit_changes(self, session): print 'commit changes', get_session() connection.commit() def create_durus_publisher(): global connection filename = os.path.join(tempfile.gettempdir(), 'quixote-demo.durus') print 'Opening %r as a Durus database.' % filename connection = Connection(FileStorage(filename)) root = connection.get_root() session_manager = root.get('session_manager', None) if session_manager is None: session_manager = PersistentSessionManager() root['session_manager'] = session_manager connection.commit() return DemoSessionPublisher(RootDirectory(), session_mgr=session_manager, display_exceptions='plain') except ImportError, exc: pass # durus not installed. #