--- a/doc/book/en/devrepo/repo/sessions.rst Thu Sep 23 10:49:05 2010 +0200
+++ b/doc/book/en/devrepo/repo/sessions.rst Thu Sep 23 13:04:01 2010 +0200
@@ -90,10 +90,21 @@
Sometimes CubicWeb's out-of-the-box authentication schemes (cookie and
http) are not sufficient. Nowadays there is a plethore of such schemes
and the framework cannot provide them all, but as the sequence above
-may show, it is extensible.
+shows, it is extensible.
Two levels have to be considered when writing an authentication
-plugin: the web client the repository.
+plugin: the web client and the repository.
+
+We invented a scenario where it makes sense to have a new plugin in
+each side: some middleware will do pre-authentication and under the
+right circumstances add a new HTTP `x-foo-user` header to the query
+before it reaches the CubicWeb instance. For a concrete example of
+this, see the `apachekerberos`_ cube.
+
+.. _`apachekerberos`: http://www.cubicweb.org/project/cubicweb-apachekerberos
+
+Repository authentication plugins
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
On the repository side, it is possible to register a source
authentifier using the following kind of code:
@@ -102,11 +113,12 @@
from cubicweb.server.sources import native
- class FooAuthentifier(native.BaseAuthentifier):
+ class FooAuthentifier(native.LoginPasswordAuthentifier):
""" a source authentifier plugin
- if the password is 'foo', it's ok
+ if 'foo' in authentication information, no need to check
+ password
"""
- auth_rql = ('Any X,P WHERE X is CWUser, X login %(login)s, X upassword P')
+ auth_rql = 'Any X WHERE X is CWUser, X login %(login)s'
def authenticate(self, session, login, **kwargs):
"""return CWUser eid for the given login
@@ -114,14 +126,19 @@
else raise `AuthenticationError`
"""
session.debug('authentication by %s', self.__class__.__name__)
+ if 'foo' not in kwargs:
+ return super(FooAuthentifier, self).authenticate(session, login, **kwargs)
try:
rset = session.execute(self.auth_rql, {'login': login})
- if rset[0][1].lower() == u'foo':
- return rset[0][0]
+ return rset[0][0]
except Exception, exc:
session.debug('authentication failure (%s)', exc)
- pass
- raise AuthenticationError('user password is not foo')
+ raise AuthenticationError('foo user is unknown to us')
+
+Since repository authentifiers are not appobjects, we have to register
+them through a `server_startup` hook.
+
+.. sourcecode:: python
class ServerStartupHook(hook.Hook):
""" register the foo authenticator """
@@ -132,4 +149,53 @@
self.debug('registering foo authentifier')
self.repo.system_source.add_authentifier(FooAuthentifier())
+Web authentication plugins
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+.. sourcecode:: python
+
+ class XFooUserRetriever(authentication.LoginPasswordRetreiver):
+ """ authenticate by the x-foo-user http header
+ or just do normal login/password authentication
+ """
+ __regid__ = 'x-foo-user'
+ order = 0
+
+ def authentication_information(self, req):
+ """retrieve authentication information from the given request, raise
+ NoAuthInfo if expected information is not found
+ """
+ self.debug('web authenticator building auth info')
+ try:
+ login = req.get_header('x-foo-user')
+ if login:
+ return login, {'foo': True}
+ else:
+ return super(XFooUserRetriever, self).authentication_information(self, req)
+ except Exception, exc:
+ self.debug('web authenticator failed (%s)', exc)
+ raise authentication.NoAuthInfo()
+
+ def authenticated(self, retriever, req, cnx, login, authinfo):
+ """callback when return authentication information have opened a
+ repository connection successfully. Take care req has no session
+ attached yet, hence req.execute isn't available.
+
+ Here we set a flag on the request to indicate that the user is
+ foo-authenticated. Can be used by a selector
+ """
+ self.debug('web authenticator running post authentication callback')
+ cnx.foo_user = authinfo.get('foo')
+
+In the `authenticated` method we add (in an admitedly slightly hackish
+way) an attribute to the connection object. This, in turn, can be used
+to build a selector dispatching on the fact that the user was
+preauthenticated or not.
+
+.. sourcecode:: python
+
+ @objectify_selector
+ def foo_authenticated(cls, req, rset=None, **kwargs):
+ if hasattr(req.cnx, 'foo_user') and req.foo_user:
+ return 1
+ return 0