# session module

## Synopsis

The `session` module provides way to group HTTP requests into "sessions" so that information can be maintained between an individual user's requests to various parts of a web site - for example, so that a user can "log in" to the web site and their username remembered as they navigate the site.

## Usage

The base `Session` class is not used directly, since it has no way of storing the data associated with a session. Instead, one of the classes derived from it are used. The choice of subclass determines what storage method is used - the module provides `FileSession` and `SQLSession`, or you can write your own.

To store data in the session, you simply use it as a mapping. For example:

`if "basket" in self.session:
self.session["basket"].append(newitem)
else:
self.session["basket"] = [newitem]`

Sessions are identified by a unique 8-character hexadecimal string, which is usually generated randomly. This is paired with another 8-character hexadecimal string which is a keyed hash generated using the identifier and a secret key unique to the individual web site. The hash simply makes it a bit more difficult for an attacker to guess a valid session identifier. The id and hash are stored in the session under the keys `id` and `hash`, so don't try to use these keys for storing your own data.

The session identifier and hash are passed from request to request either using cookies or by embedding the data at the start of the path in the URL. Which method to use depends on the situation - cookies are better, but some users disable cookies in their browsers. An especially important point to note is that if session identifiers are stored in URLs, any external links must go through a doorway page which forwards the user (using something like `<meta http-equiv="refresh"...>` rather than HTTP redirects) to the external page, because otherwise the session information may get passed to the external site in the HTTP `Referer` header.

To create and maintain the session, a session class should be instantiated at the start of processing a request, and its `save` method called at the end of processing.

Example:

`class Handler(wt.Handler):
def process(self, req):
self.session = session.FileSession(req, "s3cr3t")
if not self.session.relocated:
wt.Handler.process(self, req)
self.session.save()`

## Apache Configuration

If you are passing the session identifier in the URL, then you will need to configure your web server so that it knows what to do with this identifier. If you are not embedding the session identifier in the URL, then no extra configuration of the web server is required.

Using Apache, you should use mod_rewrite to extract the session identifier from the URL and pass it in the `SESSION` environment variable. The URL is then modified to remove the identifier so that the web server can find the requested page.

Example using `httpd.conf`:

`<VirtualHost 192.168.0.1>
ServerName www.example.com
...
RewriteEngine on
RewriteRule ^/([A-Fa-f0-9]{16})(/.*)$ $2 [E=SESSION:$1]
<VirtualHost>`

Example using an `.htaccesss` file in the web root directory:

`RewriteEngine on
RewriteBase /
RewriteRule ^([A-Fa-f0-9]{16})(/.*)$ $2 [E=SESSION:$1]`

If you are using the `wt` templating module in conjunction with the `session` module, you should simply put the single `session` `RewriteRule` line before the `wt` `RewriteCond` line, e.g.:

`RewriteEngine on
RewriteBase /
RewriteRule ^([A-Fa-f0-9]{16})(/.*)$ $2 [E=SESSION:$1]
RewriteCond %{DOCUMENT_ROOT}/wt/$1.py -f
RewriteRule ^(.*)$ /wt/$1.py [E=WT_TEMPLATE_URL:/$1,E=WT_TEMPLATE_FILENAME:%{DOCUMENT_ROOT}/$1]`

## class: Error(Exception)

The base class for all exceptions defined by the `session` module.

## class: Session(dict)

`Session` objects manage the session as well as providing a container to store data associated with the session. This class is not used directly, but is subclassed depending on what method is being used to store the session data.

### Public Instance Variables

#### `relocated`

The `relocated` map references a true value if URL-based sessions are being used and a `Location` header has been set to redirect the user's browser to a new URL.

#### `created`

The number of seconds since the epoch (i.e. `time.time()` value) when this session was created.

#### `new`

`new` references a true value if the session was created during this request (i.e. there was no pre-existing session).

#### `surl`

This variable is only present if session identifiers are being embedded in URLs for the current session. If it is present, it is the URL prefix that is used for this purpose - e.g. `/0a33817bc823781f/`.

### Public Methods

