Index: main/waeup.kofa/trunk/src/waeup/kofa/authentication.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/authentication.py	(revision 14666)
+++ main/waeup.kofa/trunk/src/waeup/kofa/authentication.py	(revision 14667)
@@ -29,4 +29,6 @@
 from zope.pluggableauth.factories import Principal
 from zope.pluggableauth.plugins.session import SessionCredentialsPlugin
+from zope.pluggableauth.plugins.httpplugins import (
+        HTTPBasicAuthCredentialsPlugin)
 from zope.pluggableauth.interfaces import (
         ICredentialsPlugin, IAuthenticatorPlugin,
@@ -50,5 +52,8 @@
     Then looks for any external utilities that want to modify the PAU.
     """
-    pau.credentialsPlugins = ('No Challenge if Authenticated', 'credentials')
+    pau.credentialsPlugins = (
+            'No Challenge if Authenticated',
+            'xmlrpc-credentials',
+            'credentials')
     pau.authenticatorPlugins = ('users',)
 
@@ -71,4 +76,6 @@
 class KofaSessionCredentialsPlugin(grok.GlobalUtility,
                                     SessionCredentialsPlugin):
+    """Session plugin that picks usernames/passwords from fields in webforms.
+    """
     grok.provides(ICredentialsPlugin)
     grok.name('credentials')
@@ -77,4 +84,33 @@
     loginfield = 'form.login'
     passwordfield = 'form.password'
+
+
+class KofaXMLRPCCredentialsPlugin(
+        grok.GlobalUtility, HTTPBasicAuthCredentialsPlugin):
+    """Plugin that picks useranams/passwords from basic-auth headers.
+
+    As XMLRPC requests send/post their authentication credentials in HTTP
+    basic-auth headers, we need a plugin that can handle this.
+
+    This plugin, however, does no challenging. If a user does not provide
+    basic-auth infos, we will not ask for some. This is correct as we plan to
+    communicate with machines.
+
+    This plugin is planned to be used in "PluggableAuthenitications" registered
+    with `University` instances.
+    """
+    grok.provides(ICredentialsPlugin)
+    grok.name('xmlrpc-credentials')
+
+    def challenge(self, request):
+        """XMLRPC is for machines. No need to challenge.
+        """
+        return False
+
+    def logout(self, request):
+        """Basic auth does not provide any logout possibility.
+        """
+        return False
+
 
 class KofaPrincipalInfo(object):
Index: main/waeup.kofa/trunk/src/waeup/kofa/tests/test_authentication.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/tests/test_authentication.py	(revision 14666)
+++ main/waeup.kofa/trunk/src/waeup/kofa/tests/test_authentication.py	(revision 14667)
@@ -21,16 +21,22 @@
 import unittest
 from cStringIO import StringIO
-from zope.component import getGlobalSiteManager
+from zope.component import getGlobalSiteManager, queryUtility
 from zope.component.hooks import setSite, clearSite
 from zope.interface.verify import verifyClass, verifyObject
 from zope.password.testing import setUpPasswordManagers
-from zope.pluggableauth.interfaces import IAuthenticatorPlugin
+from zope.pluggableauth import PluggableAuthentication
+from zope.pluggableauth.interfaces import (
+    IAuthenticatorPlugin, ICredentialsPlugin)
+from zope.publisher.browser import TestRequest
 from zope.securitypolicy.interfaces import IPrincipalRoleManager
 from waeup.kofa.testing import FunctionalTestCase, FunctionalLayer
 from waeup.kofa.authentication import (
     UserAuthenticatorPlugin, Account, KofaPrincipalInfo, FailedLoginInfo,
-    get_principal_role_manager, UsersPlugin,)
+    get_principal_role_manager, UsersPlugin, KofaXMLRPCCredentialsPlugin,
+    setup_authentication)
 from waeup.kofa.interfaces import (
-    IUserAccount, IFailedLoginInfo, IKofaPrincipalInfo, IKofaPluggable)
+    IAuthPluginUtility, IUserAccount, IFailedLoginInfo, IKofaPrincipalInfo,
+    IKofaPluggable)
+
 
 class FakeSite(grok.Site, grok.Container):
@@ -39,4 +45,81 @@
     #    return getGlobalSiteManager()
     pass
+
+
+class FakeAuthPlugin(object):
+    def register(self, pau):
+        pau.credentialsPlugins += ('foo', )
+
+
+class Test_setup_authentication(FunctionalTestCase):
+    # Tests for the `setup_authentication` function
+
+    layer = FunctionalLayer
+
+    def tearDown(self):
+        # clean up registry.
+        gsm = getGlobalSiteManager()
+        for iface, name in ((IAuthPluginUtility, 'myauth'), ):
+            to_delete = queryUtility(iface, name=name)
+            if to_delete is not None:
+                gsm.unregisterUtility(provided=iface, name=name)
+        super(Test_setup_authentication, self).tearDown()
+
+    def test_plugins_are_registered(self):
+        # We can populate a PAU with (hardcoded set of) plugins
+        pau = PluggableAuthentication()
+        setup_authentication(pau)
+        for name in (
+                'No Challenge if Authenticated',
+                'xmlrpc-credentials',
+                'credentials'):
+            assert name in pau.credentialsPlugins
+        for name in ('users', ):
+            assert name in pau.authenticatorPlugins
+
+    def test_external_plugins_are_registered(self):
+        # registered plugins are called as well
+        gsm = getGlobalSiteManager()
+        gsm.registerUtility(
+            MyFakeAuthPlugin(), IAuthPluginUtility, name='myauth')
+        pau = PluggableAuthentication()
+        setup_authentication(pau)
+        assert 'foo' in pau.credentialsPlugins
+
+
+class KofaXMLRPCCredentialsPluginTests(FunctionalTestCase):
+    # Test for XMLRPC credentials plugin
+
+    layer = FunctionalLayer
+
+    def test_ifaces(self):
+        # we meet interface requirements
+        plugin = KofaXMLRPCCredentialsPlugin()
+        self.assertTrue(
+            verifyClass(ICredentialsPlugin, KofaXMLRPCCredentialsPlugin))
+
+    def test_util_is_registered(self):
+        # we can query this named utility
+        util = queryUtility(ICredentialsPlugin, name='xmlrpc-credentials')
+        assert util is not None
+
+    def test_can_extract_creds(self):
+        # we can extract credentials from appropriate requests
+        req = TestRequest(
+            environ={'HTTP_AUTHORIZATION': u'Basic bWdyOm1ncnB3'})
+        plugin = KofaXMLRPCCredentialsPlugin()
+        assert plugin.extractCredentials(req) == {
+            'login': 'mgr', 'password': 'mgrpw'}
+
+    def test_challenge_disabled(self):
+        # we will not challenge people
+        plugin = KofaXMLRPCCredentialsPlugin()
+        assert plugin.challenge(TestRequest()) is False
+
+    def test_logout_disabled(self):
+        # we do not support logging out. HTTP basic auth cannot do this.
+        plugin = KofaXMLRPCCredentialsPlugin()
+        assert plugin.logout(TestRequest()) is False
+
 
 class UserAuthenticatorPluginTests(FunctionalTestCase):
