session.py :  » Web-Server » Jon-Python » jonpy-0.08 » jon » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Web Server » Jon Python 
Jon Python » jonpy 0.08 » jon » session.py
# $Id: session.py,v 1.14 2010/03/20 18:29:22 jribbens Exp $

import time, hmac, Cookie, re, random, os, errno, fcntl
try:
  import hashlib
  sha = hashlib.sha1
  shanew = hashlib.sha1
except ImportError:
  import sha
  shanew = sha.new
try:
  import cPickle as pickle
except ImportError:
  import pickle


class Error(Exception):
  pass


class Session(dict):
  def _make_hash(self, sid, secret):
    """Create a hash for 'sid'
    
    This function may be overridden by subclasses."""
    return hmac.new(secret, sid, sha).hexdigest()[:8]

  def _create(self, secret):
    """Create a new session ID and, optionally hash
    
    This function must insert the new session ID (which must be 8 hexadecimal
    characters) into self["id"].

    It may optionally insert the hash into self["hash"]. If it doesn't, then
    _make_hash will automatically be called later.

    This function may be overridden by subclasses.
    """
    rnd = str(time.time()) + str(random.random()) + \
      str(self._req.environ.get("UNIQUE_ID"))
    self["id"] = shanew(rnd).hexdigest()[:8]

  def _load(self):
    """Load the session dictionary from somewhere
    
    This function may be overridden by subclasses.
    It should return 1 if the load was successful, or 0 if the session could
    not be found. Any other type of error should raise an exception as usual."""
    return 1

  def save(self):
    """Save the session dictionary to somewhere
    
    This function may be overridden by subclasses."""
    pass

  def tidy():
    pass
  tidy = staticmethod(tidy)

  def __init__(self, req, secret, cookie="jonsid", url=0, root="",
    referer=None, sid=None, shash=None, secure=0, domain=None):
    dict.__init__(self)
    self["id"] = None
    self._req = req
    self.relocated = 0
    self.new = 0
    
    # try and determine existing session id
    
    if sid is not None:
      self["id"] = sid
      if shash is None:
        self["hash"] = self._make_hash(self["id"], secret)
      else:
        self["hash"] = shash
        if self["hash"] != self._make_hash(self["id"], secret):
          self["id"] = None

    if cookie and cookie in self._req.cookies:
      self["id"] = self._req.cookies[cookie].value[:8]
      self["hash"] = self._req.cookies[cookie].value[8:]
      if self["hash"] != self._make_hash(self["id"], secret):
        self["id"] = None

    if url:
      for i in range(1, 4):
        requrl = self._req.environ.get("REDIRECT_" * i + "SESSION")
        if requrl:
          break
      if requrl and self["id"] is None:
        self["id"] = requrl[:8]
        self["hash"] = requrl[8:]
        if self["hash"] != self._make_hash(self["id"], secret):
          self["id"] = None

    # check the session

    if referer:
      if "HTTP_REFERER" in self._req.environ:
        if self._req.environ["HTTP_REFERER"].find(referer) == -1:
          self["id"] = None

    # try and load the session

    if self["id"] is not None:
      if not self._load():
        self["id"] = None

    # if no session was available and loaded, create a new one

    if self["id"] is None:
      if "hash" in self:
        del self["hash"]
      self.created = time.time()
      self.new = 1
      self._create(secret)
      if "hash" not in self:
        self["hash"] = self._make_hash(self["id"], secret)
      if cookie:
        c = Cookie.SimpleCookie()
        c[cookie] = self["id"] + self["hash"]
        c[cookie]["path"] = root + "/"
        if secure:
          c[cookie]["secure"] = 1
        if domain:
          c[cookie]["domain"] = domain
        self._req.add_header("Set-Cookie", c[cookie].OutputString())

    # if using url-based sessions, redirect if necessary

    if url:
      if not requrl or self["id"] != requrl[:8] or self["hash"] != requrl[8:]:
        requrl = self._req.environ["REQUEST_URI"][len(root):]
        requrl = re.sub("^/[A-Fa-f0-9]{16}/", "/", requrl)
        self._req.add_header("Location", "http://" + 
          self._req.environ["SERVER_NAME"] + root + "/" + self["id"] +
          self["hash"] + requrl)
        self.relocated = 1
      self.surl = root + "/" + self["id"] + self["hash"] + "/"


class MemorySession(Session):
  _sessions = {}

  def _create(self, secret):
    while 1:
      Session._create(self, secret)
      if self["id"] in self._sessions:
        continue
      self._sessions[self["id"]] = {"created": self.created,
        "updated": self.created, "data": {}}
      break
  
  def _load(self):
    try:
      sess = self._sessions[self["id"]]
    except KeyError:
      return 0
    self.created = sess["created"]
    self.update(sess["data"])
    return 1

  def save(self):
    sess = self._sessions[self["id"]]
    sess["updated"] = time.time()
    sess["data"] = self.copy()

  def tidy(cls, max_idle=0, max_age=0):
    now = time.time()
    for k in cls._sessions:
      if (max_age and k["created"] < now - max_age) or \
        (max_idle and k["updated"] < now - max_idle):
        del cls._sessions[k]
  tidy = classmethod(tidy)


