[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