Ticket #7008: appengine_session_backend.1.jezdez.diff

File appengine_session_backend.1.jezdez.diff, 5.8 KB (added by jezdez, 7 years ago)

first implementation, including documentation and tests

  • django/contrib/sessions/backends/appengine.py

     
     1r"""
     2>>> from django.conf import settings
     3>>> from django.contrib.sessions.backends.appengine import SessionStore as AppEngineSession
     4>>> from django.contrib.sessions.backends.base import SessionBase
     5
     6>>> appengine_session = AppEngineSession()
     7>>> appengine_session.modified
     8False
     9>>> appengine_session['cat'] = "dog"
     10>>> appengine_session.modified
     11True
     12>>> appengine_session.pop('cat')
     13'dog'
     14>>> appengine_session.pop('some key', 'does not exist')
     15'does not exist'
     16>>> appengine_session.save()
     17>>> appengine_session.exists(appengine_session.session_key)
     18True
     19>>> appengine_session.delete(appengine_session.session_key)
     20>>> appengine_session.exists(appengine_session.session_key)
     21False
     22>>> s = SessionBase()
     23>>> s._session['some key'] = 'exists' # Pre-populate the session with some data
     24>>> s.accessed = False   # Reset to pretend this wasn't accessed previously
     25
     26>>> s.accessed, s.modified
     27(False, False)
     28
     29>>> s.pop('non existant key', 'does not exist')
     30'does not exist'
     31>>> s.accessed, s.modified
     32(True, False)
     33
     34>>> s.setdefault('foo', 'bar')
     35'bar'
     36>>> s.setdefault('foo', 'baz')
     37'bar'
     38
     39>>> s.accessed = False  # Reset the accessed flag
     40
     41>>> s.pop('some key')
     42'exists'
     43>>> s.accessed, s.modified
     44(True, True)
     45
     46>>> s.pop('some key', 'does not exist')
     47'does not exist'
     48"""
     49import md5
     50import sys
     51import base64
     52import datetime
     53import cPickle as pickle
     54
     55from django.conf import settings
     56from django.contrib.sessions.backends.base import SessionBase
     57from django.core.exceptions import SuspiciousOperation
     58
     59# add the weird appengine location (here: MacOSX & Windows) to the pythonpath
     60# if script is started from the command shell, not needed if you are running
     61# the your app with dev_appserver.py
     62if __name__ == '__main__':
     63    sys.path.insert(0, '/usr/local/google_appengine/', )
     64   
     65    # needed for using datastore on the commandline
     66    from google.appengine.api import apiproxy_stub_map
     67    from google.appengine.api import datastore_file_stub
     68    apiproxy_stub_map.apiproxy = apiproxy_stub_map.APIProxyStubMap()
     69    apiproxy_stub_map.apiproxy.RegisterStub('datastore_v3',
     70        datastore_file_stub.DatastoreFileStub('whatever',
     71            '/dev/null', '/dev/null'))
     72
     73try:
     74    from google.appengine.ext import db
     75except ImportError:
     76    raise ImportError("The app engine datastore module could not be found. Please add it to your PYTHONPATH (e.g. '/usr/local/google_appengine/' on MacOS)")
     77
     78class Session(db.Model):
     79    session_key = db.StringProperty(required=True)
     80    session_data = db.TextProperty(required=True)
     81    expire_date = db.DateTimeProperty(required=True)
     82
     83    def get_decoded(self):
     84        encoded_data = base64.decodestring(self.session_data)
     85        pickled, tamper_check = encoded_data[:-32], encoded_data[-32:]
     86        if md5.new(pickled + settings.SECRET_KEY).hexdigest() != tamper_check:
     87            raise SuspiciousOperation, "User tampered with session cookie."
     88        try:
     89            return pickle.loads(pickled)
     90        # Unpickling can cause a variety of exceptions. If something happens,
     91        # just return an empty dictionary (an empty session).
     92        except:
     93            return {}
     94
     95class SessionStore(SessionBase):
     96    """
     97    Implements appengine session store
     98    """
     99    def __init__(self, session_key=None):
     100        super(SessionStore, self).__init__(session_key)
     101
     102    def load(self):
     103        try:
     104            s = Session.all().filter(
     105                'session_key =', self.session_key).filter(
     106                    'expire_date >=', datetime.datetime.now()).get()
     107            if not s:
     108                return self.decode(result.session_data)
     109            raise SuspiciousOperation
     110        except SuspiciousOperation:
     111            # Create a new session_key for extra security.
     112            self.session_key = self._get_new_session_key()
     113            self._session_cache = {}
     114
     115            # Save immediately to minimize collision
     116            self.save()
     117            # Ensure the user is notified via a new cookie.
     118            self.modified = True
     119            return {}
     120
     121    def exists(self, session_key):
     122        if Session.all().filter('session_key =', session_key).get():
     123            return True
     124        return False
     125
     126    def save(self):
     127        Session(session_key = self.session_key,
     128                session_data = self.encode(self._session),
     129                expire_date = datetime.datetime.now() + \
     130                    datetime.timedelta(seconds=settings.SESSION_COOKIE_AGE)
     131        ).put()
     132
     133    def delete(self, session_key):
     134        s = Session.all().filter('session_key =', session_key).get()
     135        if s:
     136            s.delete()
     137
     138if __name__ == '__main__':
     139    import doctest
     140    doctest.testmod()
  • docs/sessions.txt

     
    7070    to use file or database sessions directly instead of sending everything
    7171    through the file or database cache backends.
    7272
     73Using appengine-based sessions
     74------------------------------
     75
     76To use appengine-based sessions, set the ``SESSION_ENGINE`` setting to
     77``"django.contrib.sessions.backends.appengine"``.
     78
     79You might also want to add the path to the Google App Engine backend to your
     80``PYTHONPATH``, e.g. ``/usr/local/google_appengine/`` on Mac OS X, if you want
     81to run the code on your computer. This is not needed if you run it with the
     82``dev_appserver.py`` or on Google's infrastructure.
     83
    7384Using sessions in views
    7485=======================
    7586
Back to Top