class FileSession(Session):
  def _create(self, secret):
    while 1:
      Session._create(self, secret)
      try:
        os.lstat("%s/%s" % (self.basedir, self["id"][:2]))
      except OSError, x:
        if x[0] == errno.ENOENT:
          os.mkdir("%s/%s" % (self.basedir, self["id"][:2]), 0700)
      try:
        fd = os.open("%s/%s/%s" % (self.basedir, self["id"][:2],
          self["id"][2:]), os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0700)
      except OSError, x:
        if x[0] != errno.EEXIST:
          raise
        continue
      f = os.fdopen(fd, "wb")
      f.write("%d\n" % self.created)
      pickle.dump({}, f, 1)
      f.flush()
      break

  def _load(self):
    try:
      f = open("%s/%s/%s" % (self.basedir, self["id"][:2], self["id"][2:]),
        "r+b")
    except IOError, x:
      if x[0] != errno.ENOENT:
        raise
      return 0
    fcntl.lockf(f.fileno(), fcntl.LOCK_EX)
    self.created = int(f.readline().strip())
    self.update(pickle.load(f))
    return 1

  def save(self):
    f = open("%s/%s/%s" % (self.basedir, self["id"][:2], self["id"][2:]), "r+b")
    fcntl.lockf(f.fileno(), fcntl.LOCK_EX)
    f.write("%d\n" % self.created)
    pickle.dump(self.copy(), f, 1)
    f.flush()
    f.truncate()
    
  def tidy(cls, max_idle=0, max_age=0, basedir=None):
    if not max_idle and not max_age:
      return
    basedir = cls._find_basedir(basedir)
    now = time.time()
    for d in os.listdir(basedir):
      if len(d) != 2 or not d.isalnum():
        continue
      for f in os.listdir("%s/%s" % (basedir, d)):
        if len(f) != 6 or not f.isalnum():
          continue
        p = "%s/%s/%s" % (basedir, d, f)
        if (max_idle and os.lstat(p).st_mtime < now - max_idle) or \
          (max_age and int(open(p, "rb").readline().strip()) < now - max_age):
          os.remove(p)
  tidy = classmethod(tidy)

  def _find_basedir(basedir):
    if basedir is None:
      basedir = os.environ.get("TMPDIR", "/tmp")
    while basedir[-1] == "/":
      basedir = basedir[:-1]
    basedir = "%s/jon-sessions-%d" % (basedir, os.getuid())
    try:
      st = os.lstat(basedir)
      if st[4] != os.getuid():
        raise Error("Sessions basedir is not owned by user %d" % os.getuid())
    except OSError, x:
      if x[0] == errno.ENOENT:
        os.mkdir(basedir, 0700)
    return basedir
  _find_basedir = staticmethod(_find_basedir)

  def __init__(self, req, secret, basedir=None, **kwargs):
    self.basedir = self._find_basedir(basedir)
    Session.__init__(self, req, secret, **kwargs)


class GenericSQLSession(Session):
  def _create(self, secret):
    while 1:
      Session._create(self, secret)
      self["hash"] = self._make_hash(self["id"], secret)
      try:
        self.dbc.execute("INSERT INTO %s (ID,hash,created,updated,data)"
          " VALUES (%%s,%%s,%%s,%%s,%%s)" % (self.table,),
          (self["id"], self["hash"], int(self.created), int(self.created),
          pickle.dumps({}, 1)))
        self.dbc.execute("COMMIT")
      except self.dbc.IntegrityError:
        pass
      else:
        break

  def _load(self):
    self.dbc.execute("SELECT created,data FROM %s WHERE ID=%%s" % (self.table,),
      (self["id"],))
    if self.dbc.rowcount == 0:
      return 0
    row = self.dbc.fetchone()
    self.created = row[0]
    self.update(pickle.loads(row[1]))
    return 1

  def save(self):
    self.dbc.execute("UPDATE %s SET updated=%%s,data=%%s"
      " WHERE ID=%%s" % (self.table,), (int(time.time()),
      pickle.dumps(self.copy(), 1), self["id"]))
    self.dbc.execute("COMMIT")

  def tidy(dbc, table="sessions", max_idle=0, max_age=0):
    now = time.time()
    if max_idle:
      dbc.execute("DELETE FROM %s WHERE updated < %%s" % (table,),
        (now - max_idle,))
    if max_age:
      dbc.execute("DELETE FROM %s WHERE created < %%s" % (table,),
        (now - max_age,))
    if max_idle or max_age:
      dbc.execute("COMMIT")
  tidy = staticmethod(tidy)
    
  def __init__(self, req, secret, dbc, table="sessions", **kwargs):
    self.dbc = dbc
    self.table = table
    Session.__init__(self, req, secret, **kwargs)


class MySQLSession(GenericSQLSession):
  def _create(self, secret):
    self.dbc.execute("LOCK TABLES %s WRITE" % (self.table,))
    while 1:
      Session._create(self, secret)
      self.dbc.execute("SELECT 1 FROM %s WHERE ID=%%s" % (self.table,),
        (long(self["id"], 16),))
      if self.dbc.rowcount == 0:
        break
    self["hash"] = self._make_hash(self["id"], secret)
    self.dbc.execute("INSERT INTO %s (ID,hash,created,updated,data) VALUES " \
      "(%%s,%%s,%%s,%%s,%%s)" % (self.table,),
      (long(self["id"], 16), self["hash"], int(self.created),
      int(self.created), pickle.dumps({}, 1)))
    self.dbc.execute("UNLOCK TABLES")

  def _load(self):
    self.dbc.execute("SELECT created,data FROM %s WHERE ID=%%s" % (self.table,),
      (long(self["id"], 16),))
    if self.dbc.rowcount == 0:
      return 0
    row = self.dbc.fetchone()
    self.created = row[0]
    self.update(pickle.loads(row[1]))
    return 1

  def save(self):
    self.dbc.execute("UPDATE %s SET updated=%%s,data=%%s"
      " WHERE ID=%%s" % (self.table,), (int(time.time()),
      pickle.dumps(self.copy(), 1), long(self["id"], 16)))


SQLSession = MySQLSession # backwards compatibility name
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.