summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Chauvat <nicolas.chauvat@logilab.fr>2011-02-15 23:28:07 +0100
committerNicolas Chauvat <nicolas.chauvat@logilab.fr>2011-02-15 23:28:07 +0100
commit09a81984d4057b0ffcba98b358902f01053a0c35 (patch)
treee0f1009e1bd774c5e932d723bce5877847bb70b3
parent446260f29a8d8da8fa4021dd79cb427948fdeebd (diff)
downloadlogilab-common-09a81984d4057b0ffcba98b358902f01053a0c35.tar.gz
[urllib2ext] new module with HTTPGssapiAuthHandler
applied patch urllib2ext.diff
-rw-r--r--__pkginfo__.py3
-rw-r--r--debian/control2
-rw-r--r--urllib2ext.py87
3 files changed, 89 insertions, 3 deletions
diff --git a/__pkginfo__.py b/__pkginfo__.py
index 206a5db..fe43ff9 100644
--- a/__pkginfo__.py
+++ b/__pkginfo__.py
@@ -23,7 +23,7 @@ modname = 'common'
subpackage_of = 'logilab'
subpackage_master = True
-numversion = (0, 54, 1)
+numversion = (0, 55, 0)
version = '.'.join([str(num) for num in numversion])
license = 'LGPL' # 2.1 or later
@@ -40,4 +40,3 @@ scripts = [join('bin', 'pytest')]
include_dirs = [join('test', 'data')]
install_requires = ['unittest2 >= 0.5.1']
-
diff --git a/debian/control b/debian/control
index e2b1007..a06507c 100644
--- a/debian/control
+++ b/debian/control
@@ -20,7 +20,7 @@ Package: python-logilab-common
Architecture: all
Provides: ${python:Provides}
Depends: ${python:Depends}, ${misc:Depends}
-Suggests: pyro, python-unittest2
+Suggests: pyro, python-unittest2, python-kerberos
Recommends: python-egenix-mxdatetime
Conflicts: python-constraint ( <= 0.3.0-4), python-logilab-astng ( <= 0.16.0-1), pylint ( << 0.11.0-1), devtools ( <= 0.9.0-1), logilab-doctools ( <= 0.1.6-4), python-logilab-aspects ( <= 0.1.4-2), python2.3-logilab-common, python2.4-logilab-common, cubicweb-server ( << 3.6.0-1)
Description: useful miscellaneous modules used by Logilab projects
diff --git a/urllib2ext.py b/urllib2ext.py
new file mode 100644
index 0000000..08797a4
--- /dev/null
+++ b/urllib2ext.py
@@ -0,0 +1,87 @@
+import logging
+import urllib2
+
+import kerberos as krb
+
+class GssapiAuthError(Exception):
+ """raised on error during authentication process"""
+
+import re
+RGX = re.compile('(?:.*,)*\s*Negotiate\s*([^,]*),?', re.I)
+
+def get_negociate_value(headers):
+ for authreq in headers.getheaders('www-authenticate'):
+ match = RGX.search(authreq)
+ if match:
+ return match.group(1)
+
+class HTTPGssapiAuthHandler(urllib2.BaseHandler):
+ """Negotiate HTTP authentication using context from GSSAPI"""
+
+ handler_order = 400 # before Digest Auth
+
+ def __init__(self):
+ self._reset()
+
+ def _reset(self):
+ self._retried = 0
+ self._context = None
+
+ def clean_context(self):
+ if self._context is not None:
+ krb.authGSSClientClean(self._context)
+
+ def http_error_401(self, req, fp, code, msg, headers):
+ try:
+ if self._retried > 5:
+ raise urllib2.HTTPError(req.get_full_url(), 401,
+ "negotiate auth failed", headers, None)
+ self._retried += 1
+ logging.debug('gssapi handler, try %s' % self._retried)
+ negotiate = get_negociate_value(headers)
+ if negotiate is None:
+ logging.debug('no negociate found in a www-authenticate header')
+ return None
+ logging.debug('HTTPGssapiAuthHandler: negotiate 1 is %r' % negotiate)
+ result, self._context = krb.authGSSClientInit("HTTP@%s" % req.get_host())
+ if result < 1:
+ raise GssapiAuthError("HTTPGssapiAuthHandler: init failed with %d" % result)
+ result = krb.authGSSClientStep(self._context, negotiate)
+ if result < 0:
+ raise GssapiAuthError("HTTPGssapiAuthHandler: step 1 failed with %d" % result)
+ client_response = krb.authGSSClientResponse(self._context)
+ logging.debug('HTTPGssapiAuthHandler: client response is %s...' % client_response[:10])
+ req.add_unredirected_header('Authorization', "Negotiate %s" % client_response)
+ server_response = self.parent.open(req)
+ negotiate = get_negociate_value(server_response.info())
+ if negotiate is None:
+ logging.warning('HTTPGssapiAuthHandler: failed to authenticate server')
+ else:
+ logging.debug('HTTPGssapiAuthHandler negotiate 2: %s' % negotiate)
+ result = krb.authGSSClientStep(self._context, negotiate)
+ if result < 1:
+ raise GssapiAuthError("HTTPGssapiAuthHandler: step 2 failed with %d" % result)
+ return server_response
+ except GssapiAuthError, exc:
+ logging.error(repr(exc))
+ finally:
+ self.clean_context()
+ self._reset()
+
+if __name__ == '__main__':
+ import sys
+ # debug
+ import httplib
+ httplib.HTTPConnection.debuglevel = 1
+ httplib.HTTPSConnection.debuglevel = 1
+ # debug
+ import logging
+ logging.basicConfig(level=logging.DEBUG)
+ # handle cookies
+ import cookielib
+ cj = cookielib.CookieJar()
+ ch = urllib2.HTTPCookieProcessor(cj)
+ # test with url sys.argv[1]
+ h = HTTPGssapiAuthHandler()
+ response = urllib2.build_opener(h, ch).open(sys.argv[1])
+ print '\nresponse: %s\n--------------\n' % response.code, response.info()