Commit 463185cb authored by Chris McDonough's avatar Chris McDonough

Added the capability for browser ids to be encoded in URLs.

When a set of knobs is set on the browser_id_manager "settings" screen,
("look for browser id name in..." and 'automatically encode browser ids..."),
a traversal hook is installed in the browser id manager's container which
causes a) the request to be searched for a browser id name and a browser
id as the first two elements of the URL path and b) for Zope-generated URLs
to contain these path elements.

Various documentation and interface updates.  No interface methods
were harmed in the filming of this checkin, but a few were added or extended
with defaults.
parent 4e42440b
############################################################################
#
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
......@@ -11,11 +11,11 @@
#
############################################################################
__version__='$Revision: 1.14 $'[11:-2]
__version__='$Revision: 1.15 $'[11:-2]
import Globals
from Persistence import Persistent
from ZODB import TimeStamp
from Acquisition import Implicit
from Acquisition import Implicit, aq_base, aq_parent, aq_inner
from AccessControl.Owned import Owned
from AccessControl.Role import RoleManager
from App.Management import Tabs
......@@ -27,6 +27,11 @@ from SessionPermissions import *
from common import DEBUG
import os, time, random, string, binascii, sys, re
from cgi import escape
from urllib import quote
from urlparse import urlparse, urlunparse
from ZPublisher.BeforeTraverse import registerBeforeTraverse, \
unregisterBeforeTraverse, queryBeforeTraverse
import zLOG
b64_trans = string.maketrans('+/', '-.')
b64_untrans = string.maketrans('-.', '+/')
......@@ -39,23 +44,27 @@ _marker = []
constructBrowserIdManagerForm = Globals.DTMLFile('dtml/addIdManager',globals())
BROWSERID_MANAGER_NAME = 'browser_id_manager'# imported by SessionDataManager
ALLOWED_BID_NAMESPACES = ('form', 'cookies', 'url')
ADD_BROWSER_ID_MANAGER_PERM="Add Browser Id Manager"
TRAVERSAL_APPHANDLE = 'BrowserIdManager'
def constructBrowserIdManager(
self, id, title='', idname='_ZopeId', location='cookiethenform',
cookiepath='/', cookiedomain='', cookielifedays=0, cookiesecure=0,
REQUEST=None
self, id=BROWSERID_MANAGER_NAME, title='', idname='_ZopeId',
location=('cookies', 'url', 'form'), cookiepath='/', cookiedomain='',
cookielifedays=0, cookiesecure=0, auto_url_encoding=0, REQUEST=None
):
""" """
ob = BrowserIdManager(id, title, idname, location, cookiepath,
cookiedomain, cookielifedays, cookiesecure)
cookiedomain, cookielifedays, cookiesecure,
auto_url_encoding)
self._setObject(id, ob)
ob = self._getOb(id)
if REQUEST is not None:
return self.manage_main(self, REQUEST, update_menu=1)
class BrowserIdManagerErr(Exception): pass
class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs):
""" browser id management class """
......@@ -81,17 +90,30 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs):
security.setPermissionDefault(ACCESS_CONTENTS_PERM,['Manager','Anonymous'])
security.setPermissionDefault(CHANGE_IDMGR_PERM, ['Manager'])
# backwards-compatibility for pre-2.6 instances
auto_url_encoding = 0
def __init__(self, id, title='', idname='_ZopeId',
location='cookiesthenform', cookiepath=('/'),
cookiedomain='', cookielifedays=0, cookiesecure=0):
location=('cookies', 'url', 'form'), cookiepath=('/'),
cookiedomain='', cookielifedays=0, cookiesecure=0,
auto_url_encoding=0):
self.id = str(id)
self.title = str(title)
self.setBrowserIdName(idname)
self.setBrowserIdLocation(location)
self.setBrowserIdNamespaces(location)
self.setCookiePath(cookiepath)
self.setCookieDomain(cookiedomain)
self.setCookieLifeDays(cookielifedays)
self.setCookieSecure(cookiesecure)
self.setAutoUrlEncoding(auto_url_encoding)
def manage_afterAdd(self, item, container):
""" Maybe add our traversal hook """
self.updateTraversalData()
def manage_beforeDelete(self, item, container):
""" Remove our traversal hook if it exists """
self.unregisterTraversalHook()
security.declareProtected(ACCESS_CONTENTS_PERM, 'hasBrowserId')
def hasBrowserId(self):
......@@ -99,7 +121,7 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs):
not create a browser id for the current request if one doesn't
already exist """
if self.getBrowserId(create=0): return 1
security.declareProtected(ACCESS_CONTENTS_PERM, 'getBrowserId')
def getBrowserId(self, create=1):
"""
......@@ -116,22 +138,27 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs):
if bid is not None:
# it's already set in this request so we can just return it
# if it's well-formed
if not self._isAWellFormedBrowserId(bid):
if not isAWellFormedBrowserId(bid):
# somebody screwed with the REQUEST instance during
# this request.
raise BrowserIdManagerErr, (
'Ill-formed browserid in REQUEST.browser_id_: %s' %
'Ill-formed browserid in REQUEST.browser_id_: %s' %
escape(bid)
)
return bid
# fall through & ck id namespaces if bid is not in request.
# fall through & ck form/cookie namespaces if bid is not in request.
tk = self.browserid_name
ns = self.browserid_namespaces
for name in ns:
bid = getattr(REQUEST, name).get(tk, None)
if name == 'url':
continue # browser ids in url are checked by Traverser class
current_ns = getattr(REQUEST, name, None)
if current_ns is None:
continue
bid = current_ns.get(tk, None)
if bid is not None:
# hey, we got a browser id!
if self._isAWellFormedBrowserId(bid):
if isAWellFormedBrowserId(bid):
# bid is not "plain old broken"
REQUEST.browser_id_ = bid
REQUEST.browser_id_ns_ = name
......@@ -139,7 +166,7 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs):
# fall through if bid is invalid or not in namespaces
if create:
# create a brand new bid
bid = self._getNewBrowserId()
bid = getNewBrowserId()
if 'cookies' in ns:
self._setCookie(bid, REQUEST)
REQUEST.browser_id_ = bid
......@@ -182,6 +209,15 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs):
if getattr(self.REQUEST, 'browser_id_ns_') == 'form':
return 1
security.declareProtected(ACCESS_CONTENTS_PERM, 'isBrowserIdFromUrl')
def isBrowserIdFromUrl(self):
""" returns true if browser id is from first element of the
URL """
if not self.getBrowserId(): # make sure the bid is stuck on REQUEST
raise BrowserIdManagerErr, 'There is no current browser id.'
if getattr(self.REQUEST, 'browser_id_ns_') == 'url':
return 1
security.declareProtected(ACCESS_CONTENTS_PERM, 'isBrowserIdNew')
def isBrowserIdNew(self):
"""
......@@ -193,22 +229,27 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs):
raise BrowserIdManagerErr, 'There is no current browser id.'
# ns will be None if new, negating None below returns 1, which
# would indicate that it's new on this request
return not getattr(self.REQUEST, 'browser_id_ns_')
return getattr(self.REQUEST, 'browser_id_ns_', None) == None
security.declareProtected(ACCESS_CONTENTS_PERM, 'encodeUrl')
def encodeUrl(self, url, create=1):
def encodeUrl(self, url, style='querystring', create=1):
"""
encode a URL with the browser id as a postfixed query string
element
element or inlined into the url depending on the 'style' parameter
"""
bid = self.getBrowserId(create)
if bid is None:
raise BrowserIdManagerErr, 'There is no current browser id.'
name = self.getBrowserIdName()
if '?' in url:
return '%s&%s=%s' % (url, name, bid)
else:
return '%s?%s=%s' % (url, name, bid)
if style == 'querystring': # encode bid in querystring
if '?' in url:
return '%s&%s=%s' % (url, name, bid)
else:
return '%s?%s=%s' % (url, name, bid)
else: # encode bid as first two URL path segments
proto, host, path, params, query, frag = urlparse(url)
path = '/%s/%s%s' % (name, bid, path)
return urlunparse((proto, host, path, params, query, frag))
security.declareProtected(MGMT_SCREEN_PERM, 'manage_browseridmgr')
manage_browseridmgr = Globals.DTMLFile('dtml/manageIdManager', globals())
......@@ -216,28 +257,29 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs):
security.declareProtected(CHANGE_IDMGR_PERM,
'manage_changeBrowserIdManager')
def manage_changeBrowserIdManager(
self, title='', idname='_ZopeId', location='cookiesthenform',
self, title='', idname='_ZopeId', location=('cookies', 'form', 'url'),
cookiepath='/', cookiedomain='', cookielifedays=0, cookiesecure=0,
REQUEST=None
auto_url_encoding=0, REQUEST=None
):
""" """
self.title = title
self.title = str(title)
self.setBrowserIdName(idname)
self.setCookiePath(cookiepath)
self.setCookieDomain(cookiedomain)
self.setCookieLifeDays(cookielifedays)
self.setCookieSecure(cookiesecure)
self.setBrowserIdLocation(location)
self.setBrowserIdNamespaces(location)
self.setAutoUrlEncoding(auto_url_encoding)
self.updateTraversalData()
if REQUEST is not None:
return self.manage_browseridmgr(
self, REQUEST, manage_tabs_message = 'Changes saved.'
)
msg = '/manage_browseridmgr?manage_tabs_message=Changes saved'
REQUEST.RESPONSE.redirect(self.absolute_url()+msg)
security.declareProtected(CHANGE_IDMGR_PERM, 'setBrowserIdName')
def setBrowserIdName(self, k):
""" sets browser id name string """
if not (type(k) is type('') and k and not badidnamecharsin(k)):
raise BrowserIdManagerErr, 'Bad id name string %s' % escape(repr(k))
raise BrowserIdManagerErr,'Bad id name string %s' % escape(repr(k))
self.browserid_name = k
security.declareProtected(ACCESS_CONTENTS_PERM, 'getBrowserIdName')
......@@ -246,74 +288,29 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs):
return self.browserid_name
security.declareProtected(CHANGE_IDMGR_PERM, 'setBrowserIdNamespaces')
def setBrowserIdNamespaces(self,namespacesd={1:'cookies',2:'form'}):
def setBrowserIdNamespaces(self, ns=('cookies', 'form', 'url')):
"""
accepts dictionary e.g. {1: 'cookies', 2: 'form'} as browser
id allowable namespaces and lookup ordering priority
where key is 'priority' with 1 being highest.
accepts list of allowable browser id namespaces
"""
allowed = self.getAllBrowserIdNamespaces()
for name in namespacesd.values():
if name not in allowed:
for name in ns:
if name not in ALLOWED_BID_NAMESPACES:
raise BrowserIdManagerErr, (
'Bad browser id namespace %s' % repr(name)
)
self.browserid_namespaces = []
nskeys = namespacesd.keys()
nskeys.sort()
for priority in nskeys:
self.browserid_namespaces.append(namespacesd[priority])
security.declareProtected(ACCESS_CONTENTS_PERM, 'getBrowserIdLocation')
def getBrowserIdLocation(self):
d = {}
i = 1
for name in self.browserid_namespaces:
d[name] = i
i = i + 1
if d.get('cookies') == 1:
if d.get('form'):
return 'cookiesthenform'
else:
return 'cookiesonly'
elif d.get('form') == 1:
if d.get('cookies'):
return 'formthencookies'
else:
return 'formonly'
else:
return 'cookiesthenform'
security.declareProtected(CHANGE_IDMGR_PERM, 'setBrowserIdLocation')
def setBrowserIdLocation(self, location):
""" accepts a string and turns it into a namespaces dict """
if location == 'formthencookies':
d = {1:'form', '2':'cookies'}
elif location == 'cookiesonly':
d = {1:'cookies'}
elif location == 'formonly':
d = {1:'form'}
else:
d = {1:'cookies',2:'form'}
self.setBrowserIdNamespaces(d)
self.browserid_namespaces = tuple(ns)
security.declareProtected(ACCESS_CONTENTS_PERM, 'getBrowserIdNamespaces')
def getBrowserIdNamespaces(self):
""" """
d = {}
i = 1
for name in self.browserid_namespaces:
d[i] = name
i = i + 1
return d
return self.browserid_namespaces
security.declareProtected(CHANGE_IDMGR_PERM, 'setCookiePath')
def setCookiePath(self, path=''):
""" sets cookie 'path' element for id cookie """
if not (type(path) is type('') and not badcookiecharsin(path)):
raise BrowserIdManagerErr, 'Bad cookie path %s' % escape(repr(path))
raise BrowserIdManagerErr,'Bad cookie path %s' % escape(repr(path))
self.cookie_path = path
security.declareProtected(ACCESS_CONTENTS_PERM, 'getCookiePath')
def getCookiePath(self):
""" """
......@@ -371,13 +368,21 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs):
""" """
return self.cookie_secure
security.declareProtected(ACCESS_CONTENTS_PERM,'getAllBrowserIdNamespaces')
def getAllBrowserIdNamespaces(self):
"""
These are the REQUEST namespaces searched when looking for an
browser id.
"""
return ('form', 'cookies')
security.declareProtected(CHANGE_IDMGR_PERM, 'setCookieSecure')
def setAutoUrlEncoding(self, auto_url_encoding):
""" sets 'auto url encoding' on or off """
self.auto_url_encoding = not not auto_url_encoding
security.declareProtected(ACCESS_CONTENTS_PERM, 'getAutoUrlEncoding')
def getAutoUrlEncoding(self):
""" """
return self.auto_url_encoding
security.declareProtected(ACCESS_CONTENTS_PERM, 'isUrlInBidNamespaces')
def isUrlInBidNamespaces(self):
""" Returns true if 'url' is in the browser id namespaces
for this browser id """
return 'url' in self.browserid_namespaces
security.declareProtected(ACCESS_CONTENTS_PERM, 'getHiddenFormField')
def getHiddenFormField(self):
......@@ -388,25 +393,6 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs):
s = '<input type="hidden" name="%s" value="%s">'
return s % (self.getBrowserIdName(), self.getBrowserId())
# non-interface methods follow
def _getNewBrowserId(self, randint=random.randint, maxint=99999999):
""" Returns 19-character string browser id
'AAAAAAAABBBBBBBB'
where:
A == leading-0-padded 8-char string-rep'd random integer
B == modified base64-encoded 11-char timestamp
To be URL-compatible, base64 encoding is modified as follows:
'=' end-padding is stripped off
'+' is translated to '-'
'/' is translated to '.'
An example is: 89972317A0C3EHnUi90w
"""
return '%08i%s' % (randint(0, maxint-1), self._getB64TStamp())
def _setCookie(
self, bid, REQUEST, remove=0, now=time.time, strftime=time.strftime,
gmtime=time.gmtime
......@@ -428,46 +414,14 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs):
return # should we raise an exception?
if string.split(URL1,':')[0] != 'https':
return # should we raise an exception?
cookies = REQUEST.RESPONSE.cookies
cookie = cookies[self.browserid_name]= {}
for k,v in d.items():
if v:
cookie[k] = v #only stuff things with true values
cookie['value'] = bid
def _getB64TStamp(
self, b2a=binascii.b2a_base64,gmtime=time.gmtime, time=time.time,
b64_trans=b64_trans, split=string.split,
TimeStamp=TimeStamp.TimeStamp, translate=string.translate
):
t=time()
ts=split(b2a(`apply(TimeStamp,(gmtime(t)[:5]+(t%60,)))`)[:-1],'=')[0]
return translate(ts, b64_trans)
def _getB64TStampToInt(
self, ts, TimeStamp=TimeStamp.TimeStamp, b64_untrans=b64_untrans,
a2b=binascii.a2b_base64, translate=string.translate
):
return TimeStamp(a2b(translate(ts+'=',b64_untrans))).timeTime()
def _getBrowserIdPieces(self, bid):
""" returns browser id parts in a tuple consisting of rand_id,
timestamp
"""
return (bid[:8], bid[8:19])
def _isAWellFormedBrowserId(self, bid, binerr=binascii.Error,
timestamperr=TimeStamp.error):
try:
rnd, ts = self._getBrowserIdPieces(bid)
int(rnd)
self._getB64TStampToInt(ts)
return bid
except (TypeError, ValueError, AttributeError, IndexError, binerr,
timestamperr):
return None
def _setId(self, id):
if id != self.id:
raise Globals.MessageDialog(
......@@ -475,5 +429,128 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs):
message='You cannot rename a browser id manager, sorry!',
action ='./manage_main',)
def updateTraversalData(self):
if self.isUrlInBidNamespaces():
self.registerTraversalHook()
else:
self.unregisterTraversalHook()
def unregisterTraversalHook(self):
parent = aq_parent(aq_inner(self))
name = TRAVERSAL_APPHANDLE
if self.hasTraversalHook(parent):
unregisterBeforeTraverse(parent, name)
def registerTraversalHook(self):
parent = aq_parent(aq_inner(self))
if not self.hasTraversalHook(parent):
hook = BrowserIdManagerTraverser()
name = TRAVERSAL_APPHANDLE
priority = 40 # "higher" priority than session data traverser
registerBeforeTraverse(parent, hook, name, priority)
def hasTraversalHook(self, parent):
name = TRAVERSAL_APPHANDLE
return not not queryBeforeTraverse(parent, name)
class BrowserIdManagerTraverser(Persistent):
def __call__(self, container, request, browser_id=None,
browser_id_ns=None,
BROWSERID_MANAGER_NAME=BROWSERID_MANAGER_NAME):
"""
Registered hook to set and get a browser id in the URL. If
a browser id is found in the URL of an incoming request, we put it
into a place where it can be found later by the browser id manager.
If our browser id manager's auto-url-encoding feature is on, cause
Zope-generated URLs to contain the browser id by rewriting the
request._script list.
"""
browser_id_manager = getattr(container, BROWSERID_MANAGER_NAME, None)
# fail if we cannot find a browser id manager (that means this
# instance has been "orphaned" somehow)
if browser_id_manager is None:
zLOG.LOG('Browser Id Manager Traverser', zLOG.ERROR,
'Could not locate browser id manager!')
return
try:
stack = request['TraversalRequestNameStack']
request.browser_id_ns_ = browser_id_ns
bid_name = browser_id_manager.getBrowserIdName()
# stuff the browser id and id namespace into the request
# if the URL has a browser id name and browser id as its first
# two elements. Only remove these elements from the
# traversal stack if they are a "well-formed pair".
if len(stack) >= 2 and stack[-1] == bid_name:
if isAWellFormedBrowserId(stack[-2]):
name = stack.pop() # pop the name off the stack
browser_id = stack.pop() # pop id off the stack
request.browser_id_ = browser_id
request.browser_id_ns_ = 'url'
# if the browser id manager is set up with 'auto url encoding',
# cause generated URLs to be encoded with the browser id name/value
# pair by munging request._script.
if browser_id_manager.getAutoUrlEncoding():
if browser_id is None:
request.browser_id_ = browser_id = getNewBrowserId()
request._script.append(quote(bid_name))
request._script.append(quote(browser_id))
except:
zLOG.LOG('Browser Id Manager Traverser', zLOG.ERROR,
'indeterminate error', error=sys.exc_info())
def getB64TStamp(
b2a=binascii.b2a_base64,gmtime=time.gmtime, time=time.time,
b64_trans=b64_trans, split=string.split,
TimeStamp=TimeStamp.TimeStamp, translate=string.translate
):
t=time()
ts=split(b2a(`apply(TimeStamp,(gmtime(t)[:5]+(t%60,)))`)[:-1],'=')[0]
return translate(ts, b64_trans)
def getB64TStampToInt(
ts, TimeStamp=TimeStamp.TimeStamp, b64_untrans=b64_untrans,
a2b=binascii.a2b_base64, translate=string.translate
):
return TimeStamp(a2b(translate(ts+'=',b64_untrans))).timeTime()
def getBrowserIdPieces(bid):
""" returns browser id parts in a tuple consisting of rand_id,
timestamp
"""
return (bid[:8], bid[8:19])
def isAWellFormedBrowserId(bid, binerr=binascii.Error,
timestamperr=TimeStamp.error):
try:
rnd, ts = getBrowserIdPieces(bid)
int(rnd)
getB64TStampToInt(ts)
return bid
except (TypeError, ValueError, AttributeError, IndexError, binerr,
timestamperr):
return None
def getNewBrowserId(randint=random.randint, maxint=99999999):
""" Returns 19-character string browser id
'AAAAAAAABBBBBBBB'
where:
A == leading-0-padded 8-char string-rep'd random integer
B == modified base64-encoded 11-char timestamp
To be URL-compatible, base64 encoding is modified as follows:
'=' end-padding is stripped off
'+' is translated to '-'
'/' is translated to '.'
An example is: 89972317A0C3EHnUi90w
"""
return '%08i%s' % (randint(0, maxint-1), getB64TStamp())
Globals.InitializeClass(BrowserIdManager)
############################################################################
#
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
......@@ -25,11 +25,11 @@ import SessionInterfaces
from SessionPermissions import *
from types import StringType
from common import DEBUG
from BrowserIdManager import isAWellFormedBrowserId, getNewBrowserId,\
BROWSERID_MANAGER_NAME
from ZPublisher.BeforeTraverse import registerBeforeTraverse, \
unregisterBeforeTraverse
BID_MGR_NAME = 'browser_id_manager'
bad_path_chars_in=re.compile('[^a-zA-Z0-9-_~\,\. \/]').search
class SessionDataManagerErr(Exception): pass
......@@ -103,19 +103,20 @@ class SessionDataManager(Item, Implicit, Persistent, RoleManager, Owned, Tabs):
security.declareProtected(ARBITRARY_SESSIONDATA_PERM,'getSessionDataByKey')
def getSessionDataByKey(self, key):
return self._getSessionDataObjectByKey(key)
security.declareProtected(ACCESS_CONTENTS_PERM, 'getBrowserIdManager')
def getBrowserIdManager(self):
""" """
mgr = getattr(self, BID_MGR_NAME, None)
mgr = getattr(self, BROWSERID_MANAGER_NAME, None)
if mgr is None:
raise SessionDataManagerErr,(
'No browser id manager named %s could be found.' % BID_MGR_NAME
'No browser id manager named %s could be found.' %
BROWSERID_MANAGER_NAME
)
return mgr
# END INTERFACE METHODS
def __init__(self, id, path=None, title='', requestName=None):
self.id = id
self.setContainerPath(path)
......@@ -160,14 +161,14 @@ class SessionDataManager(Item, Implicit, Persistent, RoleManager, Owned, Tabs):
self.obpath = list(path) # sequence
else:
raise SessionDataManagerErr, ('Bad path value %s' % path)
security.declareProtected(MGMT_SCREEN_PERM, 'getContainerPath')
def getContainerPath(self):
""" """
if self.obpath is not None:
return string.join(self.obpath, '/')
return '' # blank string represents undefined state
def _hasSessionDataObject(self, key):
""" """
c = self._getSessionDataContainer()
......@@ -251,6 +252,12 @@ class SessionDataManagerTraverser(Persistent):
self._sessionDataManager = sessionDataManagerName
def __call__(self, container, request, StringType=StringType):
"""
This method places a session data object reference in
the request. It is called on each and every request to Zope in
Zopes after 2.5.0 when there is a session data manager installed
in the root.
"""
try:
sdmName = self._sessionDataManager
if not isinstance(sdmName, StringType):
......@@ -268,7 +275,10 @@ class SessionDataManagerTraverser(Persistent):
msg = 'Session automatic traversal failed to get session data'
LOG('Session Tracking', WARNING, msg, error=sys.exc_info())
return
# set the getSessionData method in the "lazy" namespace
if self._requestSessionName is not None:
request.set_lazy(self._requestSessionName, getSessionData)
Globals.InitializeClass(SessionDataManager)
......@@ -12,7 +12,7 @@
############################################################################
"""
Sessioning-related Object APIs
Session APIs
See Also
......@@ -32,12 +32,24 @@ class BrowserIdManagerInterface(
visitors, and for servicing requests from Session Data Managers
related to the browser id.
"""
def encodeUrl(url):
def encodeUrl(url, style='querystring'):
"""
Encodes a provided URL with the current request's browser id
and returns the result. For example, the call
encodeUrl('http://foo.com/amethod') might return
'http://foo.com/amethod?_ZopeId=as9dfu0adfu0ad'.
and returns the result. Two forms of URL-encoding are supported:
'querystring' and 'inline'. 'querystring' is the default.
If the 'querystring' form is used, the browser id name/value pair
are postfixed onto the URL as a query string. If the 'inline'
form is used, the browser id name/value pair are prefixed onto
the URL as the first two path segment elements.
For example:
The call encodeUrl('http://foo.com/amethod', style='querystring')
might return 'http://foo.com/amethod?_ZopeId=as9dfu0adfu0ad'.
The call encodeUrl('http://foo.com/amethod, style='inline')
might return 'http://foo.com/_ZopeId/as9dfu0adfu0ad/amethod'.
Permission required: Access contents information
......
......@@ -6,7 +6,6 @@
)">
<FORM ACTION="constructBrowserIdManager" METHOD="POST">
<input type=hidden name="id" value="browser_id_manager">
<TABLE CELLSPACING="2">
<tr>
<td>&nbsp;</td>
......@@ -60,30 +59,25 @@ by interacting with the Zope sessioning machinery.
</tr>
<tr>
<td>
<div align=left class="form-label">Look for Browser Id Name in</th>
<div align=left class="form-label">Look for Browser Id in</th>
</td>
<td>
<table border=0>
<tr>
<td align=left>
<input type="radio" name="location" value="cookiesonly"> Cookies only
<input type="checkbox" name="location:list" value="cookies" CHECKED> Cookies
</td>
</tr>
<tr>
<td align=left>
<input type="radio" name="location" value="cookiesthenform" CHECKED> Cookies then form
<input type="checkbox" name="location:list" value="form" CHECKED> Forms and Query Strings
</td>
</tr>
<tr>
<td align=left>
<input type="radio" name="location" value="formonly"> Form only
<input type="checkbox" name="location:list" value="url" CHECKED> URLs
</td>
</tr>
<tr>
<td align=left>
<input type="radio" name="location" value="formthencookies"> Form then cookies
</td>
</tr>
</table>
</td>
</tr>
......@@ -93,6 +87,16 @@ by interacting with the Zope sessioning machinery.
<td>&nbsp;</td>
</tr>
<TR>
<TD ALIGN="LEFT" VALIGN="TOP">
<div class="form-label">
Automatically Encode Zope-Generated<br>URLs With A Browser Id
</div>
</TD>
<TD ALIGN="LEFT" VALIGN="TOP">
<INPUT TYPE="checkbox" NAME="auto_url_encoding" SIZE="20">
</TD>
</TR>
<TR>
<TD ALIGN="LEFT" VALIGN="TOP">
<div class="form-label">
......
......@@ -37,42 +37,47 @@
</TD>
</TR>
<dtml-let loc=getBrowserIdLocation>
<dtml-let namespaces=getBrowserIdNamespaces>
<tr valign="top">
<td>
<div align=left class="form-label">Look for Browser Id Name in</th>
<div align=left class="form-label">Look for Browser Id in</th>
</td>
<td>
<table border=0>
<tr>
<td align=left>
<input type="radio" name="location" value="cookiesonly"
<dtml-if "loc=='cookiesonly'">CHECKED</dtml-if>> Cookies only
<input type="checkbox" name="location:list" value="cookies"
<dtml-if "'cookies' in namespaces">CHECKED</dtml-if>> Cookies
</td>
</tr>
<tr>
<td align=left>
<input type="radio" name="location" value="cookiesthenform"
<dtml-if "loc=='cookiesthenform'">CHECKED</dtml-if>> Cookies then form
<input type="checkbox" name="location:list" value="form"
<dtml-if "'form' in namespaces">CHECKED</dtml-if>> Forms and Query Strings
</td>
</tr>
<tr>
<td align=left>
<input type="radio" name="location" value="formonly"
<dtml-if "loc=='formonly'">CHECKED</dtml-if>> Form only
<input type="checkbox" name="location:list" value="url"
<dtml-if "'url' in namespaces">CHECKED</dtml-if>> URLs
</td>
</tr>
<tr>
<td align=left>
<input type="radio" name="location" value="formthencookies"
<dtml-if "loc=='formthencookies'">CHECKED</dtml-if>> Form then cookies
</td>
</tr>
</table>
</td>
</tr>
</dtml-let>
<TR>
<TD ALIGN="LEFT" VALIGN="TOP">
<div class="form-label">
Automatically Encode Zope-Generated<br>URLs With A Browser Id
</div>
</TD>
<TD ALIGN="LEFT" VALIGN="TOP">
<INPUT TYPE="checkbox" NAME="auto_url_encoding"
<dtml-if getAutoUrlEncoding>CHECKED</dtml-if>>
</TD>
</TR>
<TR>
<TD ALIGN="LEFT" VALIGN="TOP">
<div class="form-label">
......
......@@ -32,12 +32,24 @@ class BrowserIdManagerInterface(
visitors, and for servicing requests from Session Data Managers
related to the browser id.
"""
def encodeUrl(url):
def encodeUrl(url, style='querystring'):
"""
Encodes a provided URL with the current request's browser id
and returns the result. For example, the call
encodeUrl('http://foo.com/amethod') might return
'http://foo.com/amethod?_ZopeId=as9dfu0adfu0ad'.
and returns the result. Two forms of URL-encoding are supported:
'querystring' and 'inline'. 'querystring' is the default.
If the 'querystring' form is used, the browser id name/value pair
are postfixed onto the URL as a query string. If the 'inline'
form is used, the browser id name/value pair are prefixed onto
the URL as the first two path segment elements.
For example:
The call encodeUrl('http://foo.com/amethod', style='querystring')
might return 'http://foo.com/amethod?_ZopeId=as9dfu0adfu0ad'.
The call encodeUrl('http://foo.com/amethod, style='inline')
might return 'http://foo.com/_ZopeId/as9dfu0adfu0ad/amethod'.
Permission required: Access contents information
......
......@@ -15,14 +15,21 @@ Browser Id Manager - Add
Title -- the browser id manager title.
Look for browser id name in -- the cookie name and/or form variable name
used for this browser id manager instance. This will be the
Browser Id Name -- the cookie name and/or form variable
name used for this browser id manager instance. This will be the
name looked up in the 'cookies' or 'form' REQUEST namespaces
when the browser id manager attempts to find a cookie or form
variable with a browser id in it.
Browser id location -- select from one of the available
lookup ordering schemes involving cookies and forms
Look for Browser Id In -- choose any of 'Forms and Query Strings',
'URLs', or 'Cookies'. The browser id name/value will be looked
for within these places.
Automatically Encode Zope-Generated URLs With A Browser Id -- if
this is selected, URLs generated by Zope (such as URLs which come
as a result of calling an object's 'absolute_url' method) will be
encoded with a browser name and browser id as the first two
elements of the URL path.
Cookie path -- this is the 'path' element which should be sent
in the session token cookie. For more information, see the
......
......@@ -4,14 +4,20 @@ Browser Id Manager - Change
Title -- the browser id manager title.
Browser id name -- the cookie name and/or form variable name
Browser id name -- the cookie or forms variable name
used for this browser id manager instance. This will be the
name looked up in the 'cookies' or 'form' REQUEST namespaces
when the browser id manager attempts to find a cookie or form
variable with a browser id in it.
Look for browser id name in -- select from one of the available
lookup ordering schemes involving cookies and forms
name looked up in the namespaces specified by "Look for browser
id name in" (below).
Look for Browser Id In -- choose any of 'Forms and Query Strings',
'URLs', or 'Cookies'. The browser id name/value will be looked
for within these places.
Automatically Encode Zope-Generated URLs With A Browser Id -- if
this is selected, URLs generated by Zope (such as URLs which come
as a result of calling an object's 'absolute_url' method) will be
encoded with a browser name and browser id as the first two
elements of the URL path.
Cookie path -- this is the 'path' element which should be sent
in the session token cookie. For more information, see the
......
......@@ -18,7 +18,7 @@ from Testing import makerequest
import ZODB # in order to get Persistence.Persistent working
import Acquisition
from Acquisition import aq_base
from Products.Sessions.BrowserIdManager import BrowserIdManager
from Products.Sessions.BrowserIdManager import BrowserIdManager, getNewBrowserId
from Products.Sessions.SessionDataManager import \
SessionDataManager, SessionDataManagerErr
from Products.Transience.Transience import \
......@@ -149,7 +149,7 @@ class BaseReaderWriter(threading.Thread):
self.conn = db.open()
self.app = self.conn.root()['Application']
self.app = makerequest.makerequest(self.app)
token = self.app.browser_id_manager._getNewBrowserId()
token = getNewBrowserId()
self.app.REQUEST.browser_id_ = token
self.iters = iters
self.sdm_name = sdm_name
......
......@@ -13,30 +13,41 @@
"""
Test suite for session id manager.
$Id: testBrowserIdManager.py,v 1.11 2002/08/14 22:25:10 mj Exp $
$Id: testBrowserIdManager.py,v 1.12 2002/08/19 19:50:18 chrism Exp $
"""
__version__ = "$Revision: 1.11 $"[11:-2]
__version__ = "$Revision: 1.12 $"[11:-2]
import sys
import ZODB
from Products.Sessions.BrowserIdManager import BrowserIdManager, BrowserIdManagerErr
from Products.Sessions.BrowserIdManager import BrowserIdManager, \
BrowserIdManagerErr, BrowserIdManagerTraverser, \
isAWellFormedBrowserId
from unittest import TestCase, TestSuite, TextTestRunner, makeSuite
from ZPublisher.HTTPRequest import HTTPRequest
from ZPublisher.HTTPResponse import HTTPResponse
from ZPublisher.BeforeTraverse import queryBeforeTraverse
from sys import stdin
from os import environ
from OFS.Application import Application
class TestBrowserIdManager(TestCase):
def setUp(self):
self.m = BrowserIdManager('foo')
self.app = Application()
self.app.id = 'App'
mgr = BrowserIdManager('browser_id_manager')
self.app._setObject('browser_id_manager', mgr)
self.m = self.app.browser_id_manager
resp = HTTPResponse()
environ['SERVER_NAME']='fred'
environ['SERVER_PORT']='80'
req = HTTPRequest(stdin, environ, resp)
self.m.REQUEST = req
self.req = HTTPRequest(stdin, environ, resp)
self.req['TraversalRequestNameStack'] = ['foo', 'bar']
self.app.REQUEST = self.req
def tearDown(self):
del self.m
self.app.REQUEST = None
del self.app
def testSetBrowserIdName(self):
self.m.setBrowserIdName('foo')
......@@ -49,30 +60,16 @@ class TestBrowserIdManager(TestCase):
lambda self=self: self.m.setBrowserIdName(1))
def testSetBadNamespaces(self):
d = {1:'gummy', 2:'froopy'}
d = ('gummy', 'froopy')
self.assertRaises(BrowserIdManagerErr,
lambda self=self,d=d:
self.m.setBrowserIdNamespaces(d))
def testSetGoodNamespaces(self):
d = {1:'cookies', 2:'form'}
d = ('cookies', 'url', 'form')
self.m.setBrowserIdNamespaces(d)
self.failUnless(self.m.getBrowserIdNamespaces() == d)
def testSetNamespacesByLocation(self):
self.m.setBrowserIdLocation('cookiesonly')
self.failUnless(self.m.getBrowserIdNamespaces() == {1:'cookies'})
self.failUnless(self.m.getBrowserIdLocation() == 'cookiesonly')
self.m.setBrowserIdLocation('cookiesthenform')
self.failUnless(self.m.getBrowserIdNamespaces()=={1:'cookies',2:'form'})
self.failUnless(self.m.getBrowserIdLocation() == 'cookiesthenform')
self.m.setBrowserIdLocation('formonly')
self.failUnless(self.m.getBrowserIdNamespaces() == {1:'form'})
self.failUnless(self.m.getBrowserIdLocation() == 'formonly')
self.m.setBrowserIdLocation('formthencookies')
self.failUnless(self.m.getBrowserIdNamespaces()=={1:'form',2:'cookies'})
self.failUnless(self.m.getBrowserIdLocation() == 'formthencookies')
def testSetBadCookiePath(self):
path = '/;'
self.assertRaises(BrowserIdManagerErr,
......@@ -148,33 +145,13 @@ class TestBrowserIdManager(TestCase):
a = self.m.getBrowserId()
self.failUnless( self.m.isBrowserIdNew() )
def testIsBrowserIdFromCookieFirst(self):
token = self.m.getBrowserId()
self.m.REQUEST.browser_id_ = token
self.m.REQUEST.browser_id_ns_ = 'cookies'
tokenkey = self.m.getBrowserIdName()
self.m.REQUEST.cookies[tokenkey] = token
self.m.setBrowserIdNamespaces({1:'cookies', 2:'form'})
a = self.m.getBrowserId()
self.failUnless( self.m.isBrowserIdFromCookie() )
def testIsBrowserIdFromFormFirst(self):
token = self.m.getBrowserId()
self.m.REQUEST.browser_id_ = token
self.m.REQUEST.browser_id_ns_ = 'form'
tokenkey = self.m.getBrowserIdName()
self.m.REQUEST.form[tokenkey] = token
self.m.setBrowserIdNamespaces({1:'form', 2:'cookies'})
a = self.m.getBrowserId()
self.failUnless( self.m.isBrowserIdFromForm() )
def testIsBrowserIdFromCookieOnly(self):
token = self.m.getBrowserId()
self.m.REQUEST.browser_id_ = token
self.m.REQUEST.browser_id_ns_ = 'cookies'
tokenkey = self.m.getBrowserIdName()
self.m.REQUEST.form[tokenkey] = token
self.m.setBrowserIdNamespaces({1:'cookies'})
self.m.setBrowserIdNamespaces(('cookies',))
a = self.m.getBrowserId()
self.failUnless( self.m.isBrowserIdFromCookie() )
self.failUnless( not self.m.isBrowserIdFromForm() )
......@@ -185,11 +162,20 @@ class TestBrowserIdManager(TestCase):
self.m.REQUEST.browser_id_ns_ = 'form'
tokenkey = self.m.getBrowserIdName()
self.m.REQUEST.form[tokenkey] = token
self.m.setBrowserIdNamespaces({1:'form'})
self.m.setBrowserIdNamespaces(('form',))
a = self.m.getBrowserId()
self.failUnless( not self.m.isBrowserIdFromCookie() )
self.failUnless( self.m.isBrowserIdFromForm() )
def testIsBrowserIdFromUrlOnly(self):
token = self.m.getBrowserId()
self.m.REQUEST.browser_id_ = token
self.m.REQUEST.browser_id_ns_ = 'url'
self.m.setBrowserIdNamespaces(('url',))
a = self.m.getBrowserId()
self.failUnless( not self.m.isBrowserIdFromCookie() )
self.failUnless( self.m.isBrowserIdFromUrl() )
def testFlushBrowserIdCookie(self):
token = self.m.getBrowserId()
self.m.REQUEST.browser_id_ = token
......@@ -226,6 +212,8 @@ class TestBrowserIdManager(TestCase):
u = 'http://www.zope.org/Members/mcdonc?foo=bar&spam=eggs'
r = self.m.encodeUrl(u)
self.failUnless( r == '%s&amp;%s=%s' % (u, keystring, key) )
r = self.m.encodeUrl(u, style='inline')
self.failUnless( r == 'http://www.zope.org/%s/%s/Members/mcdonc?foo=bar&spam=eggs' % (keystring, key))
def testGetHiddenFormField(self):
keystring = self.m.getBrowserIdName()
......@@ -235,6 +223,47 @@ class TestBrowserIdManager(TestCase):
(keystring, key))
self.failUnless( html == expected )
def testAutoUrlEncoding(self):
self.m.setAutoUrlEncoding(1)
self.m.setBrowserIdNamespaces(('url',))
self.m.updateTraversalData()
traverser = BrowserIdManagerTraverser()
traverser(self.app, self.req)
self.failUnless(isAWellFormedBrowserId(self.req.browser_id_))
print self.req.browser_id_
self.failUnless(self.req.browser_id_ns_ == None)
self.failUnless(self.req._script[-1] == self.req.browser_id_)
self.failUnless(self.req._script[-2] == '_ZopeId')
def testUrlBrowserIdIsFound(self):
bid = '43295340A0bpcu4nkCI'
name = '_ZopeId'
resp = HTTPResponse()
environ['SERVER_NAME']='fred'
environ['SERVER_PORT']='80'
self.req = HTTPRequest(stdin, environ, resp)
self.req['TraversalRequestNameStack'] = ['foo', 'bar', bid, name]
self.app.REQUEST = self.req
self.m.setAutoUrlEncoding(1)
self.m.setBrowserIdNamespaces(('url',))
self.m.updateTraversalData()
traverser = BrowserIdManagerTraverser()
traverser(self.app, self.req)
self.failUnless(isAWellFormedBrowserId(self.req.browser_id_))
self.failUnless(self.req.browser_id_ns_ == 'url')
self.failUnless(self.req._script[-1] == self.req.browser_id_)
self.failUnless(self.req._script[-2] == '_ZopeId')
self.failUnless(self.req['TraversalRequestNameStack'] == ['foo','bar'])
def testUpdateTraversalData(self):
self.m.setBrowserIdNamespaces(('url',))
self.m.updateTraversalData()
self.failUnless(self.m.hasTraversalHook(self.app))
self.failUnless(queryBeforeTraverse(self.app, 'BrowserIdManager'))
self.m.setBrowserIdNamespaces(('cookies', 'form'))
self.m.updateTraversalData()
self.failUnless(not queryBeforeTraverse(self.app,'BrowserIdManager'))
def test_suite():
testsuite = makeSuite(TestBrowserIdManager, 'test')
return testsuite
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment