[CalendarServer-changes] [104] CalendarServer/branches/users/cdaboo/acl-merge

source_changes at macosforge.org source_changes at macosforge.org
Wed Sep 6 11:54:29 PDT 2006


Revision: 104
Author:   cdaboo at apple.com
Date:     2006-09-06 11:54:28 -0700 (Wed, 06 Sep 2006)

Log Message:
-----------
Update to allow Kerberos authentication to work with Twisted creds architecture.

Modified Paths:
--------------
    CalendarServer/branches/users/cdaboo/acl-merge/patches/Twisted/twisted.web2.dav.resource.patch
    CalendarServer/branches/users/cdaboo/acl-merge/twistedcaldav/authkerb.py

Added Paths:
-----------
    CalendarServer/branches/users/cdaboo/acl-merge/patches/Twisted/twisted.web2.auth.basic.patch
    CalendarServer/branches/users/cdaboo/acl-merge/patches/Twisted/twisted.web2.auth.digest.patch
    CalendarServer/branches/users/cdaboo/acl-merge/patches/Twisted/twisted.web2.auth.interfaces.patch
    CalendarServer/branches/users/cdaboo/acl-merge/patches/Twisted/twisted.web2.auth.wrapper.patch
    CalendarServer/branches/users/cdaboo/acl-merge/patches/Twisted/twisted.web2.test.test_httpauth.patch

Added: CalendarServer/branches/users/cdaboo/acl-merge/patches/Twisted/twisted.web2.auth.basic.patch
===================================================================
--- CalendarServer/branches/users/cdaboo/acl-merge/patches/Twisted/twisted.web2.auth.basic.patch	                        (rev 0)
+++ CalendarServer/branches/users/cdaboo/acl-merge/patches/Twisted/twisted.web2.auth.basic.patch	2006-09-06 18:54:28 UTC (rev 104)
@@ -0,0 +1,13 @@
+Index: twisted/web2/auth/basic.py
+===================================================================
+--- twisted/web2/auth/basic.py	(revision 17932)
++++ twisted/web2/auth/basic.py	(working copy)
+@@ -20,7 +20,7 @@
+     def getChallenge(self, peer):
+         return {'realm': self.realm}
+ 
+-    def decode(self, response, method=None):
++    def decode(self, response, request):
+         try:
+             creds = (response + '===').decode('base64')
+         except:

Added: CalendarServer/branches/users/cdaboo/acl-merge/patches/Twisted/twisted.web2.auth.digest.patch
===================================================================
--- CalendarServer/branches/users/cdaboo/acl-merge/patches/Twisted/twisted.web2.auth.digest.patch	                        (rev 0)
+++ CalendarServer/branches/users/cdaboo/acl-merge/patches/Twisted/twisted.web2.auth.digest.patch	2006-09-06 18:54:28 UTC (rev 104)
@@ -0,0 +1,19 @@
+Index: twisted/web2/auth/digest.py
+===================================================================
+--- twisted/web2/auth/digest.py	(revision 17932)
++++ twisted/web2/auth/digest.py	(working copy)
+@@ -154,7 +154,7 @@
+                 'algorithm': self.algorithm,
+                 'realm': self.realm}
+ 
+-    def decode(self, response, method='GET'):
++    def decode(self, response, request):
+         def unq(s):
+             if s[0] == s[-1] == '"':
+                 return s[1:-1]
+@@ -172,4 +172,4 @@
+ 
+         del self.outstanding[auth['opaque']]
+             
+-        return DigestedCredentials(username, method, self.realm, auth)
++        return DigestedCredentials(username, request.method, self.realm, auth)

