score.session

A small module for handling sessions. Originally written for web usage, but can be used outside of web environments without any constraints.

Quickstart

For a quick integration of this module, just add it and score.kvcache to your modules list. It also makes sense to disable the https-only flag during development:

[score.init]
modules =
    score.ctx
    score.session
    score.kvcache

[session]
# cookies are allowed on non-HTTPS sites during development:
cookie.secure = False

[kvcache]
# default backend, we're configuring a sqlite file in
# the same folder as this config file:
backend.default = score.kvcache.backend.FileCache
backend.default.path = ${here}/_cache.sqlite3
# session cache:
container.score.session.backend = default

You should now have a new context member called session, which behaves just like a regular dict. It will start out without an id, but will gain a uuid4 string once it contains something:

>>> ctx.session.id
>>> ctx.session['parrot'] = 'dead'
>>> ctx.session.id
b9a1aa34-e44a-4370-9d06-1431ab692b94

If you want to access a specific session, you have to set ctx.session_id before accessing the session:

>>> ctx.session_id = 'b9a1aa34-e44a-4370-9d06-1431ab692b94'
>>> ctx.session['parrot']
'dead'

If you’re using the score.http module, there is nothing more to do: The module will automatically set appropriate session cookies and load the sessions if it receives them back.

Configuration

score.session.init(confdict, db=None, kvcache=None, ctx=None)[source]

Initializes this module acoording to our module initialization guidelines with the following configuration keys:

db.class [default=None]
The path to the database class, that should be used as backend.
kvcache.container [default=score.session]
The name of the cache container to use for storing session data when using score.kvcache as backend.
kvcache.livedata [default=false]
This value defines whether sessions must always pull the newest session data for every operation. This has the advantage that all session data will be immediately up-to-date across all processes using the same session, but also the disadvantage that it will make using the session a lot slower.
ctx.member [default=session]
This is the name of the context member, that should be registered with the configured score.ctx module (if there is one).
cookie [default=session]
Name of the cookie to set when used in combination with the score.http module. It is recommended to provide a non-default, obscure value here. Setting this value to the string None will disable setting cookies.
cookie.max_age [default=None]
The max-age parameter of the cookie. The default value of None means that the cookie will be valid until the browser is closed.
cookie.path [default=/]
The path parameter of the cookie.
cookie.domain [default=None]
The domain parameter of the cookie.
cookie.secure [default=True]
The secure parameter of the cookie. Please be aware that you are exposing your user sessions to man-in-the-middle attacks (like wireless sniffers), if you set this value to False in production.
cookie.httponly [default=True]
The httponly parameter of the cookie. Please be aware that setting this value to False in production can lead to session hijacking, if an attacker manages to sneak in malicious javascript code into your application (using XSS, for example).

Details

Loading Sessions

Sessions can be accessed through the methods create and load on the configured module. If you are using score.ctx, though, you can set the context’s session id on the context:

>>> ctx.session_id = 'b9a1aa34-e44a-4370-9d06-1431ab692b94'
>>> ctx.session['username']
'sirlancelot'

Note

The name of the id-member is constructed using the configured context member. If you have configured your score.session to register its context member as storage, the id-member will be storage_id:

>>> ctx.storage_id = 'b9a1aa34-e44a-4370-9d06-1431ab692b94'
>>> ctx.storage['username']
'sirlancelot'

You must take special care when using this approch, since the id-member will only be read once: whenever you access the ctx.session for the first time. The following corde leads to undefined behaviour:

# WARNING! Broken code! Always set ctx.session_id *before*
# accessing ctx.session for the first time!
ctx.session['parrot'] = 'passed on'
ctx.session_id = 515

Backends

The module is capable of storing session data in either of two backends: a score.kvcache container, or a score.db table.

Using score.kvcache is much easier, but does not allow you to access all sessions of a single user efficiently, for example. It merely stores a mapping of session ids to session data. This should really not come as a big surprise when using the key-value-cache as backend.

The alternative backend is score.db. Its usage requires a bit more configuration. Not only in your configuration file …

[session]
db.class = path.to.Session

… but also in your application: you will need a database class with an additional mixin from this package:

from .storable import Storable
from score.db import IdType
from score.session.db import DbSessionMixin
from sqlalchemy import Column, ForeignKey
from sqlalchemy.orm import relationship


class Session(Storable, DbSessionMixin):
    user_id = Column(IdType, ForeignKey('_user.id'))
    user = relationship('User', backref='sessions')

This approach has the advantage that you can operate on sessions just like any other database entity. There is also a small drawback, however: You can no longer delete the user_id on your sessions:

>>> ctx.session['user_id'] is None
True
>>> del(ctx.session['user_id'])
>>> ctx.session['user_id'] is None
True
>>> del(ctx.session['I-dont-exist'])
KeyError: 'I-dont-exist'

The mixin will allow you to write arbitrary values, just like before:

>>> ctx.session['actor_id'] = 14
>>> ctx.session['parrot'] = 'ceased to be'

These additional values will be stored as JSON value in the column _data:

sqlite> .schema _session
CREATE TABLE _session (
    _uuid VARCHAR(36) NOT NULL,
    _data VARCHAR NOT NULL,
    actor_id INTEGER,
    _type VARCHAR(100) NOT NULL,
    id INTEGER NOT NULL,
    PRIMARY KEY (id),
    UNIQUE (_uuid),
    FOREIGN KEY(actor_id) REFERENCES _actor (id)
);
sqlite> select * from _session;
a9fd7ad0-1ed0-45ab-bf5f-bb2b00741ded|{"parrot": "ceased to be"}|14|session|1

API

class score.session.ConfiguredSessionModule(ctx, ctx_member, cookie_kwargs)[source]

This module’s configuration class.

ctx_member

Name of the registered context member, or None if no context member was registered.

create(ctx=None)[source]

Creates a new, empty Session.

load(id, ctx=None)[source]

Loads an existing session with given id.

class score.session.Session(ctx, id)[source]

A dict-like object managing session data. The modified session information is persisted when this object is destroyed. You can also call store() manually to make the data of this session available to other processes.

store()[source]

Persists the information in this session instance.

revert()[source]

Throws away all changes to the current session.

was_changed()[source]

Returns a bool indicating whether a modifying operation was performed on this session.