[CalendarServer-changes] [1760]
CalendarClientSimulator/trunk/src/calendarclient.py
source_changes at macosforge.org
source_changes at macosforge.org
Mon Aug 6 11:27:59 PDT 2007
Revision: 1760
http://trac.macosforge.org/projects/calendarserver/changeset/1760
Author: cdaboo at apple.com
Date: 2007-08-06 11:27:58 -0700 (Mon, 06 Aug 2007)
Log Message:
-----------
Support digest auth.
Modified Paths:
--------------
CalendarClientSimulator/trunk/src/calendarclient.py
Modified: CalendarClientSimulator/trunk/src/calendarclient.py
===================================================================
--- CalendarClientSimulator/trunk/src/calendarclient.py 2007-08-06 15:37:10 UTC (rev 1759)
+++ CalendarClientSimulator/trunk/src/calendarclient.py 2007-08-06 18:27:58 UTC (rev 1760)
@@ -19,6 +19,7 @@
from xml.etree import ElementTree
from random import randint
from random import sample
+import md5
import icalutils
import uuid
import os
@@ -66,6 +67,101 @@
else:
return s
+algorithms = {
+ 'md5': md5.new,
+ 'md5-sess': md5.new,
+}
+
+# DigestCalcHA1
+def calcHA1(
+ pszAlg,
+ pszUserName,
+ pszRealm,
+ pszPassword,
+ pszNonce,
+ pszCNonce,
+ preHA1=None
+):
+ """
+ @param pszAlg: The name of the algorithm to use to calculate the digest.
+ Currently supported are md5 md5-sess and sha.
+
+ @param pszUserName: The username
+ @param pszRealm: The realm
+ @param pszPassword: The password
+ @param pszNonce: The nonce
+ @param pszCNonce: The cnonce
+
+ @param preHA1: If available this is a str containing a previously
+ calculated HA1 as a hex string. If this is given then the values for
+ pszUserName, pszRealm, and pszPassword are ignored.
+ """
+
+ if (preHA1 and (pszUserName or pszRealm or pszPassword)):
+ raise TypeError(("preHA1 is incompatible with the pszUserName, "
+ "pszRealm, and pszPassword arguments"))
+
+ if preHA1 is None:
+ # We need to calculate the HA1 from the username:realm:password
+ m = algorithms[pszAlg]()
+ m.update(pszUserName)
+ m.update(":")
+ m.update(pszRealm)
+ m.update(":")
+ m.update(pszPassword)
+ HA1 = m.digest()
+ else:
+ # We were given a username:realm:password
+ HA1 = preHA1.decode('hex')
+
+ if pszAlg == "md5-sess":
+ m = algorithms[pszAlg]()
+ m.update(HA1)
+ m.update(":")
+ m.update(pszNonce)
+ m.update(":")
+ m.update(pszCNonce)
+ HA1 = m.digest()
+
+ return HA1.encode('hex')
+
+# DigestCalcResponse
+def calcResponse(
+ HA1,
+ algo,
+ pszNonce,
+ pszNonceCount,
+ pszCNonce,
+ pszQop,
+ pszMethod,
+ pszDigestUri,
+ pszHEntity,
+):
+ m = algorithms[algo]()
+ m.update(pszMethod)
+ m.update(":")
+ m.update(pszDigestUri)
+ if pszQop == "auth-int":
+ m.update(":")
+ m.update(pszHEntity)
+ HA2 = m.digest().encode('hex')
+
+ m = algorithms[algo]()
+ m.update(HA1)
+ m.update(":")
+ m.update(pszNonce)
+ m.update(":")
+ if pszNonceCount and pszCNonce and pszQop:
+ m.update(pszNonceCount)
+ m.update(":")
+ m.update(pszCNonce)
+ m.update(":")
+ m.update(pszQop)
+ m.update(":")
+ m.update(HA2)
+ respHash = m.digest().encode('hex')
+ return respHash
+
class CalendarClient(object):
"""
CalendarClient client simulator.
@@ -100,6 +196,7 @@
"calendar_data": {}
}
self.logger = None
+ self.authType = None
def valid(self):
if (self.server is None or
@@ -373,6 +470,79 @@
def doRequest(self, ruri, method='GET', headers={}, data=None):
+ if self.authType == "basic":
+ return self.doBasicRequest(ruri, method, headers, data)
+ elif self.authType == "digest":
+ return self.doDigestRequest(ruri, method, headers, data)
+ else:
+ # Do on demand auth
+ status, response_headers, response_data = self.doAuthenticatedRequest(ruri, method, headers, data)
+ if status == 401:
+ # Get WWW-Authenticate
+ for header, value in response_headers:
+ if header == "www-authenticate":
+ www_authenticate = value
+ if www_authenticate.lower().startswith("basic"):
+ self.authType = "basic"
+ return self.doBasicRequest(ruri, method, headers, data)
+ elif www_authenticate.lower().startswith("digest"):
+ self.authType = "digest"
+ return self.doDigestRequest(ruri, method, headers, data, www_authenticate)
+ break
+
+ return status, response_headers, response_data
+
+ def doBasicRequest(self, ruri, method='GET', headers={}, data=None):
+
+ basicauth = "%s:%s" % (self.user, self.password)
+ basicauth = "Basic " + basicauth.encode("base64")
+ headers["Authorization"] = basicauth
+ return self.doAuthenticatedRequest(ruri, method, headers, data)
+
+ def doDigestRequest(self, ruri, method='GET', headers={}, data=None, www_authenticate=None):
+
+ details = None
+ if hasattr(self, "digest_details"):
+ details = self.digest_details
+ else:
+ www_authenticate = www_authenticate[7:]
+ parts = www_authenticate.split(',')
+
+ details = {}
+
+ for (k, v) in [p.split('=', 1) for p in parts]:
+ details[k.strip()] = unq(v.strip())
+
+ self.digest_details = details
+
+ if details:
+ digest = calcResponse(
+ calcHA1(details.get('algorithm'), self.user, details.get('realm'), self.password, details.get('nonce'), details.get('cnonce')),
+ details.get('algorithm'), details.get('nonce'), details.get('nc'), details.get('cnonce'), details.get('qop'), method, ruri, None
+ )
+
+ if details.get('qop'):
+ response = ('Digest username="%s", realm="%s", '
+ 'nonce="%s", uri="%s", '
+ 'response=%s, algorithm=%s, cnonce="%s", qop=%s, nc=%s' % (self.user, details.get('realm'), details.get('nonce'), ruri, digest, details.get('algorithm'), details.get('cnonce'), details.get('qop'), details.get('nc'), ))
+ else:
+ response = ('Digest username="%s", realm="%s", '
+ 'nonce="%s", uri="%s", '
+ 'response=%s, algorithm=%s' % (self.user, details.get('realm'), details.get('nonce'), ruri, digest, details.get('algorithm'), ))
+ else:
+ return 401, {}, ""
+
+ #if www_authenticate
+ headers["Authorization"] = response
+ status, response_headers, response_data = self.doAuthenticatedRequest(ruri, method, headers, data)
+ if status == 401 and www_authenticate is None:
+ www_authenticate = response_headers["www-authenticate"]
+ return self.doDigestRequest(ruri, method, headers, data, www_authenticate)
+ else:
+ return status, response_headers, response_data
+
+ def doAuthenticatedRequest(self, ruri, method='GET', headers={}, data=None):
+
if self.server.startswith("https://"):
conn = httplib.HTTPSConnection(self.server[8:])
elif self.server.startswith("http://"):
@@ -380,10 +550,6 @@
else:
raise ValueError("Server address invalid: %s" (self.server,))
- basicauth = "%s:%s" % (self.user, self.password)
- basicauth = "Basic " + basicauth.encode("base64")
- headers["Authorization"] = basicauth
-
if data:
conn.request(method, ruri, data, headers=headers)
else:
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20070806/e74bb7d6/attachment.html
More information about the calendarserver-changes
mailing list