-
-
Save KnightKu/300e640529a3efb3858b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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 = "[email protected]" | |
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