从基本认证提取网址保护的詹金斯服务器的urllib2 [英] Fetching a URL from a basic-auth protected Jenkins server with urllib2

查看:210
本文介绍了从基本认证提取网址保护的詹金斯服务器的urllib2的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图获取从Jekins服务器的URL。直到最近,有些我能使用此页上描述的模式( HOWTO获取网络资源使用的urllib2 )创建一个密码管理器,正确回应BASICAUTH使用用户名和放大器的挑战;密码。一切正常,直到詹金斯队改变了他们的安全模型和即code不再奏效。

I'm trying to fetch a URL from a Jekins server. Until somewhat recently I was able to use the pattern described on this page (HOWTO Fetch Internet Resources Using urllib2) to create a password-manager that correctly responded to BasicAuth challenges with the user-name & password. All was fine until the Jenkins team changed their security model, and that code no longer worked.

# DOES NOT WORK!
import urllib2
password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
top_level_url = "http://localhost:8080"

password_mgr.add_password(None, top_level_url, 'sal', 'foobar')
handler = urllib2.HTTPBasicAuthHandler(password_mgr)
opener = urllib2.build_opener(handler)

a_url = 'http://localhost:8080/job/foo/4/api/python'
print opener.open(a_url).read()

堆栈跟踪:

Traceback (most recent call last):
  File "/home/sal/workspace/jenkinsapi/src/examples/password.py", line 11, in <module>
    print opener.open(a_url).read()
  File "/usr/lib/python2.7/urllib2.py", line 410, in open
    response = meth(req, response)
  File "/usr/lib/python2.7/urllib2.py", line 523, in http_response
    'http', request, response, code, msg, hdrs)
  File "/usr/lib/python2.7/urllib2.py", line 448, in error
    return self._call_chain(*args)
  File "/usr/lib/python2.7/urllib2.py", line 382, in _call_chain
    result = func(*args)
  File "/usr/lib/python2.7/urllib2.py", line 531, in http_error_default
    raise HTTPError(req.get_full_url(), code, msg, hdrs, fp)
urllib2.HTTPError: HTTP Error 403: Forbidden
[Finished in 0.0s with exit code 1]

的问题似乎是詹金斯返回不与预期的401 code,但一个403的urllib2间$ P $点作为对话的一端。它从来没有真正发送密码。经过各地github上一些冲浪发现了另一个开发商的解决方案,它的工作原理...

The problem appears to be that Jenkins returns not with the expected 401 code, but a 403 which urllib2 interprets as an end of conversation. It never actually sends the password. After some surfing around github found another developer's solution which works...

# WORKS... SORTA
def auth_headers(username, password):
   return 'Basic ' + base64.encodestring('%s:%s' % (username, password))[:-1]

auth = auth_headers('sal', 'foobar')
top_level_url = "http://localhost:8080"
a_url = 'http://localhost:8080/job/foo/4/api/python'
req = urllib2.Request(a_url)
req.add_header('Authorization', auth)
print urllib2.urlopen(req).read()

但是,这似乎相当令人满意。它不打扰检查域名是否是相关的用户名和密码......它只是送我的登录信息不管!

But that seems rather unsatisfying. It's not bothering to check whether the domain is relevant to the username and password... it's just sending my login details regardless!

任何人能否提供一个方法,使原始脚本的工作?我想用一个urllib2的密码管理器中,这样我可以登录到詹金斯的方式。

Can anybody suggest a way to make the original script work? I'd like to use a urllib2 password manager in such a way that I can login to Jenkins.

推荐答案

请参阅此要点,以及:的https: //gist.github.com/dnozay/194d816aa6517dc67ca1

See this gist as well: https://gist.github.com/dnozay/194d816aa6517dc67ca1

詹金斯不会返回 401 - 重试 HTTP错误code,当你需要访问一个需要验证的页面;相反,它返回 403 - 禁止。在维基, https://wiki.jenkins-ci.org/display / JENKINS /身份验证+脚本+客户端的,它表明使用命令行工具的wget 你需要使用 wget的--auth -no挑战这是该行为正是因为

Jenkins does not return 401 - retry HTTP error code when you need to access a page that needs authentication; instead it returns 403 - forbidden. In the wiki, https://wiki.jenkins-ci.org/display/JENKINS/Authenticating+scripted+clients, it shows that using the command-line tool wget you need to use wget --auth-no-challenge which is exactly because of that behavior.

假设你定义的:

jenkins_url = "https://jenkins.example.com"
username = "johndoe@example.com"
api_token = "my-api-token"

您也可以继承一个 urllib2.HTTPBasicAuthHandler 来处理 403 HTTP响应。

You can subclass a urllib2.HTTPBasicAuthHandler to handle 403 HTTP responses.

import urllib2

class HTTPBasic403AuthHandler(urllib2.HTTPBasicAuthHandler):
    # retry with basic auth when facing a 403 forbidden
    def http_error_403(self, req, fp, code, msg, headers):
        host = req.get_host()
        realm = None
        return self.retry_http_basic_auth(host, req, realm)

然后,它是利用该处理程序的问题,例如你可以因此它适用于所有安装 urllib2.urlopen 调用:

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.'''
    auth_handler = HTTPBasic403AuthHandler()
    auth_handler.add_password(
        realm=None,
        uri=jenkins_url,
        user=username,
        passwd=api_token)
    opener = urllib2.build_opener(auth_handler)
    # install it for all urllib2.urlopen calls
    urllib2.install_opener(opener)

和这里是一个简单的测试,看看它是否工作正常。

and here is a simple test to see if it works okay.

if __name__ == "__main__":
    # test
    install_auth_opener()
    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

使用pre-先发制人的身份验证。

有在这个答案一个很好的例子: http://stackoverflow.com/a/8513913/1733117
而不是重试当你得到一个 403禁止你就送授权头当URL匹配。

Using pre-emptive authentication.

There is a good example in this answer: http://stackoverflow.com/a/8513913/1733117. Rather than retrying when you get a 403 forbidden you would send the Authorization header when the url matches.

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

这篇关于从基本认证提取网址保护的詹金斯服务器的urllib2的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