Added kerberos authentication
authorVojtech Juranek <vjuranek@redhat.com>
Mon, 17 Dec 2012 21:16:19 +0000 (22:16 +0100)
committerVojtech Juranek <vjuranek@redhat.com>
Mon, 17 Dec 2012 21:16:19 +0000 (22:16 +0100)
jenkinsapi/jenkins.py
jenkinsapi/jenkinsbase.py
jenkinsapi/utils/urlopener.py

index b1a11a2..f306023 100644 (file)
@@ -4,7 +4,7 @@ from jenkinsapi.job import Job
 from jenkinsapi.view import View
 from jenkinsapi.node import Node
 from jenkinsapi.exceptions import UnknownJob, NotAuthorized
-from utils.urlopener import mkurlopener, mkopener, NoAuto302Handler
+from utils.urlopener import mkurlopener, mkkrbopener, mkopener, NoAuto302Handler
 import logging
 import time
 import urllib2
@@ -22,7 +22,7 @@ class Jenkins(JenkinsBase):
     """
     Represents a jenkins environment.
     """
-    def __init__(self, baseurl, username=None, password=None, proxyhost=None, proxyport=None, proxyuser=None, proxypass=None, formauth=False):
+    def __init__(self, baseurl, username=None, password=None, proxyhost=None, proxyport=None, proxyuser=None, proxypass=None, formauth=False, krbauth=False):
         """
 
         :param baseurl: baseurl for jenkins instance including port, str
@@ -40,13 +40,13 @@ class Jenkins(JenkinsBase):
         self.proxyport = proxyport
         self.proxyuser = proxyuser
         self.proxypass = proxypass
-        JenkinsBase.__init__(self, baseurl, formauth=formauth)
+        JenkinsBase.__init__(self, baseurl, formauth=formauth, krbauth=krbauth)
 
     def _clone(self):
         return Jenkins(self.baseurl, username=self.username,
                        password=self.password, proxyhost=self.proxyhost,
                        proxyport=self.proxyport, proxyuser=self.proxyuser,
-                       proxypass=self.proxypass, formauth=self.formauth)
+                       proxypass=self.proxypass, formauth=self.formauth, krbauth=self.krbauth)
 
     def get_proxy_auth(self):
         return self.proxyhost, self.proxyport, self.proxyuser, self.proxypass
@@ -64,6 +64,8 @@ class Jenkins(JenkinsBase):
     def get_opener(self):
         if self.formauth:
             return self.get_login_opener()
+        if self.krbauth:
+            return self.get_krb_opener()
         return mkurlopener(*self.get_auth())
 
     def get_login_opener(self):
@@ -75,6 +77,9 @@ class Jenkins(JenkinsBase):
             hdrs.append(urllib2.HTTPCookieProcessor(mcj))
         return mkopener(*hdrs)
 
+    def get_krb_opener(self):
+        return mkkrbopener(self.baseurl)
+
     def login(self):
         formdata = dict(j_username=self.username, j_password=self.password,
                         remember_me=True, form='/')
index ca5c182..b17aab2 100644 (file)
@@ -23,12 +23,13 @@ class JenkinsBase(object):
     def __str__(self):
         raise NotImplemented
 
-    def __init__(self, baseurl, poll=True, formauth=False):
+    def __init__(self, baseurl, poll=True, formauth=False, krbauth=False):
         """
         Initialize a jenkins connection
         """
         self.baseurl = baseurl
         self.formauth = formauth
+        self.krbauth = krbauth
         if poll and not self.formauth:
             try:
                 self.poll()
index 9dd5a2f..d13fd5d 100644 (file)
@@ -1,5 +1,7 @@
 import urllib2
 import base64
+import kerberos as krb
+from urlparse import urlparse
 
 import logging
 
@@ -30,6 +32,21 @@ class PreemptiveBasicAuthHandler(urllib2.BaseHandler):
     def https_request(self,req):
         return self.http_request(req)
 
+class KerberosAuthHandler(urllib2.BaseHandler):
+    """
+    A BaseHandler class that will add Kerberos Auth headers to a request
+    """
+    def __init__(self,tgt):
+        self.tgt = tgt
+
+    def http_request(self,req):
+        req.add_unredirected_header('Authorization', 'Negotiate %s' % self.tgt)
+        return req
+
+    def https_request(self,req):
+        return self.http_request(req)
+
+
 def mkurlopener( jenkinsuser, jenkinspass, jenkinsurl, proxyhost, proxyport, proxyuser, proxypass ):
     """
      Creates an url opener that works with both jenkins auth and proxy auth
@@ -51,6 +68,20 @@ def mkurlopener( jenkinsuser, jenkinspass, jenkinsurl, proxyhost, proxyport, pro
     opener = urllib2.build_opener(*handlers)
     return opener.open
 
+def mkkrbopener( jenkinsurl ):
+    """
+     Creates an url opener that works with kerberos auth
+
+    :param jenkinsurl: jenkins url, str
+    :return: urllib2.opener configured for kerberos auth
+    """
+    handlers = []
+    for handler in get_kerberos_auth_handler(jenkinsurl=jenkinsurl):
+        handlers.append(handler)
+    opener = urllib2.build_opener(*handlers)
+    return opener.open
+
+
 def mkopener(*handlers):
     opener = urllib2.build_opener(*handlers)
     return opener.open
@@ -100,6 +131,27 @@ def get_proxy_handler(proxyhost, proxyport, proxyuser, proxypass):
     return [proxy_handler, proxy_auth_handler]
 
 
+def get_kerberos_auth_handler(jenkinsurl):
+    """
+    Get a handler which enabled authentication over GSSAPI
+
+    :param jenkinsurl: jenkins base url, str
+    :return: a list of handlers
+    """
+    jenkinsnetloc = urlparse(jenkinsurl).netloc
+    assert type( jenkinsnetloc ) == str, "Jenkins network location should be a string, got %s" % repr( jenkinsnetloc )
+
+    _ignore, ctx = krb.authGSSClientInit('HTTP@%s' % jenkinsnetloc, gssflags=krb.GSS_C_DELEG_FLAG|krb.GSS_C_MUTUAL_FLAG|krb.GSS_C_SEQUENCE_FLAG)
+    rc = krb.authGSSClientStep(ctx,'')
+    if rc != krb.AUTH_GSS_CONTINUE:
+        return []
+    tgt = krb.authGSSClientResponse(ctx)
+    if not tgt:
+        return []
+
+    krb_handler = KerberosAuthHandler(tgt)
+    return [ krb_handler ]
+
 class NoAuto302Handler(urllib2.HTTPRedirectHandler):
     def http_error_302(self, req, fp, code, msg, hdrs):
         return fp