From: Jens W. K. <je...@pl...> - 2015-06-02 13:11:04
|
Repository: plone.session Branch: refs/heads/cleanup Date: 2015-06-02T15:10:42+02:00 Author: Jens W. Klein (jensens) <jk...@kl...> Commit: https://github.com/plone/plone.session/commit/a334ba868f3c94a8ac024572074887ef8aa39114 cleanup: followpep8, plone code conventions, better readability Files changed: M CHANGES.rst M plone/session/__init__.py M plone/session/configure.zcml M plone/session/interfaces.py M plone/session/plugins/__init__.py M plone/session/plugins/session.py M plone/session/profiles.zcml M plone/session/resources.zcml M plone/session/tktauth.py diff --git a/CHANGES.rst b/CHANGES.rst index 2a94aa4..8544186 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,7 +4,8 @@ Changelog 3.5.6 (unreleased) ------------------ -- Nothing changed yet. +- Cleanup: Pep8, plone style conventions, better readbility. + [jensens] 3.5.5 (2015-04-29) diff --git a/plone/session/__init__.py b/plone/session/__init__.py index c081135..e111fe7 100644 --- a/plone/session/__init__.py +++ b/plone/session/__init__.py @@ -1,15 +1,18 @@ +# -*- coding: utf-8 -*- from AccessControl.Permissions import add_user_folders +from plone.session.plugins import session from Products.PluggableAuthService.PluggableAuthService import \ registerMultiPlugin -from plone.session.plugins import session - registerMultiPlugin(session.SessionPlugin.meta_type) def initialize(context): - context.registerClass(session.SessionPlugin, - permission = add_user_folders, - constructors = (session.manage_addSessionPluginForm, - session.manage_addSessionPlugin), - visibility = None) + context.registerClass( + session.SessionPlugin, + permission=add_user_folders, + constructors=( + session.manage_addSessionPluginForm, + session.manage_addSessionPlugin), + visibility=None + ) diff --git a/plone/session/configure.zcml b/plone/session/configure.zcml index ea77bd0..6a4af78 100644 --- a/plone/session/configure.zcml +++ b/plone/session/configure.zcml @@ -3,15 +3,17 @@ xmlns:five="http://namespaces.zope.org/five" xmlns:zcml="http://namespaces.zope.org/zcml"> - <five:registerPackage package="." initialize=".initialize" /> + <five:registerPackage + initialize=".initialize" + package="." + /> - <include package="plone.protect" /> + <include package="plone.protect" /> + <include file="resources.zcml" /> - <include file="resources.zcml" /> - - <include - zcml:condition="installed Products.CMFPlone" - file="profiles.zcml" - /> + <include + file="profiles.zcml" + zcml:condition="installed Products.CMFPlone" + /> </configure> diff --git a/plone/session/interfaces.py b/plone/session/interfaces.py index e420722..4f836a4 100644 --- a/plone/session/interfaces.py +++ b/plone/session/interfaces.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- from zope.interface import Interface diff --git a/plone/session/plugins/__init__.py b/plone/session/plugins/__init__.py index 792d600..40a96af 100644 --- a/plone/session/plugins/__init__.py +++ b/plone/session/plugins/__init__.py @@ -1 +1 @@ -# +# -*- coding: utf-8 -*- diff --git a/plone/session/plugins/session.py b/plone/session/plugins/session.py index 3f70a56..5bcacc4 100644 --- a/plone/session/plugins/session.py +++ b/plone/session/plugins/session.py @@ -1,19 +1,22 @@ -from AccessControl.SecurityInfo import ClassSecurityInfo +# -*- coding: utf-8 -*- from AccessControl.requestmethod import postonly +from AccessControl.SecurityInfo import ClassSecurityInfo from App.config import getConfiguration -from Products.PageTemplates.PageTemplateFile import PageTemplateFile -from Products.PluggableAuthService.interfaces.plugins \ - import IExtractionPlugin, IAuthenticationPlugin, \ - ICredentialsResetPlugin, ICredentialsUpdatePlugin -from Products.PluggableAuthService.permissions import ManageUsers -from Products.PluggableAuthService.plugins.BasePlugin import BasePlugin -from Products.PluggableAuthService.utils import classImplements +from email.Utils import formatdate from plone.keyring.interfaces import IKeyManager from plone.session import tktauth from plone.session.interfaces import ISessionPlugin -from zope.component import getUtility, queryUtility +from Products.PageTemplates.PageTemplateFile import PageTemplateFile +from Products.PluggableAuthService.interfaces.plugins import IAuthenticationPlugin # noqa +from Products.PluggableAuthService.interfaces.plugins import ICredentialsResetPlugin # noqa +from Products.PluggableAuthService.interfaces.plugins import ICredentialsUpdatePlugin # noqa +from Products.PluggableAuthService.interfaces.plugins import IExtractionPlugin +from Products.PluggableAuthService.permissions import ManageUsers +from Products.PluggableAuthService.plugins.BasePlugin import BasePlugin +from zope.component import getUtility +from zope.component import queryUtility +from zope.interface import implementer -from email.Utils import formatdate import binascii import time @@ -29,13 +32,16 @@ def manage_addSessionPlugin(dispatcher, id, title=None, path='/', REQUEST=None): """Add a session plugin.""" - sp=SessionPlugin(id, title=title, path=path) + sp = SessionPlugin(id, title=title, path=path) dispatcher._setObject(id, sp) if REQUEST is not None: - REQUEST.response.redirect('%s/manage_workspace?' - 'manage_tabs_message=Session+plugin+created.' % - dispatcher.absolute_url()) + REQUEST.response.redirect( + '{0}/manage_workspace?' + 'manage_tabs_message=Session+plugin+created.'.format( + dispatcher.absolute_url() + ) + ) def cookie_expiration_date(days): @@ -43,6 +49,13 @@ def cookie_expiration_date(days): return formatdate(expires, usegmt=True) +@implementer( + ISessionPlugin, + IExtractionPlugin, + IAuthenticationPlugin, + ICredentialsResetPlugin, + ICredentialsUpdatePlugin +) class SessionPlugin(BasePlugin): """Session authentication plugin. """ @@ -54,66 +67,66 @@ class SessionPlugin(BasePlugin): cookie_lifetime = 0 cookie_domain = '' mod_auth_tkt = False - timeout = 12*60*60 # 12h. Default is 2h in mod_auth_tkt - refresh_interval = 1*60*60 # -1 to disable + timeout = 12*60*60 # 12h. Default is 2h in mod_auth_tkt + refresh_interval = 1*60*60 # -1 to disable external_ticket_name = 'ticket' secure = False _shared_secret = None - # These mod_auth_tkt options are not yet implemented - #ignoreIP = True # you always want this on the public internet - #timeoutRefresh = 0 # default is 0.5 in mod_auth_tkt + # These mod_auth_tkt options are not yet implemented (by intent) + # ignoreIP = True # you always want this on the public internet + # timeoutRefresh = 0 # default is 0.5 in mod_auth_tkt _properties = ( - { - "id": "timeout", - "label": "Cookie validity timeout (in seconds)", - "type": "int", - "mode": "w", - }, - { - "id": "refresh_interval", - "label": "Refresh interval (in seconds, -1 to disable refresh)", - "type": "int", - "mode": "w", - }, - { - "id": "mod_auth_tkt", - "label": "Use mod_auth_tkt compatible hashing algorithm", - "type": "boolean", - "mode": "w", - }, - { - "id": "cookie_name", - "label": "Cookie name", - "type": "string", - "mode": "w", - }, - { - "id": "cookie_lifetime", - "label": "Cookie lifetime (in days)", - "type": "int", - "mode": "w", - }, - { - "id": "cookie_domain", - "label": "Cookie domain (blank for default)", - "type": "string", - "mode": "w", - }, - { - "id": "path", - "label": "Cookie path", - "type": "string", - "mode": "w", - }, - { - "id": "secure", - "label": "Only Send Cookie Over HTTPS", - "type": "boolean", - "mode": "w", - }, - ) + { + "id": "timeout", + "label": "Cookie validity timeout (in seconds)", + "type": "int", + "mode": "w", + }, + { + "id": "refresh_interval", + "label": "Refresh interval (in seconds, -1 to disable refresh)", + "type": "int", + "mode": "w", + }, + { + "id": "mod_auth_tkt", + "label": "Use mod_auth_tkt compatible hashing algorithm", + "type": "boolean", + "mode": "w", + }, + { + "id": "cookie_name", + "label": "Cookie name", + "type": "string", + "mode": "w", + }, + { + "id": "cookie_lifetime", + "label": "Cookie lifetime (in days)", + "type": "int", + "mode": "w", + }, + { + "id": "cookie_domain", + "label": "Cookie domain (blank for default)", + "type": "string", + "mode": "w", + }, + { + "id": "path", + "label": "Cookie path", + "type": "string", + "mode": "w", + }, + { + "id": "secure", + "label": "Only Send Cookie Over HTTPS", + "type": "boolean", + "mode": "w", + }, + ) manage_options = ( dict(label='Manage secrets', action='manage_secret'), @@ -121,13 +134,13 @@ class SessionPlugin(BasePlugin): def __init__(self, id, title=None, path="/"): self._setId(id) - self.title=title - self.path=path + self.title = title + self.path = path def _getSigningSecret(self): if self._shared_secret is not None: return self._shared_secret - manager=getUtility(IKeyManager) + manager = getUtility(IKeyManager) return manager.secret() # ISessionPlugin implementation @@ -142,7 +155,7 @@ def _setupSession(self, userid, response, tokens=(), user_data=''): self._setCookie(cookie, response) def _setCookie(self, cookie, response): - cookie=binascii.b2a_base64(cookie).rstrip() + cookie = binascii.b2a_base64(cookie).rstrip() # disable secure cookie in development mode, to ease local testing if getConfiguration().debug_mode: secure = False @@ -157,34 +170,36 @@ def _setCookie(self, cookie, response): # IExtractionPlugin implementation def extractCredentials(self, request): - creds={} + creds = {} - if not self.cookie_name in request: + if self.cookie_name not in request: return creds try: - creds["cookie"]=binascii.a2b_base64(request.get(self.cookie_name)) + creds["cookie"] = binascii.a2b_base64( + request.get(self.cookie_name) + ) except binascii.Error: # If we have a cookie which is not properly base64 encoded it # can not be ours. return creds - creds["source"]="plone.session" # XXX should this be the id? + creds["source"] = "plone.session" # XXX should this be the id? return creds # IAuthenticationPlugin implementation def authenticateCredentials(self, credentials): - if not credentials.get("source", None)=="plone.session": + if not credentials.get("source", None) == "plone.session": return None - ticket=credentials["cookie"] + ticket = credentials["cookie"] ticket_data = self._validateTicket(ticket) if ticket_data is None: return None (digest, userid, tokens, user_data, timestamp) = ticket_data - pas=self._getPAS() - info=pas._verifyUser(pas.plugins, user_id=userid) + pas = self._getPAS() + info = pas._verifyUser(pas.plugins, user_id=userid) if info is None: return None @@ -195,8 +210,13 @@ def _validateTicket(self, ticket, now=None): if now is None: now = time.time() if self._shared_secret is not None: - ticket_data = tktauth.validateTicket(self._shared_secret, ticket, - timeout=self.timeout, now=now, mod_auth_tkt=self.mod_auth_tkt) + ticket_data = tktauth.validateTicket( + self._shared_secret, + ticket, + timeout=self.timeout, + now=now, + mod_auth_tkt=self.mod_auth_tkt + ) else: ticket_data = None manager = queryUtility(IKeyManager) @@ -205,23 +225,28 @@ def _validateTicket(self, ticket, now=None): for secret in manager[u"_system"]: if secret is None: continue - ticket_data = tktauth.validateTicket(secret, ticket, - timeout=self.timeout, now=now, mod_auth_tkt=self.mod_auth_tkt) + ticket_data = tktauth.validateTicket( + secret, + ticket, + timeout=self.timeout, + now=now, + mod_auth_tkt=self.mod_auth_tkt + ) if ticket_data is not None: break return ticket_data # ICredentialsUpdatePlugin implementation def updateCredentials(self, request, response, login, new_password): - pas=self._getPAS() - info=pas._verifyUser(pas.plugins, login=login) + pas = self._getPAS() + info = pas._verifyUser(pas.plugins, login=login) if info is not None: # Only setup a session for users in our own user folder. self._setupSession(info["id"], response) # ICredentialsResetPlugin implementation def resetCredentials(self, request, response): - response=self.REQUEST["RESPONSE"] + response = self.REQUEST["RESPONSE"] if self.cookie_domain: response.expireCookie( self.cookie_name, path=self.path, domain=self.cookie_domain) @@ -230,35 +255,39 @@ def resetCredentials(self, request, response): manage_secret = PageTemplateFile("secret.pt", globals()) - security.declareProtected(ManageUsers, 'manage_clearSecrets') + @security.protected(ManageUsers) @postonly def manage_clearSecrets(self, REQUEST): """Clear all secrets from this source. This invalidates all current sessions and requires users to login again. """ - manager=getUtility(IKeyManager) + manager = getUtility(IKeyManager) manager.clear() manager.rotate() response = REQUEST.response - response.redirect('%s/manage_secret?manage_tabs_message=%s' % - (self.absolute_url(), 'All+secrets+cleared.')) + response.redirect( + '%s/manage_secret?manage_tabs_message=%s' % + (self.absolute_url(), 'All+secrets+cleared.') + ) - security.declareProtected(ManageUsers, 'manage_createNewSecret') + @security.protected(ManageUsers) @postonly def manage_createNewSecret(self, REQUEST): """Create a new (signing) secret. """ - manager=getUtility(IKeyManager) + manager = getUtility(IKeyManager) manager.rotate() response = REQUEST.response - response.redirect('%s/manage_secret?manage_tabs_message=%s' % - (self.absolute_url(), 'New+secret+created.')) + response.redirect( + '%s/manage_secret?manage_tabs_message=%s' % + (self.absolute_url(), 'New+secret+created.') + ) - security.declareProtected(ManageUsers, 'haveSharedSecret') + @security.protected(ManageUsers) def haveSharedSecret(self): return self._shared_secret is not None - security.declareProtected(ManageUsers, 'manage_removeSharedSecret') + @security.protected(ManageUsers) @postonly def manage_removeSharedSecret(self, REQUEST): """Clear the shared secret. This invalidates all current sessions and @@ -266,10 +295,12 @@ def manage_removeSharedSecret(self, REQUEST): """ self._shared_secret = None response = REQUEST.response - response.redirect('%s/manage_secret?manage_tabs_message=%s' % - (self.absolute_url(), 'Shared+secret+removed.')) + response.redirect( + '%s/manage_secret?manage_tabs_message=%s' % + (self.absolute_url(), 'Shared+secret+removed.') + ) - security.declareProtected(ManageUsers, 'manage_setSharedSecret') + @security.protected(ManageUsers) @postonly def manage_setSharedSecret(self, REQUEST): """Set the shared secret. @@ -277,16 +308,20 @@ def manage_setSharedSecret(self, REQUEST): secret = REQUEST.get('shared_secret') response = REQUEST.response if not secret: - response.redirect('%s/manage_secret?manage_tabs_message=%s' % - (self.absolute_url(), 'Shared+secret+must+not+be+blank.')) + response.redirect( + '%s/manage_secret?manage_tabs_message=%s' % + (self.absolute_url(), 'Shared+secret+must+not+be+blank.') + ) return self._shared_secret = secret - response.redirect('%s/manage_secret?manage_tabs_message=%s' % - (self.absolute_url(), 'New+shared+secret+set.')) + response.redirect( + '%s/manage_secret?manage_tabs_message=%s' % + (self.absolute_url(), 'New+shared+secret+set.') + ) def _refreshSession(self, request, now=None): # Refresh a ticket. Does *not* check the user is in the use folder - if not self.cookie_name in request: + if self.cookie_name not in request: return None try: ticket = binascii.a2b_base64(request.get(self.cookie_name)) @@ -313,12 +348,8 @@ def _refresh_content(self, REQUEST): elif type == 'js': setHeader('Content-Type', 'text/javascript') return "" - #if content: - # return "still_logged_in = still_logged_in;\n" - #else: - # return "still_logged_in = false;\n" - security.declarePublic('refresh') + @security.public def refresh(self, REQUEST): """Refresh the cookie""" setHeader = REQUEST.response.setHeader @@ -330,23 +361,28 @@ def refresh(self, REQUEST): refreshed = self._refreshSession(REQUEST, now) if not refreshed: # We have an unauthenticated user - setHeader('Cache-Control', 'public, must-revalidate, max-age=%d, s-max-age=86400' % self.refresh_interval) + setHeader( + 'Cache-Control', + 'public, must-revalidate, max-age=%d, s-max-age=86400' % + self.refresh_interval + ) setHeader('Vary', 'Cookie') else: - setHeader('Cache-Control', 'private, must-revalidate, proxy-revalidate, max-age=%d, s-max-age=0' % self.refresh_interval) + setHeader( + 'Cache-Control', + 'private, must-revalidate, proxy-revalidate, max-age=%d, ' + 's-max-age=0' % self.refresh_interval) return self._refresh_content(REQUEST) - security.declarePublic('remove') + @security.public def remove(self, REQUEST): """Remove the cookie""" self.resetCredentials(REQUEST, REQUEST.response) setHeader = REQUEST.response.setHeader # Disable HTTP 1.0 Caching setHeader('Expires', formatdate(0, usegmt=True)) - setHeader('Cache-Control', 'public, must-revalidate, max-age=0, s-max-age=86400') + setHeader( + 'Cache-Control', + 'public, must-revalidate, max-age=0, s-max-age=86400' + ) return self._refresh_content(REQUEST) - - -classImplements(SessionPlugin, ISessionPlugin, - IExtractionPlugin, IAuthenticationPlugin, - ICredentialsResetPlugin, ICredentialsUpdatePlugin) diff --git a/plone/session/profiles.zcml b/plone/session/profiles.zcml index dcdce44..01aec3d 100644 --- a/plone/session/profiles.zcml +++ b/plone/session/profiles.zcml @@ -1,13 +1,13 @@ <configure - xmlns="http://namespaces.zope.org/genericsetup" - i18n_domain="plone"> + i18n_domain="plone" + xmlns="http://namespaces.zope.org/genericsetup"> - <registerProfile - name="default" - title="Session refresh support" - directory="profiles/default" - description="Optional plone.session refresh support." - provides="Products.GenericSetup.interfaces.EXTENSION" - /> + <registerProfile + description="Optional plone.session refresh support." + directory="profiles/default" + name="default" + provides="Products.GenericSetup.interfaces.EXTENSION" + title="Session refresh support" + /> </configure> diff --git a/plone/session/resources.zcml b/plone/session/resources.zcml index 5ea96da..a940585 100644 --- a/plone/session/resources.zcml +++ b/plone/session/resources.zcml @@ -1,8 +1,8 @@ <configure xmlns="http://namespaces.zope.org/browser"> - <resource - name="plone.session.refreshsupport.js" - file="resources/refreshsupport.js" - /> + <resource + file="resources/refreshsupport.js" + name="plone.session.refreshsupport.js" + /> </configure> diff --git a/plone/session/tktauth.py b/plone/session/tktauth.py index 6e56be6..e5f8989 100755 --- a/plone/session/tktauth.py +++ b/plone/session/tktauth.py @@ -46,7 +46,9 @@ We will create a mod_auth_tkt compatible ticket. In the simplest case no extra data is supplied. - >>> tkt = createTicket(SECRET, userid, timestamp=timestamp, mod_auth_tkt=True) + >>> tkt = createTicket( + ... SECRET, userid, timestamp=timestamp, mod_auth_tkt=True + ... ) >>> tkt 'c7c7300ac5cf529656444123aca345294885afa0jbloggs!' @@ -77,14 +79,18 @@ We will validate it an hour after it was created: >>> NOW = timestamp + 60*60 - >>> data = validateTicket(SECRET, tkt, timeout=TIMEOUT, now=NOW, mod_auth_tkt=True) + >>> data = validateTicket( + ... SECRET, tkt, timeout=TIMEOUT, now=NOW, mod_auth_tkt=True + ... ) >>> data is not None True After the timeout the ticket is no longer valid >>> LATER = NOW + TIMEOUT - >>> data = validateTicket(SECRET, tkt, timeout=TIMEOUT, now=LATER, mod_auth_tkt=True) + >>> data = validateTicket( + ... SECRET, tkt, timeout=TIMEOUT, now=LATER, mod_auth_tkt=True + ... ) >>> data is not None False @@ -98,13 +104,18 @@ >>> user_data = 'Joe Bloggs' >>> tokens = ['foo', 'bar'] - >>> tkt = createTicket(SECRET, userid, tokens, user_data, timestamp=timestamp, mod_auth_tkt=True) + >>> tkt = createTicket( + ... SECRET, userid, tokens, user_data, timestamp=timestamp, + ... mod_auth_tkt=True + ... ) >>> tkt 'eea3630e98177bdbf0e7f803e1632b7e4885afa0jbloggs!foo,bar!Joe Bloggs' >>> cookie['auth_tkt'] = binascii.b2a_base64(tkt).strip() >>> print cookie Set-Cookie: auth_tkt=ZWVhMzYzMGU5ODE3N2JkYmYwZTdmODAzZTE2MzJiN2U0ODg1YWZh... - >>> data = validateTicket(SECRET, tkt, timeout=TIMEOUT, now=NOW, mod_auth_tkt=True) + >>> data = validateTicket( + ... SECRET, tkt, timeout=TIMEOUT, now=NOW, mod_auth_tkt=True + ... ) >>> data ('eea3630e98177bdbf0e7f803e1632b7e', 'jbloggs', ('foo', 'bar'), 'Joe Bloggs', 1216720800) @@ -148,7 +159,8 @@ def mod_auth_tkt_digest(secret, data1, data2): return digest -def createTicket(secret, userid, tokens=(), user_data='', ip='0.0.0.0', timestamp=None, encoding='utf-8', mod_auth_tkt=False): +def createTicket(secret, userid, tokens=(), user_data='', ip='0.0.0.0', + timestamp=None, encoding='utf-8', mod_auth_tkt=False): """ By default, use a more compatible """ @@ -193,7 +205,7 @@ def splitTicket(ticket, encoding=None): remainder = ticket[40:] if not val: raise ValueError - timestamp = int(val, 16) # convert from hexadecimal+ + timestamp = int(val, 16) # convert from hexadecimal+ if encoding is not None: remainder = remainder.decode(encoding) @@ -218,8 +230,16 @@ def validateTicket(secret, ticket, ip='0.0.0.0', timeout=0, now=None, splitTicket(ticket) except ValueError: return None - new_ticket = createTicket(secret, userid, tokens, - user_data, ip, timestamp, encoding, mod_auth_tkt) + new_ticket = createTicket( + secret, + userid, + tokens, + user_data, + ip, + timestamp, + encoding, + mod_auth_tkt + ) if is_equal(new_ticket[:32], digest): if not timeout: return data @@ -233,7 +253,9 @@ def validateTicket(secret, ticket, ip='0.0.0.0', timeout=0, now=None, # doctest runner def _test(): import doctest - doctest.testmod(optionflags=doctest.ELLIPSIS + doctest.NORMALIZE_WHITESPACE) + doctest.testmod( + optionflags=doctest.ELLIPSIS + doctest.NORMALIZE_WHITESPACE + ) if __name__ == "__main__": _test() |