[Checkins] SVN: z3c.tan/trunk/src/z3c/tan/ Created a TAN package
that works with the pluggable authentication
Stephan Richter
srichter at cosmos.phy.tufts.edu
Sun Jan 14 17:00:30 EST 2007
Log message for revision 72036:
Created a TAN package that works with the pluggable authentication
utility.
TANs can be used to give users more access rights for a limited amount
of time. The TAN system can be used in two ways:
* The TAN is a group that is added, if the TAN has been entered. The TAN
is also stored in a session, so it can be used after being entered.
* The TAN is a principal and one can "log in" using the TAN.
The package provides some other tools to aid TAN management. I hope that
in the future I will be able to provide more policy-centric, optional
features like true one-time use, timed availability, grant management,
etc.
The development was paid for by the Medical University of Vienna.
Changed:
A z3c.tan/trunk/src/z3c/tan/
A z3c.tan/trunk/src/z3c/tan/README.txt
A z3c.tan/trunk/src/z3c/tan/__init__.py
A z3c.tan/trunk/src/z3c/tan/generator.py
A z3c.tan/trunk/src/z3c/tan/interfaces.py
A z3c.tan/trunk/src/z3c/tan/manager.py
A z3c.tan/trunk/src/z3c/tan/session.py
A z3c.tan/trunk/src/z3c/tan/tan.py
A z3c.tan/trunk/src/z3c/tan/tests.py
-=-
Added: z3c.tan/trunk/src/z3c/tan/README.txt
===================================================================
--- z3c.tan/trunk/src/z3c/tan/README.txt 2007-01-14 21:42:38 UTC (rev 72035)
+++ z3c.tan/trunk/src/z3c/tan/README.txt 2007-01-14 22:00:29 UTC (rev 72036)
@@ -0,0 +1,356 @@
+=================================
+Transaction Authorization Numbers
+=================================
+
+A Transaction Authorization Number, short TAN, is a security feature to give a
+principal additional permissions for a limited amount of time, either one
+transaction or a short time span, like a day or a week.
+
+This package implements a TAN system as a plugin to the authentication utility
+in ``zope.app.authentication``. A TAN is simply a group. Roles and permissions
+are granted to the TAN group representing the additional access a principal
+will have when s/he entered the TAN. When the principal is created at the
+beginning of a request, an event listener extracts the TAN from the request
+and adds the TAN group to the principal's groups, if it is aware of groups.
+
+
+Setup
+=====
+
+Before we can start explaining this package, we have to bring up the
+authentication utility:
+
+ >>> from zope.app.folder import Folder, rootFolder
+ >>> root = rootFolder()
+
+ >>> from zope.app.testing import setup
+ >>> sm = setup.createSiteManager(root, True)
+
+ >>> from zope.app.authentication import authentication
+ >>> from zope.app.security.interfaces import IAuthentication
+ >>> pau = authentication.PluggableAuthentication('auth.')
+ >>> sm['default']['auth'] = pau
+ >>> sm.registerUtility(pau, IAuthentication)
+
+ >>> from zope.app.authentication import principalfolder
+ >>> pau[u'users'] = principalfolder.PrincipalFolder('users.')
+ >>> pau.authenticatorPlugins += (u'users',)
+
+ >>> import zope.component
+ >>> zope.component.provideAdapter(
+ ... principalfolder.FoundPrincipalFactory)
+ >>> zope.component.provideAdapter(
+ ... principalfolder.AuthenticatedPrincipalFactory)
+
+
+TAN Information
+===============
+
+A TAN is a very simple object and so is the TAN information:
+
+ >>> from z3c.tan import tan
+ >>> info1 = tan.TANInformation(u'T2581KL2')
+ >>> info1
+ <TANInformation u'T2581KL2'>
+
+The first argument if the TAN object is the TAN itself.
+
+ >>> info1.tan
+ u'T2581KL2'
+
+There is no title and description by default:
+
+ >>> info1.title
+ >>> info1.description
+
+Optionally, you can specify the title and description in the constructor:
+
+ >>> tan2 = tan.TANInformation(
+ ... u'HSI89S4G',
+ ... u'Transfer money',
+ ... u'This TAN is intended to transfer money to another account.')
+
+ >>> tan2.tan
+ u'HSI89S4G'
+ >>> tan2.title
+ u'Transfer money'
+ >>> tan2.description
+ u'This TAN is intended to transfer money to another account.'
+
+Oftentimes you want to restrict a TAN to paricular principals. In this case
+you can specify the list of allowed principals.
+
+ >>> info1.allowedPrincipals
+
+Initially the attribute is set to ``None``, meaning that all principals can
+use the TAN. The allowed principals are a list of principal:
+
+ >>> srichter = principalfolder.InternalPrincipal(
+ ... 'srichter', 'foobar', 'Stephan Richter')
+ >>> pau[u'users']['srichter'] = srichter
+
+ >>> info1.allowedPrincipals = ('auth.users.srichter',)
+
+Let's now have a look at how the TANs are manged.
+
+
+TAN Managers
+============
+
+The TANs are managed by a TAN manager.
+
+ >>> from z3c.tan import manager
+ >>> tans = manager.TANManager('tans.')
+
+The TAN manager is a container for the TAN information objects.
+
+ >>> from zope.app.container.interfaces import IContainer
+ >>> IContainer.providedBy(tans)
+ True
+
+ >>> tans.add(info1)
+ >>> sorted(tans.items())
+ [(u'T2581KL2', <TANInformation u'T2581KL2'>)]
+
+ >>> tans.add(tan2)
+
+Besides being a container, the TAN Manager is also an
+``IAuthenticatorPlugin``:
+
+ >>> from zope.app.authentication.interfaces import IAuthenticatorPlugin
+ >>> IAuthenticatorPlugin.providedBy(tans)
+ True
+
+Let's see whether all the authenticator plugin methods work as expected. The
+first method authenticates the creadentials. In this case the credentials is
+the id:
+
+ >>> tans.authenticateCredentials(u'T2581KL2')
+ <TANInformation u'T2581KL2'>
+
+ >>> tans.authenticateCredentials(u'FOO12345')
+
+ >>> tans.authenticateCredentials({'login': 'foo', 'password': 'bar'})
+
+You can also ask for the principal information directly:
+
+ >>> tans.principalInfo('tans.T2581KL2')
+ <TANInformation u'T2581KL2'>
+
+ >>> tans.principalInfo(u'FOO12345')
+
+Further, the TAN manager supports the searching:
+
+ >>> from zope.app.authentication.interfaces import IQuerySchemaSearch
+ >>> IQuerySchemaSearch.providedBy(tans)
+ True
+
+ >>> sorted(tans.search({'search': 'T2'}))
+ [u'tans.T2581KL2']
+
+Finally, you can never use a TAN twice. Let's create another TAN and add it to
+the system:
+
+ >>> tan3 = tan.TANInformation(u'HU1MHG60')
+ >>> tans.add(tan3)
+
+Next we remove the TAN:
+
+ >>> del tans[tan3.tan]
+
+If we try to add the TAN again, the system should deny the action by raising a
+``TANAlreadyUsed`` error:
+
+ >>> tans.add(tan3)
+ Traceback (most recent call last):
+ ...
+ TANAlreadyUsed: HU1MHG60
+
+
+TAN Assignment Subscriber
+=========================
+
+Whenever a principal is created not originating from a TAN but a login and
+password, it must be checked whether a TAN has been specified and added as a
+group, if necessary.
+
+First we have to add the TAN Manager to the authentication service and register
+it:
+
+ >>> pau[u'tans'] = tans
+ >>> pau.authenticatorPlugins += (u'tans',)
+
+Next we have to create a principal created event:
+
+ >>> from z3c.tan.tests import TestSession
+ >>> zope.component.provideAdapter(TestSession)
+
+ >>> from zope.publisher.browser import TestRequest
+ >>> from zope.app.authentication.interfaces import \
+ ... AuthenticatedPrincipalCreated
+
+ >>> srichter = pau.getPrincipal('auth.users.srichter')
+ >>> request = TestRequest()
+ >>> event = AuthenticatedPrincipalCreated(pau, srichter, None, request)
+
+In this first case no TAN was specified, so sending the event to the
+subscriber should not yield a TAN group:
+
+ >>> manager.assignTAN(event)
+ >>> srichter.groups
+ []
+
+Now we create a new request that has the TAN information:
+
+ >>> request = TestRequest(form={'login.tan': u'T2581KL2'})
+ >>> event = AuthenticatedPrincipalCreated(pau, srichter, None, request)
+ >>> manager.assignTAN(event)
+ >>> srichter.groups
+ [u'auth.T2581KL2']
+
+Clean up the session, so we can start over again:
+
+ >>> clearSessionData()
+
+
+Credentials Extractor
+=====================
+
+This package also provides a session-based credentials extractor, so that the
+TAN Manager can also be used for authentication. Let's check it out:
+
+ >>> from z3c.tan import session
+ >>> cred = session.SessionCredentialsPlugin()
+
+First, the credentials must be extracted from the request. Of course, only
+HTTP requests make sense here:
+
+ >>> cred.extractCredentials(object())
+
+ >>> cred.extractCredentials(TestRequest())
+
+ >>> request = TestRequest(form={'login.tan': u'T2581KL2'})
+ >>> cred.extractCredentials(request)
+ u'T2581KL2'
+
+ >>> cred.extractCredentials(TestRequest())
+ u'T2581KL2'
+
+If the authentication fails, the credentials plugin has a chance to create a
+new challenge:
+
+ >>> cred.challenge(object())
+ False
+
+ >>> request = TestRequest()
+ >>> cred.challenge(request)
+ True
+
+ >>> request.response._headers
+ {'location': ['http://127.0.0.1/@@tanEntry.html?camefrom=%2F']}
+
+Logging out works basically means to end using the TAN.
+
+ >>> from zope.app.session.interfaces import ISession
+ >>> ISession(request)[session.SESSION_KEY]['tan']
+ u'T2581KL2'
+
+ >>> cred.logout(object())
+ False
+
+ >>> cred.logout(request)
+ True
+
+ >>> ISession(request)[session.SESSION_KEY]['tan']
+
+
+Integration
+===========
+
+Let's now check the integration of the full authentication mechanism. First we
+register the TAN assignment subscriber:
+
+ >>> zope.component.provideHandler(manager.assignTAN)
+
+We also have to add a credentials plugin for the users:
+
+ >>> from zope.app.authentication.session import SessionCredentialsPlugin
+ >>> pau[u'creds'] = SessionCredentialsPlugin()
+ >>> pau.credentialsPlugins += (u'creds',)
+
+Now we can authenticate the user:
+
+ >>> request = TestRequest(form={'login': 'srichter', 'password': 'foobar'})
+ >>> user = pau.authenticate(request)
+ >>> user
+ Principal(u'auth.users.srichter')
+ >>> user.groups
+ []
+
+Now the user enters a TAN and submits it:
+
+ >>> request = TestRequest(form={'login.tan': u'T2581KL2'})
+ >>> user = pau.authenticate(request)
+ >>> user
+ Principal(u'auth.users.srichter')
+ >>> user.groups
+ [u'auth.T2581KL2']
+
+Finally, we allow to authenticate withe w=only the TAN:
+
+ >>> clearSessionData()
+
+ >>> pau[u'tan-creds'] = cred
+ >>> pau.credentialsPlugins += (u'tan-creds',)
+
+ >>> request = TestRequest(form={'login.tan': u'T2581KL2'})
+ >>> user = pau.authenticate(request)
+ >>> user
+ Principal(u'auth.tans.T2581KL2')
+ >>> user.groups
+ []
+
+
+TAN Generator
+=============
+
+Usually, TANs want to be auto-generated. The TAN generator adds a certain
+amount of TANs to a manager. You can also specify all the other attributes as
+arguments to the generate method:
+
+ >>> from z3c.tan import generator
+ >>> gen = generator.CommonTANGenerator()
+
+ >>> tans = manager.TANManager()
+ >>> gen.generate(
+ ... tans,
+ ... amount=2,
+ ... title=u'Transfer Money',
+ ... description=u"Stephan's TANs to transfer money.",
+ ... allowedPrincipals=('auth.users.srichter',))
+ [u'40OIQMX6', u'RU4QJXSH']
+
+Let's now make sure that the attributes are set too:
+
+ >>> tan4 = tans[u'40OIQMX6']
+ >>> tan4
+ <TANInformation u'40OIQMX6'>
+
+ >>> tan4.id
+ u'40OIQMX6'
+ >>> tan4.tan
+ u'40OIQMX6'
+ >>> tan4.title
+ u'Transfer Money'
+ >>> tan4.description
+ u"Stephan's TANs to transfer money."
+ >>> tan4.allowedPrincipals
+ ('auth.users.srichter',)
+
+
+Conclusion
+==========
+
+The way TANs are implemented in this package, it is possible to use them as
+free-standing principals that can be "logged in", but also as groups on a
+principal.
Property changes on: z3c.tan/trunk/src/z3c/tan/README.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: z3c.tan/trunk/src/z3c/tan/__init__.py
===================================================================
--- z3c.tan/trunk/src/z3c/tan/__init__.py 2007-01-14 21:42:38 UTC (rev 72035)
+++ z3c.tan/trunk/src/z3c/tan/__init__.py 2007-01-14 22:00:29 UTC (rev 72036)
@@ -0,0 +1 @@
+# Make a package
Property changes on: z3c.tan/trunk/src/z3c/tan/__init__.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3c.tan/trunk/src/z3c/tan/generator.py
===================================================================
--- z3c.tan/trunk/src/z3c/tan/generator.py 2007-01-14 21:42:38 UTC (rev 72035)
+++ z3c.tan/trunk/src/z3c/tan/generator.py 2007-01-14 22:00:29 UTC (rev 72036)
@@ -0,0 +1,54 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""TAN Generator Implementation
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import random
+import zope.component
+import zope.interface
+from zope.schema.fieldproperty import FieldProperty
+
+from z3c.tan import interfaces, tan
+
+class CommonTANGenerator(object):
+ zope.interface.implements(interfaces.ICommonTANGenerator)
+
+ length = FieldProperty(interfaces.ICommonTANGenerator['length'])
+ characters = FieldProperty(interfaces.ICommonTANGenerator['characters'])
+
+ def __init__(self, seed=0):
+ self._random = random.Random(seed)
+
+ def generate(self, manager, amount=1, title=None, description=None,
+ allowedPrincipals=None):
+ all = []
+ for i in xrange(amount):
+ added = False
+ # Loop to catch duplicates
+ while not added:
+ tanStr = u''.join(
+ self._random.sample(self.characters, self.length))
+ tanObj = tan.TANInformation(tanStr, title, description)
+ tanObj.allowedPrincipals = allowedPrincipals
+ try:
+ manager.add(tanObj)
+ except interfaces.TANAlreadyUsed:
+ continue
+ else:
+ all.append(tanStr)
+ added = True
+ return all
+
Property changes on: z3c.tan/trunk/src/z3c/tan/generator.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3c.tan/trunk/src/z3c/tan/interfaces.py
===================================================================
--- z3c.tan/trunk/src/z3c/tan/interfaces.py 2007-01-14 21:42:38 UTC (rev 72035)
+++ z3c.tan/trunk/src/z3c/tan/interfaces.py 2007-01-14 22:00:29 UTC (rev 72036)
@@ -0,0 +1,120 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""TAN Interfaces
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+
+import string
+import zope.interface
+import zope.schema
+import zope.app.security.vocabulary
+import zope.app.container.interfaces
+from zope.app.container import constraints
+
+from zope.i18nmessageid import MessageFactory
+_ = MessageFactory('z3c.tan')
+
+
+class ITANInformation(zope.interface.Interface):
+ """Component providing all information about a TAN."""
+ constraints.containers('.ITANManager')
+
+ tan = zope.schema.TextLine(
+ title=_("TAN"),
+ description=_("The TAN number/code itself."),
+ required=True)
+
+ title = zope.schema.TextLine(
+ title=_("Title"),
+ description=_("Title as which the TAN is known."),
+ required=False)
+
+ description = zope.schema.Text(
+ title=_("Description"),
+ description=_("A short description describing the TAN's purpose."),
+ required=False)
+
+ allowedPrincipals = zope.schema.Tuple(
+ title=_("Allowed Principals"),
+ value_type=zope.schema.Choice(
+ source=zope.app.security.vocabulary.PrincipalSource()),
+ description=_(
+ "List of principals (ids) that are allowed to use the TAN"),
+ missing_value=None,
+ default=None,
+ required=False)
+
+
+class ITANAlreadyUsed(zope.interface.Interface):
+ """Exception expressing that a TAN had been used already."""
+
+class TANAlreadyUsed(ValueError):
+ zope.interface.implements(ITANAlreadyUsed)
+
+
+class ITANManager(zope.app.container.interfaces.IContainer):
+ """A TAN Manager.
+
+ The TAN manager is responsible for not allowing to repeat TANs.
+ """
+ constraints.contains(ITANInformation)
+
+ def add(tan):
+ """Add a tan to the manager."""
+
+
+class ITANGenerator(zope.interface.Interface):
+ """Generates TANs for consumption."""
+
+ def generate(manager, amount=1, title=None, description=None,
+ allowedPrincipals=None):
+ """Generate *valid* TANs and add them to the manager.
+
+ Return a list of new TAN ids.
+
+ ``manager``
+ The TAN manager to which the TANs will be added.
+
+ ``amount``
+ Specifies how many TANs should be generated.
+
+ ``title``
+ The title for the TAN information object that will be added to
+ every generated TAN.
+
+ ``description``
+ The description for the TAN information object that will be added
+ to every generated TAN.
+
+ ``allowedPrincipals``
+ The description for the TAN information object that will be added
+ to every generated TAN.
+ """
+
+class ICommonTANGenerator(ITANGenerator):
+ """Allows to specify some common sense options."""
+
+ length = zope.schema.Int(
+ title=_("Length"),
+ description=_("The total length in characters of the TAN."),
+ default=8,
+ required=True)
+
+ characters = zope.schema.BytesLine(
+ title=_("Characters"),
+ description=_("A list of allowed characters."),
+ default=string.letters[26:]+string.digits,
+ required=True)
Property changes on: z3c.tan/trunk/src/z3c/tan/interfaces.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3c.tan/trunk/src/z3c/tan/manager.py
===================================================================
--- z3c.tan/trunk/src/z3c/tan/manager.py 2007-01-14 21:42:38 UTC (rev 72035)
+++ z3c.tan/trunk/src/z3c/tan/manager.py 2007-01-14 22:00:29 UTC (rev 72036)
@@ -0,0 +1,115 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""TAN Manager Implementation
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import persistent.list
+import zope.component
+import zope.interface
+from zope.app.authentication.interfaces import IAuthenticatorPlugin
+from zope.app.authentication.interfaces import IPrincipalInfo
+from zope.app.authentication.interfaces import IQuerySchemaSearch
+from zope.app.authentication.interfaces import IAuthenticatedPrincipalCreated
+from zope.app.authentication import principalfolder
+from zope.app.container import btree
+
+from z3c.tan import interfaces, session
+
+
+class TANManager(btree.BTreeContainer):
+ zope.interface.implements(
+ interfaces.ITANManager, IAuthenticatorPlugin, IQuerySchemaSearch)
+
+ schema = principalfolder.ISearchSchema
+
+ def __init__(self, prefix=u''):
+ super(TANManager, self).__init__()
+ self.prefix = prefix
+ self._usedTANs = persistent.list.PersistentList()
+
+ def __setitem__(self, key, value):
+ """See zope.app.container.interfaces.IContainer"""
+ if value.tan in self._usedTANs:
+ raise interfaces.TANAlreadyUsed(value.tan)
+ self._usedTANs.append(value.tan)
+ super(TANManager, self).__setitem__(key, value)
+
+ def add(self, tan):
+ """See interfaces.ITANManager"""
+ self.__setitem__(tan.tan, tan)
+
+ def authenticateCredentials(self, credentials):
+ """See zope.app.authentication.interfaces.IAuthenticatorPlugin"""
+ if not isinstance(credentials, basestring):
+ return None
+ if credentials not in self:
+ return None
+ return self.principalInfo(self.prefix+credentials)
+
+ def principalInfo(self, id):
+ """See zope.app.authentication.interfaces.IAuthenticatorPlugin"""
+ if not id.startswith(self.prefix):
+ return None
+ id = id[len(self.prefix):]
+ if id not in self:
+ return None
+ internal = self[id]
+ clone = internal.__class__.__new__(internal.__class__)
+ clone.__dict__.update(internal.__dict__)
+ clone.id = self.prefix + clone.id
+ zope.interface.directlyProvides(clone, IPrincipalInfo)
+ return clone
+
+ def search(self, query, start=None, batch_size=None):
+ """See zope.app.authentication.interfaces.IQuerySchemaSearch"""
+ search = query.get('search')
+ if search is None:
+ return
+ search = search.lower()
+ n = 1
+ for i, value in enumerate(self.values()):
+ if ((value.title and search in value.title.lower()) or
+ (value.description and search in value.description.lower()) or
+ search in value.tan.lower()):
+ if not ((start is not None and i < start)
+ or (batch_size is not None and n > batch_size)):
+ n += 1
+ yield self.prefix + value.tan
+
+
+ at zope.component.adapter(IAuthenticatedPrincipalCreated)
+def assignTAN(event):
+ """Assign a TAN as group to a principal."""
+ credentials = session.SessionCredentialsPlugin()
+ tan = credentials.extractCredentials(event.request)
+ if tan is None:
+ return
+ elif event.principal.id.endswith(tan):
+ # The principal is the TAN
+ return
+ # Look in the plugins for TAN managers and assign the TAN
+ for pluginName in event.authentication.authenticatorPlugins:
+ plugin = event.authentication[pluginName]
+ if not interfaces.ITANManager.providedBy(plugin):
+ continue
+ tanInfo = plugin.get(tan)
+ if tanInfo is None:
+ continue
+ # The principal is not allowed to use that TAN
+ if (tanInfo.allowedPrincipals is not None and
+ event.principal.id not in tanInfo.allowedPrincipals):
+ return
+ event.principal.groups.append(event.authentication.prefix + tan)
Property changes on: z3c.tan/trunk/src/z3c/tan/manager.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3c.tan/trunk/src/z3c/tan/session.py
===================================================================
--- z3c.tan/trunk/src/z3c/tan/session.py 2007-01-14 21:42:38 UTC (rev 72035)
+++ z3c.tan/trunk/src/z3c/tan/session.py 2007-01-14 22:00:29 UTC (rev 72036)
@@ -0,0 +1,113 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+""" Implementations of the session-based and cookie-based extractor and
+ challenge plugins.
+
+$Id$
+"""
+__docformat__ = 'restructuredtext'
+
+import persistent
+import transaction
+import zope.interface
+import zope.schema
+from urllib import urlencode
+from zope.publisher.interfaces.http import IHTTPRequest
+from zope.traversing.browser import absoluteurl
+
+from zope.app.component import hooks
+from zope.app.container import contained
+from zope.app.session.interfaces import ISession
+from zope.app.authentication import interfaces
+
+
+SESSION_KEY = 'z3c.tan.session.credentials'
+
+
+class IBrowserFormChallenger(zope.interface.Interface):
+ """A challenger that uses a browser form to collect user credentials."""
+
+ entryPageName = zope.schema.TextLine(
+ title=u'Entry Page Name',
+ description=u"Name of the entry form of the TAN.",
+ default=u'tanEntry.html')
+
+ tanField = zope.schema.TextLine(
+ title=u'TAN Field',
+ description=u"Field name for entering the TAN to be used.",
+ default=u"login.tan")
+
+
+class SessionCredentialsPlugin(contained.Contained, persistent.Persistent):
+ """A credentials plugin that uses Zope sessions to get/store credentials.
+ """
+ zope.interface.implements(
+ interfaces.ICredentialsPlugin, IBrowserFormChallenger)
+
+ entryPageName = 'tanEntry.html'
+ tanField = 'login.tan'
+
+ def __init__(self, field=None, page=None):
+ if field is not None:
+ self.tanField = field
+ if page is not None:
+ self.entryPageName = page
+
+ def extractCredentials(self, request):
+ """Extracts credentials from a session if they exist."""
+ if not IHTTPRequest.providedBy(request):
+ return None
+ sessionData = ISession(request)[SESSION_KEY]
+ tan = request.get(self.tanField, None)
+ if tan:
+ sessionData['tan'] = tan
+ else:
+ tan = sessionData.get('tan', None)
+ if not tan:
+ return None
+ return tan
+
+ def challenge(self, request):
+ """See zope.app.authentication.interfaces.ICredentialsPlugin"""
+ if not IHTTPRequest.providedBy(request):
+ return False
+
+ site = hooks.getSite()
+ # We need the traversal stack to complete the 'camefrom' parameter
+ stack = request.getTraversalStack()
+ stack.reverse()
+ # Better to add the query string, if present
+ query = request.get('QUERY_STRING')
+
+ camefrom = '/'.join([request.getURL(path_only=True)] + stack)
+ if query:
+ camefrom = camefrom + '?' + query
+ url = '%s/@@%s?%s' % (absoluteurl.absoluteURL(site, request),
+ self.entryPageName,
+ urlencode({'camefrom': camefrom}))
+ request.response.redirect(url)
+ return True
+
+ def logout(self, request):
+ """See zope.app.authentication.interfaces.ICredentialsPlugin"""
+ if not IHTTPRequest.providedBy(request):
+ return False
+
+ sessionData = ISession(request)[SESSION_KEY]
+ sessionData['tan'] = None
+ transaction.commit()
+ return True
+
+ def __repr__(self):
+ return '<%s field=%r>' %(self.__class__.__name__, self.tanField)
Property changes on: z3c.tan/trunk/src/z3c/tan/session.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3c.tan/trunk/src/z3c/tan/tan.py
===================================================================
--- z3c.tan/trunk/src/z3c/tan/tan.py 2007-01-14 21:42:38 UTC (rev 72035)
+++ z3c.tan/trunk/src/z3c/tan/tan.py 2007-01-14 22:00:29 UTC (rev 72036)
@@ -0,0 +1,47 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""TAN Interfaces
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import persistent
+import zope.interface
+from zope.app.container import contained
+from zope.schema.fieldproperty import FieldProperty
+
+from z3c.tan import interfaces
+
+
+class TANInformation(contained.Contained, persistent.Persistent):
+ zope.interface.implements(interfaces.ITANInformation)
+
+ tan = FieldProperty(
+ interfaces.ITANInformation['tan'])
+ title = FieldProperty(
+ interfaces.ITANInformation['title'])
+ description = FieldProperty(
+ interfaces.ITANInformation['description'])
+ allowedPrincipals = FieldProperty(
+ interfaces.ITANInformation['allowedPrincipals'])
+
+ def __init__(self, tan, title=None, description=None):
+ self.id = tan
+ self.tan = tan
+ self.title = title
+ self.description = description
+ self.allowedPrincipals = None
+
+ def __repr__(self):
+ return '<%s %r>' %(self.__class__.__name__, self.tan)
Property changes on: z3c.tan/trunk/src/z3c/tan/tan.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3c.tan/trunk/src/z3c/tan/tests.py
===================================================================
--- z3c.tan/trunk/src/z3c/tan/tests.py 2007-01-14 21:42:38 UTC (rev 72035)
+++ z3c.tan/trunk/src/z3c/tan/tests.py 2007-01-14 22:00:29 UTC (rev 72036)
@@ -0,0 +1,53 @@
+import unittest
+import zope.component
+import zope.interface
+from zope.testing import doctest
+from zope.app.testing import setup
+
+from zope.publisher.interfaces import IRequest
+from zope.app.session.interfaces import ISession
+from zope.app.session.session import RAMSessionDataContainer, SessionPkgData
+
+sessionData = RAMSessionDataContainer()
+
+class TestSession(object):
+ """See zope.app.session.interfaces.ISession"""
+ zope.interface.implements(ISession)
+ zope.component.adapts(IRequest)
+
+ def __init__(self, request):
+ self.request = request
+
+ def get(self, pkg_id, default=None):
+ """See zope.app.session.interfaces.ISession"""
+ return sessionData.get(pkg_id, default)
+
+ def __getitem__(self, pkg_id):
+ """See zope.app.session.interfaces.ISession"""
+ if pkg_id not in sessionData:
+ sessionData[pkg_id] = SessionPkgData()
+ return sessionData[pkg_id]
+
+def clearSessionData():
+ global sessionData
+ sessionData = RAMSessionDataContainer()
+
+def setUp(test):
+ setup.placefulSetUp()
+ clearSessionData()
+
+def tearDown(test):
+ setup.placefulTearDown()
+ clearSessionData()
+
+def test_suite():
+ return unittest.TestSuite((
+ doctest.DocFileSuite(
+ 'README.txt',
+ setUp=setUp, tearDown=tearDown,
+ globs={'clearSessionData': clearSessionData},
+ optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS),
+ ))
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
Property changes on: z3c.tan/trunk/src/z3c/tan/tests.py
___________________________________________________________________
Name: svn:keywords
+ Id
More information about the Checkins
mailing list