web/lib/django/contrib/sessions/backends/base.py
changeset 0 0d40e90630ef
equal deleted inserted replaced
-1:000000000000 0:0d40e90630ef
       
     1 import base64
       
     2 import os
       
     3 import random
       
     4 import sys
       
     5 import time
       
     6 from datetime import datetime, timedelta
       
     7 try:
       
     8     import cPickle as pickle
       
     9 except ImportError:
       
    10     import pickle
       
    11 
       
    12 from django.conf import settings
       
    13 from django.core.exceptions import SuspiciousOperation
       
    14 from django.utils.hashcompat import md5_constructor
       
    15 
       
    16 # Use the system (hardware-based) random number generator if it exists.
       
    17 if hasattr(random, 'SystemRandom'):
       
    18     randrange = random.SystemRandom().randrange
       
    19 else:
       
    20     randrange = random.randrange
       
    21 MAX_SESSION_KEY = 18446744073709551616L     # 2 << 63
       
    22 
       
    23 class CreateError(Exception):
       
    24     """
       
    25     Used internally as a consistent exception type to catch from save (see the
       
    26     docstring for SessionBase.save() for details).
       
    27     """
       
    28     pass
       
    29 
       
    30 class SessionBase(object):
       
    31     """
       
    32     Base class for all Session classes.
       
    33     """
       
    34     TEST_COOKIE_NAME = 'testcookie'
       
    35     TEST_COOKIE_VALUE = 'worked'
       
    36 
       
    37     def __init__(self, session_key=None):
       
    38         self._session_key = session_key
       
    39         self.accessed = False
       
    40         self.modified = False
       
    41 
       
    42     def __contains__(self, key):
       
    43         return key in self._session
       
    44 
       
    45     def __getitem__(self, key):
       
    46         return self._session[key]
       
    47 
       
    48     def __setitem__(self, key, value):
       
    49         self._session[key] = value
       
    50         self.modified = True
       
    51 
       
    52     def __delitem__(self, key):
       
    53         del self._session[key]
       
    54         self.modified = True
       
    55 
       
    56     def keys(self):
       
    57         return self._session.keys()
       
    58 
       
    59     def items(self):
       
    60         return self._session.items()
       
    61 
       
    62     def get(self, key, default=None):
       
    63         return self._session.get(key, default)
       
    64 
       
    65     def pop(self, key, *args):
       
    66         self.modified = self.modified or key in self._session
       
    67         return self._session.pop(key, *args)
       
    68 
       
    69     def setdefault(self, key, value):
       
    70         if key in self._session:
       
    71             return self._session[key]
       
    72         else:
       
    73             self.modified = True
       
    74             self._session[key] = value
       
    75             return value
       
    76 
       
    77     def set_test_cookie(self):
       
    78         self[self.TEST_COOKIE_NAME] = self.TEST_COOKIE_VALUE
       
    79 
       
    80     def test_cookie_worked(self):
       
    81         return self.get(self.TEST_COOKIE_NAME) == self.TEST_COOKIE_VALUE
       
    82 
       
    83     def delete_test_cookie(self):
       
    84         del self[self.TEST_COOKIE_NAME]
       
    85 
       
    86     def encode(self, session_dict):
       
    87         "Returns the given session dictionary pickled and encoded as a string."
       
    88         pickled = pickle.dumps(session_dict, pickle.HIGHEST_PROTOCOL)
       
    89         pickled_md5 = md5_constructor(pickled + settings.SECRET_KEY).hexdigest()
       
    90         return base64.encodestring(pickled + pickled_md5)
       
    91 
       
    92     def decode(self, session_data):
       
    93         encoded_data = base64.decodestring(session_data)
       
    94         pickled, tamper_check = encoded_data[:-32], encoded_data[-32:]
       
    95         if md5_constructor(pickled + settings.SECRET_KEY).hexdigest() != tamper_check:
       
    96             raise SuspiciousOperation("User tampered with session cookie.")
       
    97         try:
       
    98             return pickle.loads(pickled)
       
    99         # Unpickling can cause a variety of exceptions. If something happens,
       
   100         # just return an empty dictionary (an empty session).
       
   101         except:
       
   102             return {}
       
   103 
       
   104     def update(self, dict_):
       
   105         self._session.update(dict_)
       
   106         self.modified = True
       
   107 
       
   108     def has_key(self, key):
       
   109         return self._session.has_key(key)
       
   110 
       
   111     def values(self):
       
   112         return self._session.values()
       
   113 
       
   114     def iterkeys(self):
       
   115         return self._session.iterkeys()
       
   116 
       
   117     def itervalues(self):
       
   118         return self._session.itervalues()
       
   119 
       
   120     def iteritems(self):
       
   121         return self._session.iteritems()
       
   122 
       
   123     def clear(self):
       
   124         # To avoid unnecessary persistent storage accesses, we set up the
       
   125         # internals directly (loading data wastes time, since we are going to
       
   126         # set it to an empty dict anyway).
       
   127         self._session_cache = {}
       
   128         self.accessed = True
       
   129         self.modified = True
       
   130 
       
   131     def _get_new_session_key(self):
       
   132         "Returns session key that isn't being used."
       
   133         # The random module is seeded when this Apache child is created.
       
   134         # Use settings.SECRET_KEY as added salt.
       
   135         try:
       
   136             pid = os.getpid()
       
   137         except AttributeError:
       
   138             # No getpid() in Jython, for example
       
   139             pid = 1
       
   140         while 1:
       
   141             session_key = md5_constructor("%s%s%s%s"
       
   142                     % (randrange(0, MAX_SESSION_KEY), pid, time.time(),
       
   143                        settings.SECRET_KEY)).hexdigest()
       
   144             if not self.exists(session_key):
       
   145                 break
       
   146         return session_key
       
   147 
       
   148     def _get_session_key(self):
       
   149         if self._session_key:
       
   150             return self._session_key
       
   151         else:
       
   152             self._session_key = self._get_new_session_key()
       
   153             return self._session_key
       
   154 
       
   155     def _set_session_key(self, session_key):
       
   156         self._session_key = session_key
       
   157 
       
   158     session_key = property(_get_session_key, _set_session_key)
       
   159 
       
   160     def _get_session(self, no_load=False):
       
   161         """
       
   162         Lazily loads session from storage (unless "no_load" is True, when only
       
   163         an empty dict is stored) and stores it in the current instance.
       
   164         """
       
   165         self.accessed = True
       
   166         try:
       
   167             return self._session_cache
       
   168         except AttributeError:
       
   169             if self._session_key is None or no_load:
       
   170                 self._session_cache = {}
       
   171             else:
       
   172                 self._session_cache = self.load()
       
   173         return self._session_cache
       
   174 
       
   175     _session = property(_get_session)
       
   176 
       
   177     def get_expiry_age(self):
       
   178         """Get the number of seconds until the session expires."""
       
   179         expiry = self.get('_session_expiry')
       
   180         if not expiry:   # Checks both None and 0 cases
       
   181             return settings.SESSION_COOKIE_AGE
       
   182         if not isinstance(expiry, datetime):
       
   183             return expiry
       
   184         delta = expiry - datetime.now()
       
   185         return delta.days * 86400 + delta.seconds
       
   186 
       
   187     def get_expiry_date(self):
       
   188         """Get session the expiry date (as a datetime object)."""
       
   189         expiry = self.get('_session_expiry')
       
   190         if isinstance(expiry, datetime):
       
   191             return expiry
       
   192         if not expiry:   # Checks both None and 0 cases
       
   193             expiry = settings.SESSION_COOKIE_AGE
       
   194         return datetime.now() + timedelta(seconds=expiry)
       
   195 
       
   196     def set_expiry(self, value):
       
   197         """
       
   198         Sets a custom expiration for the session. ``value`` can be an integer,
       
   199         a Python ``datetime`` or ``timedelta`` object or ``None``.
       
   200 
       
   201         If ``value`` is an integer, the session will expire after that many
       
   202         seconds of inactivity. If set to ``0`` then the session will expire on
       
   203         browser close.
       
   204 
       
   205         If ``value`` is a ``datetime`` or ``timedelta`` object, the session
       
   206         will expire at that specific future time.
       
   207 
       
   208         If ``value`` is ``None``, the session uses the global session expiry
       
   209         policy.
       
   210         """
       
   211         if value is None:
       
   212             # Remove any custom expiration for this session.
       
   213             try:
       
   214                 del self['_session_expiry']
       
   215             except KeyError:
       
   216                 pass
       
   217             return
       
   218         if isinstance(value, timedelta):
       
   219             value = datetime.now() + value
       
   220         self['_session_expiry'] = value
       
   221 
       
   222     def get_expire_at_browser_close(self):
       
   223         """
       
   224         Returns ``True`` if the session is set to expire when the browser
       
   225         closes, and ``False`` if there's an expiry date. Use
       
   226         ``get_expiry_date()`` or ``get_expiry_age()`` to find the actual expiry
       
   227         date/age, if there is one.
       
   228         """
       
   229         if self.get('_session_expiry') is None:
       
   230             return settings.SESSION_EXPIRE_AT_BROWSER_CLOSE
       
   231         return self.get('_session_expiry') == 0
       
   232 
       
   233     def flush(self):
       
   234         """
       
   235         Removes the current session data from the database and regenerates the
       
   236         key.
       
   237         """
       
   238         self.clear()
       
   239         self.delete()
       
   240         self.create()
       
   241 
       
   242     def cycle_key(self):
       
   243         """
       
   244         Creates a new session key, whilst retaining the current session data.
       
   245         """
       
   246         data = self._session_cache
       
   247         key = self.session_key
       
   248         self.create()
       
   249         self._session_cache = data
       
   250         self.delete(key)
       
   251 
       
   252     # Methods that child classes must implement.
       
   253 
       
   254     def exists(self, session_key):
       
   255         """
       
   256         Returns True if the given session_key already exists.
       
   257         """
       
   258         raise NotImplementedError
       
   259 
       
   260     def create(self):
       
   261         """
       
   262         Creates a new session instance. Guaranteed to create a new object with
       
   263         a unique key and will have saved the result once (with empty data)
       
   264         before the method returns.
       
   265         """
       
   266         raise NotImplementedError
       
   267 
       
   268     def save(self, must_create=False):
       
   269         """
       
   270         Saves the session data. If 'must_create' is True, a new session object
       
   271         is created (otherwise a CreateError exception is raised). Otherwise,
       
   272         save() can update an existing object with the same key.
       
   273         """
       
   274         raise NotImplementedError
       
   275 
       
   276     def delete(self, session_key=None):
       
   277         """
       
   278         Deletes the session data under this key. If the key is None, the
       
   279         current session key value is used.
       
   280         """
       
   281         raise NotImplementedError
       
   282 
       
   283     def load(self):
       
   284         """
       
   285         Loads the session data and returns a dictionary.
       
   286         """
       
   287         raise NotImplementedError