Metadata-Version: 1.0
Name: z3c.authenticator
Version: 0.7.0
Summary: IAuthentication implementation for for Zope3
Home-page: http://pypi.python.org/pypi/z3c.authenticator
Author: Roger Ineichen and the Zope Community
Author-email: zope-dev@zope.org
License: ZPL 2.1
Description: This package provides an IAuthentication implementation for Zope3. Note that
        this implementation is independent of zope.app.authentication and it doesn't
        depend on that package. This means it doesn't even use the credential or
        authentication plugins offered from zope.app.authentication package.
        
        
        .. contents::
        
        =======================
        IAuthentication Utility
        =======================
        
        The Authenticator package provides a framework for authenticating principals
        and associating information with them. It uses plugins and subscribers to get
        its work done.
        
        For a simple authentication utility to be used, it should be registered as a
        utility providing the `zope.authentication.interfaces.IAuthentication` interface.
        
        Our target is to support a handy IAuthentication utility which offers a simple
        API for custom IUser implementations and does not depend on the default
        zope.app.authentication implementation.
        
        
        Security
        --------
        
        The Authenticator supports unique id tokens for principals. This means
        principal that get deleted and again added with the same id, login etc. do
        not have the same id again. We support this by generate a user id token
        generated by the host id, timestamp, a random string and the login attribute.
        
        
        What's different then in PluggableAuthentication
        ------------------------------------------------
        
        We use a different pattern for IAuthenticatorPlugins in this implementation
        then used in PluggableAuthentication located in zope.app.authentication.
        Because the pluggable authentication is not very handy when it comes to
        implement custom principal information. The IPrincipalInfo hook supporting
        not propagate the password of a IInternalPrincipal is droped in this
        implementation.
        
        In our implementation we offer a IFoundPrincipal and IAuthenticatedPrincipal
        which are implemented as adapters for a IUser. This adapters do not offer
        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 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
        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.
        
        
        Authentication
        ==============
        
        The primary job of Authenticator is to authenticate principals. It uses
        two types of plug-ins in its work:
        
        - Credentials Plugins
        
        - Authenticator Plugins
        
        Credentials plugins are responsible for extracting user credentials from a
        request. A credentials plugin may in some cases issue a 'challenge' to obtain
        credentials. For example, a 'session' credentials plugin reads credentials
        from a session (the "extraction"). If it cannot find credentials, it will
        redirect the user to a login form in order to provide them (the "challenge").
        
        Authenticator plugins are responsible for authenticating the credentials
        extracted by a credentials plugin. They are also typically able to create
        principal objects for credentials they successfully authenticate.
        
        Given a request object, the Authenticator returns a principal object, if it
        can. The Authenticator utility does this by first iterateing through its
        credentials plugins to obtain a set of credentials. If it gets credentials, it
        iterates through its authenticator plugins to authenticate them.
        
        If an authenticator succeeds in authenticating a set of credentials, the
        Authenticator uses the authenticator to create a principal
        corresponding to the credentials. The authenticator notifies subscribers if
        an authenticated principal is created. Subscribers are responsible for adding
        data, especially groups, to the principal. Typically, if a subscriber adds
        data, it should also add corresponding interface declarations.
        
        
        FAQ
        ---
        
        Here some useful hints:
        
        How should I set permission for principals?
        
        You can apply a roles to groups
        and apply permissions to roles. Or you can directly apply local permisssions
        to groups or to principals. After setup this mappings you can grant roles to
        groups. I always recommend a principal - group and permission - role mapping,
        then this gives you the most possible abstraction which is useful if it comes
        to manage permission and principals without to invoke directly principals and
        permissions itself. but of corse you can grant permissions to groups or the
        worst thing directly to principals. Grant permission to principals is only
        useful if it comes to selective local permission settings for selected
        principals e.g. a ownership like permission setup.
        
        How can I set permission for all principals?
        
        You can register one
        group as IEveryone utility. This IGroup utility get applied to all principals.
        
        Can I apply local groups to unauthenticated principals?
        
        Yes this will work.
        Since the last refactoring I refactored the IGroup implementation which makes
        it compatible with the principalregistry API. This means you can now register
        one local group as a unnamed IUnauthenticatedGroup. You can also register one
        local group as an unnamed IAuthenticatedGroup utility which will get applied
        to every authenticated principal or a unnamed utility for
        IUnauthenticatedGroup.
        
        Can I apply a local group to every principal?
        
        Yes, this is possible if
        register a local unnamed utility providing IEveryoneGroup.
        
        
        Principal
        ---------
        
        First we create a principal:
        
        >>> from z3c.authenticator import interfaces
        >>> from z3c.authenticator.user import User
        >>> login = u'bob'
        >>> password = u'secret'
        >>> title = u'Bob'
        >>> p = User(login, password, title)
        
        Such a principal provides the following attributes be default
        
        >>> p.login
        u'bob'
        
        >>> p.password
        u'secret'
        
        >>> p.title
        u'Bob'
        
        and IUser:
        
        >>> interfaces.IUser.providedBy(p)
        True
        
        
        Authenticator Plugin
        --------------------
        
        First setup a UserContainer which will store the principals:
        
        >>> from z3c.authenticator.user import UserContainer
        >>> authPlugin = UserContainer()
        
        Now we have a UserContainer that provides a IUserContainer:
        
        >>> interfaces.IUserContainer.providedBy(authPlugin)
        True
        
        Now we will add the created principal to the principal container using the
        containers method ``add``:
        
        >>> uid, user = authPlugin.add(p)
        
        The method returns the user id and the user object. The id get generated
        by the host IP address, the time, a random string and the user login attr.
        This token should be unique and guranted that it never get generated twice.
        This allows us to add, delete and add the same user again without that such a
        user will inherit existing permissions. We can test this token by compare it
        only with the __name__ of the object in this test since the token will
        different every testrun.
        
        >>> user.__name__ == uid
        True
        
        The returned user still is our previous added IUser
        
        >>> user is p
        True
        
        >>> len(user.__name__)
        32
        
        >>> user.login
        u'bob'
        
        >>> user.password
        u'secret'
        
        >>> user.title
        u'Bob'
        
        let's register the UserContainer as a named IAuthenticatorPlugin utility:
        
        >>> import zope.component
        >>> zope.component.provideUtility(authPlugin,
        ...     provides=interfaces.IAuthenticatorPlugin,
        ...     name='My Authenticator Plugin')
        
        
        Credentials Plugin
        ------------------
        
        After setup the user and user container, we'll create a simple credentials
        plugin:
        
        >>> import zope.interface
        >>> import zope.component
        
        >>> class MyCredentialsPlugin(object):
        ...
        ...     zope.interface.implements(interfaces.ICredentialsPlugin)
        ...
        ...     def extractCredentials(self, request):
        ...         return {'login':request.get('login', ''),
        ...                 'password':request.get('password', '')}
        ...
        ...     def challenge(self, request):
        ...         pass # challenge is a no-op for this plugin
        ...
        ...     def logout(self, request):
        ...         pass # logout is a no-op for this plugin
        
        As a plugin, MyCredentialsPlugin needs to be registered as a named utility or
        it could be stored in the Authenticator attribute credentialsPlugins.
        Use the first and register the plugina utility:
        
        >>> myCredentialsPlugin = MyCredentialsPlugin()
        >>> zope.component.provideUtility(myCredentialsPlugin,
        ...     name='My Credentials Plugin')
        
        
        AuthenticatedPrincipal and FoundPrincipal
        -----------------------------------------
        
        While authenticator plugins provide user, they are not responsible for
        creating principals. This function is performed by the Authenticator:
        
        >>> from z3c.authenticator.principal import AuthenticatedPrincipal
        >>> from z3c.authenticator.principal import FoundPrincipal
        >>> zope.component.provideAdapter(AuthenticatedPrincipal,
        ...     provides=interfaces.IAuthenticatedPrincipal)
        
        >>> zope.component.provideAdapter(FoundPrincipal,
        ...     provides=interfaces.IFoundPrincipal)
        
        
        Configuring Authenticator
        -------------------------
        
        Finally, we'll create the Authenticator itself:
        
        >>> from z3c.authenticator import authentication
        >>> auth = authentication.Authenticator()
        
        and configure it with the two plugins:
        
        >>> auth.credentialsPlugins = ('My Credentials Plugin', )
        >>> auth.authenticatorPlugins = ('My Authenticator Plugin', )
        
        
        Authenticate
        ------------
        
        We can now use the Authenticator to authenticate a sample request:
        
        >>> from zope.publisher.browser import TestRequest
        >>> print auth.authenticate(TestRequest())
        None
        
        In this case, we cannot authenticate an empty request. In the same way, we
        will not be able to authenticate a request with the wrong credentials:
        
        >>> request = TestRequest(form={'login':'let me in!', 'password':'secret'})
        >>> print auth.authenticate(request)
        None
        
        However, if we provide the proper credentials:
        
        >>> request = TestRequest(form={'login':'bob', 'password':'secret'})
        >>> bob = auth.authenticate(request)
        >>> bob
        <AuthenticatedPrincipal...>
        
        >>> interfaces.IAuthenticatedPrincipal.providedBy(bob)
        True
        
        we get an authenticated principal.
        
        
        Change login
        ------------
        
        Change the login of a principal is a allwas a critical task because such a
        login together with a password is the key to our implemenation. Let's try to
        change the login and check if everything is correct. We can do this by get the
        principal from the UserContainer and change the login on the IUser
        implementation:
        
        >>> internal = authPlugin[bob.id]
        >>> internal.login = u'bob2'
        
        Now we should be able to login with the new login:
        
        >>> request = TestRequest(form={'login':'bob2', 'password':'secret'})
        >>> bob2 = auth.authenticate(request)
        >>> bob2
        <AuthenticatedPrincipal ...>
        
        >>> bob2.title
        u'Bob'
        
        But not with the old one:
        
        >>> request = TestRequest(form={'login':'bob', 'password':'secret'})
        >>> auth.authenticate(request) == None
        True
        
        The user bob has still the same id as bob2 since the user id token doesn't
        get changed be changing the login:
        
        >>> bob.id == bob2.id
        True
        
        
        Events
        ------
        
        Authenticate principal will create events.
        
        >>> from zope.component.eventtesting import getEvents
        >>> from zope.component.eventtesting import clearEvents
        
        We can verify that the appropriate event was published:
        
        >>> clearEvents()
        >>> request = TestRequest(form={'login':'bob2', 'password':'secret'})
        >>> bobAgain = auth.authenticate(request)
        
        And the principal attribute in the event provides the authenticated principal:
        
        >>> [event] = getEvents(interfaces.IAuthenticatedPrincipalCreated)
        >>> event.principal is bobAgain
        True
        
        >>> event.principal
        <AuthenticatedPrincipal ...>
        
        >>> event.request is request
        True
        
        The principal has the id, title, and description.
        
        >>> event.principal.title
        u'Bob'
        
        >>> event.principal.id == uid
        True
        
        >>> event.principal.description
        u''
        
        We provide subscribers to these events that can be used for doing custom
        processing. Note, the principal attibute provides a IAuthenticatedPrincipal:
        
        >>> def addInfo(event):
        ...     id = event.principal.id
        ...     event.principal.description = 'Description for: %s' % id
        
        >>> zope.component.provideHandler(addInfo,
        ...     [interfaces.IAuthenticatedPrincipalCreated])
        
        Now, if we authenticate a principal, its description is set:
        
        >>> principal = auth.authenticate(request)
        >>> principal.description
        u'Description for: ...'
        
        
        Customization
        -------------
        
        Let's show you how the existing pattern can get used in a real use case. In
        the next sample we like to provide a additional email attribute for principals.
        First we have to define the interfaces declaring the email attribute:
        
        >>> class IMyEmail(zope.interface.Interface):
        ...     email = zope.schema.TextLine(
        ...         title=u'EMail',
        ...         description=u'The EMail')
        
        >>> class IMyUser(IMyEmail, interfaces.IUser):
        ...     """Custom IUser interface."""
        
        >>> class IMyFoundPrincipal(IMyEmail, interfaces.IFoundPrincipal):
        ...     """Custom IIMyFoundrincipal interface."""
        
        >>> class IMyAuthenticatedPrincipal(IMyEmail,
        ...     interfaces.IAuthenticatedPrincipal):
        ...     """Custom IAuthenticatedPrincipal interface."""
        
        After the schema, we define a custom principal implementation implementing
        this interface:
        
        >>> class MyUser(User):
        ...     zope.interface.implements(IMyUser)
        ...     def __init__(self, login, password, title, description, email):
        ...         super(MyUser, self).__init__(login, password, title,
        ...                                           description)
        ...         self.email = email
        
        Now we have to define the AuthenticatedPrincipal for MyUser:
        
        >>> class MyAuthenticatedPrincipal(AuthenticatedPrincipal):
        ...     zope.interface.implements(IMyAuthenticatedPrincipal)
        ...     def __init__(self, principal):
        ...         super(MyAuthenticatedPrincipal, self).__init__(principal)
        ...         self.email = principal.email
        
        And we have to define the FoundPrincipal for MyUser:
        
        >>> class MyFoundPrincipal(FoundPrincipal):
        ...     zope.interface.implements(IMyFoundPrincipal)
        ...     def __init__(self, principal):
        ...         super(MyFoundPrincipal, self).__init__(principal)
        ...         self.email = principal.email
        
        Note that you can provide different attributes for the found and authenticated
        principal if needed. That's up to you what you like to do with this attributes
        later.
        
        Now we need to register our custom authenticated and found principal as
        adapters:
        
        >>> zope.component.provideAdapter(MyAuthenticatedPrincipal,
        ...     provides=interfaces.IAuthenticatedPrincipal)
        
        >>> zope.component.provideAdapter(MyFoundPrincipal,
        ...     provides=interfaces.IFoundPrincipal)
        
        Now we can use them without any other event subscriber or other registration
        in our principal container. Let's add a principal tho this container:
        
        >>> p = MyUser(u'max', u'password', u'Max', u'', u'max@foobar.com')
        >>> token, max = authPlugin.add(p)
        >>> len(token)
        32
        
        >>> max.__name__ == token
        True
        
        >>> max.password
        u'password'
        
        >>> max.title
        u'Max'
        
        >>> max.email
        u'max@foobar.com'
        
        Let's try to authenticate...
        
        >>> request = TestRequest(form={'login':'max', 'password':'password'})
        >>> authenticated = auth.authenticate(request)
        
        and check your authenticated principal:
        
        >>> interfaces.IAuthenticatedPrincipal.providedBy(authenticated)
        True
        
        >>> authenticated
        <MyAuthenticatedPrincipal ...>
        
        >>> authenticated.id == token
        True
        
        >>> authenticated.email
        u'max@foobar.com'
        
        
        =====
        Group
        =====
        
        Group container provide support for groups information stored in the ZODB.
        They are persistent, and must be contained within the IAuthentication that
        use them.
        
        
        Group
        -----
        
        Like other users, groups are created when they are needed.
        
        >>> from z3c.authenticator import interfaces
        >>> from z3c.authenticator.group import Group
        >>> group1 = Group(u'groups')
        >>> group1
        <Group None>
        
        >>> interfaces.IGroup.providedBy(group1)
        True
        
        >>> group1.title
        u'groups'
        
        >>> group1.description
        u''
        
        >>> group1.principals
        ()
        
        
        GroupContainer
        --------------
        
        Group containers contain IGroup objects. A IAuthentication will adapt
        IFoundGroup to this IGroup objects.
        
        >>> from z3c.authenticator.group import GroupContainer
        >>> groups = GroupContainer('groups.')
        
        >>> interfaces.IGroupContainer.providedBy(groups)
        True
        
        We can add your previous created group to the group container using the
        addGroup method which returns the group id and group:
        
        >>> gid, g1 = groups.addGroup(u'g1', group1)
        >>> gid
        u'groups.g1'
        
        >>> interfaces.IGroup.providedBy(g1)
        True
        
        >>> g1.__name__
        u'groups.g1'
        
        Note that when group is added, a GroupAdded event is generated:
        
        >>> from zope.component.eventtesting import getEvents
        >>> getEvents(interfaces.IGroupAdded)
        [<GroupAdded u'groups.g1'>]
        
        Groups are defined with respect to an authentication service. Groups must be
        accessible via an authentication service and can contain principals accessible
        via an authentication service. To illustrate the group interaction with the
        authentication service, we will setup a Authenticator utility:
        
        >>> from z3c.authenticator.authentication import Authenticator
        >>> authenticator = Authenticator()
        
        Give them a location and register them as a IAuthentication utility :
        
        >>> import zope.component
        >>> from zope.authentication.interfaces import IAuthentication
        >>> rootFolder['authenticator'] = authenticator
        >>> zope.component.provideUtility(authenticator, IAuthentication)
        
        We will create and register a new principals utility:
        
        >>> zope.component.provideUtility(authenticator, IAuthentication)
        
        We also need to register the group athentication plugin:
        
        >>> zope.component.provideUtility(groups,
        ...     provides=interfaces.IAuthenticatorPlugin,
        ...     name='My Group Plugin')
        
        After setup the group and group container, we will create a simple credentials
        plugin and add them to the authentication utility:
        
        >>> import zope.interface
        >>> from z3c.authenticator import interfaces
        
        >>> class MyCredentialsPlugin(object):
        ...
        ...     zope.interface.implements(interfaces.ICredentialsPlugin)
        ...
        ...     def extractCredentials(self, request):
        ...         return {'login':request.get('login', ''),
        ...                 'password':request.get('password', '')}
        ...
        ...     def challenge(self, request):
        ...         pass # challenge is a no-op for this plugin
        ...
        ...     def logout(self, request):
        ...         pass # logout is a no-op for this plugin
        
        and configure and add the credential plugin to the Authenticator:
        
        >>> myCredentialsPlugin = MyCredentialsPlugin()
        >>> authenticator['credentials'] = myCredentialsPlugin
        >>> authenticator.credentialsPlugins = ('credentials', )
        
        We also need a principal and a IAuthenticationPlugin:
        
        >>> from z3c.authenticator.user import User
        >>> p1 = User(u'p1', u'password', u'Principal 1')
        >>> p2 = User(u'p2', u'password', u'Principal 2')
        >>> p3 = User(u'p3', u'password', u'Principal 3')
        >>> p4 = User(u'p4', u'password', u'Principal 4')
        
        >>> from z3c.authenticator.user import UserContainer
        >>> users = UserContainer()
        >>> token1, p1 = users.add(p1)
        >>> token2, p2 = users.add(p2)
        >>> token3, p3 = users.add(p3)
        >>> token4, p4 = users.add(p4)
        
        Add the GroupContainer and UserContainer to the Authenticator and
        set the correct plugin names
        
        >>> authenticator['users'] = users
        >>> authenticator['groups'] = groups
        >>> authenticator.authenticatorPlugins = ('users', 'groups')
        
        
        Adding users to groups
        ----------------------
        
        Now we can set the users on the group but first we need to register the
        IFoundPrincipal adapter for groups. The FoundGroup adapter provides this
        interface:
        
        >>> from z3c.authenticator.principal import FoundGroup
        >>> zope.component.provideAdapter(FoundGroup,
        ...     provides=interfaces.IFoundPrincipal)
        
        And we also need to provide the IFoundPrincipal and IAuthenticatedPrincipal
        adapter for IPrincipal objects:
        
        >>> from z3c.authenticator.principal import AuthenticatedPrincipal
        >>> from z3c.authenticator.principal import FoundPrincipal
        >>> zope.component.provideAdapter(AuthenticatedPrincipal,
        ...     provides=interfaces.IAuthenticatedPrincipal)
        
        >>> zope.component.provideAdapter(FoundPrincipal,
        ...     provides=interfaces.IFoundPrincipal)
        
        And we need the ``setGroupsForPrincipal`` subscriber:
        
        >>> from z3c.authenticator.group import setGroupsForPrincipal
        >>> zope.component.provideHandler(setGroupsForPrincipal,
        ...     [interfaces.IPrincipalCreated])
        
        >>> g1.principals = [p1.__name__, p2.__name__]
        >>> g1.principals
        (..., ...)
        
        >>> g1.principals[0] == p1.__name__
        True
        
        >>> g1.principals[1] == p2.__name__
        True
        
        Adding users fires an event.
        
        >>> getEvents(interfaces.IPrincipalsAddedToGroup)[-1]
        <PrincipalsAddedToGroup [..., ...] u'groups.g1'>
        
        We can now look up groups for the users:
        
        >>> groups.getGroupsForPrincipal(p1.__name__)
        (u'groups.g1',)
        
        Note that the group id is a concatenation of the group-folder prefix
        and the name of the group object within the folder.
        
        If we delete a group:
        
        >>> del groups['groups.g1']
        
        then the groups folder loses the group information for that group's users:
        
        >>> groups.getGroupsForPrincipal('p1')
        ()
        
        but the principal information on the group is unchanged:
        
        >>> g1.principals
        (..., ...)
        
        >>> g1.principals[0] == p1.__name__
        True
        
        >>> g1.principals[1] == p2.__name__
        True
        
        It also fires an event showing that the users are removed from the groups.
        
        >>> getEvents(interfaces.IPrincipalsRemovedFromGroup)[-1]
        <PrincipalsRemovedFromGroup [..., ...] 'groups.g1'>
        
        Adding the group again within a different name will make the groups
        available for the principal. Let's use a different group name:
        
        >>> groups['groups.G1'] = g1
        
        >>> groups.getGroupsForPrincipal(p1.__name__)
        (u'groups.G1',)
        
        Here we see that the new name is reflected in the group information.
        
        An event is fired, as usual.
        
        >>> getEvents(interfaces.IPrincipalsAddedToGroup)[-1]
        <PrincipalsAddedToGroup [..., ...] u'groups.G1'>
        
        In terms of member events (members added and removed from groups), we have
        now seen that events are fired when a group object is added and when it is
        removed from a group container; and we have seen that events are fired
        when a principal is added to an already-registered groups.  Events are also
        fired when a principal is removed from an already-registered groups.  Let's
        quickly see some more examples.
        
        >>> g1.principals = (p1.__name__, p3.__name__, p4.__name__)
        >>> g1.principals
        (..., ..., ...)
        
        >>> g1.principals[0] == p1.__name__
        True
        
        >>> g1.principals[1] == p3.__name__
        True
        
        >>> g1.principals[2] == p4.__name__
        True
        
        >>> getEvents(interfaces.IPrincipalsAddedToGroup)[-1]
        <PrincipalsAddedToGroup [..., ...] u'groups.G1'>
        
        >>> getEvents(interfaces.IPrincipalsRemovedFromGroup)[-1]
        <PrincipalsRemovedFromGroup [...] u'groups.G1'>
        
        >>> g1.principals = (p1.__name__, p2.__name__)
        >>> g1.principals
        (..., ...)
        
        >>> g1.principals[0] == p1.__name__
        True
        
        >>> g1.principals[1] == p2.__name__
        True
        
        >>> getEvents(interfaces.IPrincipalsAddedToGroup)[-1]
        <PrincipalsAddedToGroup [...] u'groups.G1'>
        
        >>> getEvents(interfaces.IPrincipalsRemovedFromGroup)[-1]
        <PrincipalsRemovedFromGroup [..., ...] u'groups.G1'>
        
        >>> groups.getGroupsForPrincipal(p2.__name__)
        (u'groups.G1',)
        
        
        Groups in groups
        ----------------
        
        Groups can contain groups:
        
        >>> g2 = Group('Group Two')
        >>> groups['groups.G2'] = g2
        >>> g2.principals
        ()
        
        >>> g2.principals = ['groups.G1']
        
        >>> g2.principals
        ('groups.G1',)
        
        >>> groups.getGroupsForPrincipal('groups.G2')
        ()
        
        >>> g1.principals
        (..., ...)
        
        >>> g1.principals[0] == p1.__name__
        True
        
        >>> g1.principals[1] == p2.__name__
        True
        
        >>> groups.getGroupsForPrincipal('groups.G1')
        (u'groups.G2',)
        
        >>> old = getEvents(interfaces.IPrincipalsAddedToGroup)[-1]
        >>> old
        <PrincipalsAddedToGroup ['groups.G1'] u'groups.G2'>
        
        Groups cannot contain cycles:
        
        >>> g1.principals
        (..., ...)
        
        >>> g1.principals[0] == p1.__name__
        True
        
        >>> g1.principals[1] == p2.__name__
        True
        
        >>> g2.principals
        ('groups.G1',)
        
        >>> g1.principals = (p1.__name__, p2.__name__, 'groups.G2')
        Traceback (most recent call last):
        ...
        GroupCycle: (...)
        
        >>> g1.principals
        (..., ...)
        
        
        >>> g1.principals[0] == p1.__name__
        True
        
        >>> g1.principals[1] == p2.__name__
        True
        
        Trying to do so does not fire an event.
        
        >>> getEvents(interfaces.IPrincipalsAddedToGroup)[-1] is old
        True
        
        They need not be hierarchical:
        
        >>> ga = Group("Group A")
        >>> groups['groups.GA'] = ga
        
        >>> gb = Group("Group B")
        >>> groups['groups.GB'] = gb
        >>> gb.principals = ['groups.GA']
        
        >>> gc = Group("Group C")
        >>> groups['groups.GC'] = gc
        >>> gc.principals = ['groups.GA']
        
        >>> gd = Group("Group D")
        >>> groups['groups.GD'] = gd
        >>> gd.principals = ['groups.GA', 'groups.GB']
        
        >>> ga.principals = [p1.__name__]
        
        Group containers provide a very simple search interface.  They perform
        simple string searches on group titles and descriptions.
        
        >>> list(groups.search({'search': 'grou'}))
        [u'groups.G1', u'groups.G2',
        u'groups.GA', u'groups.GB', u'groups.GC', u'groups.GD']
        
        >>> list(groups.search({'search': 'two'}))
        [u'groups.G2']
        
        They also support batching:
        
        >>> list(groups.search({'search': 'grou'}, 2, 3))
        [u'groups.GA', u'groups.GB', u'groups.GC']
        
        
        If you don't supply a search key, no results will be returned:
        
        >>> list(groups.search({}))
        []
        
        
        Identifying groups
        ------------------
        
        The function, `setGroupsForPrincipal`, is a subscriber to
        principal-creation events.  It adds any group-folder-defined groups to
        users in those groups:
        
        >>> auth1 = authenticator.getPrincipal(p1.__name__)
        
        >>> auth1.groups
        [u'groups.G1', u'groups.GA']
        
        Of course, this applies to groups too:
        
        >>> g1 = authenticator.getPrincipal('groups.G1')
        >>> g1.id
        u'groups.G1'
        
        >>> g1.groups
        [u'groups.G2']
        
        A FoundGroup provides IFoundGroup which is inherited from
        IFoundPrincipal and IGroup:
        
        >>> interfaces.IFoundGroup.providedBy(g1)
        True
        
        >>> interfaces.IFoundPrincipal.providedBy(g1)
        True
        
        >>> import zope.security.interfaces
        >>> zope.security.interfaces.IGroup.providedBy(g1)
        True
        
        
        specialGroups
        -------------
        
        Two special groups, IAuthenticatedGroup, and IEveryoneGroup may apply to users
        created by the IAuthentication utility.  There is a subscriber called
        ``specialGroups``. This subscriber can set this special groups on any
        principal if IAuthenticatedGroup, or IEveryoneGroup utilities are
        provided. The subscriber knows also how to apply local groups to principals.
        Note, principals means IAuthenticatedPrincipal, IFoundPrincipal or IFoundGroup.
        
        If we notify the subscriber with the principal, nothing will happen
        because the groups haven't been defined:
        
        >>> from z3c.authenticator.principal import FoundPrincipal
        >>> from z3c.authenticator.event import FoundPrincipalCreated
        >>> from z3c.authenticator.group import specialGroups
        >>> x = User('x', 'password', 'X')
        >>> found = FoundPrincipal(x)
        >>> event = FoundPrincipalCreated(authenticator, found)
        >>> specialGroups(event)
        >>> found.groups
        []
        
        Now, if we define the Everybody group:
        
        >>> import zope.authentication.interfaces
        >>> class EverybodyGroup(Group):
        ...     zope.interface.implements(
        ...        zope.authentication.interfaces.IEveryoneGroup)
        
        >>> all = EverybodyGroup('groups.all')
        >>> groups['groups.all'] = all
        >>> zope.component.provideUtility(all,
        ...     zope.authentication.interfaces.IEveryoneGroup)
        
        Then the group will be added to the principal:
        
        >>> specialGroups(event)
        >>> found.groups
        [u'groups.all']
        
        Similarly for the authenticated group:
        
        >>> class AuthenticatedGroup(Group):
        ...     zope.interface.implements(
        ...         zope.authentication.interfaces.IAuthenticatedGroup)
        
        >>> authenticated = AuthenticatedGroup('groups.authenticated')
        >>> groups['groups.authenticated'] = authenticated
        >>> zope.component.provideUtility(authenticated,
        ...     zope.authentication.interfaces.IAuthenticatedGroup)
        
        Then the group will be added to the principal:
        
        >>> found.groups = []
        >>> specialGroups(event)
        >>> found.groups.sort()
        >>> found.groups
        [u'groups.all', u'groups.authenticated']
        
        
        allGroups
        ---------
        
        The `allGroups` attribute is a readonly iterable of the full closure of the
        groups in the `groups` attribute. Let's define a new principal first:
        
        >>> p = User(u'p', u'password', u'Principal')
        >>> token, p = users.add(p)
        
        And the groups:
        
        >>> ga = Group("Administrators")
        >>> gr = Group("Reviewers")
        >>> gid, ga = groups.addGroup(u'Administrators', ga)
        >>> gid, gr = groups.addGroup(u'Reviewers', gr)
        
        If the principal is a direct member of the 'Administrators' group,
        
        >>> ga.principals = [p.__name__]
        
        then getGroupsForPrincipal would be ['Administrators']
        
        >>> groups.getGroupsForPrincipal(p.__name__)
        (u'groups.Administrators',)
        
        and if the 'Administrators' group is a member of the 'Reviewers' group,
        
        >>> gr.principals = [ga.id]
        
        then groups would be ['Administrators'] too.
        
        >>> groups.getGroupsForPrincipal(p.__name__)
        (u'groups.Administrators',)
        
        now let's use the setGroupsForPrincipal subscriber which knows how to apply
        the groups to the found principal:
        
        >>> pFound = FoundPrincipal(p)
        >>> event = FoundPrincipalCreated(authenticator, pFound)
        >>> setGroupsForPrincipal(event)
        
        As you can see and pFound.groups is ['Administrators'].
        
        >>> sorted(pFound.groups)
        [u'groups.Administrators']
        
        And pFound.allGroups is ['Administrators', 'Reviewers'].
        
        >>> sorted(pFound.allGroups)
        [u'groups.Administrators', u'groups.Reviewers']
        
        
        ==========
        Vocabulary
        ==========
        
        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 (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
        ("contained plugins"), or may be utilities registered for the specified
        interface, found in the context of the Authenticator
        ("utility plugins"). Contained plugins mask utility plugins of the same name.
        They also may be names currently selected in the Authenticator that do
        not actually have a corresponding plugin at this time.
        
        Here is a short example of how the vocabulary should work.  Let's say we're
        working with authentication plugins.  We'll create some faux
        authentication plugins, and register some of them as utilities and put
        others in a faux Authenticator.
        
        >>> from z3c.authenticator import interfaces
        >>> import zope.interface
        >>> import zope.component
        >>> class DemoPlugin(object):
        ...     zope.interface.implements(interfaces.IAuthenticatorPlugin)
        ...     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))
        >>> sorted(utility_plugins.keys())
        [0, 1, 2, 3]
        >>> for p in utility_plugins.values():
        ...     zope.component.provideUtility(p, name=p.name)
        ...
        >>> sorted(contained_plugins.keys()) # 1 will mask utility plugin 1
        [1, 2, 3, 4]
        >>> class DemoAuth(dict):
        ...     zope.interface.implements(interfaces.IAuthenticator)
        ...     def __init__(self, *args, **kwargs):
        ...         super(DemoAuth, self).__init__(*args, **kwargs)
        ...         self.authenticatorPlugins = (u'Plugin 3', u'Plugin X')
        ...         self.credentialsPlugins = (u'Plugin 4', u'Plugin X')
        ...
        >>> auth = DemoAuth((p.name, p) for p in contained_plugins.values())
        
        >>> @zope.component.adapter(zope.interface.Interface)
        ... @zope.interface.implementer(zope.component.IComponentLookup)
        ... def getSiteManager(context):
        ...     return zope.component.getGlobalSiteManager()
        ...
        >>> 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`.
        
        >>> from z3c.authenticator import vocabulary
        >>> vocab = vocabulary.authenticatorPlugins(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.)
        
        >>> [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']
        
        Similarly, we can use `in` to test for the presence of values in the
        vocabulary.
        
        >>> ['Plugin %s' % i in vocab for i in range(-1, 6)]
        [False, True, True, True, True, True, False]
        >>> 'Plugin X' in vocab
        True
        
        The length reports the expected value.
        
        >>> 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.
        
        >>> import zope.dublincore.interfaces
        >>> class ISpecial(zope.interface.Interface):
        ...     pass
        ...
        >>> zope.interface.directlyProvides(contained_plugins[1], ISpecial)
        >>> class DemoDCAdapter(object):
        ...     zope.interface.implements(
        ...         zope.dublincore.interfaces.IDCDescriptiveProperties)
        ...     zope.component.adapts(ISpecial)
        ...     def __init__(self, context):
        ...         pass
        ...     title = u'Special Title'
        ...
        >>> zope.component.provideAdapter(DemoDCAdapter)
        
        We need to regenerate the vocabulary, since it calculates all of its data at
        once.
        
        >>> vocab = vocabulary.authenticatorPlugins(auth)
        
        Now we'll check the titles.  We'll have to translate them to see what we
        expect.
        
        >>> from zope import i18n
        >>> import pprint
        >>> 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)']
        
        
        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)']
        
        
        =======
        CHANGES
        =======
        
        Version 0.7.0 (2009-05-11)
        --------------------------
        
        - Update dependencies:
        
        * Use ``zope.container`` instead of ``zope.app.container``.
        * Use ``zope.site`` instead of ``zope.app.component``.
        * Use ``zope.authentication`` and ``zope.principalregistry`` instead
        of ``zope.app.security``.
        * Use ``zope.password`` instead of maintaining a copy of password
        managers.
        
        - Drop dependency on z3c.i18n and recreate a message factory instead.
        
        
        Version 0.6.0 (2009-01-04)
        --------------------------
        
        - Feature: added support for local IUnauthenticatedPrincipal. This is usefull
        if you need to apply local roles to IUnauthenticatedPrincipal. This was not
        possible before and is not possible in zope.app.authentication
        
        - Feature: implemented initial grant view based on ISource widget. Note, this
        source widget terms implementation which is very complex to understand will
        get moved to z3c.from if we fixed the ITerm dependency. Which means ITerm
        needs to get moved out of zope.app.form first.
        
        - Feature: added support for next utility lookup in authenticate call. By
        default the principals from the global principalregistry get involved now.
        You can disable this feature by setting includeNextUtilityForAuthenticate to
        False.
        
        - Feature: added PrincipalRegistryAuthenticatorPlugin which allows to
        authenticate principals defined in global principal registry.
        
        - Feature: implemented z3c.form prefix support in SessionCredentialsPlugin. Now
        there is an option called prefixes which can be used for define a list of
        used z3c.form prefixes. This makes it simpler for supporting different forms
        and adjust the credential extraction.
        
        - Renamed IGroupPrincipal to IFoundGroup which makes it more understandable
        why this adapter implementation is needed. The IFoundGroup adapter is now
        also used for zope.security.interfaces.IGroup principals. This makes it
        possible to use them in the new principalregistry credential. Provide
        deprecation message for the old IGroupPrincipal implementation.
        
        - Removed dependency for zapi. But it's not really gone since other packages
        use zapi too.
        
        - Removed unused InvalidPrincipalIds and InvalidGroupId exceptions
        
        - Removed unused IMemberAwareGroup support. This interface is not used in zope
        at all.
        
        - Added documentation for Pypi home page.
        
        
        Version 0.5.1 (2008-04-16)
        --------------------------
        
        - Cleanup imports and adjust dependencies
        
        
        Version 0.5.0 (2008-04-16)
        --------------------------
        
        - Initial Release
        
Keywords: zope3 z3c authentication auth group
Platform: UNKNOWN
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Zope Public License
Classifier: Programming Language :: Python
Classifier: Natural Language :: English
Classifier: Operating System :: OS Independent
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Framework :: Zope3
