durusmail: quixote-users: SessionPublisher / read_config in SCGI
SessionPublisher / read_config in SCGI
2002-05-07
2002-05-08
2002-05-08
2002-05-08
2002-05-08
SessionPublisher / read_config in SCGI
Greg Ward
2002-05-08
On 07 May 2002, Michael Watkins said:
> Would anyone on the list have some example Quixote code showing the use of
> sessions to share? I could use some examples of sessions in use:
>
> - for SCGI driven applications

Quixote's session management mechanism should work the same for FastCGI,
SCGI, and mod_python -- ie. any scheme whereby Quixote (and your
application) stick around in memory to handle multiple requests.

Since you didn't say what you want session management for, it's a little
hard to give you a relevant example.  Nevertheless, I'll give it a
shot.  This code is from SPLAT!, a simple bug tracker that we've been
using in-house for about a year now, and which I'm slowly getting close
to releasable.  SPLAT! uses session mgmt to keep track of the current
user (so we know who's adding comments to bugs), the user's current
preferences (how to sort/filter/group bugs on the index page), and a few
other things which I don't remember offhand.

In the driver script, I define two classes: the first,

  class SplatSessionManager (SessionManager):

      def new_session (self, request, id):
          return SplatSession(request, id)

is only needed to ensure that Quixote generates instances of the right
Session class:

  class SplatSession (Session):

      def __init__ (self, request, id):
          [...]
          self.user = [...]
          self.prefs = Preferences()
          self.widgets = {}               # widget name -> Widget object
          self.visible_bugs = []          # list of bug IDs in current bug index

The [...] is stuff that's too complicated to get into now.  (Associating
a user with a session obviously requires some sort of authentication
mechanism, and SPLAT!'s current authentication mechanism is specific to
our web site and very hairy.)

The Preferences class is fairly simple -- it just records the user's
current choice of grouping, sorting, and filtering mechanisms.  When the
UI is generating the list-of-all-bugs, it queries the Preferences
instance attached to the current session to find out what to do.  Note
that user preferences are exactly as persistent as your session: if the
shuts their browser done and comes back to SPLAT! later, their
preferences are lost.  That shouldn't be difficult to "fix", it was just
the easiest way to code things.

Here's an example of using the session object.  _q_index() is the
template that generates the list of all bugs, and it starts like this:

  template _q_index (request):

      prefs = request.session.prefs

One of the pieces of information provided by prefs is the list of
columns the user wishes to see: eg. I might want to see bug status and
fixer, while you might be more interested in its priority or the
submitter.  So pretty early on, _q_index() does this:

      '''\
  [display preferences]
  [filter preferences]
  
  '''
      for col in prefs.columns:
          '  \n' % _column_heading.get(col, col)
      ' \n'

(_column_heading is a global dict.)

Obviously, there's not much point in constructing and carrying around a
Preferences object if the user can't change it.  So SPLAT! includes a
form where the user can update her preferred bug filter, setting up
queries like "show me all new bugs assigned to gward" or "show me all
new or assigned bugs with priority 4 or 5 assigned to amk".  This form
is processed by the following code:

  def set_filter (request):
      action = request.form.get("action")
      if action != "cancel":
          widgets = request.session.widgets
          prefs = request.session.prefs

          for f in prefs.VALID_FILTERS:
              values = widgets[f].parse(request)
              if values:
                  prefs.set_filter(f, values)
              else:
                  prefs.set_filter(f, None)

      config = get_config()
      if action in ("filter", "cancel"):
          return request.redirect(config.get_base_url(absolute=0))
      else:
          return request.redirect("%s/display"
                                  % config.get_base_url(absolute=0))

All of the filtering code is in the Preferences object, so it's a bit
more than just a bag of options.  It makes it really easy to set and use
the bug filter, though.

> - CGI driven (persist to a cookie)

If I understand what you're saying here, you do *NOT* want to do this.
Persisting data to a cookie means that you're trusting the client to
return the cookie data you sent it.  Cookies should almost always be
opaque blobs of information that mean nothing to the client, so that
malicious or broken clients can't screw things up on your server.  Doing
otherwise breaks the #1 rule of secure web programming: Never Trust The
Client.

> I am planning on persisting sessions to a PostgreSQL db, but test on CGI.

By default, Quixote sessions live for as long as the process that
handles requests from the web server: so if you're using Quixote's
SessionManager with a CGI driver script, then your "session" objects are
lost with every request.  You either need to write your own session
manager (to implement persistent sessions), or use a long-lived
execution mechanism like SCGI, FastCGI, or mod_python.  You'll be
happiest if you do both.

        Greg
--
Greg Ward - software developer                gward@mems-exchange.org
MEMS Exchange                            http://www.mems-exchange.org


%s