#### `__init__(self, req, secret, cookie="jonsid", url=0, root="",
referer=None, sid=None, shash=None)`

`req`: `cgi.Request` instance
`secret`: string
`cookie`: string
`url`: true or false value
`root`: string
`referer`: string
`sid`: string
`shash`: string

This method may be overridden by subclasses. It creates a new `Session` instance. `secret` is a secret key (i.e. any arbitrary string that you do not disclose) which is used for keying the hash used to verify session identifiers (see _make_hash). This value should be unique for each web site, unless you are deliberately trying to share session identifiers between sites.

`cookie` is the name of the session cookie which is used to store the session identifier. There is usually no need to change this from the default of `jonsid`. If you do not wish to use cookies to identify the session then set this parameter to `None`.

If you wish to use session identifiers embedded in URLs then set `url` to a true value. You may then optionally also set `root` to be the web root at which sessions are anchored (i.e. a string to put before the session identifier in the url). `root` should begin with a `/` character but should not end in one. When using session identifiers in URLs, the session identifier will be looked for in environment variables matching `(REDIRECT_){1,4}SESSION`. Instructions on how to configure Apache to provide this variable are shown above.

You can optionally use the HTTP `Referer` header to help with session verification when using session identifiers in urls. If you set the `referer` parameter to a non-empty string (for example, the host name of your web site) and the user's browser sends a `Referer` header which does not contain that string, then the session is invalidated. This helps avoid unwanted session sharing in the situation where a user emails a URL to a friend. Note that this is not a security measure but just a helpful heuristic, since the contents of this header are under user control.

Finally, if you wish to identify the session in some other way, you can set `cookie` to `None` and `url` to `0` and then pass in the session identifier in `sid`. You may optionally also pass in the identifier hash in `shash` - if you do then the session identifier will be ignored and a new session created if the hash does not match the identifier. If you omit the `shash` parameter then the identifier will be assumed correct.

After initialisation has completed, the session dictionary contains the session identifier under key `id`, the identifier hash under key `hash`, and if the session was not new, any stored information from previous requests.

#### `save(self)`

This method may be overridden by subclasses. It stores the information from the current session's dictionary so that it may be retrieved in later requests. The default implementation does nothing.

### Public Static Methods

#### `tidy()`

This method may be overridden by subclasses. It is used to delete session information that has expired, and should therefore be called periodically, perhaps from a `cron` job. Subclass versions of this method may require different parameters to the function - you cannot call this method without knowing the particulars of the particular subclass being used. The default implementation does nothing.

### Protected Instance Variables

#### `_req`

The `cgi.Request` instance passed to the session's `__init__` method.

### Protected Methods

#### `_make_hash(self, sid, secret)`

`sid`: string
`secret`: string
Returns: string

This method may be overridden by subclasses. It returns the 8-character hexadecimal string which is a keyed hash of the session identifier `sid` and the secret key `secret`. The default implementation uses HMAC-SHA, and should not usually need to be overridden.

#### `_create(self, secret)`

`secret`: string

This method may be overridden by subclasses. It creates a new unique 8-character hexadecimal session identifier and initialises it in whatever storage system is being used. `self["id"]` should be set to the session identifier. Optionally, `self["hash"]` may also be set to the identifier hash. If the hash is not set then `_make_hash` will be called subsequently to determine it. The default implementation simply sets `self["id"]` to a random identifier.

#### `_load(self)`

This method may be overridden by subclasses. It updates the session's dictionary with whatever information the storage system has recorded about the session with identifier `self["id"]`. It should return `1` if the session identifier was located in the storage system, or `0` if it was not (in which case a new session will be created). The default implementation simply returns `1`.

Note that this method should only return `0` if the session information could not be located because it did not exist. If any other error occurs (e.g. an I/O error while searching for the session), then an exception should be raised as usual.

## class: FileSession(Session)

The `FileSession` class is a subclass of `Session` that uses the filesystem to store session data.

### Public Methods

#### `__init__(self, req, secret, basedir=None, **kwargs)`

`req`: `cgi.Request` instance
`secret`: string
`basedir`: string

Creates a new FileSession instance. Session data will be stored using individual files in the filesystem, under the `basedir` directory. If `basedir` is not specified then the environment variable `TMPDIR` is used instead, or if there is no such environment variable then `/tmp` is used. A directory is created under this named `jon-sessions-%d` where `%d` is the current user id. If this directory already exists then it must be owned by the current user. `req`, `secret`, and `kwargs` are passed to the base class's `__init__` method.

#### `save(self)`

The session's dictionary is written to the filesystem.

### Public Static Methods

#### `tidy(max_idle=0, max_age=0, basedir=None)`

`max_idle`: integer
`max_age`: integer
`basedir`: string

Deletes session files that have expired. If `max_idle` is non-zero then it is the maximum number of seconds old a file's modification time may be before it is deleted. If `max_age` is non-zero then it is the maximum number of seconds old a session may be (since it was created) before it is deleted.

### Protected Methods

#### `_create(self, secret)`

`secret`: string

Creates the session identifier and its associated file.

#### `_load(self)`

Updates the session's dictionary with the stored information from the session's file. Returns `0` if the file did not exist, otherwise `1`.

## class: SQLSession(Session)

The `SQLSession` class is a subclass of `Session` that uses an SQL database to store session data.

### Public Methods

#### `__init__(self, req, secret, dbc, table="sessions", **kwargs)`

`req`: `cgi.Request` instance
`secret`: string
`dbc`: Python DB API `Cursor` instance
`table`: string

Creates a new SQLSession instance. Session data will be stored using rows in an SQL table. The database is accessed via the `Cursor` instance `dbc` and the table name `table`. `req`, `secret`, and `kwargs` are passed to the base class's `__init__` method.

The table must have at least the following fields:

`CREATE TABLE sessions (
ID INT UNSIGNED NOT NULL,
hash VARCHAR(8) NOT NULL,
data BLOB NOT NULL,
created INT UNSIGNED NOT NULL,
updated INT UNSIGNED NOT NULL,
PRIMARY KEY (ID)
)`

This class has only been tested with MySQL, although the intention is that it should work with any DB API database.

#### `save(self)`

The session's dictionary is written to the database.

### Public Static Methods

#### `tidy(dbc, table="sessions", max_idle=0, max_age=0)`

`dbc`: Python DB API `Cursor` instance
`table`: string
`max_idle`: integer
`max_age`: integer

Deletes session rows that have expired. If `max_idle` is non-zero then it is the maximum number of seconds old a session's `updated` time may be before it is deleted. If `max_age` is non-zero then it is the maximum number of seconds old a session's `created` time may be before it is deleted.

### Protected Methods

#### `_create(self, secret)`

`secret`: string

Creates the session identifier and its associated table row.

#### `_load(self)`

Updates the session's dictionary with the stored information from the session's database row. Returns `0` if the row was not found, otherwise `1`.

$Id: session.html,v 1.8 2003/02/07 14:38:11 jribbens Exp $