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
...@@ -11,11 +11,11 @@ ...@@ -11,11 +11,11 @@
# #
############################################################################ ############################################################################
__version__='$Revision: 1.14 $'[11:-2] __version__='$Revision: 1.15 $'[11:-2]
import Globals import Globals
from Persistence import Persistent from Persistence import Persistent
from ZODB import TimeStamp 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.Owned import Owned
from AccessControl.Role import RoleManager from AccessControl.Role import RoleManager
from App.Management import Tabs from App.Management import Tabs
...@@ -27,6 +27,11 @@ from SessionPermissions import * ...@@ -27,6 +27,11 @@ from SessionPermissions import *
from common import DEBUG from common import DEBUG
import os, time, random, string, binascii, sys, re import os, time, random, string, binascii, sys, re
from cgi import escape 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_trans = string.maketrans('+/', '-.')
b64_untrans = string.maketrans('-.', '+/') b64_untrans = string.maketrans('-.', '+/')
...@@ -39,16 +44,20 @@ _marker = [] ...@@ -39,16 +44,20 @@ _marker = []
constructBrowserIdManagerForm = Globals.DTMLFile('dtml/addIdManager',globals()) 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" ADD_BROWSER_ID_MANAGER_PERM="Add Browser Id Manager"
TRAVERSAL_APPHANDLE = 'BrowserIdManager'
def constructBrowserIdManager( def constructBrowserIdManager(
self, id, title='', idname='_ZopeId', location='cookiethenform', self, id=BROWSERID_MANAGER_NAME, title='', idname='_ZopeId',
cookiepath='/', cookiedomain='', cookielifedays=0, cookiesecure=0, location=('cookies', 'url', 'form'), cookiepath='/', cookiedomain='',
REQUEST=None cookielifedays=0, cookiesecure=0, auto_url_encoding=0, REQUEST=None
): ):
""" """ """ """
ob = BrowserIdManager(id, title, idname, location, cookiepath, ob = BrowserIdManager(id, title, idname, location, cookiepath,
cookiedomain, cookielifedays, cookiesecure) cookiedomain, cookielifedays, cookiesecure,
auto_url_encoding)
self._setObject(id, ob) self._setObject(id, ob)
ob = self._getOb(id) ob = self._getOb(id)
if REQUEST is not None: if REQUEST is not None:
...@@ -81,17 +90,30 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs): ...@@ -81,17 +90,30 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs):
security.setPermissionDefault(ACCESS_CONTENTS_PERM,['Manager','Anonymous']) security.setPermissionDefault(ACCESS_CONTENTS_PERM,['Manager','Anonymous'])
security.setPermissionDefault(CHANGE_IDMGR_PERM, ['Manager']) security.setPermissionDefault(CHANGE_IDMGR_PERM, ['Manager'])
# backwards-compatibility for pre-2.6 instances
auto_url_encoding = 0
def __init__(self, id, title='', idname='_ZopeId', def __init__(self, id, title='', idname='_ZopeId',
location='cookiesthenform', cookiepath=('/'), location=('cookies', 'url', 'form'), cookiepath=('/'),
cookiedomain='', cookielifedays=0, cookiesecure=0): cookiedomain='', cookielifedays=0, cookiesecure=0,
auto_url_encoding=0):
self.id = str(id) self.id = str(id)
self.title = str(title) self.title = str(title)
self.setBrowserIdName(idname) self.setBrowserIdName(idname)
self.setBrowserIdLocation(location) self.setBrowserIdNamespaces(location)
self.setCookiePath(cookiepath) self.setCookiePath(cookiepath)
self.setCookieDomain(cookiedomain) self.setCookieDomain(cookiedomain)
self.setCookieLifeDays(cookielifedays) self.setCookieLifeDays(cookielifedays)
self.setCookieSecure(cookiesecure) 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') security.declareProtected(ACCESS_CONTENTS_PERM, 'hasBrowserId')
def hasBrowserId(self): def hasBrowserId(self):
...@@ -116,7 +138,7 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs): ...@@ -116,7 +138,7 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs):
if bid is not None: if bid is not None:
# it's already set in this request so we can just return it # it's already set in this request so we can just return it
# if it's well-formed # if it's well-formed
if not self._isAWellFormedBrowserId(bid): if not isAWellFormedBrowserId(bid):
# somebody screwed with the REQUEST instance during # somebody screwed with the REQUEST instance during
# this request. # this request.
raise BrowserIdManagerErr, ( raise BrowserIdManagerErr, (
...@@ -124,14 +146,19 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs): ...@@ -124,14 +146,19 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs):
escape(bid) escape(bid)
) )
return 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 tk = self.browserid_name
ns = self.browserid_namespaces ns = self.browserid_namespaces
for name in ns: 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: if bid is not None:
# hey, we got a browser id! # hey, we got a browser id!
if self._isAWellFormedBrowserId(bid): if isAWellFormedBrowserId(bid):
# bid is not "plain old broken" # bid is not "plain old broken"
REQUEST.browser_id_ = bid REQUEST.browser_id_ = bid
REQUEST.browser_id_ns_ = name REQUEST.browser_id_ns_ = name
...@@ -139,7 +166,7 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs): ...@@ -139,7 +166,7 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs):
# fall through if bid is invalid or not in namespaces # fall through if bid is invalid or not in namespaces
if create: if create:
# create a brand new bid # create a brand new bid
bid = self._getNewBrowserId() bid = getNewBrowserId()
if 'cookies' in ns: if 'cookies' in ns:
self._setCookie(bid, REQUEST) self._setCookie(bid, REQUEST)
REQUEST.browser_id_ = bid REQUEST.browser_id_ = bid
...@@ -182,6 +209,15 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs): ...@@ -182,6 +209,15 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs):
if getattr(self.REQUEST, 'browser_id_ns_') == 'form': if getattr(self.REQUEST, 'browser_id_ns_') == 'form':
return 1 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') security.declareProtected(ACCESS_CONTENTS_PERM, 'isBrowserIdNew')
def isBrowserIdNew(self): def isBrowserIdNew(self):
""" """
...@@ -193,22 +229,27 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs): ...@@ -193,22 +229,27 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs):
raise BrowserIdManagerErr, 'There is no current browser id.' raise BrowserIdManagerErr, 'There is no current browser id.'
# ns will be None if new, negating None below returns 1, which # ns will be None if new, negating None below returns 1, which
# would indicate that it's new on this request # 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') 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 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) bid = self.getBrowserId(create)
if bid is None: if bid is None:
raise BrowserIdManagerErr, 'There is no current browser id.' raise BrowserIdManagerErr, 'There is no current browser id.'
name = self.getBrowserIdName() name = self.getBrowserIdName()
if style == 'querystring': # encode bid in querystring
if '?' in url: if '?' in url:
return '%s&%s=%s' % (url, name, bid) return '%s&%s=%s' % (url, name, bid)
else: else:
return '%s?%s=%s' % (url, name, bid) 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') security.declareProtected(MGMT_SCREEN_PERM, 'manage_browseridmgr')
manage_browseridmgr = Globals.DTMLFile('dtml/manageIdManager', globals()) manage_browseridmgr = Globals.DTMLFile('dtml/manageIdManager', globals())
...@@ -216,28 +257,29 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs): ...@@ -216,28 +257,29 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs):
security.declareProtected(CHANGE_IDMGR_PERM, security.declareProtected(CHANGE_IDMGR_PERM,
'manage_changeBrowserIdManager') 'manage_changeBrowserIdManager')
def 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, 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.setBrowserIdName(idname)
self.setCookiePath(cookiepath) self.setCookiePath(cookiepath)
self.setCookieDomain(cookiedomain) self.setCookieDomain(cookiedomain)
self.setCookieLifeDays(cookielifedays) self.setCookieLifeDays(cookielifedays)
self.setCookieSecure(cookiesecure) self.setCookieSecure(cookiesecure)
self.setBrowserIdLocation(location) self.setBrowserIdNamespaces(location)
self.setAutoUrlEncoding(auto_url_encoding)
self.updateTraversalData()
if REQUEST is not None: if REQUEST is not None:
return self.manage_browseridmgr( msg = '/manage_browseridmgr?manage_tabs_message=Changes saved'
self, REQUEST, manage_tabs_message = 'Changes saved.' REQUEST.RESPONSE.redirect(self.absolute_url()+msg)
)
security.declareProtected(CHANGE_IDMGR_PERM, 'setBrowserIdName') security.declareProtected(CHANGE_IDMGR_PERM, 'setBrowserIdName')
def setBrowserIdName(self, k): def setBrowserIdName(self, k):
""" sets browser id name string """ """ sets browser id name string """
if not (type(k) is type('') and k and not badidnamecharsin(k)): 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 self.browserid_name = k
security.declareProtected(ACCESS_CONTENTS_PERM, 'getBrowserIdName') security.declareProtected(ACCESS_CONTENTS_PERM, 'getBrowserIdName')
...@@ -246,72 +288,27 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs): ...@@ -246,72 +288,27 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs):
return self.browserid_name return self.browserid_name
security.declareProtected(CHANGE_IDMGR_PERM, 'setBrowserIdNamespaces') 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 accepts list of allowable browser id namespaces
id allowable namespaces and lookup ordering priority
where key is 'priority' with 1 being highest.
""" """
allowed = self.getAllBrowserIdNamespaces() for name in ns:
for name in namespacesd.values(): if name not in ALLOWED_BID_NAMESPACES:
if name not in allowed:
raise BrowserIdManagerErr, ( raise BrowserIdManagerErr, (
'Bad browser id namespace %s' % repr(name) 'Bad browser id namespace %s' % repr(name)
) )
self.browserid_namespaces = [] self.browserid_namespaces = tuple(ns)
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)
security.declareProtected(ACCESS_CONTENTS_PERM, 'getBrowserIdNamespaces') security.declareProtected(ACCESS_CONTENTS_PERM, 'getBrowserIdNamespaces')
def getBrowserIdNamespaces(self): def getBrowserIdNamespaces(self):
""" """ """ """
d = {} return self.browserid_namespaces
i = 1
for name in self.browserid_namespaces:
d[i] = name
i = i + 1
return d
security.declareProtected(CHANGE_IDMGR_PERM, 'setCookiePath') security.declareProtected(CHANGE_IDMGR_PERM, 'setCookiePath')
def setCookiePath(self, path=''): def setCookiePath(self, path=''):
""" sets cookie 'path' element for id cookie """ """ sets cookie 'path' element for id cookie """
if not (type(path) is type('') and not badcookiecharsin(path)): 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 self.cookie_path = path
security.declareProtected(ACCESS_CONTENTS_PERM, 'getCookiePath') security.declareProtected(ACCESS_CONTENTS_PERM, 'getCookiePath')
...@@ -371,13 +368,21 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs): ...@@ -371,13 +368,21 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs):
""" """ """ """
return self.cookie_secure return self.cookie_secure
security.declareProtected(ACCESS_CONTENTS_PERM,'getAllBrowserIdNamespaces') security.declareProtected(CHANGE_IDMGR_PERM, 'setCookieSecure')
def getAllBrowserIdNamespaces(self): def setAutoUrlEncoding(self, auto_url_encoding):
""" """ sets 'auto url encoding' on or off """
These are the REQUEST namespaces searched when looking for an self.auto_url_encoding = not not auto_url_encoding
browser id.
""" security.declareProtected(ACCESS_CONTENTS_PERM, 'getAutoUrlEncoding')
return ('form', 'cookies') 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') security.declareProtected(ACCESS_CONTENTS_PERM, 'getHiddenFormField')
def getHiddenFormField(self): def getHiddenFormField(self):
...@@ -388,25 +393,6 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs): ...@@ -388,25 +393,6 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs):
s = '<input type="hidden" name="%s" value="%s">' s = '<input type="hidden" name="%s" value="%s">'
return s % (self.getBrowserIdName(), self.getBrowserId()) 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( def _setCookie(
self, bid, REQUEST, remove=0, now=time.time, strftime=time.strftime, self, bid, REQUEST, remove=0, now=time.time, strftime=time.strftime,
gmtime=time.gmtime gmtime=time.gmtime
...@@ -436,8 +422,87 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs): ...@@ -436,8 +422,87 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs):
cookie[k] = v #only stuff things with true values cookie[k] = v #only stuff things with true values
cookie['value'] = bid cookie['value'] = bid
def _getB64TStamp( def _setId(self, id):
self, b2a=binascii.b2a_base64,gmtime=time.gmtime, time=time.time, if id != self.id:
raise Globals.MessageDialog(
title='Cannot rename',
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, b64_trans=b64_trans, split=string.split,
TimeStamp=TimeStamp.TimeStamp, translate=string.translate TimeStamp=TimeStamp.TimeStamp, translate=string.translate
): ):
...@@ -445,35 +510,47 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs): ...@@ -445,35 +510,47 @@ class BrowserIdManager(Item, Persistent, Implicit, RoleManager, Owned, Tabs):
ts=split(b2a(`apply(TimeStamp,(gmtime(t)[:5]+(t%60,)))`)[:-1],'=')[0] ts=split(b2a(`apply(TimeStamp,(gmtime(t)[:5]+(t%60,)))`)[:-1],'=')[0]
return translate(ts, b64_trans) return translate(ts, b64_trans)
def _getB64TStampToInt( def getB64TStampToInt(
self, ts, TimeStamp=TimeStamp.TimeStamp, b64_untrans=b64_untrans, ts, TimeStamp=TimeStamp.TimeStamp, b64_untrans=b64_untrans,
a2b=binascii.a2b_base64, translate=string.translate a2b=binascii.a2b_base64, translate=string.translate
): ):
return TimeStamp(a2b(translate(ts+'=',b64_untrans))).timeTime() return TimeStamp(a2b(translate(ts+'=',b64_untrans))).timeTime()
def _getBrowserIdPieces(self, bid): def getBrowserIdPieces(bid):
""" returns browser id parts in a tuple consisting of rand_id, """ returns browser id parts in a tuple consisting of rand_id,
timestamp timestamp
""" """
return (bid[:8], bid[8:19]) return (bid[:8], bid[8:19])
def _isAWellFormedBrowserId(self, bid, binerr=binascii.Error,
def isAWellFormedBrowserId(bid, binerr=binascii.Error,
timestamperr=TimeStamp.error): timestamperr=TimeStamp.error):
try: try:
rnd, ts = self._getBrowserIdPieces(bid) rnd, ts = getBrowserIdPieces(bid)
int(rnd) int(rnd)
self._getB64TStampToInt(ts) getB64TStampToInt(ts)
return bid return bid
except (TypeError, ValueError, AttributeError, IndexError, binerr, except (TypeError, ValueError, AttributeError, IndexError, binerr,
timestamperr): timestamperr):
return None return None
def _setId(self, id):
if id != self.id: def getNewBrowserId(randint=random.randint, maxint=99999999):
raise Globals.MessageDialog( """ Returns 19-character string browser id
title='Cannot rename', 'AAAAAAAABBBBBBBB'
message='You cannot rename a browser id manager, sorry!', where:
action ='./manage_main',)
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) Globals.InitializeClass(BrowserIdManager)
...@@ -25,11 +25,11 @@ import SessionInterfaces ...@@ -25,11 +25,11 @@ import SessionInterfaces
from SessionPermissions import * from SessionPermissions import *
from types import StringType from types import StringType
from common import DEBUG from common import DEBUG
from BrowserIdManager import isAWellFormedBrowserId, getNewBrowserId,\
BROWSERID_MANAGER_NAME
from ZPublisher.BeforeTraverse import registerBeforeTraverse, \ from ZPublisher.BeforeTraverse import registerBeforeTraverse, \
unregisterBeforeTraverse unregisterBeforeTraverse
BID_MGR_NAME = 'browser_id_manager'
bad_path_chars_in=re.compile('[^a-zA-Z0-9-_~\,\. \/]').search bad_path_chars_in=re.compile('[^a-zA-Z0-9-_~\,\. \/]').search
class SessionDataManagerErr(Exception): pass class SessionDataManagerErr(Exception): pass
...@@ -107,10 +107,11 @@ class SessionDataManager(Item, Implicit, Persistent, RoleManager, Owned, Tabs): ...@@ -107,10 +107,11 @@ class SessionDataManager(Item, Implicit, Persistent, RoleManager, Owned, Tabs):
security.declareProtected(ACCESS_CONTENTS_PERM, 'getBrowserIdManager') security.declareProtected(ACCESS_CONTENTS_PERM, 'getBrowserIdManager')
def getBrowserIdManager(self): def getBrowserIdManager(self):
""" """ """ """
mgr = getattr(self, BID_MGR_NAME, None) mgr = getattr(self, BROWSERID_MANAGER_NAME, None)
if mgr is None: if mgr is None:
raise SessionDataManagerErr,( 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 return mgr
...@@ -251,6 +252,12 @@ class SessionDataManagerTraverser(Persistent): ...@@ -251,6 +252,12 @@ class SessionDataManagerTraverser(Persistent):
self._sessionDataManager = sessionDataManagerName self._sessionDataManager = sessionDataManagerName
def __call__(self, container, request, StringType=StringType): 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: try:
sdmName = self._sessionDataManager sdmName = self._sessionDataManager
if not isinstance(sdmName, StringType): if not isinstance(sdmName, StringType):
...@@ -268,7 +275,10 @@ class SessionDataManagerTraverser(Persistent): ...@@ -268,7 +275,10 @@ class SessionDataManagerTraverser(Persistent):
msg = 'Session automatic traversal failed to get session data' msg = 'Session automatic traversal failed to get session data'
LOG('Session Tracking', WARNING, msg, error=sys.exc_info()) LOG('Session Tracking', WARNING, msg, error=sys.exc_info())
return return
# set the getSessionData method in the "lazy" namespace
if self._requestSessionName is not None: if self._requestSessionName is not None:
request.set_lazy(self._requestSessionName, getSessionData) request.set_lazy(self._requestSessionName, getSessionData)
Globals.InitializeClass(SessionDataManager) Globals.InitializeClass(SessionDataManager)
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
############################################################################ ############################################################################
""" """
Sessioning-related Object APIs Session APIs
See Also See Also
...@@ -32,12 +32,24 @@ class BrowserIdManagerInterface( ...@@ -32,12 +32,24 @@ class BrowserIdManagerInterface(
visitors, and for servicing requests from Session Data Managers visitors, and for servicing requests from Session Data Managers
related to the browser id. related to the browser id.
""" """
def encodeUrl(url): def encodeUrl(url, style='querystring'):
""" """
Encodes a provided URL with the current request's browser id Encodes a provided URL with the current request's browser id
and returns the result. For example, the call and returns the result. Two forms of URL-encoding are supported:
encodeUrl('http://foo.com/amethod') might return 'querystring' and 'inline'. 'querystring' is the default.
'http://foo.com/amethod?_ZopeId=as9dfu0adfu0ad'.
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 Permission required: Access contents information
......
...@@ -6,7 +6,6 @@ ...@@ -6,7 +6,6 @@
)"> )">
<FORM ACTION="constructBrowserIdManager" METHOD="POST"> <FORM ACTION="constructBrowserIdManager" METHOD="POST">
<input type=hidden name="id" value="browser_id_manager">
<TABLE CELLSPACING="2"> <TABLE CELLSPACING="2">
<tr> <tr>
<td>&nbsp;</td> <td>&nbsp;</td>
...@@ -60,28 +59,23 @@ by interacting with the Zope sessioning machinery. ...@@ -60,28 +59,23 @@ by interacting with the Zope sessioning machinery.
</tr> </tr>
<tr> <tr>
<td> <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>
<td> <td>
<table border=0> <table border=0>
<tr> <tr>
<td align=left> <td align=left>
<input type="radio" name="location" value="cookiesonly"> Cookies only <input type="checkbox" name="location:list" value="cookies" CHECKED> Cookies
</td> </td>
</tr> </tr>
<tr> <tr>
<td align=left> <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> </td>
</tr> </tr>
<tr> <tr>
<td align=left> <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> </td>
</tr> </tr>
</table> </table>
...@@ -93,6 +87,16 @@ by interacting with the Zope sessioning machinery. ...@@ -93,6 +87,16 @@ by interacting with the Zope sessioning machinery.
<td>&nbsp;</td> <td>&nbsp;</td>
</tr> </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> <TR>
<TD ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT" VALIGN="TOP">
<div class="form-label"> <div class="form-label">
......
...@@ -37,35 +37,29 @@ ...@@ -37,35 +37,29 @@
</TD> </TD>
</TR> </TR>
<dtml-let loc=getBrowserIdLocation> <dtml-let namespaces=getBrowserIdNamespaces>
<tr valign="top"> <tr valign="top">
<td> <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>
<td> <td>
<table border=0> <table border=0>
<tr> <tr>
<td align=left> <td align=left>
<input type="radio" name="location" value="cookiesonly" <input type="checkbox" name="location:list" value="cookies"
<dtml-if "loc=='cookiesonly'">CHECKED</dtml-if>> Cookies only <dtml-if "'cookies' in namespaces">CHECKED</dtml-if>> Cookies
</td> </td>
</tr> </tr>
<tr> <tr>
<td align=left> <td align=left>
<input type="radio" name="location" value="cookiesthenform" <input type="checkbox" name="location:list" value="form"
<dtml-if "loc=='cookiesthenform'">CHECKED</dtml-if>> Cookies then form <dtml-if "'form' in namespaces">CHECKED</dtml-if>> Forms and Query Strings
</td> </td>
</tr> </tr>
<tr> <tr>
<td align=left> <td align=left>
<input type="radio" name="location" value="formonly" <input type="checkbox" name="location:list" value="url"
<dtml-if "loc=='formonly'">CHECKED</dtml-if>> Form only <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> </td>
</tr> </tr>
</table> </table>
...@@ -73,6 +67,17 @@ ...@@ -73,6 +67,17 @@
</tr> </tr>
</dtml-let> </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> <TR>
<TD ALIGN="LEFT" VALIGN="TOP"> <TD ALIGN="LEFT" VALIGN="TOP">
<div class="form-label"> <div class="form-label">
......
...@@ -32,12 +32,24 @@ class BrowserIdManagerInterface( ...@@ -32,12 +32,24 @@ class BrowserIdManagerInterface(
visitors, and for servicing requests from Session Data Managers visitors, and for servicing requests from Session Data Managers
related to the browser id. related to the browser id.
""" """
def encodeUrl(url): def encodeUrl(url, style='querystring'):
""" """
Encodes a provided URL with the current request's browser id Encodes a provided URL with the current request's browser id
and returns the result. For example, the call and returns the result. Two forms of URL-encoding are supported:
encodeUrl('http://foo.com/amethod') might return 'querystring' and 'inline'. 'querystring' is the default.
'http://foo.com/amethod?_ZopeId=as9dfu0adfu0ad'.
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 Permission required: Access contents information
......
...@@ -15,14 +15,21 @@ Browser Id Manager - Add ...@@ -15,14 +15,21 @@ Browser Id Manager - Add
Title -- the browser id manager title. Title -- the browser id manager title.
Look for browser id name in -- the cookie name and/or form variable name Browser Id Name -- the cookie name and/or form variable
used for this browser id manager instance. This will be the name used for this browser id manager instance. This will be the
name looked up in the 'cookies' or 'form' REQUEST namespaces name looked up in the 'cookies' or 'form' REQUEST namespaces
when the browser id manager attempts to find a cookie or form when the browser id manager attempts to find a cookie or form
variable with a browser id in it. variable with a browser id in it.
Browser id location -- select from one of the available Look for Browser Id In -- choose any of 'Forms and Query Strings',
lookup ordering schemes involving cookies and forms '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 Cookie path -- this is the 'path' element which should be sent
in the session token cookie. For more information, see the in the session token cookie. For more information, see the
......
...@@ -4,14 +4,20 @@ Browser Id Manager - Change ...@@ -4,14 +4,20 @@ Browser Id Manager - Change
Title -- the browser id manager title. 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 used for this browser id manager instance. This will be the
name looked up in the 'cookies' or 'form' REQUEST namespaces name looked up in the namespaces specified by "Look for browser
when the browser id manager attempts to find a cookie or form id name in" (below).
variable with a browser id in it.
Look for Browser Id In -- choose any of 'Forms and Query Strings',
Look for browser id name in -- select from one of the available 'URLs', or 'Cookies'. The browser id name/value will be looked
lookup ordering schemes involving cookies and forms 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 Cookie path -- this is the 'path' element which should be sent
in the session token cookie. For more information, see the in the session token cookie. For more information, see the
......
...@@ -18,7 +18,7 @@ from Testing import makerequest ...@@ -18,7 +18,7 @@ from Testing import makerequest
import ZODB # in order to get Persistence.Persistent working import ZODB # in order to get Persistence.Persistent working
import Acquisition import Acquisition
from Acquisition import aq_base from Acquisition import aq_base
from Products.Sessions.BrowserIdManager import BrowserIdManager from Products.Sessions.BrowserIdManager import BrowserIdManager, getNewBrowserId
from Products.Sessions.SessionDataManager import \ from Products.Sessions.SessionDataManager import \
SessionDataManager, SessionDataManagerErr SessionDataManager, SessionDataManagerErr
from Products.Transience.Transience import \ from Products.Transience.Transience import \
...@@ -149,7 +149,7 @@ class BaseReaderWriter(threading.Thread): ...@@ -149,7 +149,7 @@ class BaseReaderWriter(threading.Thread):
self.conn = db.open() self.conn = db.open()
self.app = self.conn.root()['Application'] self.app = self.conn.root()['Application']
self.app = makerequest.makerequest(self.app) self.app = makerequest.makerequest(self.app)
token = self.app.browser_id_manager._getNewBrowserId() token = getNewBrowserId()
self.app.REQUEST.browser_id_ = token self.app.REQUEST.browser_id_ = token
self.iters = iters self.iters = iters
self.sdm_name = sdm_name self.sdm_name = sdm_name
......
...@@ -13,30 +13,41 @@ ...@@ -13,30 +13,41 @@
""" """
Test suite for session id manager. 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 sys
import ZODB 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 unittest import TestCase, TestSuite, TextTestRunner, makeSuite
from ZPublisher.HTTPRequest import HTTPRequest from ZPublisher.HTTPRequest import HTTPRequest
from ZPublisher.HTTPResponse import HTTPResponse from ZPublisher.HTTPResponse import HTTPResponse
from ZPublisher.BeforeTraverse import queryBeforeTraverse
from sys import stdin from sys import stdin
from os import environ from os import environ
from OFS.Application import Application
class TestBrowserIdManager(TestCase): class TestBrowserIdManager(TestCase):
def setUp(self): 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() resp = HTTPResponse()
environ['SERVER_NAME']='fred' environ['SERVER_NAME']='fred'
environ['SERVER_PORT']='80' environ['SERVER_PORT']='80'
req = HTTPRequest(stdin, environ, resp) self.req = HTTPRequest(stdin, environ, resp)
self.m.REQUEST = req self.req['TraversalRequestNameStack'] = ['foo', 'bar']
self.app.REQUEST = self.req
def tearDown(self): def tearDown(self):
del self.m del self.m
self.app.REQUEST = None
del self.app
def testSetBrowserIdName(self): def testSetBrowserIdName(self):
self.m.setBrowserIdName('foo') self.m.setBrowserIdName('foo')
...@@ -49,30 +60,16 @@ class TestBrowserIdManager(TestCase): ...@@ -49,30 +60,16 @@ class TestBrowserIdManager(TestCase):
lambda self=self: self.m.setBrowserIdName(1)) lambda self=self: self.m.setBrowserIdName(1))
def testSetBadNamespaces(self): def testSetBadNamespaces(self):
d = {1:'gummy', 2:'froopy'} d = ('gummy', 'froopy')
self.assertRaises(BrowserIdManagerErr, self.assertRaises(BrowserIdManagerErr,
lambda self=self,d=d: lambda self=self,d=d:
self.m.setBrowserIdNamespaces(d)) self.m.setBrowserIdNamespaces(d))
def testSetGoodNamespaces(self): def testSetGoodNamespaces(self):
d = {1:'cookies', 2:'form'} d = ('cookies', 'url', 'form')
self.m.setBrowserIdNamespaces(d) self.m.setBrowserIdNamespaces(d)
self.failUnless(self.m.getBrowserIdNamespaces() == 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): def testSetBadCookiePath(self):
path = '/;' path = '/;'
self.assertRaises(BrowserIdManagerErr, self.assertRaises(BrowserIdManagerErr,
...@@ -148,33 +145,13 @@ class TestBrowserIdManager(TestCase): ...@@ -148,33 +145,13 @@ class TestBrowserIdManager(TestCase):
a = self.m.getBrowserId() a = self.m.getBrowserId()
self.failUnless( self.m.isBrowserIdNew() ) 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): def testIsBrowserIdFromCookieOnly(self):
token = self.m.getBrowserId() token = self.m.getBrowserId()
self.m.REQUEST.browser_id_ = token self.m.REQUEST.browser_id_ = token
self.m.REQUEST.browser_id_ns_ = 'cookies' self.m.REQUEST.browser_id_ns_ = 'cookies'
tokenkey = self.m.getBrowserIdName() tokenkey = self.m.getBrowserIdName()
self.m.REQUEST.form[tokenkey] = token self.m.REQUEST.form[tokenkey] = token
self.m.setBrowserIdNamespaces({1:'cookies'}) self.m.setBrowserIdNamespaces(('cookies',))
a = self.m.getBrowserId() a = self.m.getBrowserId()
self.failUnless( self.m.isBrowserIdFromCookie() ) self.failUnless( self.m.isBrowserIdFromCookie() )
self.failUnless( not self.m.isBrowserIdFromForm() ) self.failUnless( not self.m.isBrowserIdFromForm() )
...@@ -185,11 +162,20 @@ class TestBrowserIdManager(TestCase): ...@@ -185,11 +162,20 @@ class TestBrowserIdManager(TestCase):
self.m.REQUEST.browser_id_ns_ = 'form' self.m.REQUEST.browser_id_ns_ = 'form'
tokenkey = self.m.getBrowserIdName() tokenkey = self.m.getBrowserIdName()
self.m.REQUEST.form[tokenkey] = token self.m.REQUEST.form[tokenkey] = token
self.m.setBrowserIdNamespaces({1:'form'}) self.m.setBrowserIdNamespaces(('form',))
a = self.m.getBrowserId() a = self.m.getBrowserId()
self.failUnless( not self.m.isBrowserIdFromCookie() ) self.failUnless( not self.m.isBrowserIdFromCookie() )
self.failUnless( self.m.isBrowserIdFromForm() ) 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): def testFlushBrowserIdCookie(self):
token = self.m.getBrowserId() token = self.m.getBrowserId()
self.m.REQUEST.browser_id_ = token self.m.REQUEST.browser_id_ = token
...@@ -226,6 +212,8 @@ class TestBrowserIdManager(TestCase): ...@@ -226,6 +212,8 @@ class TestBrowserIdManager(TestCase):
u = 'http://www.zope.org/Members/mcdonc?foo=bar&spam=eggs' u = 'http://www.zope.org/Members/mcdonc?foo=bar&spam=eggs'
r = self.m.encodeUrl(u) r = self.m.encodeUrl(u)
self.failUnless( r == '%s&amp;%s=%s' % (u, keystring, key) ) 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): def testGetHiddenFormField(self):
keystring = self.m.getBrowserIdName() keystring = self.m.getBrowserIdName()
...@@ -235,6 +223,47 @@ class TestBrowserIdManager(TestCase): ...@@ -235,6 +223,47 @@ class TestBrowserIdManager(TestCase):
(keystring, key)) (keystring, key))
self.failUnless( html == expected ) 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(): def test_suite():
testsuite = makeSuite(TestBrowserIdManager, 'test') testsuite = makeSuite(TestBrowserIdManager, 'test')
return testsuite 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