=======================
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 from PluggableAuthentication
---------------------------------------------

We use a different pattern for IAuthenticatorPlugins in this implementation
than used in PluggableAuthentication from zope.app.authentication,
because the pluggable authentication is not very handy when it comes to
implementing 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. These adapters do not offer
their 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 iterating 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 roles to groups
  and apply permissions to roles. Or you can directly apply local permisssions
  to groups or to principals. After setting up these mappings you can grant roles to
  groups. I always recommend a principal - group and permission - role mapping,
  this gives you the most possible abstraction which is useful if it comes
  to managing permissions and principals without directly invoking principals and
  permissions themselves. But of course you can grant permissions to groups or the
  worst thing, directly to principals. Granting permissions to principals is only
  useful if it comes to selective local permission settings for selected
  principals, e.g. an ownership-like permission setup.

How can I set permissions 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 an unnamed IUnauthenticatedGroup. You can also register one
  local group as an unnamed IAuthenticatedGroup utility which will get applied
  to every authenticated principal or an unnamed utility for
  IUnauthenticatedGroup.

Can I apply a local group to every principal?

  Yes, this is possible if you
  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.decode('utf-8')
  u'secret'

  >>> p.title
  u'Bob'

and IUser:

  >>> interfaces.IUser.providedBy(p)
  True


Authenticator Plugin
--------------------

First set up a UserContainer which will store the principals:

  >>> from z3c.authenticator.user import UserContainer
  >>> authPlugin = UserContainer()

Now we have a UserContainer that provides an IUserContainer:

  >>> interfaces.IUserContainer.providedBy(authPlugin)
  True

Now we will add the created principal to the principal container using the
container's ``add`` method:

  >>> uid, user = authPlugin.add(p)

The method returns the user id and the user object. The id gets generated
from the host IP address, the time, a random string and the user login attr.
This token should be unique and guaranteed that it will never get generated twice.
This allows us to add, delete and add the same user again without having such a
user inheriting existing permissions. We can test this token by comparing it
only with the __name__ of the object in this test since the token will be
different every test run.

  >>> user.__name__ == uid
  True

The returned user is still our previous added IUser

  >>> user is p
  True

  >>> len(user.__name__)
  32

  >>> user.login
  u'bob'

  >>> user.password.decode('utf-8')
  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 seting up the user and user container, we'll create a simple credentials
plugin:

  >>> import zope.interface
  >>> import zope.component

  >>> @zope.interface.implementer(interfaces.ICredentialsPlugin)
  ... class MyCredentialsPlugin(object):
  ...
  ...
  ...     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 users, 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 the 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.


Changing login names
--------------------

Changing the login (i.e. username) of a principal is always 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 getting the
principal from the UserContainer and changing 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 by changing the login:

  >>> bob.id == bob2.id
  True


Events
------

Authenticating a 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 an 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'd like to provide an 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:

  >>> @zope.interface.implementer(IMyUser)
  ... class MyUser(User):
  ...     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:

  >>> @zope.interface.implementer(IMyAuthenticatedPrincipal)
  ... class MyAuthenticatedPrincipal(AuthenticatedPrincipal):
  ...     def __init__(self, principal):
  ...         super(MyAuthenticatedPrincipal, self).__init__(principal)
  ...         self.email = principal.email

And we have to define the FoundPrincipal for MyUser:

  >>> @zope.interface.implementer(IMyFoundPrincipal)
  ... class MyFoundPrincipal(FoundPrincipal):
  ...     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
principals if needed. That's up to you what you like to do with these attributes
later.

Now we need to register our custom authenticated and found principal
adapters:

  >>> zope.component.provideAdapter(MyAuthenticatedPrincipal,
  ...     provides=interfaces.IAuthenticatedPrincipal)

  >>> zope.component.provideAdapter(MyFoundPrincipal,
  ...     provides=interfaces.IFoundPrincipal)

Now we can use them without any other event subscribers or other registration
in our principal container. Let's add a principal to 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.decode('utf-8')
  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'

Check getUserByLogin:

  >>> authPlugin.getUserByLogin('max')
  <MyUser object at ...>

  >>> authPlugin.getUserByLogin('max').login
  u'max'

  >>> authPlugin.getUserByLogin('max').__name__ == token
  True


A handy feature for migration is that you can set your own ``token``.
Usually in z.a.authentication the ``token`` == login and we want to keep it
that way, unless you want to iterate through all permissions and whatever.
Note, the __name__ and the id in the container must be the *SAME* object.

  >>> login = u'migrateduser'
  >>> p = User(login, u'password', u'John')

Preset the ``token``

  >>> p.__name__ = login

Watch out, we use __setitem__ instead of add(), because add() would kill off
the preset ``token`` in __name__.

  >>> authPlugin[login] = p

Here we are, the user is set with the non-generated token.

  >>> u'migrateduser' in authPlugin.keys()
  True

  >>> authPlugin['migrateduser']
  <z3c.authenticator.user.User object at ...>

  >>> authPlugin.getUserByLogin('migrateduser')
  <z3c.authenticator.user.User object at ...>


Edge cases
----------

We can have Users with unicode logins, as we allow this with TextLine in IUser.

  >>> try:
  ...     unichr = unichr
  ... except NameError:
  ...     unichr = chr # PY3
  >>> p = User(u'bob'+unichr(233), 'password', 'title')

Adding it should not fail:

  >>> uid, user = authPlugin.add(p)

