[Checkins] SVN: Sandbox/J1m/session. Adding code to influence the
session-credential extractor in zope.app.session
Jim Fulton
jim at zope.com
Thu Apr 3 09:57:43 EDT 2008
Log message for revision 85080:
Adding code to influence the session-credential extractor in zope.app.session
Changed:
A Sandbox/J1m/session.py
A Sandbox/J1m/session.test
-=-
Added: Sandbox/J1m/session.py
===================================================================
--- Sandbox/J1m/session.py (rev 0)
+++ Sandbox/J1m/session.py 2008-04-03 13:57:43 UTC (rev 85080)
@@ -0,0 +1,97 @@
+##############################################################################
+#
+# Copyright (c) 2005 Zope Corporation. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Visible Source
+# License, Version 1.0 (ZVSL). A copy of the ZVSL should accompany this
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE
+#
+##############################################################################
+
+import sha
+
+import zope.publisher.interfaces.http
+
+import zope.app.authentication.session
+import zope.session.interfaces
+import zope.app.http.httpdate
+
+class CredentialsDontMakeSecurityDeclarationsForMe:
+ # Credentials class. We use this rather than a dict to prevent
+ # leakage to untrusted code. As long as no one is fool enough to
+ # make security declarations for this then untrusted code will get
+ # forbidden errors trying to access data.
+
+ domain = None
+
+ def __init__(self, **kw):
+ self.__dict__.update(kw)
+
+
+class SessionCredentialsPlugin(
+ zope.app.authentication.session.SessionCredentialsPlugin,
+ ):
+
+ _fields = ('login', 'login.login'), ('password', 'login.password')
+
+ def extractCredentials(self, request):
+ """Extracts credentials from a session if they exist."""
+
+ if not zope.publisher.interfaces.http.IHTTPRequest.providedBy(request):
+ return None
+
+ data = dict((k, request[rk]) for (k, rk) in self._fields
+ if rk in request)
+ credentials = None
+
+ session = zope.session.interfaces.ISession(request)
+
+ if len(data) == len(self._fields):
+ data['sha'] = sha.new(data.pop('password').encode('utf-8')
+ ).hexdigest()
+ self.save_credentials(data, session)
+ data['logging_in'] = True
+ return self._update_cookie(request, data)
+
+ sessionData = session.get('zope.app.authentication.browserplugins')
+ if sessionData:
+ return self._update_cookie(request,
+ sessionData.get('credentials').__dict__)
+
+ return None
+
+ def _update_cookie(self, request, credentials):
+ if credentials:
+ domain = credentials.get('domain')
+ if domain and (request.cookies.get('login.domain') != domain):
+ request.response.setCookie(
+ 'login.domain', domain,
+ expires = 'Wed, 01-Jan-3000 00:00:00 GMT',
+ )
+ credentials['request-annotations'] = request.annotations
+ return credentials
+
+ def save_credentials(self, credentials, session=None, request=None):
+ if session is None:
+ session = zope.session.interfaces.ISession(request)
+ sessionData = session['zope.app.authentication.browserplugins']
+ sessionData['credentials'] = (
+ CredentialsDontMakeSecurityDeclarationsForMe(**credentials)
+ )
+
+ def logout(self, request):
+ self.save_credentials({}, request=request)
+
+ def challenge(self, request):
+ if 'login.ignore' in request:
+ return False
+ return super(SessionCredentialsPlugin, self).challenge(request)
+
+class DomainSessionCredentialsPlugin(SessionCredentialsPlugin):
+
+ _fields = SessionCredentialsPlugin._fields + (('domain', 'login.domain'),)
Property changes on: Sandbox/J1m/session.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Added: Sandbox/J1m/session.test
===================================================================
--- Sandbox/J1m/session.test (rev 0)
+++ Sandbox/J1m/session.test 2008-04-03 13:57:43 UTC (rev 85080)
@@ -0,0 +1,151 @@
+Credentials extraction
+======================
+
+We provide custom credentials extractors, one for applications to
+use and one for the dashboard or other apps that let people log in
+from multiple domains.
+
+Our credentials plugin subclasses the zope.app.authentication.session
+plugin and overrides credentials extraction. In addition to supporting
+domains, when necessary, it also stores sha-encoded passwords, rather
+than storing passwords in the clear.
+
+We'll look first at the normal credentials extractor:
+
+ >>> request = TestRequest()
+ >>> import session
+ >>> plugin = session.SessionCredentialsPlugin()
+ >>> plugin.extractCredentials(request) is None
+ True
+
+If we provide credentials in the request:
+
+ >>> request.form['login.login'] = 'sample-login'
+ >>> request.form['login.password'] = '123'
+
+Then they will be extracted.
+
+ >>> from pprint import pprint
+ >>> pprint(plugin.extractCredentials(request), width=1)
+ {'logging_in': True,
+ 'login': 'sample-login',
+ 'request-annotations': {},
+ 'sha': '40bd001563085fc35165329ea1ff5c5ecbdbbeef'}
+
+Note that the credentials have the sha-encoded password, not the
+password itself. Also, if credentials come from the request, then a
+logging_in flag is set to indicate that the user is logging in.
+Finally, the request annotations are included to provide a way to
+comminivate back to the login view.
+
+If we create a new request, the credentials will be retrieved from
+session data:
+
+ >>> request = TestRequest()
+ >>> pprint(plugin.extractCredentials(request), width=1)
+ {'login': 'sample-login',
+ 'request-annotations': {},
+ 'sha': '40bd001563085fc35165329ea1ff5c5ecbdbbeef'}
+
+Credentials are stored in session data. Session data based on a
+request identifier of some sort. In this test environment, our session
+data is based on request ids, which default to 0. If we change the
+request ids to something else, then we won't get credentials from the
+session:
+
+ >>> request.form['id'] = 1
+ >>> plugin.extractCredentials(request) is None
+ True
+
+Of course, if we provide some credentials, then the will be saved
+under that id:
+
+ >>> request.form['login.login'] = 'other-login'
+ >>> request.form['login.password'] = 'abc'
+ >>> pprint(plugin.extractCredentials(request), width=1)
+ {'logging_in': True,
+ 'login': 'other-login',
+ 'request-annotations': {},
+ 'sha': 'a9993e364706816aba3e25717850c26c9cd0d89d'}
+
+ >>> request = TestRequest()
+ >>> pprint(plugin.extractCredentials(request), width=1)
+ {'login': 'sample-login',
+ 'request-annotations': {},
+ 'sha': '40bd001563085fc35165329ea1ff5c5ecbdbbeef'}
+
+ >>> request.form['id'] = 1
+ >>> pprint(plugin.extractCredentials(request), width=1)
+ {'login': 'other-login',
+ 'request-annotations': {},
+ 'sha': 'a9993e364706816aba3e25717850c26c9cd0d89d'}
+
+ >>> request.form['id'] = 2
+ >>> plugin.extractCredentials(request) is None
+ True
+
+Collecting domains
+------------------
+
+The regular extractor ignores domain information:
+
+ >>> request = TestRequest()
+ >>> request.form['login.domain'] = 'sample-domain'
+ >>> request.form['login.login'] = 'sample-login'
+ >>> request.form['login.password'] = '123'
+ >>> pprint(plugin.extractCredentials(request), width=1)
+ {'logging_in': True,
+ 'login': 'sample-login',
+ 'request-annotations': {},
+ 'sha': '40bd001563085fc35165329ea1ff5c5ecbdbbeef'}
+
+The DomainSessionCredentialsPlugin is aware of domains.
+
+ >>> dplugin = session.DomainSessionCredentialsPlugin()
+ >>> pprint(dplugin.extractCredentials(request), width=1)
+ {'domain': 'sample-domain',
+ 'logging_in': True,
+ 'login': 'sample-login',
+ 'request-annotations': {},
+ 'sha': '40bd001563085fc35165329ea1ff5c5ecbdbbeef'}
+
+ >>> request = TestRequest()
+ >>> pprint(dplugin.extractCredentials(request), width=1)
+ {'domain': 'sample-domain',
+ 'login': 'sample-login',
+ 'request-annotations': {},
+ 'sha': '40bd001563085fc35165329ea1ff5c5ecbdbbeef'}
+
+Note that as a convenience to users, the plugin sets the domain as a
+cookie:
+
+ >>> request.response.getCookie('login.domain')
+ {'expires': 'Wed, 01-Jan-3000 00:00:00 GMT', 'value': 'sample-domain'}
+
+Changing credentials
+--------------------
+
+Applications for changing user credentials will want to keep people
+logged in by setting the new credentials in the session.
+
+ >>> dplugin.save_credentials(dict(
+ ... domain='ddd', login='bob',
+ ... sha='afc97ea131fd7e2695a98ef34013608f97f34e1d'
+ ... ), request=request)
+
+ >>> pprint(dplugin.extractCredentials(request), width=1)
+ {'domain': 'ddd',
+ 'login': 'bob',
+ 'request-annotations': {},
+ 'sha': 'afc97ea131fd7e2695a98ef34013608f97f34e1d'}
+
+ >>> request.response.getCookie('login.domain')
+ {'expires': 'Wed, 01-Jan-3000 00:00:00 GMT', 'value': 'ddd'}
+
+Logging out
+-----------
+
+To log out, just call the logout method:
+
+ >>> dplugin.logout(request)
+ >>> dplugin.extractCredentials(request)
Property changes on: Sandbox/J1m/session.test
___________________________________________________________________
Name: svn:eol-style
+ native
More information about the Checkins
mailing list