~launchpad-pqm/launchpad/devel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# Copyright 2009-2010 Canonical Ltd.  This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).

"""Simple paste-y session manager tuned for the needs of launchpad-loggerhead.
"""

import pickle

from paste.auth.cookie import (
    AuthCookieHandler,
    AuthCookieSigner,
    )


class MyAuthCookieSigner(AuthCookieSigner):
    """Fix a bug in AuthCookieSigner."""

    def sign(self, content):
        # XXX 2008-01-13 Michael Hudson: paste.auth.cookie generates bogus
        # cookies when the value is long:
        # http://trac.pythonpaste.org/pythonpaste/ticket/257.  This is fixed
        # now, so when a new version is released and packaged we can remove
        # this class.
        r = AuthCookieSigner.sign(self, content)
        return r.replace('\n', '')


class SessionHandler(object):
    """Middleware that provides a cookie-based session.

    The session dict is stored, pickled (and HMACed), in a cookie, so don't
    store very much in the session!
    """

    def __init__(self, application, session_var, secret=None):
        """Initialize a SessionHandler instance.

        :param application: This is the wrapped application which will have
            access to the ``environ[session_var]`` dictionary managed by this
            middleware.
        :param session_var: The key under which to store the session
            dictionary in the environment.
        :param secret: A secret value used for signing the cookie.  If not
            supplied, a new secret will be used for each instantiation of the
            SessionHandler.
        """
        self.application = application
        self.cookie_handler = AuthCookieHandler(
            self._process, scanlist=[session_var],
            signer=MyAuthCookieSigner(secret))
        self.session_var = session_var

    def __call__(self, environ, start_response):
        # We need to put the request through the cookie handler first, so we
        # can access the validated string in the environ in `_process` below.
        return self.cookie_handler(environ, start_response)

    def _process(self, environ, start_response):
        """Process a request.

        AuthCookieHandler takes care of getting the text value of the session
        in and out of the cookie (and validating the text using HMAC) so we
        just need to convert that string to and from a real dictionary using
        pickle.
        """
        if self.session_var in environ:
            session = pickle.loads(environ[self.session_var])
        else:
            session = {}
        existed = bool(session)
        environ[self.session_var] = session
        def response_hook(status, response_headers, exc_info=None):
            session = environ.pop(self.session_var)
            # paste.auth.cookie does not delete cookies (see
            # http://trac.pythonpaste.org/pythonpaste/ticket/139).  A
            # reasonable workaround is to make the value empty.  Therefore,
            # we explicitly set the value in the session (to be encrypted)
            # if the value is non-empty *or* if it was non-empty at the start
            # of the request.
            if existed or session:
                environ[self.session_var] = pickle.dumps(session)
            return start_response(status, response_headers, exc_info)
        return self.application(environ, response_hook)