Index: main/waeup.cas/trunk/setup.py
===================================================================
--- main/waeup.cas/trunk/setup.py	(revision 10508)
+++ main/waeup.cas/trunk/setup.py	(revision 10509)
@@ -69,4 +69,5 @@
     dummy = waeup.cas.authenticators:DummyAuthenticator
     kofa1 = waeup.cas.authenticators:KofaAuthenticator
+    kofa_moodle1 = waeup.cas.authenticators:KofaMoodleAuthenticator
     """,
 )
Index: main/waeup.cas/trunk/waeup/cas/authenticators.py
===================================================================
--- main/waeup.cas/trunk/waeup/cas/authenticators.py	(revision 10508)
+++ main/waeup.cas/trunk/waeup/cas/authenticators.py	(revision 10509)
@@ -170,2 +170,61 @@
                 return (True, '')
         return (False, 'Invalid username or password.')
+
+class KofaMoodleAuthenticator(KofaAuthenticator):
+    """Authenticate against a running Kofa instance and transfer
+    data to Moodle.
+
+    Configuration of Moodle:
+    1. Set 'passwordpolicy' to No
+    2. Create external web service 'Kofa' with the following functions:
+      core_user_create_users, core_user_get_users,
+      core_user_update_users, enrol_manual_enrol_users
+    3. Create token for the admin user (no special web service user needed)
+      and for service 'Kofa'
+    4. Enable and configure CAS server authentication.
+      CAS protocol version is 1.0. Moodle expects SSL/TLS protocol.
+    """
+
+    name = 'kofa_moodle1'
+
+    def check_credentials(self, username='', password=''):
+        """Do the real check.
+        """
+        for backend_name, backend in self.backends.items():
+            if not re.match(backend['marker'], username):
+                continue
+            # remove school marker
+            username = RE_SCHOOL_MARKER.sub('', username)
+            proxy = xmlrpclib.ServerProxy(
+                backend['url'], allow_none=True)
+            moodle = xmlrpclib.ServerProxy(
+                backend['moodle_url'], allow_none=True)
+            principal = proxy.check_applicant_credentials(username, password)
+            if principal is None:
+                principal = proxy.check_student_credentials(username, password)
+            if principal is not None:
+                if principal['type'] == 'student':
+                    student = proxy.get_moodle_data(username)
+                    try:
+                        # Usernames in Moodle must not contain uppercase
+                        # letters even if extendedusernamechars is set True.
+                        result = moodle.core_user_create_users([
+                            {'username':username.lower(),
+                             'password':'dummy',
+                             'firstname':student['firstname'],
+                             'lastname':student['lastname'],
+                             'email':student['email']}])
+                    except xmlrpclib.Fault:
+                        # user exists
+                        pass
+                    result = moodle.core_user_get_users([
+                        {'key':'username', 'value':username}])
+                    user_id = result['users'][0]['id']
+                    # Due to a lack of Moodle (Moodle requires an LDAP
+                    # connection) the authentication method can't
+                    # be set when the user is created. It must be updated
+                    # after creation.
+                    result = moodle.core_user_update_users([
+                        {'id':user_id,'auth':'cas'}])
+                return (True, '')
+        return (False, 'Invalid username or password.')
Index: main/waeup.cas/trunk/waeup/cas/tests/sample4.ini
===================================================================
--- main/waeup.cas/trunk/waeup/cas/tests/sample4.ini	(revision 10509)
+++ main/waeup.cas/trunk/waeup/cas/tests/sample4.ini	(revision 10509)
@@ -0,0 +1,26 @@
+# A sample config to run WSGI components with paster
+# This config is with all possible options set explicitly.
+[app:main]
+use = egg:waeup.cas#server
+# SQLAlchemy connection string
+# By default we get an in-memory SQLite database
+# Samples:
+#   sqlite in-memory db:  sqlite://:memory:
+#   sqlite file-based db: sqlite:////path/to/db
+
+db = sqlite:///:memory:
+
+auth = kofa_moodle1
+auth_backends = {
+  'kofa_moodle1':
+    {'url': 'http://xmlrpcuser1:xmlrpcuser1@localhost:8080/app1/',
+     'marker': '^K',
+     'moodle_url': 'http://localhost/moodle/webservice/xmlrpc/server.php?wstoken=de1e1cbacf91ec6290bb6f6339122e7d',
+    },
+  }
+
+[server:main]
+# run a WSGI server on localhost:8008
+use = egg:Paste#http
+host = 0.0.0.0
+port = 8008