Added: CalendarServer/branches/users/cdaboo/acl-merge/patches/Twisted/twisted.web2.auth.interfaces.patch
===================================================================
--- CalendarServer/branches/users/cdaboo/acl-merge/patches/Twisted/twisted.web2.auth.interfaces.patch	                        (rev 0)
+++ CalendarServer/branches/users/cdaboo/acl-merge/patches/Twisted/twisted.web2.auth.interfaces.patch	2006-09-06 18:54:28 UTC (rev 104)
@@ -0,0 +1,24 @@
+Index: twisted/web2/auth/interfaces.py
+===================================================================
+--- twisted/web2/auth/interfaces.py	(revision 17932)
++++ twisted/web2/auth/interfaces.py	(working copy)
+@@ -13,7 +13,7 @@
+         @return: dictionary of challenge arguments
+         """
+ 
+-    def decode(response, method=None):
++    def decode(response, request):
+         """Create a credentials object from the given response.
+         May raise twisted.cred.error.LoginFailed if the response is invalid.
+     
+@@ -20,8 +20,8 @@
+         @type response: C{str}
+         @param response: scheme specific response string
+ 
+-        @type method: C{str}
+-        @param method: the method by which this response was sent
++        @type request: L{twisted.web2.server.Request}
++        @param request: the request being processed
+ 
+         @return: ICredentials
+         """

Added: CalendarServer/branches/users/cdaboo/acl-merge/patches/Twisted/twisted.web2.auth.wrapper.patch
===================================================================
--- CalendarServer/branches/users/cdaboo/acl-merge/patches/Twisted/twisted.web2.auth.wrapper.patch	                        (rev 0)
+++ CalendarServer/branches/users/cdaboo/acl-merge/patches/Twisted/twisted.web2.auth.wrapper.patch	2006-09-06 18:54:28 UTC (rev 104)
@@ -0,0 +1,13 @@
+Index: twisted/web2/auth/wrapper.py
+===================================================================
+--- twisted/web2/auth/wrapper.py	(revision 17932)
++++ twisted/web2/auth/wrapper.py	(working copy)
+@@ -87,7 +87,7 @@
+             return UnauthorizedResource(self.credentialFactories)
+ 
+         try:
+-            creds = factory.decode(response, req.method)
++            creds = factory.decode(response, req)
+         except error.LoginFailed:
+             return UnauthorizedResource(self.credentialFactories)
+ 

Modified: CalendarServer/branches/users/cdaboo/acl-merge/patches/Twisted/twisted.web2.dav.resource.patch
===================================================================
--- CalendarServer/branches/users/cdaboo/acl-merge/patches/Twisted/twisted.web2.dav.resource.patch	2006-09-06 18:52:42 UTC (rev 103)
+++ CalendarServer/branches/users/cdaboo/acl-merge/patches/Twisted/twisted.web2.dav.resource.patch	2006-09-06 18:54:28 UTC (rev 104)
@@ -1,221 +1,13 @@
 Index: twisted/web2/dav/resource.py
 ===================================================================
---- twisted/web2/dav/resource.py	(revision 17967)
+--- twisted/web2/dav/resource.py	(revision 18065)
 +++ twisted/web2/dav/resource.py	(working copy)
-@@ -551,8 +551,15 @@
-             def onErrors(failure):
-                 from twisted.web2.dav.acl import AccessDeniedError
-                 failure.trap(AccessDeniedError)
--                response = NeedPrivilegesResponse(request.uri,
--                                                  failure.value.errors)
-+                
-+                # If we were unauthorized to start with (no Authorization header from client) then
-+                # we should return an unauthorized response instead to force the client to login if it can
-+                if request.user == davxml.Principal(davxml.Unauthenticated()):
-+                    response = UnauthorizedResponse(request.credentialFactories,
-+                                                    request.remoteAddr)
-+                else:
-+                    response = NeedPrivilegesResponse(request.uri,
-+                                                      failure.value.errors)
-                 #
-                 # We're not adding the headers here because this response
-                 # class is supposed to be a FORBIDDEN status code and
-@@ -593,9 +600,6 @@
+@@ -599,7 +599,7 @@
+             else:
                  factory = request.credentialFactories[authHeader[0]]
  
-                 creds = factory.decode(authHeader[1], request.method)
--                
--
 -                creds = factory.decode(authHeader[1], request.method)
++                creds = factory.decode(authHeader[1], request)
  
                  # Try to match principals in each principal collection on 
                  # the resource
-@@ -635,7 +639,7 @@
-         @return: the current principal, as derived from the given request.
-         """
-         if hasattr(request, "user"):
--            return request.user.element
-+            return request.user
-         else:
-             return unauthenticatedPrincipal
- 
-@@ -760,7 +764,10 @@
-                 elif isinstance(item, davxml.SupportedPrivilege):
-                     addSupportedPrivilege(item)
- 
--        for item in self.supportedPrivileges(request).children:
-+        supportedPrivs = waitForDeferred(self.supportedPrivileges(request))
-+        yield supportedPrivs
-+        supportedPrivs = supportedPrivs.getResult()
-+        for item in supportedPrivs.children:
-             assert (
-                 isinstance(item, davxml.SupportedPrivilege),
-                 "Not a SupportedPrivilege: %r" % (item,)
-@@ -920,11 +927,16 @@
-         yield ign
-         ign.getResult()
- 
--        for resource, subpath in resources:
--            acl = waitForDeferred(resource.accessControlList(request))
-+        for resource, uri in resources:
-+            acl = waitForDeferred(resource.accessControlList(request, inheritedaces=inheritedaces))
-             yield acl
-             acl = acl.getResult()
- 
-+            # Check for disabled
-+            if acl is None:
-+                errors.append((uri, list(privileges)))
-+                continue
-+
-             pending = list(privileges)
-             denied = []
- 
-@@ -930,7 +942,7 @@
- 
-             for ace in acl.children:
-                 for privilege in tuple(pending):
--                    if not match_privilege(privilege, ace.privileges):
-+                    if not match_privilege(davxml.Privilege(privilege), ace.privileges):
-                         continue
- 
-                     match = waitForDeferred(self.matchPrincipal(principal, ace.principal, request))
-@@ -952,7 +964,7 @@
-             denied += pending # If no matching ACE, then denied
- 
-             if denied: 
--                errors.append((subpath, denied))
-+                errors.append((uri, denied))
- 
-         if errors:
-             raise AccessDeniedError(errors,)
-@@ -1015,6 +1027,7 @@
-         disabled = self.hasDeadProperty(TwistedAccessDisabledProperty)
-         if disabled:
-             yield None
-+            return
- 
-         myURL = None
- 
-@@ -1069,6 +1082,7 @@
-                         # Check disabled
-                         if parent_acl is None:
-                             yield None
-+                            return
-     
-                         for ace in parent_acl.children:
-                             if ace.inherited:
-@@ -1100,7 +1114,6 @@
- 
-     accessControlList = deferredGenerator(accessControlList)
- 
--    # TODO: Convert to deferredGenerator
-     def inheritedACEsforChildren(self, request):
-         """
-         Do some optimisation of access control calculation by determining any inherited ACLs outside of
-@@ -1112,11 +1125,14 @@
-         """
-         
-         # Get the parent ACLs with inheritance and preserve the <inheritable> element.
--        parent_acl = self.accessControlList(request, inheritance=True, expanding=True)
-+        parent_acl = waitForDeferred(self.accessControlList(request, inheritance=True, expanding=True))
-+        yield parent_acl
-+        parent_acl = parent_acl.getResult()
-         
-         # Check disabled
-         if parent_acl is None:
--            return None
-+            yield None
-+            return
- 
-         # Filter out those that are not inheritable (and remove the inheritable element from those that are)
-         aces = []
-@@ -1141,7 +1157,9 @@
-                 if not ace.invert:
-                     continue
-             filteredaces.append(ace)
--        return filteredaces
-+        yield filteredaces
-+
-+    inheritedACEsforChildren = deferredGenerator(inheritedACEsforChildren)
- 
-     def inheritedACLSet(self):
-         """
-@@ -1180,6 +1198,14 @@
-                 yield (principal, principalURI)
-                 return
-         else:
-+            principalCollections = waitForDeferred(self.principalCollections(request))
-+            yield principalCollections
-+            principalCollections = principalCollections.getResult()
-+
-+            if len(principalCollections) == 0:
-+                log.msg("<DAV:principal-collection-set> property cannot be found on the resource being authorized: %s" % self)
-+            else:
-+                log.msg("Could not find principal matching user id: %s" % authid)
-             raise HTTPError(responsecode.FORBIDDEN)
- 
-     findPrincipalForAuthID = deferredGenerator(findPrincipalForAuthID)
-@@ -1261,7 +1287,7 @@
-         assert principal2 is not None, "principal2 is None"
- 
- 
--         # Compare two HRefs and do group membership test as well
-+        # Compare two HRefs and do group membership test as well
-         if principal1 == principal2:
-             yield True
-             return
-@@ -1301,7 +1327,7 @@
-         d = request.locateResource(principal2)
-         d.addCallback(_testGroup)
-         return d
--
-+        
-     def validPrincipal(self, ace_principal, request):
-         """
-         Check whether the supplied principal is valid for this resource.
-@@ -1317,16 +1343,16 @@
-             # We know that the element contains a valid element type, so all
-             # we need to do is check for a valid property and a valid href.
-             #
--            ace_principal = ace_principal.children[0]
-+            real_principal = ace_principal.children[0]
- 
--            if isinstance(ace_principal, davxml.Property):
-+            if isinstance(real_principal, davxml.Property):
-                 # See comments in matchPrincipal().  We probably need some common code.
-                 log.err("Encountered a property principal (%s), but handling is not implemented.  Invalid for ACL use."
--                        % (ace_principal,))
-+                        % (real_principal,))
-                 return False
- 
--            if isinstance(ace_principal, davxml.HRef):
--                return self.validHrefPrincipal(ace_principal, request)
-+            if isinstance(real_principal, davxml.HRef):
-+                return self.validHrefPrincipal(real_principal, request)
- 
-             return True
- 
-@@ -1367,7 +1393,6 @@
-         @param request: the request being processed.
-         @return: a deferred L{davxml.HRef} element or C{None}.
-         """
--        principal = principal.children[0]
- 
-         if isinstance(principal, davxml.Property):
-             # raise NotImplementedError("Property principals are not implemented.")
-@@ -1428,15 +1453,6 @@
-         if isinstance(principal, davxml.HRef):
-             yield principal
-         else:
--            principalCollections = waitForDeferred(
--                self.principalCollections(request))
--            yield principalCollections
--            principalCollections = principalCollections.getResult()
--
--            if len(principalCollections) == 0:
--                log.msg("<DAV:principal-collection-set> property cannot be found on the resource being authorized: %s" % self)
--            else:
--                log.msg("Could not find principal matching user id: %s" % authid)
-             yield None
- 
-         assert (

Added: CalendarServer/branches/users/cdaboo/acl-merge/patches/Twisted/twisted.web2.test.test_httpauth.patch
===================================================================
--- CalendarServer/branches/users/cdaboo/acl-merge/patches/Twisted/twisted.web2.test.test_httpauth.patch	                        (rev 0)
+++ CalendarServer/branches/users/cdaboo/acl-merge/patches/Twisted/twisted.web2.test.test_httpauth.patch	2006-09-06 18:54:28 UTC (rev 104)
@@ -0,0 +1,71 @@
+Index: twisted/web2/test/test_httpauth.py
+===================================================================
+--- twisted/web2/test/test_httpauth.py	(revision 17932)
++++ twisted/web2/test/test_httpauth.py	(working copy)
+@@ -2,6 +2,7 @@
+ from twisted.internet import defer
+ from twisted.cred import error
+ from twisted.web2.auth import basic, digest, wrapper
++from twisted.web2.test.test_server import SimpleRequest
+ 
+ from twisted.web2.test import test_server
+ 
+@@ -25,7 +26,7 @@
+                 self.username,
+                 self.password))
+ 
+-        creds = self.credentialFactory.decode(response)
++        creds = self.credentialFactory.decode(response, SimpleRequest(None, 'GET', '/'))
+         self.failUnless(creds.checkPassword(self.password))
+ 
+     def testIncorrectPassword(self):
+@@ -33,7 +34,7 @@
+                 self.username,
+                 'incorrectPassword'))
+ 
+-        creds = self.credentialFactory.decode(response)
++        creds = self.credentialFactory.decode(response, SimpleRequest(None, 'GET', '/'))
+         self.failIf(creds.checkPassword(self.password))
+ 
+     def testIncorrectPadding(self):
+@@ -43,7 +44,7 @@
+ 
+         response = response.strip('=')
+ 
+-        creds = self.credentialFactory.decode(response)
++        creds = self.credentialFactory.decode(response, SimpleRequest(None, 'GET', '/'))
+         self.failUnless(creds.checkPassword(self.password))
+ 
+     def testInvalidCredentials(self):
+@@ -51,7 +52,7 @@
+ 
+         self.assertRaises(error.LoginFailed, 
+                           self.credentialFactory.decode, 
+-                          response)
++                          response, SimpleRequest(None, 'GET', '/'))
+ 
+ challengeResponse = ('digest', {'nonce': '178288758716122392881254770685', 
+                                 'qop': 'auth', 'realm': 'test realm', 
+@@ -74,7 +75,7 @@
+     def testResponse(self):
+         challenge = self.credentialFactory.getChallenge(None)
+ 
+-        creds = self.credentialFactory.decode(authRequest, 'GET')
++        creds = self.credentialFactory.decode(authRequest, SimpleRequest(None, 'GET', '/'))
+         self.failUnless(creds.checkPassword('password'))
+ 
+     def testFailsWithDifferentMethod(self):
+@@ -80,11 +81,11 @@
+     def testFailsWithDifferentMethod(self):
+         challenge = self.credentialFactory.getChallenge(None)
+         
+-        creds = self.credentialFactory.decode(authRequest, 'POST')
++        creds = self.credentialFactory.decode(authRequest, SimpleRequest(None, 'POST', '/'))
+         self.failIf(creds.checkPassword('password'))
+ 
+     def testNoUsername(self):
+-        self.assertRaises(error.LoginFailed, self.credentialFactory.decode, namelessAuthRequest, 'GET')
++        self.assertRaises(error.LoginFailed, self.credentialFactory.decode, namelessAuthRequest, SimpleRequest(None, 'GET', '/'))
+ 
+ from zope.interface import Interface, implements
+ from twisted.cred import portal, checkers

Modified: CalendarServer/branches/users/cdaboo/acl-merge/twistedcaldav/authkerb.py
===================================================================
--- CalendarServer/branches/users/cdaboo/acl-merge/twistedcaldav/authkerb.py	2006-09-06 18:52:42 UTC (rev 103)
+++ CalendarServer/branches/users/cdaboo/acl-merge/twistedcaldav/authkerb.py	2006-09-06 18:54:28 UTC (rev 104)
@@ -29,22 +29,23 @@
      that implements full GSSAPI authentication.
 """
 
