[CalendarServer-changes] [252] CalendarServer/trunk

source_changes at macosforge.org source_changes at macosforge.org
Thu Oct 5 11:23:24 PDT 2006


Revision: 252
          http://trac.macosforge.org/projects/calendarserver/changeset/252
Author:   cdaboo at apple.com
Date:     2006-10-05 11:23:24 -0700 (Thu, 05 Oct 2006)

Log Message:
-----------
Authentication configuration now done via repository.xml config file.

Modified Paths:
--------------
    CalendarServer/trunk/conf/repository-static.xml
    CalendarServer/trunk/conf/repository.dtd
    CalendarServer/trunk/conf/repository.xml
    CalendarServer/trunk/twistedcaldav/repository.py

Modified: CalendarServer/trunk/conf/repository-static.xml
===================================================================
--- CalendarServer/trunk/conf/repository-static.xml	2006-10-05 04:32:42 UTC (rev 251)
+++ CalendarServer/trunk/conf/repository-static.xml	2006-10-05 18:23:24 UTC (rev 252)
@@ -116,6 +116,18 @@
     </collection>
   </docroot>
 
+  <authentication>
+  	<basic enable="yes" onlyssl="yes" credentials="property">
+  	  <realm></realm>
+  	</basic>
+  	<digest enable="no" onlyssl="no" credentials="property">
+  	  <realm></realm>
+  	</digest>
+  	<kerberos enable="no" onlyssl="no">
+  	  <service></service>
+  	</kerberos>
+  </authentication>
+
   <accounts>
     <user>
       <uid>admin</uid>

Modified: CalendarServer/trunk/conf/repository.dtd
===================================================================
--- CalendarServer/trunk/conf/repository.dtd	2006-10-05 04:32:42 UTC (rev 251)
+++ CalendarServer/trunk/conf/repository.dtd	2006-10-05 18:23:24 UTC (rev 252)
@@ -16,7 +16,7 @@
 DRI: Cyrus Daboo, cdaboo at apple.com
  -->
 
-<!ELEMENT repository (docroot, accounts) >
+<!ELEMENT repository (docroot, authentication, accounts) >
 
   <!ELEMENT docroot (collection) >
 	<!ATTLIST docroot auto-principal-collection-set (yes|no) "yes">
@@ -48,6 +48,22 @@
 
           <!ELEMENT read EMPTY>
 
