Skip to content

Instantly share code, notes, and snippets.

@dnozay
Last active February 13, 2020 15:02
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save dnozay/194d816aa6517dc67ca1 to your computer and use it in GitHub Desktop.
Save dnozay/194d816aa6517dc67ca1 to your computer and use it in GitHub Desktop.
urllib2 + jenkins basic auth when 403 - forbidden
# some servers do not send "401 retry" responses when authentication is needed
# and return "403 forbidden" instead; e.g. jenkins does that.
import urllib2
import logging
# configure logging for library
class NullHandler(logging.Handler):
def emit(self, record):
pass
_LOGGER = logging.getLogger(__name__)
_LOGGER.addHandler(NullHandler())
del NullHandler
class HTTPBasic403AuthHandler(urllib2.HTTPBasicAuthHandler):
# retry with basic auth when facing a 403 forbidden
def http_error_403(self, req, fp, code, msg, headers):
page = req.get_full_url()
_LOGGER.debug('page [%s] got 403 - retrying with authentication.', page)
host = req.get_host()
realm = None
return self.retry_http_basic_auth(host, req, realm)
# this is an alternative:
class PreemptiveBasicAuthHandler(urllib2.HTTPBasicAuthHandler):
'''Preemptive basic auth.
Instead of waiting for a 403 to then retry with the credentials,
send the credentials if the url is handled by the password manager.
Note: please use realm=None when calling add_password.'''
def http_request(self, req):
url = req.get_full_url()
realm = None
# this is very similar to the code from retry_http_basic_auth()
# but returns a request object.
user, pw = self.passwd.find_user_password(realm, url)
if pw:
raw = "%s:%s" % (user, pw)
auth = 'Basic %s' % base64.b64encode(raw).strip()
req.add_unredirected_header(self.auth_header, auth)
return req
https_request = http_request
def install_auth_opener():
'''install the authentication handler.
This handles non-standard behavior where the server responds with
403 forbidden, instead of 401 retry. Which means it does not give you the
chance to provide your credentials.'''
global AUTH_HANDLER
# create opener / 403 error handler
if AUTH_HANDLER is None:
auth_handler = HTTPBasic403AuthHandler()
# install it.
opener = urllib2.build_opener(auth_handler)
urllib2.install_opener(opener)
_LOGGER.debug('installed 403 retry-with-auth opener')
def add_password(uri, username, passwd, realm=None):
'''add credentials for basic auth to the password manager'''
AUTH_HANDLER.add_password(realm=realm, uri=uri, user=username,
passwd=passwd)
if __name__ == "__main__":
# test
jenkins_url = "https://jenkins.example.com"
username = "johndoe@example.com"
api_token = "my-api-token"
logging.basicConfig(level=logging.DEBUG)
install_auth_opener()
add_password(jenkins_url, username, passwd=api_token)
page = "%s/me/api/python" % jenkins_url
try:
result = urllib2.urlopen(page)
assert result.code == 200
print "ok"
except urllib2.HTTPError, err:
assert err.code != 401, 'BAD CREDENTIALS!'
raise err
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment