[Zope3-checkins] CVS: Zope3/src/zope/app/utilities -
session.py:1.1.2.1 session.stx:1.1.2.1 configure.zcml:1.4.20.1
Stuart Bishop
zen at shangri-la.dropbear.id.au
Sat Feb 7 22:19:34 EST 2004
Update of /cvs-repository/Zope3/src/zope/app/utilities
In directory cvs.zope.org:/tmp/cvs-serv15955/src/zope/app/utilities
Modified Files:
Tag: ozzope-session-branch
configure.zcml
Added Files:
Tag: ozzope-session-branch
session.py session.stx
Log Message:
Non-service session design and initial Browser Id Manager Utility
implementation.
=== Added File Zope3/src/zope/app/utilities/session.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Simplistic session service implemented using cookies.
This is more of a demonstration than a full implementation, but it should
work.
"""
# System imports
import sha, time, string, random, hmac
# Zope3 imports
from persistence import Persistent
from persistence.dict import PersistentDict
from zope.server.http.http_date import build_http_date
from zope.component import getService
from zope.interface import implements
from zope.app import zapi
# Sibling imports
from zope.app.interfaces.utilities.session import IBrowserIdManager, IBrowserId
from zope.app.interfaces.utilities.session import ICookieBrowserIdManager
from zope.app.interfaces.container import IContained
cookieSafeTrans = string.maketrans("+/", "-.")
def digestEncode(s):
"""Encode SHA digest for cookie."""
return s.encode("base64")[:-2].translate(cookieSafeTrans)
class BrowserId(str):
"""A browser id"""
implements(IBrowserId)
class CookieBrowserIdManager(Persistent):
"""Session service implemented using cookies."""
implements(ICookieBrowserIdManager, IContained)
__parent__ = __name__ = None
def __init__(self):
self.namespace = "zope3_cs_%x" % (int(time.time()) - 1000000000)
self.secret = "%.20f" % random.random()
self.cookieLifeSeconds = 3600
def generateUniqueId(self):
"""Generate a new, random, unique id."""
data = "%.20f%.20f%.20f" % (random.random(), time.time(), time.clock())
digest = sha.sha(data).digest()
s = digestEncode(digest)
# we store a HMAC of the random value together with it, which makes
# our session ids unforgeable.
mac = hmac.new(s, self.secret, digestmod=sha).digest()
return BrowserId(s + digestEncode(mac))
def getRequestId(self, request):
"""Return the IBrowserId encoded in request or None if it's
non-existent."""
# If there is an id set on the response, use that but don't trust it.
# We need to check the response in case there has already been a new
# session created during the course of this request.
response_cookie = request.response.getCookie(self.namespace)
if response_cookie:
sid = response_cookie['value']
else:
sid = request.cookies.get(self.namespace)
if sid is None or len(sid) != 54:
return None
s, mac = sid[:27], sid[27:]
if (digestEncode(hmac.new(s, self.secret, digestmod=sha).digest())
!= mac):
return None
else:
return BrowserId(sid)
def setRequestId(self, request, id):
"""Set cookie with id on request."""
# XXX Currently, the path is the ApplicationURL. This is reasonable,
# and will be adequate for most purposes.
# A better path to use would be that of the folder that contains
# the service-manager this service is registered within. However,
# that would be expensive to look up on each request, and would
# have to be altered to take virtual hosting into account.
# Seeing as this service instance has a unique namespace for its
# cookie, using ApplicationURL shouldn't be a problem.
# XXX: Fix this as per documentation in ICookieBrowserIdManager
if self.cookieLifeSeconds == 0 or self.cookieLifeSeconds is None:
raise NotImplementedError, \
'Need to implement advanced cookie lifetime'
if self.cookieLifeSeconds:
expires = build_http_date(time.time() + self.cookieLifeSeconds)
else:
expires = None
request.response.setCookie(
self.namespace,
id,
expires=expires,
path=request.getApplicationURL(path_only=True)
)
#######################################
# Implementation of IBrowserIdManager #
def getBrowserId(self, request):
sid = self.getRequestId(request)
if sid is None:
sid = self.generateUniqueId()
self.setRequestId(request, sid)
return sid
def getBrowserId(request):
''' Get the browser id for the given request, setting it if necessary '''
bim = zapi.getUtility(None, IBrowserIdManager)
return bim.getBrowserId(request)
=== Added File Zope3/src/zope/app/utilities/session.stx ===
Session Support
---------------
Sessions allow us to fake state over a stateless protocol - HTTP. We do this
by having a unique identifier stored across multiple HTTP requests, be it
a cookie or some id mangled into the URL.
The IBrowserIdManager Utility provides this unique id. It is responsible
for propagating this id so that future requests from the browser get
the same id (eg. by setting an HTTP cookie)
ISessionDataContainer Utilities provide a mapping interface to store
session data. The ISessionDataContainer is responsible for expiring
data.
Python example::
>>> browser_id = getAdapter(request, IBrowserId))
>>> default_dm = getUtility(None, ISessionDataContainer)
>>> explicit_dm = getUtility(None, ISessionDataContainer,
... 'zopeproducts.fooprod')
>>> session = Session(explicit_dm, browser_id, 'zopeproducts.foorprod')
>>> session['color'] = 'red'
or....
>>> session = zapi.getSession(request, 'zopeproducts.fooprod')
>>> session['color'] = 'red'
Page Template example::
<tal:x condition="exists:session/zopeproducts.fooprod/count">
<tal:x condition="python:
session['zopeproducts.fooprod']['count'] += 1" />
</tal:x>
<tal:x condition="not:exists:session/zopeprodicts.fooprod/count">
<tal:x condition="python:
session['zopeproducts.fooprod']['count'] = 1 />
</tal:x>
<span content="session/zopeproducts.fooprod/count">6</span>
TODO
----
Do we want to provide one or more 'default' ISessionDataContainer's out of the
box (eg. 'memory' and 'zodb', or 'persistant' and 'transient')?
=== Zope3/src/zope/app/utilities/configure.zcml 1.4 => 1.4.20.1 ===
--- Zope3/src/zope/app/utilities/configure.zcml:1.4 Sun Sep 21 13:33:48 2003
+++ Zope3/src/zope/app/utilities/configure.zcml Sat Feb 7 22:19:03 2004
@@ -1,4 +1,40 @@
-<configure xmlns="http://namespaces.zope.org/zope">
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ xmlns:browser="http://namespaces.zope.org/browser">
+
+ <!-- Session Templates -->
+
+ <content class=".session.CookieBrowserIdManager">
+ <implements
+ interface="zope.app.interfaces.utilities.session.IBrowserIdManager" />
+ <implements
+ interface="zope.app.interfaces.utilities.session.ICookieBrowserIdManager"
+ />
+ <implements
+ interface="zope.app.interfaces.services.utility.ILocalUtility" />
+ <implements
+ interface="zope.app.interfaces.annotation.IAttributeAnnotatable" />
+ <require
+ interface="zope.app.interfaces.utilities.session.ICookieBrowserIdManager"
+ permission="zope.Public" />
+ <require
+ set_schema="zope.app.interfaces.utilities.session.ICookieBrowserIdManager"
+ permission="zope.ManageContent" />
+ </content>
+
+ <!-- XXX: Do we want this too?
+ <utility
+ name="Cookie Browser Id Manager"
+ factory=".session.CookieBrowserIdManager"
+ provides="zope.app.interfaces.utilities.session.IBrowserIdManager"
+ permission="zope.Public" />
+ -->
+
+ <adapter
+ for="zope.app.interfaces.utilities.session.IBrowserIdManager"
+ provides="zope.app.interfaces.utilities.session.IBrowserId"
+ factory=".session.getBrowserId"
+ permission="zope.Public" />
<!-- Mutable Schema -->
More information about the Zope3-Checkins
mailing list