[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