[Zope3-checkins] CVS: Zope3/lib/python/Zope/App/OFS/Services/SessionService - configure.zcml:1.1 __init__.py:1.1 README.txt:1.1 ISessions.py:1.1 CookieSessionService.py:1.1

Itamar Shtull-Trauring zope@itamarst.org
Wed, 4 Dec 2002 16:46:17 -0500


Update of /cvs-repository/Zope3/lib/python/Zope/App/OFS/Services/SessionService
In directory cvs.zope.org:/tmp/cvs-serv16834/SessionService

Added Files:
	configure.zcml __init__.py README.txt ISessions.py 
	CookieSessionService.py 
Log Message:
initial session interfaces and simplistic session service implementation


=== Added File Zope3/lib/python/Zope/App/OFS/Services/SessionService/configure.zcml ===
<zopeConfigure
   xmlns='http://namespaces.zope.org/zope'
   xmlns:browser='http://namespaces.zope.org/browser'
   xmlns:service='http://namespaces.zope.org/service'
>
  <serviceType
      id="SessionService" 
      interface=".ISessions.ISessionService" />

  <content class=".CookieSessionService.">
    <require
        permission="Zope.Public"
        interface=".ISessions.ISessionService." />
    <factory
        id="ISessionService"
        permission="Zope.ManageServices" />
  <implements interface="Zope.App.OFS.Annotation.IAttributeAnnotatable." /> 
  </content>

  <browser:menuItem menu="add_component" for="Zope.App.OFS.Container.IAdding." 
     action="ISessionService"  title='Cookie Session Service'
     description='Simplistic session support using cookies' />

</zopeConfigure>


=== Added File Zope3/lib/python/Zope/App/OFS/Services/SessionService/__init__.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.
# 
##############################################################################

"""Session support for HTTP."""

from Zope.ComponentArchitecture import getService


def getSessionDataObject(context, request, name):
    """Get data object from appropriate ISessionDataManager."""
    service = getService(context, "SessionService")
    sid = service.getSessionId(request)
    return service.getDataManager(name).getDataObject(sid)


=== Added File Zope3/lib/python/Zope/App/OFS/Services/SessionService/README.txt ===
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.

ISessionService provides this unique id. ISessionDataManagers attach data
objects of some sort to a specific session id. This data object may be
a Persistent ZODB object, or an object that stores data in a RDBMS using
the session id as the key.

ISessionDataManagers are registered with ISessionServices using a name,
which ought to be unique for each application. Thus you can have multiple
data objects registered with a single session.

ISessionServices may choose to expire sessions. In this case they
should notify all ISessionDataManagers registered with them and
invalidate the attached data objects. Likewise ISessionDataManagers
probably want to expire data.


--
$Id: README.txt,v 1.1 2002/12/04 21:46:16 itamar Exp $



=== Added File Zope3/lib/python/Zope/App/OFS/Services/SessionService/ISessions.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.
# 
##############################################################################

"""Interfaces for session service."""

from Interface import Interface


class ISessionService(Interface):
    """Manages sessions - fake state over multiple browser requests."""

    def getSessionId(browserRequest):
        """Return sessionId for the given request.

        If the request doesn't have an attached sessionId a new one will
        be generated.
        
        This will do whatever is possible to do the HTTP request to ensure
        the session id will be preserved. Depending on the specific
        method, further action might be necessary on the part of the user.
        See the documentation for the specific implementation and its
        interfaces.
        """

    def invalidate(sessionId):
        """Destroy all attached data and invalidate the session."""
    
    def getDataManager(name):
        """Get the ISessionDataManager for given name.

        Raises KeyError if name is unknown.
        """


class IConfigureSessionService(Interface):
    """Configuration for ISessionService."""
    
    def registerDataManager(name, dataManager):
        """Register ISessionDataManager under given name.

        Raises ValueError if a data manager with that name already
        """

    def unregisterDataManager(name):
        """Remove ISessionDataManager."""


class ISessionDataManager(Interface):
    """Stores data objects for sessions.

    In general, a data object will be stored for each sessionId requested from
    the ISessionDataManager.
    
    Sub-interfaces should specify the interface(s) implemented by the data
    objects.
    """
    
    def getDataObject(sessionId):
        """Returns data attached to session.

        Should create new object if this is a new session.
        """

    def deleteData(sessionId):
        """Delete data attached to session."""


=== Added File Zope3/lib/python/Zope/App/OFS/Services/SessionService/CookieSessionService.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

# Zope3 imports
from Persistence import Persistent
from Persistence.PersistentDict import PersistentDict
from Zope.Server.HTTP.http_date import build_http_date

# Sibling imports
from ISessions import ISessionService, IConfigureSessionService


cookieSafeTrans = string.maketrans("+/", "-.")


class CookieSessionService(Persistent):
    """Session service implemented using cookies."""

    __implements__ = Persistent.__implements__, ISessionService, IConfigureSessionService
    
    def __init__(self):
        self.dataManagers = PersistentDict()
        self.namespace = "zope3-cs-%x" % (int(time.time()) - 1000000000)
    
    def generateUniqueId(self):
        """Generate a new, random, unique id."""
        data = "%.20f%.20f%.20f" % (random.random(), time.time(), time.clock())
        digest = sha.sha(data).digest()
        return digest.encode("base64")[:-2].translate(cookieSafeTrans)

    def getRequestId(self, request):
        """Return the sessionId encoded in request or None if it's non-existent."""
        sid = request.cookies.get(self.namespace)
        if sid is None or len(sid) != 27:
            return None
        else:
            return sid

    def setRequestId(self, request, id):
        """Set cookie with id on request."""
        request.response.setCookie(self.namespace, id, expires=build_http_date(time.time() + 1800))
    

    #####################################
    # Implementation of ISessionService #
    
    def getSessionId(self, request):
        sid = self.getRequestId(request)
        if sid is None:
            sid = self.generateUniqueId()
        self.setRequestId(request, sid)
        return sid

    def invalidate(self, sessionId):
        for d in self.dataManagers.values():
            d.deleteData(sessionId)

    def getDataManager(self, name):
        return self.dataManagers[name]

    def registerDataManager(self, name, dataManager):
        if self.dataManagers.has_key(name):
            raise ValueError, "DataManager already registered with name %r" % name
        self.dataManagers[name] = dataManager

    def unregisterDataManager(self, name):
        del self.dataManagers[name]