diff --git a/docs/api.rst b/docs/api.rst index 8d93ff4508..8e7c0c2831 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -25,6 +25,7 @@ documentation is organized alphabetically by module name. api/router api/scripting api/security + api/session api/settings api/testing api/threadlocal diff --git a/docs/api/interfaces.rst b/docs/api/interfaces.rst index 7193fd11b1..2bf55474ec 100644 --- a/docs/api/interfaces.rst +++ b/docs/api/interfaces.rst @@ -25,3 +25,7 @@ Other Interfaces .. autointerface:: IRoutePregenerator + .. autointerface:: ISession + + .. autointerface:: ISessionFactory + diff --git a/docs/api/request.rst b/docs/api/request.rst index e53028b0f7..9e851ba8d4 100644 --- a/docs/api/request.rst +++ b/docs/api/request.rst @@ -85,3 +85,11 @@ of ``request.exception`` will be ``None`` within response and finished callbacks. + .. attribute:: session + + If a :term:`session factory` has been configured, this attribute + will represent the current user's :term:`session` object. If a + session factory *has not* been configured, requesting the + ``request.session`` attribute will cause a + :class:`pyramid.exceptions.ConfigurationError` to be raised. + diff --git a/docs/api/session.rst b/docs/api/session.rst new file mode 100644 index 0000000000..daed9fc33a --- /dev/null +++ b/docs/api/session.rst @@ -0,0 +1,9 @@ +.. _session_module: + +:mod:`pyramid.session` +--------------------------- + +.. automodule:: pyramid.session + + .. autofunction:: InsecureCookieSessionFactoryConfig + diff --git a/docs/glossary.rst b/docs/glossary.rst index 2e2b11aaa9..93d86b6641 100644 --- a/docs/glossary.rst +++ b/docs/glossary.rst @@ -803,3 +803,14 @@ Glossary ``route_url``. See :class:`repoze.bfg.interfaces.IRoutePregenerator` for more information. + + session + A namespace that is valid for some period of continual activity + that can be used to represent a user's interaction with a web + application. + + session factory + A callable, which, when called with a single argument named + ``request`` (a :term:`request` object), returns a + :term:`session` object. + diff --git a/docs/index.rst b/docs/index.rst index 3b62b5fac9..c774c11d14 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -40,6 +40,7 @@ Narrative documentation in chapter form explaining how to use narr/views narr/static narr/webob + narr/sessions narr/templates narr/models narr/security diff --git a/docs/narr/security.rst b/docs/narr/security.rst index 109009842b..5782837aa6 100644 --- a/docs/narr/security.rst +++ b/docs/narr/security.rst @@ -241,7 +241,7 @@ application: When a default permission is registered: -- if a view configuration names an explicit ``permission`, the default +- if a view configuration names an explicit ``permission``, the default permission is ignored for that view registration, and the view-configuration-named permission is used. diff --git a/docs/narr/sessions.rst b/docs/narr/sessions.rst new file mode 100644 index 0000000000..e460e2d74d --- /dev/null +++ b/docs/narr/sessions.rst @@ -0,0 +1,152 @@ +.. index:: + single: session + +.. _sessions_chapter: + +Session Objects +=============== + +A :term:`session` is a namespace which is valid for some period of +continual activity that can be used to represent a user's interaction +with a web application. + +Using The Default Session Factory +--------------------------------- + +In order to use sessions, you must set up a :term:`session factory` +during your :mod:`pyramid` configuration. + +A very basic, insecure sample session factory implementation is +provided in the :mod:`pyramid` core. It uses a cookie to store +session information. This implementation has the following +limitation: + +- The session information in the cookies used by this implementation + is *not* encrypted, so it can be viewed by anyone with access to the + cookie storage of the user's browser or anyone with access to the + network along which the cookie travels. + +- The maximum number of bytes that are storable in a serialized + representation of the session is fewer than 4000. Only very small + data sets can be kept in this + +It is, however, digitally signed, and thus its data cannot easily be +tampered with. + +You can configure this session factory in your :mod:`pyramid` +application by using the ``session_factory`` argument to the +:class:`pyramid.configuration.Configurator` class: + +.. code-block:: python + :linenos: + + from pyramid.session import InsecureCookieSessionFactoryConfig + my_session_factory = InsecureCookieSessionFactoryConfig('itsaseekreet') + + from pyramid.configuration import Configurator + config = Configurator(session_factory = my_session_factory) + +.. warning:: + + Note the very long, very explicit name for + ``InsecureCookieSessionFactoryConfig``. It's trying to tell you + that this implementation is, by default, *insecure*. You should + not use it when you keep sensitive information in the session + object, as the information can be easily read by both users of your + application and third parties who have access to your users' + network traffic. Use a different session factory implementation + (preferably one which keeps session data on the server) for + anything but the most basic of applications where "session security + doesn't matter". + +Using a Session Object +---------------------- + +Once a session factory has been configured for your application, you +can access session objects provided by the session factory by asking +for the ``session`` attribute of any :term:`request` object. For +example: + +.. code-block:: python + :linenos: + + from webob import Response + + def myview(request): + session = request.session + if 'abc' in session: + session['fred'] = 'yes' + session['abc'] = '123' + if 'fred' in session: + return Response('Fred was in the session') + else: + return Response('Fred was not in the session') + +You can use a session much like a Python dictionary. It supports all +methods of a Python dictionary, and it has three extra attributes, and +two extra methods. + +Extra attributes: + +``modified`` + An integer timestamp indicating the last time the session was modified. + +``created`` + An integer timestamp indicating the time that this session was created. + +``new`` + A boolean. If ``new`` is True, this session is new. Otherwise, it has + been constituted from data that was already serialized. + +Extra methods: + +``changed()`` + Call this when you mutate a mutable value in the session namespace. + +``invalidate()`` + Call this when you want to invalidate the session (dump all data, + and -- perhaps -- set a clearing cookie). + +The formal definition of the methods and attributes supported by the +session object are in the :class:`pyramid.interfaces.ISession` +documentation. + +Some gotchas: + +- Keys and values of session data must be *pickleable*. This means, + typically, that they must be instances of basic types of objects, + such as strings, lists, dictionaries, tuples, integers, etc. If you + place an object in a session data key or value that is not + pickleable, an error will be raised when the session is serialized. + +- If you place a mutable value (for example, a list or a dictionary) + in a session object, and you subsequently mutate that value, you + must call the ``changed()`` method of the session object. This is + because, although the session object can detect when you call its + data-modifying methods such as ``__setitem__``, ``pop`` and other + (and thus the session knows it needs to reserialize the session + data), when you change a mutable object stored in the session + itself, the session has no way to know that you changed that value. + When in doubt, call ``changed()`` after you've changed sessioning + data. + +Using Alternate Session Factories +--------------------------------- + +At the time of this writing, alternate session factories don't yet +exist. It is our intent that we will soon provide at least one other +session factory which will be easily installable: one that uses the +`Beaker `_ library as a backend. + +Creating Your Own Session Factory +--------------------------------- + +If none of the default or otherwise available sessioning +implementations for :mod:`pyramid` suit you, you may create your own +session object by implementing a :term:`session factory`. Your +session factory should return a :term:`session`. The interfaces for +both types are available in +:class:`pyramid.interfaces.ISessionFactory` and +:class:`pyramid.interfaces.ISession`. You might use the cookie +implementation in the :mod:`pyramid.session` module as inspiration. + diff --git a/docs/narr/webob.rst b/docs/narr/webob.rst index b3bec882e2..15f8da9cf8 100644 --- a/docs/narr/webob.rst +++ b/docs/narr/webob.rst @@ -109,12 +109,12 @@ instance, ``req.if_modified_since`` returns a `datetime Special Attributes Added to the Request by :mod:`pyramid` ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -In addition to the standard :term:`WebOb` attributes, -:mod:`pyramid` adds special attributes to every request: -``context``, ``registry``, ``root``, ``subpath``, ``traversed``, -``view_name``, ``virtual_root`` , and ``virtual_root_path``. These -attributes are documented further within the -:class:`pyramid.request.Request` API documentation. +In addition to the standard :term:`WebOb` attributes, :mod:`pyramid` +adds special attributes to every request: ``context``, ``registry``, +``root``, ``subpath``, ``traversed``, ``view_name``, ``virtual_root`` +, ``virtual_root_path``, and ``session``. These attributes are +documented further within the :class:`pyramid.request.Request` API +documentation. .. index:: single: request URLs diff --git a/pyramid/interfaces.py b/pyramid/interfaces.py index 204d713b40..e1d7491b52 100644 --- a/pyramid/interfaces.py +++ b/pyramid/interfaces.py @@ -424,7 +424,7 @@ def changed(): def __getitem__(key): """Get a value for a key - A KeyError is raised if there is no value for the key. + A ``KeyError`` is raised if there is no value for the key. """ def get(key, default=None): @@ -436,7 +436,7 @@ def get(key, default=None): def __delitem__(key): """Delete a value from the mapping using the key. - A KeyError is raised if there is no value for the key. + A ``KeyError`` is raised if there is no value for the key. """ def __setitem__(key, value): @@ -470,17 +470,17 @@ def update(d): " Update D from E: for k in E.keys(): D[k] = E[k]" def setdefault(key, default=None): - "D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D" + " D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D " def pop(k, *args): """remove specified key and return the corresponding value - *args may contain a single default value, or may not be supplied. + ``*args`` may contain a single default value, or may not be supplied. If key is not found, default is returned if given, otherwise - KeyError is raised""" + ``KeyError`` is raised""" def popitem(): """remove and return some (key, value) pair as a - 2-tuple; but raise KeyError if mapping is empty""" + 2-tuple; but raise ``KeyError`` if mapping is empty""" def __len__(): """Return the number of items in the session. diff --git a/pyramid/request.py b/pyramid/request.py index b3e398b6d8..7ffdb74959 100644 --- a/pyramid/request.py +++ b/pyramid/request.py @@ -151,7 +151,7 @@ def session(self): if factory is None: raise ConfigurationError( 'No session factory registered ' - '(use ``config.add_session_factory``)') + '(see the Session Objects chapter of the documentation)') return factory(self) # override default WebOb "environ['adhoc_attr']" mutation behavior diff --git a/pyramid/session.py b/pyramid/session.py index 88eb8720b0..50f071398d 100644 --- a/pyramid/session.py +++ b/pyramid/session.py @@ -49,6 +49,52 @@ def InsecureCookieSessionFactoryConfig( cookie_httponly=False, cookie_on_exception=False, ): + """ + Configure a :term:`session factory` which will provide insecure + (but signed) cookie-based sessions. The return value of this + function is a :term:`session factory`, which may be provided as + the ``session_factory`` argument of a + :class:`pyramid.configuration.Configurator` constructor, or used + as the ``session_factory`` argument of the + :meth:`pyramid.configuration.Configurator.set_session_factory` + method. + + The session factory returned by this function will create sessions + which are limited to storing fewer than 4000 bytes of data (as the + payload must fit into a single cookie). + + Parameters: + + ``secret`` + A string which is used to sign the cookie. + + ``timeout`` + A number of seconds of inactivity before a session times out. + + ``cookie_name`` + The name of the cookie used for sessioning. Default: ``session``. + + ``cookie_max_age`` + The maximum age of the cookie used for sessioning (in seconds). + Default: ``None`` (browser scope). + + ``cookie_path`` + The path used for the session cookie. Default: ``/``. + + ``cookie_domain`` + The domain used for the session cookie. Default: ``None`` (no domain). + + ``cookie_secure`` + The 'secure' flag of the session cookie. Default: ``False``. + + ``cookie_httponly`` + The 'httpOnly' flag of the session cookie. Default: ``False``. + + ``cookie_on_exception`` + If ``True``, set a session cookie even if an exception occurs + while rendering a view. Default: ``False``. + + """ class InsecureCookieSessionFactory(dict): """ Dictionary-like session object """