-from twisted.web2.auth.interfaces import ICredentialFactory
-from zope import interface
-from twisted.cred.credentials import ICredentials
 
 __all__ = [
     "BasicKerberosCredentials",
     "BasicKerberosAuthorizer",
-    #"NegotiateCredentials",
-    #"NegotiateAuthorizer",
+    "BasicKerberosCredentialsChecker",
+    "NegotiateCredentials",
+    "NegotiateAuthorizer",
+    "NegotiateCredentialsChecker",
 ]
 
-from twisted.cred import credentials, error
-from twisted.python import log
-from twisted.web2.dav import davxml
 from zope.interface import implements
 
+from twisted.cred import checkers, credentials, error
+from twisted.internet.defer import succeed
+from twisted.web2.auth.interfaces import ICredentialFactory
+from twisted.web2.dav.auth import IPrincipalCredentials
+
 import kerberos
 
 class BasicKerberosCredentials(credentials.UsernamePassword):
@@ -58,35 +59,7 @@
         # Convert Kerberos principal spec into service and realm
         self.service = service
         self.default_realm = realm
-
-    def verify(self, request, resource):
-        """
-        Check this set of credentials to verify they are correct and extract the current principal
-        authorization identifier.
         
-        @param request:  the L{IRequest} for the request in progress.
-        @param resource: the L{DAVResource} for which credentials are being supplied.
-        @return:         tuple of (result, principal) where: result is True if the credentials match,
-            or false otherwise; principal is the L{davxml.Principal} that matches the credentials if result
-            is True, or None if result is False.
-        """
-
-        # In our default setup the user's password is stored as a property on the principal, so
-        # we first find the matching principal and then get the password and do the comparison.
-        
-        # Try to match principals in each principal collection on the resource
-        result = kerberos.checkPassword(self.username, self.password, self.service, self.default_realm)
-        if not result:
-            log.err("Client authentication password for %s incorrect" % (self.username,))
-            return False, None
-
-        pdetails = resource.findPrincipalForAuthID(request, self.username)
-        if pdetails:
-            principalURI = pdetails[1]
-            return True, davxml.Principal(davxml.HRef().fromString(principalURI))
-        else:
-            return False, None
-        
 class BasicKerberosCredentialFactory:
     """
     Authorizer for insecure Basic (base64-encoded plaintext) authentication.
@@ -97,118 +70,60 @@
 
     implements(ICredentialFactory)
 
-    def __init__(self, realm):
+    scheme = 'basic'
 
+    def __init__(self, service, realm):
+        """
+        The realm string can be of the form service/realm at domain. We split that
+        into service at domain, and realm.
+        """
+        self.service = service
         self.realm = realm
 
-        self.service = ""
-        if len(self.realm) > 0:
-            splits = self.realm.split('/', 1)
-            if len(splits) == 2:
-                service = splits[0]
-                splits = splits[1].split('@', 1)
-                if len(splits) == 2:
-                    self.service = service + "@" + splits[1]
-                    self.realm = splits[0]
+    def getChallenge(self, peer):
+        return {'realm': self.realm}
 
-    def validForRequest(self, request): #@UnusedVariable
-        """
-        Determine whether this authorizer type is valid for the current request.
-        This is where we should check whether SSL is in use or not and reject authorizer
-        that are insecure if SSL is not being used.
+    def decode(self, response, request): #@UnusedVariable
+        try:
+            creds = (response + '===').decode('base64')
+        except:
+            raise error.LoginFailed('Invalid credentials')
 
