From 81ddbcf01ee28a5bcfe2faa889ae2ac2d7562aca Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Fri, 27 Sep 2013 22:17:06 +0200 Subject: [PATCH 1/4] Attempt to fetch a crumb (needed if CSRF protection is enabled) --- jenkins/__init__.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/jenkins/__init__.py b/jenkins/__init__.py index 8e3da95..55827f1 100644 --- a/jenkins/__init__.py +++ b/jenkins/__init__.py @@ -58,6 +58,7 @@ LAUNCHER_COMMAND = 'hudson.slaves.CommandLauncher' LAUNCHER_WINDOWS_SERVICE = 'hudson.os.windows.ManagedWindowsServiceLauncher' INFO = 'api/json' +CRUMB_URL = 'crumbIssuer/api/json' JOB_INFO = 'job/%(name)s/api/json?depth=0' JOB_NAME = 'job/%(name)s/api/json?tree=name' Q_INFO = 'queue/api/json?depth=0' @@ -154,6 +155,13 @@ class Jenkins(object): else: self.auth = None + def add_crumb(self, req): + response = self.jenkins_open(urllib2.Request( + self.server + CRUMB_URL), add_crumb=False) + if response: + data = json.loads(response) + req.add_header(data['crumbRequestField'], data['crumb']) + def get_job_info(self, name): ''' Get job information dictionary. @@ -200,14 +208,17 @@ class Jenkins(object): for k, v in self.get_job_info(job_name).iteritems(): print k, v - def jenkins_open(self, req): + def jenkins_open(self, req, add_crumb=True): ''' + Utility routine for opening an HTTP request to a Jenkins server. This should only be used to extends the :class:`Jenkins` API. ''' try: if self.auth: req.add_header('Authorization', self.auth) + if add_crumb: + self.add_crumb(req) return urllib2.urlopen(req).read() except urllib2.HTTPError, e: # Jenkins's funky authentication means its nigh impossible to From 76a94ba17372fc0b5544f5e54453386cdac14d55 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Sat, 28 Sep 2013 21:58:55 +0200 Subject: [PATCH 2/4] Recycle crumb. Apparently, it's not a nonce. --- jenkins/__init__.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/jenkins/__init__.py b/jenkins/__init__.py index 55827f1..39856bd 100644 --- a/jenkins/__init__.py +++ b/jenkins/__init__.py @@ -154,13 +154,20 @@ class Jenkins(object): self.auth = auth_headers(username, password) else: self.auth = None + self.crumb = None def add_crumb(self, req): - response = self.jenkins_open(urllib2.Request( - self.server + CRUMB_URL), add_crumb=False) - if response: - data = json.loads(response) - req.add_header(data['crumbRequestField'], data['crumb']) + # We don't know yet whether we need a crumb + if self.crumb is None: + response = self.jenkins_open(urllib2.Request( + self.server + CRUMB_URL), add_crumb=False) + if response: + self.crumb = json.loads(response) + else: + # Don't need crumbs + self.crumb = False + if self.crumb: + req.add_header(self.crumb['crumbRequestField'], self.crumb['crumb']) def get_job_info(self, name): ''' From df4c87ab01c7b5f860804f89a6f1b30c52ab888c Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Sat, 28 Sep 2013 22:00:43 +0200 Subject: [PATCH 3/4] Change method name to reflect behaviour --- jenkins/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jenkins/__init__.py b/jenkins/__init__.py index 39856bd..97fe9dd 100644 --- a/jenkins/__init__.py +++ b/jenkins/__init__.py @@ -156,7 +156,7 @@ class Jenkins(object): self.auth = None self.crumb = None - def add_crumb(self, req): + def maybe_add_crumb(self, req): # We don't know yet whether we need a crumb if self.crumb is None: response = self.jenkins_open(urllib2.Request( @@ -225,7 +225,7 @@ class Jenkins(object): if self.auth: req.add_header('Authorization', self.auth) if add_crumb: - self.add_crumb(req) + self.maybe_add_crumb(req) return urllib2.urlopen(req).read() except urllib2.HTTPError, e: # Jenkins's funky authentication means its nigh impossible to