[Checkins] SVN: z3c.authenticator/trunk/ Added coverage recipe
Roger Ineichen
roger at projekt01.ch
Thu Mar 27 02:36:49 EDT 2008
Log message for revision 84958:
Added coverage recipe
Cleanup tests
Changed:
U z3c.authenticator/trunk/TODO.txt
U z3c.authenticator/trunk/buildout.cfg
U z3c.authenticator/trunk/src/z3c/authenticator/README.txt
U z3c.authenticator/trunk/src/z3c/authenticator/credential.py
U z3c.authenticator/trunk/src/z3c/authenticator/group.txt
U z3c.authenticator/trunk/src/z3c/authenticator/interfaces.py
U z3c.authenticator/trunk/src/z3c/authenticator/password.py
U z3c.authenticator/trunk/src/z3c/authenticator/testing.py
U z3c.authenticator/trunk/src/z3c/authenticator/tests.py
U z3c.authenticator/trunk/src/z3c/authenticator/vocabulary.txt
-=-
Modified: z3c.authenticator/trunk/TODO.txt
===================================================================
--- z3c.authenticator/trunk/TODO.txt 2008-03-26 22:05:39 UTC (rev 84957)
+++ z3c.authenticator/trunk/TODO.txt 2008-03-27 06:36:47 UTC (rev 84958)
@@ -2,13 +2,4 @@
TODO
====
-- remove session setup from zope.app.authentication
-
-- implement own zope.app.authentication.idpicker.IdPicker
-
-- implement own zope.app.authentication.interfaces import IPasswordManager
-
- implement nickName, email and phone?
-
-- check README and make sure the documentation is conform with the
- implementation
Modified: z3c.authenticator/trunk/buildout.cfg
===================================================================
--- z3c.authenticator/trunk/buildout.cfg 2008-03-26 22:05:39 UTC (rev 84957)
+++ z3c.authenticator/trunk/buildout.cfg 2008-03-27 06:36:47 UTC (rev 84958)
@@ -3,16 +3,27 @@
externals/z3c.contents
externals/z3c.table
-parts = test checker coverage
+parts = test checker coverage-test coverage-report
+
[test]
recipe = zc.recipe.testrunner
eggs = z3c.authenticator [test]
+
[checker]
recipe = lovely.recipe:importchecker
path = src/z3c/authenticator
-[coverage]
+
+[coverage-test]
+recipe = zc.recipe.testrunner
+eggs = z3c.authenticator [test]
+defaults = ['--coverage', '../../coverage']
+
+
+[coverage-report]
recipe = zc.recipe.egg
eggs = z3c.coverage
+scripts = coverage=coverage-report
+arguments = ('coverage', 'coverage/report')
Modified: z3c.authenticator/trunk/src/z3c/authenticator/README.txt
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/README.txt 2008-03-26 22:05:39 UTC (rev 84957)
+++ z3c.authenticator/trunk/src/z3c/authenticator/README.txt 2008-03-27 06:36:47 UTC (rev 84958)
@@ -2,7 +2,7 @@
IAuthentication Utility
=======================
-The Authenticator Utility provides a framework for authenticating principals
+The Authenticator package provides a framework for authenticating principals
and associating information with them. It uses plugins and subscribers to get
its work done.
@@ -10,7 +10,8 @@
utility providing the `zope.app.security.interfaces.IAuthentication` interface.
Our target is to support a handy IAuthentication utility which offers a simple
-API for custom IUser implementations.
+API for custom IUser implementations and does not depend on the default
+zope.app.authentication implementation.
Security
@@ -37,11 +38,11 @@
it's context which is the real IUser.
The Authenticator doesn't use a prefix. The usage of a prefix is only
-implemented in the IUserContainer.
+implemented in the IGroupContainer.
We do not use a prefix in the IUserContainer because of the used unique user
id tokens. This will make sure that the same principal id doesn't get used at
-a later time (Common criteria). There is a ``add`` method which creates
+a later time (common criteria). There is a ``add`` method which creates
this id for you based on the login. The __setitem__ should not get used
directly for adding IUser instances anymore. We heavily restricted the
usage of this method. See the inline doc tests in __setitem__ for more info.
Modified: z3c.authenticator/trunk/src/z3c/authenticator/credential.py
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/credential.py 2008-03-26 22:05:39 UTC (rev 84957)
+++ z3c.authenticator/trunk/src/z3c/authenticator/credential.py 2008-03-27 06:36:47 UTC (rev 84958)
@@ -175,9 +175,8 @@
To illustrate how a session plugin works, we'll first setup some session
machinery:
- >>> from zope.session.session import RAMSessionDataContainer
- >>> from zope.app.authentication.tests import sessionSetUp
- >>> sessionSetUp(RAMSessionDataContainer)
+ >>> from z3c.authenticator.testing import sessionSetUp
+ >>> sessionSetUp()
This lets us retrieve the same session info from any test request, which
simulates what happens when a user submits a session ID as a cookie.
@@ -190,6 +189,12 @@
credentials it gets from a request. Credentials can be retrieved from
subsequent requests using the session-stored credentials.
+ If the given extractCredentials argument doesn't provide IHTTPRequest the
+ result will always be None:
+
+ >>> print plugin.extractCredentials(None)
+ None
+
Our test environment is initially configured without credentials:
>>> from zope.publisher.tests.httprequest import TestRequest
@@ -235,10 +240,20 @@
>>> plugin.extractCredentials(request)
{'login': 'luke', 'password': 'the_force'}
- Finally, we clear the session credentials using the logout method:
+ Finally, we clear the session credentials using the logout method.
+ If the given logout argument doesn't provide IHTTPRequest the
+ result will always be False:
+ >>> plugin.logout(None)
+ False
+
+ Now try to logout with the correct argument:
+
>>> plugin.logout(TestRequest())
True
+
+ After logout we can not logaout again:
+
>>> print plugin.extractCredentials(TestRequest())
None
@@ -334,6 +349,12 @@
>>> request.response.getHeader('location') # doctest: +ELLIPSIS
'.../@@mylogin.html?camefrom=%2Ffoo%2Fbar%2Ffolder%2Fpage+1.html%3Fq%3Dvalue'
+ If the given challenge argument doesn't provide IHTTPRequest the
+ result will always be False:
+
+ >>> plugin.challenge(None)
+ False
+
This can be used by the login form to redirect the user back to the
originating URL upon successful authentication.
"""
Modified: z3c.authenticator/trunk/src/z3c/authenticator/group.txt
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/group.txt 2008-03-26 22:05:39 UTC (rev 84957)
+++ z3c.authenticator/trunk/src/z3c/authenticator/group.txt 2008-03-27 06:36:47 UTC (rev 84958)
@@ -15,6 +15,9 @@
>>> from z3c.authenticator import interfaces
>>> from z3c.authenticator.group import Group
>>> group1 = Group(u'groups')
+ >>> group1
+ <Group None>
+
>>> interfaces.IGroup.providedBy(group1)
True
@@ -65,18 +68,18 @@
authentication service, we will setup a Authenticator utility:
>>> from z3c.authenticator.authentication import Authenticator
- >>> sau = Authenticator()
+ >>> authenticator = Authenticator()
Give them a location and register them as a IAuthentication utility :
>>> import zope.component
>>> from zope.app.security.interfaces import IAuthentication
- >>> rootFolder['sau'] = sau
- >>> zope.component.provideUtility(sau, IAuthentication)
+ >>> rootFolder['authenticator'] = authenticator
+ >>> zope.component.provideUtility(authenticator, IAuthentication)
We will create and register a new principals utility:
- >>> zope.component.provideUtility(sau, IAuthentication)
+ >>> zope.component.provideUtility(authenticator, IAuthentication)
We also need to register the group athentication plugin:
@@ -107,8 +110,8 @@
and configure and add the credential plugin to the Authenticator:
>>> myCredentialsPlugin = MyCredentialsPlugin()
- >>> sau['credentials'] = myCredentialsPlugin
- >>> sau.credentialsPlugins = ('credentials', )
+ >>> authenticator['credentials'] = myCredentialsPlugin
+ >>> authenticator.credentialsPlugins = ('credentials', )
We also need a principal and a IAuthenticationPlugin:
@@ -128,9 +131,9 @@
Add the GroupContainer and UserContainer to the Authenticator and
set the correct plugin names
- >>> sau['users'] = users
- >>> sau['groups'] = groups
- >>> sau.authenticatorPlugins = ('users', 'groups')
+ >>> authenticator['users'] = users
+ >>> authenticator['groups'] = groups
+ >>> authenticator.authenticatorPlugins = ('users', 'groups')
Adding users to groups
@@ -384,14 +387,14 @@
principal-creation events. It adds any group-folder-defined groups to
users in those groups:
- >>> auth1 = sau.getPrincipal(p1.__name__)
+ >>> auth1 = authenticator.getPrincipal(p1.__name__)
>>> auth1.groups
[u'groups.G1', u'groups.GA']
Of course, this applies to groups too:
- >>> g1 = sau.getPrincipal('groups.G1')
+ >>> g1 = authenticator.getPrincipal('groups.G1')
>>> g1.id
u'groups.G1'
@@ -429,7 +432,7 @@
>>> from z3c.authenticator.group import specialGroups
>>> x = User('x', 'password', 'X')
>>> found = FoundPrincipal(x)
- >>> event = FoundPrincipalCreated(sau, found)
+ >>> event = FoundPrincipalCreated(authenticator, found)
>>> specialGroups(event)
>>> found.groups
[]
@@ -473,6 +476,16 @@
>>> found.groups
[u'groups.all', u'groups.authenticated']
+The `allGroups` attribute is a readonly iterable of the full closure of the
+groups in the `groups` attribute--that is, if the principal is a direct member
+of the 'Administrators' group, and the 'Administrators' group is a member of
+the 'Reviewers' group, then p.groups would be ['Administrators'] and
+list(p.allGroups) would be ['Administrators', 'Reviewers'].
+
+ >>> sorted(found.allGroups)
+ [u'groups.all', u'groups.authenticated']
+
+
These groups are only added to non-group principals:
>>> found.groups = []
@@ -487,12 +500,13 @@
... zope.interface.implements(zope.security.interfaces.IPrincipal)
... id = title = description = ''
- >>> event = FoundPrincipalCreated(sau, SolitaryPrincipal())
+ >>> event = FoundPrincipalCreated(authenticator, SolitaryPrincipal())
>>> specialGroups(event)
>>> found.groups
[]
+
UserAwareGroup
----------------
@@ -527,55 +541,3 @@
>>> zope.security.interfaces.IMemberAwareGroup.providedBy(foundGroup)
True
-
-#User-aware groups
-#-------------------
-#The groupfolder includes a subscriber that gives group principals the
-#zope.security.interfaces.IGroupAware interface and an implementation thereof.
-#This allows groups to be able to get and set their members.
-#
-#Given an info object and a groups...
-#
-# >>> class DemoGroupInformation(object):
-# ... interface.implements(
-# ... zope.app.authentication.groupfolder.IGroupInformation)
-# ... def __init__(self, title, description, principals):
-# ... self.title = title
-# ... self.description = description
-# ... self.principals = principals
-# ...
-# >>> i = DemoGroupInformation(
-# ... 'Managers', 'Taskmasters', ('joe', 'jane'))
-# ...
-# >>> info = zope.app.authentication.groupfolder.GroupInfo(
-# ... 'groups.managers', i)
-# >>> class DummyGroup(object):
-# ... interface.implements(IGroupAwarePrincipal)
-# ... def __init__(self, id, title=u'', description=u''):
-# ... self.id = id
-# ... self.title = title
-# ... self.description = description
-# ... self.groups = []
-# ...
-# >>> principal = DummyGroup('foo')
-# >>> zope.security.interfaces.IMemberAwareGroup.providedBy(principal)
-# False
-#
-#...when you call the subscriber, it adds the two pseudo-methods to the
-#principal and makes the principal provide the IMemberAwareGroup interface.
-#
-# >>> zope.app.authentication.groupfolder.setUserSubscriber(
-# ... interfaces.FoundPrincipalCreated(
-# ... 'dummy auth (ignored)', principal, info))
-# >>> principal.getUsers()
-# ('joe', 'jane')
-# >>> principal.setUsers(('joe', 'jane', 'jaimie'))
-# >>> principal.getUsers()
-# ('joe', 'jane', 'jaimie')
-# >>> zope.security.interfaces.IMemberAwareGroup.providedBy(principal)
-# True
-#
-#The two methods work with the value on the IGroupInformation object.
-#
-# >>> i.principals == principal.getUsers()
-# True
Modified: z3c.authenticator/trunk/src/z3c/authenticator/interfaces.py
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/interfaces.py 2008-03-26 22:05:39 UTC (rev 84957)
+++ z3c.authenticator/trunk/src/z3c/authenticator/interfaces.py 2008-03-27 06:36:47 UTC (rev 84958)
@@ -409,9 +409,6 @@
access to the credentials.
"""
- def __init__(login, password):
- pass
-
def getLogin():
"""Return login name."""
Modified: z3c.authenticator/trunk/src/z3c/authenticator/password.py
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/password.py 2008-03-26 22:05:39 UTC (rev 84957)
+++ z3c.authenticator/trunk/src/z3c/authenticator/password.py 2008-03-27 06:36:47 UTC (rev 84958)
@@ -28,7 +28,6 @@
from z3c.authenticator.interfaces import IPasswordManager
-
_encoder = getencoder("utf-8")
Modified: z3c.authenticator/trunk/src/z3c/authenticator/testing.py
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/testing.py 2008-03-26 22:05:39 UTC (rev 84957)
+++ z3c.authenticator/trunk/src/z3c/authenticator/testing.py 2008-03-27 06:36:47 UTC (rev 84958)
@@ -17,9 +17,22 @@
__docformat__ = "reStructuredText"
import zope.component
+import zope.interface
+from zope.publisher.interfaces import IRequest
+from zope.app.testing import setup
+from zope.app.testing import placelesssetup
+
+from zope.session.interfaces import IClientId
+from zope.session.interfaces import IClientIdManager
+from zope.session.interfaces import ISession
+from zope.session.interfaces import ISessionDataContainer
+from zope.session.session import ClientId
+from zope.session.session import Session
+from zope.session.session import RAMSessionDataContainer
+from zope.session.http import CookieClientIdManager
+
from z3c.authenticator.interfaces import IPasswordManager
from z3c.authenticator.password import PlainTextPasswordManager
-from zope.app.testing import setup
###############################################################################
#
@@ -32,17 +45,32 @@
IPasswordManager, "Plain Text")
+class TestClientId(object):
+ zope.interface.implements(IClientId)
+ def __new__(cls, request):
+ return 'dummyclientidfortesting'
+
+
+def sessionSetUp(session_data_container_class=RAMSessionDataContainer):
+ placelesssetup.setUp()
+ zope.component.provideAdapter(TestClientId, (IRequest,), IClientId)
+ zope.component.provideAdapter(Session, (IRequest,), ISession)
+ zope.component.provideUtility(CookieClientIdManager(), IClientIdManager)
+ sdc = session_data_container_class()
+ zope.component.provideUtility(sdc, ISessionDataContainer, name='')
+
+
###############################################################################
#
# placeful setup/teardown
#
###############################################################################
-def siteSetUp(test):
+def placefulSetUp(test):
site = setup.placefulSetUp(site=True)
test.globs['rootFolder'] = site
setUpPasswordManager()
-def siteTearDown(test):
+def placefulTearDown(test):
setup.placefulTearDown()
Modified: z3c.authenticator/trunk/src/z3c/authenticator/tests.py
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/tests.py 2008-03-26 22:05:39 UTC (rev 84957)
+++ z3c.authenticator/trunk/src/z3c/authenticator/tests.py 2008-03-27 06:36:47 UTC (rev 84958)
@@ -147,10 +147,10 @@
def test_suite():
return unittest.TestSuite((
doctest.DocFileSuite('README.txt',
- setUp=testing.siteSetUp, tearDown=testing.siteTearDown,
+ setUp=testing.placefulSetUp, tearDown=testing.placefulSetUp,
optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS),
doctest.DocFileSuite('group.txt',
- setUp=testing.siteSetUp, tearDown=testing.siteTearDown,
+ setUp=testing.placefulSetUp, tearDown=testing.placefulSetUp,
optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS),
doctest.DocTestSuite('z3c.authenticator.credential',
setUp=placelesssetup.setUp, tearDown=placelesssetup.tearDown),
Modified: z3c.authenticator/trunk/src/z3c/authenticator/vocabulary.txt
===================================================================
--- z3c.authenticator/trunk/src/z3c/authenticator/vocabulary.txt 2008-03-26 22:05:39 UTC (rev 84957)
+++ z3c.authenticator/trunk/src/z3c/authenticator/vocabulary.txt 2008-03-27 06:36:47 UTC (rev 84958)
@@ -2,11 +2,12 @@
Vocabulary
==========
-The vocabulary module provides vocabularies for the authenticatorPlugins.
+The vocabulary module provides vocabularies for the authenticator plugins and
+the credentials plugins.
The options should include the unique names of all of the plugins that provide
-the appropriate interface (interfaces.IAuthentiatorPlugin, respectively) for
-the current context-- which is expected to be a pluggable authentication
+the appropriate interface (IAuthentiatorPlugin or ICredentialsPlugin,
+respectively) for the current context which is expected to be a IAuthenticator
utility, hereafter referred to as a Authenticator.
These names may be for objects contained within the Authenticator
@@ -56,6 +57,10 @@
...
>>> zope.component.provideAdapter(getSiteManager)
+
+authenticatorPlugins
+--------------------
+
We are now ready to create a vocabulary that we can use. The context is
our faux authentication utility, `auth`.
@@ -134,3 +139,70 @@
u'Plugin 3 (in contents)',
u'Plugin 4 (in contents)',
u'Plugin X (not found; deselecting will remove)']
+
+
+credentialsPlugins
+------------------
+
+For completeness, we'll do the same review of the credentialsPlugins.
+
+ >>> class DemoPlugin(object):
+ ... zope.interface.implements(interfaces.ICredentialsPlugin)
+ ... def __init__(self, name):
+ ... self.name = name
+ ...
+ >>> utility_plugins = dict(
+ ... (i, DemoPlugin(u'Plugin %d' % i)) for i in range(4))
+ >>> contained_plugins = dict(
+ ... (i, DemoPlugin(u'Plugin %d' % i)) for i in range(1, 5))
+ >>> for p in utility_plugins.values():
+ ... zope.component.provideUtility(p, name=p.name)
+ ...
+ >>> auth = DemoAuth((p.name, p) for p in contained_plugins.values())
+ >>> vocab = vocabulary.credentialsPlugins(auth)
+
+Iterating over the vocabulary results in all of the terms, in a relatively
+arbitrary order of their names. (This vocabulary should typically use a
+widget that sorts values on the basis of localized collation order of the term
+titles.) Similarly, we can use `in` to test for the presence of values in the
+vocabulary. The length reports the expected value.
+
+ >>> [term.value for term in vocab] # doctest: +NORMALIZE_WHITESPACE
+ [u'Plugin 0', u'Plugin 1', u'Plugin 2', u'Plugin 3', u'Plugin 4',
+ u'Plugin X']
+ >>> ['Plugin %s' % i in vocab for i in range(-1, 6)]
+ [False, True, True, True, True, True, False]
+ >>> 'Plugin X' in vocab
+ True
+ >>> len(vocab)
+ 6
+
+One can get a term for a given value using `getTerm()`; its token, in
+turn, should also return the same effective term from `getTermByToken`.
+
+ >>> values = ['Plugin 0', 'Plugin 1', 'Plugin 2', 'Plugin 3', 'Plugin 4',
+ ... 'Plugin X']
+ >>> for val in values:
+ ... term = vocab.getTerm(val)
+ ... assert term.value == val
+ ... term2 = vocab.getTermByToken(term.token)
+ ... assert term2.token == term.token
+ ... assert term2.value == val
+ ...
+
+The terms have titles, which are message ids that show the plugin title or id
+and whether the plugin is a utility or just contained in the auth utility.
+We'll give one of the plugins a dublin core title just to show the
+functionality. We need to regenerate the vocabulary, since it calculates all
+of its data at once. Then we'll check the titles. We'll have to translate
+them to see what we expect.
+
+ >>> zope.interface.directlyProvides(contained_plugins[1], ISpecial)
+ >>> vocab = vocabulary.credentialsPlugins(auth)
+ >>> pprint.pprint([i18n.translate(term.title) for term in vocab])
+ [u'Plugin 0 (a utility)',
+ u'Special Title (in contents)',
+ u'Plugin 2 (in contents)',
+ u'Plugin 3 (in contents)',
+ u'Plugin 4 (in contents)',
+ u'Plugin X (not found; deselecting will remove)']
More information about the Checkins
mailing list