-        @param request: the L{IRequest} for the request in progress.
-        @return:        True if the authorizer can safely be used during this request, False otherwise.
-        """
-        # Always available irrespective of SSL.
-        return True
+        creds = creds.split(':', 1)
+        if len(creds) == 2:
+            c = BasicKerberosCredentials(creds[0], creds[1], self.service, self.realm)
+            return c
+        raise error.LoginFailed('Invalid credentials')
 
-    def getScheme(self):
-        return "basic"
+class BasicKerberosCredentialsChecker:
 
-    def hasChallenge(self):
-        """
-        Indicates whether this authenticator sends some data in the initial WWW-Authenticate challenge.
-        
-        @return: True if a challenge needs to be sent back, False if not.
-        """
-        return True
-    
-    def getChallenge(self):
-        return 'realm="%s"' % self.realm
+    implements(checkers.ICredentialsChecker)
 
-    def hasResponse(self):
-        """
-        Indicates whether this authenticator sends back a WWW-Authenticate response after
-        the initial client challenge.
-        
-        @return: True if a response needs to be sent back, False if not.
-        """
-        return False
+    credentialInterfaces = (IPrincipalCredentials,)
 
-    def getResponse(self):
-        """
-        The response to send back to the client.
-        
-        @return: the C{str} for the response to send back.
-        """
-        return ""
+    def requestAvatarId(self, credentials):
 
-    def decode(self, response, method=None): #@UnusedVariable
-        # At least one SIP client improperly pads its Base64 encoded messages
-        for i in range(3):
-            try:
-                creds = (response + ('=' * i)).decode('base64')
-            except:
-                pass
-            else:
-                break
-        else:
-            # Totally bogus
-            raise error.LoginFailed('Invalid credentials')
-        p = creds.split(':', 1)
-        if len(p) == 2:
-            c = BasicKerberosCredentials(p[0], p[1], self.service, self.realm)
-            return c
-        raise error.LoginFailed('Invalid credentials')
+        # If there is no calendar principal URI then the calendar user is disabled.
+        pcreds = IPrincipalCredentials(credentials)
 
+        creds = pcreds.credentials
+        if isinstance(creds, BasicKerberosCredentials):
+            if kerberos.checkPassword(creds.username, creds.password, creds.service, creds.default_realm):
+                return succeed(pcreds.principalURI)
+        
+        raise error.UnauthorizedLogin("Bad credentials for: %s" % (pcreds.principalURI,))
 
-class NegotiateCredentials(credentials.UsernamePassword):
+class NegotiateCredentials:
     """
     A set of user/password credentials that checks itself against Kerberos.
     """
 
-    interface.implements(ICredentials)
+    implements(credentials.ICredentials)
 
-    def __init__(self, user):
+    def __init__(self, username):
         
-        self.username = user
-
-    def verify(self, request, resource):
-        """
-        Check this set of credentials to verify they are correct and extract the current principal
-        authorization identifier.
+        self.username = username
         
