[CalendarServer-changes] [1656] CalDAVTester/trunk
source_changes at macosforge.org
source_changes at macosforge.org
Mon Jul 9 13:33:14 PDT 2007
Revision: 1656
http://trac.macosforge.org/projects/calendarserver/changeset/1656
Author: cdaboo at apple.com
Date: 2007-07-09 13:33:13 -0700 (Mon, 09 Jul 2007)
Log Message:
-----------
Crude digest authentication support.
Modified Paths:
--------------
CalDAVTester/trunk/scripts/server/serverinfo.dtd
CalDAVTester/trunk/scripts/server/serverinfo.xml
CalDAVTester/trunk/src/manager.py
CalDAVTester/trunk/src/request.py
CalDAVTester/trunk/src/serverinfo.py
CalDAVTester/trunk/src/xmlDefs.py
Modified: CalDAVTester/trunk/scripts/server/serverinfo.dtd
===================================================================
--- CalDAVTester/trunk/scripts/server/serverinfo.dtd 2007-07-09 20:31:09 UTC (rev 1655)
+++ CalDAVTester/trunk/scripts/server/serverinfo.dtd 2007-07-09 20:33:13 UTC (rev 1656)
@@ -16,10 +16,11 @@
DRI: Cyrus Daboo, cdaboo at apple.com
-->
-<!ELEMENT serverinfo (host, port, ssl?, substitutions, serverfilepath)? >
+<!ELEMENT serverinfo (host, port, authtype?, ssl?, substitutions, serverfilepath)? >
<!ELEMENT host (#PCDATA)>
<!ELEMENT port (#PCDATA)>
+ <!ELEMENT authtype (#PCDATA)>
<!ELEMENT ssl EMPTY>
<!ELEMENT substitutions (substitution*)>
<!ELEMENT substitution (key, value)>
Modified: CalDAVTester/trunk/scripts/server/serverinfo.xml
===================================================================
--- CalDAVTester/trunk/scripts/server/serverinfo.xml 2007-07-09 20:31:09 UTC (rev 1655)
+++ CalDAVTester/trunk/scripts/server/serverinfo.xml 2007-07-09 20:33:13 UTC (rev 1656)
@@ -23,6 +23,7 @@
<serverinfo>
<host>localhost</host>
<port>8008</port>
+ <authtype>basic</authtype>
<substitutions>
<substitution>
<key>$host:</key>
Modified: CalDAVTester/trunk/src/manager.py
===================================================================
--- CalDAVTester/trunk/src/manager.py 2007-07-09 20:31:09 UTC (rev 1655)
+++ CalDAVTester/trunk/src/manager.py 2007-07-09 20:33:13 UTC (rev 1656)
@@ -40,7 +40,8 @@
"""
Main class that runs test suites defined in an XML config file.
"""
- __slots__ = ['server_info', 'populator', 'depopulate', 'tests', 'textMode', 'pid', 'memUsage', 'logLevel']
+ __slots__ = ['server_info', 'populator', 'depopulate', 'tests', 'textMode', 'pid', 'memUsage', 'logLevel',
+ 'digestCache']
LOG_NONE = 0
LOG_LOW = 1
@@ -56,6 +57,7 @@
self.pid = 0
self.memUsage = None
self.logLevel = level
+ self.digestCache = {}
def log(self, level, str, indent = 0, indentStr = " ", after = 1, before = 0):
if self.textMode and level <= self.logLevel:
Modified: CalDAVTester/trunk/src/request.py
===================================================================
--- CalDAVTester/trunk/src/request.py 2007-07-09 20:31:09 UTC (rev 1655)
+++ CalDAVTester/trunk/src/request.py 2007-07-09 20:33:13 UTC (rev 1656)
@@ -21,9 +21,108 @@
"""
import base64
+import httplib
+import md5
+import sha
import src.xmlDefs
import time
+algorithms = {
+ 'md5': md5.new,
+ 'md5-sess': md5.new,
+ 'sha': sha.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 request( object ):
"""
Represents the HTTP request to be executed, and verifcation information to
@@ -71,11 +170,14 @@
# Auth
if self.auth:
- hdrs["Authorization"] = self.gethttpauth( si )
+ if si.authtype.lower() == "digest":
+ hdrs["Authorization"] = self.gethttpdigestauth( si )
+ else:
+ hdrs["Authorization"] = self.gethttpbasicauth( si )
return hdrs
- def gethttpauth( self, si ):
+ def gethttpbasicauth( self, si ):
basicauth = [self.user, si.user][self.user == ""]
basicauth += ":"
basicauth += [self.pswd, si.pswd][self.pswd == ""]
@@ -83,6 +185,67 @@
basicauth = basicauth.replace( "\n", "" )
return basicauth
+ def gethttpdigestauth( self, si, wwwauthorize=None ):
+
+ # Check the nonce cache to see if we've used this user before
+ user = [self.user, si.user][self.user == ""]
+ pswd = [self.pswd, si.pswd][self.pswd == ""]
+ details = None
+ if self.manager.digestCache.has_key(user):
+ details = self.manager.digestCache[user]
+ else:
+ if si.ssl:
+ http = httplib.HTTPSConnection( self.manager.server_info.host, self.manager.server_info.port )
+ else:
+ http = httplib.HTTPConnection( self.manager.server_info.host, self.manager.server_info.port )
+ try:
+ http.request( "OPTIONS", self.getURI(si) )
+
+ response = http.getresponse()
+
+ finally:
+ http.close()
+
+ if response.status == 401:
+
+ wwwauthorize = response.msg.getheaders("WWW-Authenticate")
+ for item in wwwauthorize:
+ if not item.startswith("digest "):
+ continue
+ wwwauthorize = item[7:]
+ def unq(s):
+ if s[0] == s[-1] == '"':
+ return s[1:-1]
+ return s
+ parts = wwwauthorize.split(',')
+
+ details = {}
+
+ for (k, v) in [p.split('=', 1) for p in parts]:
+ details[k.strip()] = unq(v.strip())
+
+ self.manager.digestCache[user] = details
+ break
+
+ if details:
+ digest = calcResponse(
+ calcHA1(details.get('algorithm'), user, details.get('realm'), pswd, details.get('nonce'), details.get('cnonce')),
+ details.get('algorithm'), details.get('nonce'), details.get('nc'), details.get('cnonce'), details.get('qop'), self.method, self.getURI(si), 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' % (user, details.get('realm'), details.get('nonce'), self.getURI(si), 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' % (user, details.get('realm'), details.get('nonce'), self.getURI(si), digest, details.get('algorithm'), ))
+
+ return response
+ else:
+ return ""
+
def getFilePath( self ):
if self.data != None:
return self.data.filepath
Modified: CalDAVTester/trunk/src/serverinfo.py
===================================================================
--- CalDAVTester/trunk/src/serverinfo.py 2007-07-09 20:31:09 UTC (rev 1655)
+++ CalDAVTester/trunk/src/serverinfo.py 2007-07-09 20:33:13 UTC (rev 1656)
@@ -26,12 +26,13 @@
"""
Maintains information about the server beiung targetted.
"""
- __slots__ = ['host', 'port', 'ssl', 'calendarpath', 'user', 'pswd', 'serverfilepath', 'subsdict', 'extrasubsdict',]
+ __slots__ = ['host', 'port', 'authtype', 'ssl', 'calendarpath', 'user', 'pswd', 'serverfilepath', 'subsdict', 'extrasubsdict',]
def __init__( self ):
self.host = ""
self.port = 80
+ self.authtype = "basic"
self.ssl = False
self.calendarpath = ""
self.user = ""
@@ -73,6 +74,8 @@
self.host = child.firstChild.data.encode("utf-8")
elif child._get_localName() == src.xmlDefs.ELEMENT_PORT:
self.port = int( child.firstChild.data )
+ elif child._get_localName() == src.xmlDefs.ELEMENT_AUTHTYPE:
+ self.authtype = child.firstChild.data.encode("utf-8")
elif child._get_localName() == src.xmlDefs.ELEMENT_SSL:
self.ssl = True
elif child._get_localName() == src.xmlDefs.ELEMENT_SERVERFILEPATH:
Modified: CalDAVTester/trunk/src/xmlDefs.py
===================================================================
--- CalDAVTester/trunk/src/xmlDefs.py 2007-07-09 20:31:09 UTC (rev 1655)
+++ CalDAVTester/trunk/src/xmlDefs.py 2007-07-09 20:33:13 UTC (rev 1656)
@@ -20,6 +20,7 @@
ELEMENT_ACCOUNT = "account"
ELEMENT_ARG = "arg"
+ELEMENT_AUTHTYPE = "authtype"
ELEMENT_BODY = "body"
ELEMENT_CALDAVTEST = "caldavtest"
ELEMENT_CALENDARS = "calendars"
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20070709/73aed198/attachment.html
More information about the calendarserver-changes
mailing list