+  <!ELEMENT authentication (basic, digest, kerberos) >
+  	<!ELEMENT basic (realm, service?)>
+	  <!ATTLIST basic enable  (yes|no) "yes"
+	                  onlyssl (yes|no) "yes"
+	                  credentials (property|directory|kerberos) "property">
+  	<!ELEMENT digest (realm)>
+	  <!ATTLIST digest enable  (yes|no) "no"
+	                   onlyssl (yes|no) "no"
+	                   credentials (property) "property">
+  	<!ELEMENT kerberos (service)>
+	  <!ATTLIST kerberos enable  (yes|no) "no"
+	                     onlyssl (yes|no) "no">
+    
+    <!ELEMENT realm         (#PCDATA)>
+    <!ELEMENT service       (#PCDATA)>
+  	
   <!ELEMENT accounts (user*) >
 
     <!ELEMENT user (uid, pswd, name, cuaddr*, calendar*, acl?, quota?, autorespond?)>

Modified: CalendarServer/trunk/conf/repository.xml
===================================================================
--- CalendarServer/trunk/conf/repository.xml	2006-10-05 04:32:42 UTC (rev 251)
+++ CalendarServer/trunk/conf/repository.xml	2006-10-05 18:23:24 UTC (rev 252)
@@ -187,6 +187,18 @@
     </collection>
   </docroot>
 
+  <authentication>
+  	<basic enable="yes" onlyssl="yes" credentials="directory">
+  	  <realm></realm>
+  	</basic>
+  	<digest enable="no" onlyssl="no" credentials="property">
+  	  <realm></realm>
+  	</digest>
+  	<kerberos enable="no" onlyssl="no">
+  	  <service></service>
+  	</kerberos>
+  </authentication>
+
   <accounts/>
 
 </repository>

Modified: CalendarServer/trunk/twistedcaldav/repository.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/repository.py	2006-10-05 04:32:42 UTC (rev 251)
+++ CalendarServer/trunk/twistedcaldav/repository.py	2006-10-05 18:23:24 UTC (rev 252)
@@ -24,8 +24,6 @@
 
 __all__ = ["RepositoryBuilder"]
 
-
-
 from twisted.application.internet import SSLServer, TCPServer
 from twisted.application.service import Application, IServiceCollection, MultiService
 from twisted.cred.portal import Portal
@@ -33,20 +31,21 @@
 from twisted.python import log
 from twisted.python.filepath import FilePath
 from twisted.python.reflect import namedObject
-from twisted.web2.auth import basic
+from twisted.web2.auth import basic, digest
 from twisted.web2.channel.http import HTTPFactory
 from twisted.web2.dav import auth, davxml
-from twisted.web2.dav.auth import TwistedPasswordProperty
 from twisted.web2.dav.element.base import PCDATAElement
 from twisted.web2.dav.element.parser import lookupElement
 from twisted.web2.dav.resource import TwistedACLInheritable
 from twisted.web2.dav.util import joinURL
 from twisted.web2.log import LogWrapperResource
 from twisted.web2.server import Site
+
 from twistedcaldav import caldavxml, customxml
 from twistedcaldav.logging import RotatingFileAccessLoggingObserver
 from twistedcaldav.resource import CalDAVResource
 from twistedcaldav.static import CalDAVFile, CalendarHomeFile, CalendarPrincipalFile
+from twistedcaldav import authkerb, directory
 
 import os
 
@@ -94,6 +93,21 @@
 ATTRVALUE_PRINCIPALS = "principals"
 ATTRVALUE_CALENDARS = "calendars"
 
+ELEMENT_AUTHENTICATION = "authentication"
+ELEMENT_BASIC = "basic"
+ELEMENT_DIGEST = "digest"
+ELEMENT_KERBEROS = "kerberos"
+ELEMENT_REALM = "realm"
+ELEMENT_SERVICE = "service"
+
+ATTRIBUTE_ENABLE = "enable"
+ATTRIBUTE_ONLYSSL = "onlyssl"
+ATTRIBUTE_CREDENTIALS = "credentials"
+
+ATTRIBUTE_VALUE_PROPERTY = "property"
+ATTRIBUTE_VALUE_DIRECTORY = "directory"
+ATTRIBUTE_VALUE_KERBEROS = "kerberos"
+
 ELEMENT_ACCOUNTS = "accounts"
 ELEMENT_USER = "user"
 ELEMENT_USERID = "uid"
@@ -107,10 +121,15 @@
 ATTRIBUTE_REPEAT = "repeat"
 
 def startServer(docroot, repo, doacct, doacl, dossl, keyfile, certfile, onlyssl, port, sslport, maxsize, quota, serverlogfile, manhole):
-
+    """
+    Start the server using XML-based configuration details and supplied .plist based options.
+    """
+    
+    # Make sure SSL options make sense
     if not dossl and onlyssl:
         dossl = True
     
+    # Check the file paths for validity
     if os.path.exists(docroot):
         print "Document root is: %s" % (docroot,)
     else:
@@ -132,6 +151,7 @@
         else:
             raise IOError("SSL Certificate file does not exist: %s" % (certfile,))
     
+    # We need a special service for the access log
     class Web2Service(MultiService):
         def __init__(self, logObserver):
             self.logObserver = logObserver
@@ -145,6 +165,7 @@
             MultiService.stopService(self)
             self.logObserver.stop()
     
+    # Build the server
     builder = RepositoryBuilder(docroot,
                                 doAccounts=doacct,
                                 resetACLs=doacl,
@@ -159,13 +180,33 @@
     web2.setServiceParent(parent)
     parent = web2
     
+    # Configure appropriate authentication 
+    authenticator = builder.authentication.getEnabledAuthenticator()
+    
     portal = Portal(auth.DavRealm())
-    portal.registerChecker(auth.TwistedPropertyChecker())
+    if authenticator.credentials == ATTRIBUTE_VALUE_PROPERTY:
+        portal.registerChecker(auth.TwistedPropertyChecker())
+    elif authenticator.credentials == ATTRIBUTE_VALUE_DIRECTORY:
+        portal.registerChecker(directory.DirectoryCredentialsChecker())
+    elif authenticator.credentials == ATTRIBUTE_VALUE_KERBEROS:
+        if authenticator.type == "basic":
+            portal.registerChecker(authkerb.BasicKerberosCredentialsChecker())
+        elif authenticator.type == "kerberos":
+            portal.registerChecker(authkerb.NegotiateCredentialsChecker())
     
-    credentialFactories = (basic.BasicCredentialFactory(""),)
+    if authenticator.type == "basic":
+        if authenticator.credentials == ATTRIBUTE_VALUE_KERBEROS:
+            credentialFactories = (authkerb.BasicKerberosCredentialFactory(authenticator.service, authenticator.realm),)
+        else:
+            credentialFactories = (basic.BasicCredentialFactory(authenticator.realm),)
+    elif authenticator.type == "digest":
+        credentialFactories = (digest.DigestCredentialFactory("md5", authenticator.realm),)
+    elif authenticator.type == "kerberos":
+        credentialFactories = (authkerb.NegotiateCredentialFactory(authenticator.service),)
     
     loginInterfaces = (auth.IPrincipal,)
     
+    # Build the site and server instances
     site = Site(LogWrapperResource(auth.AuthenticationWrapper(rootresource, 
                                                               portal,
                                                               credentialFactories,
@@ -216,6 +257,7 @@
         """
         self.docRoot = DocRoot(docroot)
         self.doAccounts = doAccounts
+        self.authentication = Authentication()
         self.accounts = Provisioner()
         self.resetACLs = resetACLs
         self.maxsize = maxsize
@@ -259,6 +301,8 @@
         for child in node._get_childNodes():
             if child._get_localName() == ELEMENT_DOCROOT:
                 self.docRoot.parseXML(child)
+            elif child._get_localName() == ELEMENT_AUTHENTICATION:
+                self.authentication.parseXML(child)
             elif child._get_localName() == ELEMENT_ACCOUNTS:
                 self.accounts.parseXML(child)
 
@@ -647,9 +691,9 @@
             log.msg("Created principal: %s" % principalURL)
         principal = CalendarPrincipalFile(principal.path, principalURL)
         if len(item.pswd):
-            principal.writeDeadProperty(TwistedPasswordProperty.fromString(item.pswd))
+            principal.writeDeadProperty(auth.TwistedPasswordProperty.fromString(item.pswd))
         else:
-            principal.removeDeadProperty(TwistedPasswordProperty())
+            principal.removeDeadProperty(auth.TwistedPasswordProperty())
         if len(item.name):
             principal.writeDeadProperty(davxml.DisplayName.fromString(item.name))
         else:
@@ -854,3 +898,82 @@
                 self.acl.parseXML(child)
             elif child._get_localName() == ELEMENT_AUTORESPOND:
                 self.autorespond = True
+
+class Authentication:
+    """
+    Parses authentication information  for XML file.
+    """
+    
+    class AuthType:
+        """
+        Base class for authentication method behaviors.
+        """
+        
+        def __init__(self, type):
+            self.type = type
+            self.enabled = False
+            self.onlyssl = False
+            self.credentials = ATTRIBUTE_VALUE_PROPERTY
+            self.realm = ""
+            self.service = ""
+            
+        def parseXML(self, node):
+            if node.hasAttribute(ATTRIBUTE_ENABLE):
+                self.enabled = node.getAttribute(ATTRIBUTE_ENABLE) == ATTRIBUTE_VALUE_YES
+            if node.hasAttribute(ATTRIBUTE_ONLYSSL):
+                self.onlyssl = node.getAttribute(ATTRIBUTE_ONLYSSL) == ATTRIBUTE_VALUE_YES
+            if node.hasAttribute(ATTRIBUTE_CREDENTIALS):
+                self.credentials = node.getAttribute(ATTRIBUTE_CREDENTIALS)
+            for child in node._get_childNodes():
+                if child._get_localName() == ELEMENT_REALM:
+                    if child.firstChild is not None:
+                       self.realm = child.firstChild.data.encode("utf-8")
+                elif child._get_localName() == ELEMENT_SERVICE:
+                    if child.firstChild is not None:
+                       self.service = child.firstChild.data.encode("utf-8")
+            
+    def __init__(self):
+        self.basic = Authentication.AuthType("basic")
+        self.digest = Authentication.AuthType("digest")
+        self.kerberos = Authentication.AuthType("kerberos")
+    
+    def getEnabledAuthenticator(self):
+        if self.basic.enabled:
+            return self.basic
+        elif self.digest.enabled:
+            return self.digest
+        elif self.kerberos.enabled:
+            return self.kerberos
+        else:
+            return None
+
+    def parseXML(self, node):
+        for child in node._get_childNodes():
+            if child._get_localName() == ELEMENT_BASIC:
+                self.basic.parseXML(child)
+            elif child._get_localName() == ELEMENT_DIGEST:
+                self.digest.parseXML(child)
+            elif child._get_localName() == ELEMENT_KERBEROS:
+                self.kerberos.parseXML(child)
+        
+        # Sanity checks
+        ctr = 0
+        if self.basic.enabled:
+            ctr += 1
+        if self.digest.enabled:
+            ctr += 1
+        if self.kerberos.enabled:
+            ctr += 1
+        if ctr == 0:
+            log.msg("One authentication method must be enabled.")
+            raise ValueError, "One authentication method must be enabled."
+        elif ctr > 1:
+            log.msg("Only one authentication method allowed.")
+            raise ValueError, "Only one authentication method allowed."
+        
+        # FIXME: currently we have no way to turn off an auth mechanism based on whether SSL is in use or not,
+        # so the onlyssl attribute is meaning less for now.
+#        if self.basic.enabled and not self.basic.onlyssl:
+#            log.msg("IMPORTANT: plain text passwords are allowed without an encrypted/secure connection.")
+        if self.basic.enabled:
+            log.msg("IMPORTANT: plain text passwords are allowed without an encrypted/secure connection.")

-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20061005/d06bc20d/attachment.html


More information about the calendarserver-changes mailing list