-        @param request:  the L{IRequest} for the request in progress.
-        @param resource: the L{DAVResource} for which credentials are being supplied.
-        @return:         tuple of (result, principal) where: result is True if the credentials match,
-            or false otherwise; principal is the L{davxml.Principal} that matches the credentials if result
-            is True, or None if result is False.
-        """
-
-        # When we get here we know that Kerberos authentication succeeded if the user name is not empty.
-        if len(self.username) == 0:
-            log.err("Client authentication failed")
-            return False, None
-        
-        # Try to match principals in each principal collection on the resource
-        pdetails = resource.findPrincipalForAuthID(request, self.username)
-        if pdetails:
-            principalURI = pdetails[1]
-            return True, davxml.Principal(davxml.HRef().fromString(principalURI))
-        else:
-            return False, None
-        
 class NegotiateCredentialFactory:
     """
     Authorizer for insecure Basic (base64-encoded plaintext) authentication.
@@ -219,68 +134,28 @@
 
     implements(ICredentialFactory)
 
+    scheme = 'negotiate'
+
     def __init__(self, service):
 
         self.service = service
-        self.response = ""
 
-    def validForRequest(self, request): #@UnusedVariable
-        """
-        Determine whether this authorizer type is valid for the current request.
-        This is where we should check whether SSL is in use or not and reject authorizer
-        that are insecure if SSL is not being used.
+    def getChallenge(self, peer):
+        return {}
 
-        @param request: the L{IRequest} for the request in progress.
-        @return:        True if the authorizer can safely be used during this request, False otherwise.
-        """
-         # Always available irrespective of SSL.
-        return True
-
-    def getScheme(self):
-        return "negotiate"
-
-    def hasChallenge(self):
-        """
-        Indicates whether this authenticator sends some data in the initial WWW-Authenticate challenge.
+    def decode(self, base64data, request):
         
-        @return: True if a challenge needs to be sent back, False if not.
-        """
-        return False
-    
-    def getChallenge(self):
-        return ""
-
-    def hasResponse(self):
-        """
-        Indicates whether this authenticator sends back a WWW-Authenticate response after
-        the initial client challenge.
-        
-        @return: True if a response needs to be sent back, False if not.
-        """
-        return True
-
-    def getResponse(self):
-        """
-        The response to send back to the client.
-        
-        @return: the C{str} for the response to send back.
-        """
-        return self.response
-
-    def decode(self, response, method=None): #@UnusedVariable
-        
         # Init GSSAPI first
         result, context = kerberos.authGSSServerInit(self.service);
         if result != 1:
             raise error.LoginFailed('Authentication System Failure')
 
         # Do the GSSAPI step and get response and username
-        result = kerberos.authGSSServerStep(context, response);
+        result = kerberos.authGSSServerStep(context, base64data);
         if result == -1:
-            self.response = ""
-            username = ""
+            raise error.UnauthorizedLogin("Bad credentials for")
         else:
-            self.response = kerberos.authGSSServerResponse(context)
+            response = kerberos.authGSSServerResponse(context)
             username = kerberos.authGSSServerUserName(context)
             
             # Username may include realm suffix which we want to strip
@@ -292,4 +167,37 @@
         if result != 1:
             raise error.LoginFailed('Authentication System Failure')
         
+        # If we successfully decoded and verified the Kerberos credentials we need to add the Kerberos
+        # response data to the outgoing request
+
+        wwwauth = '%s %s' % (self.scheme, response)
+
+        def responseFilterAddWWWAuthenticate(request, response): #@UnusedVariable
+            response.headers.addRawHeader('www-authenticate', wwwauth)
+            return response
+
+        responseFilterAddWWWAuthenticate.handleErrors = True
+
+        request.addResponseFilter(responseFilterAddWWWAuthenticate)
+
         return NegotiateCredentials(username)
+
+class NegotiateCredentialsChecker:
+
+    implements(checkers.ICredentialsChecker)
+
+    credentialInterfaces = (IPrincipalCredentials,)
+
+    def requestAvatarId(self, credentials):
+        # NB If we get here authentication has already succeeded as it is done in NegotiateCredentialsFactory.decode
+        # So all we need to do is return the principal URI from the credentials.
+
+        # If there is no calendar principal URI then the calendar user is disabled.
+        pcreds = IPrincipalCredentials(credentials)
+
+        creds = pcreds.credentials
+        if isinstance(creds, NegotiateCredentials):
+            return succeed(pcreds.principalURI)
+        
+        raise error.UnauthorizedLogin("Bad credentials for: %s" % (pcreds.principalURI,))
+

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


More information about the calendarserver-changes mailing list