issue #61672: make jenkins_plugin module work in a session when CSRF … (#61673)

* issue #61672: make jenkins_plugin module work in a session when CSRF enabled

This commit modifies the signature of `fetch_url` so that a cookie jar can be
specified allowing multiple calls to operate with the same session.  It uses
a similar construct to the `Request` class to initialise the cookie jar if
it is not provided.

The jenkins_plugin module is modified to create a cookie jar if CSRF is
enabled.  This cookie jar is then submitted with every call to fetch_url.
Also changed is to submit the crumb in the request headers rather than
in the data field.

This has been tested with Jenkins 2.176.

* issue #61672: fix jenkins_script module

This commit modifies the jenkins_script module to use the authorization crumb
in a session in a similar fashion to the jenkins_plugin change for the same
issue.
This commit is contained in:
JKDingwall 2019-09-10 14:44:37 +01:00 committed by Brian Coca
parent dfc023209f
commit 76b5b90bd6
3 changed files with 19 additions and 11 deletions

View file

@ -1423,7 +1423,7 @@ def url_argument_spec():
def fetch_url(module, url, data=None, headers=None, method=None, def fetch_url(module, url, data=None, headers=None, method=None,
use_proxy=True, force=False, last_mod_time=None, timeout=10, use_proxy=True, force=False, last_mod_time=None, timeout=10,
use_gssapi=False, unix_socket=None, ca_path=None): use_gssapi=False, unix_socket=None, ca_path=None, cookies=None):
"""Sends a request via HTTP(S) or FTP (needs the module as parameter) """Sends a request via HTTP(S) or FTP (needs the module as parameter)
:arg module: The AnsibleModule (used to get username, password etc. (s.b.). :arg module: The AnsibleModule (used to get username, password etc. (s.b.).
@ -1479,6 +1479,7 @@ def fetch_url(module, url, data=None, headers=None, method=None,
client_cert = module.params.get('client_cert') client_cert = module.params.get('client_cert')
client_key = module.params.get('client_key') client_key = module.params.get('client_key')
if not isinstance(cookies, cookiejar.CookieJar):
cookies = cookiejar.LWPCookieJar() cookies = cookiejar.LWPCookieJar()
r = None r = None

View file

@ -264,6 +264,7 @@ state:
''' '''
from ansible.module_utils.basic import AnsibleModule, to_bytes from ansible.module_utils.basic import AnsibleModule, to_bytes
from ansible.module_utils.six.moves import http_cookiejar as cookiejar
from ansible.module_utils.six.moves.urllib.parse import urlencode from ansible.module_utils.six.moves.urllib.parse import urlencode
from ansible.module_utils.urls import fetch_url, url_argument_spec from ansible.module_utils.urls import fetch_url, url_argument_spec
from ansible.module_utils._text import to_native, text_type, binary_type from ansible.module_utils._text import to_native, text_type, binary_type
@ -287,8 +288,11 @@ class JenkinsPlugin(object):
# Crumb # Crumb
self.crumb = {} self.crumb = {}
# Cookie jar for crumb session
self.cookies = None
if self._csrf_enabled(): if self._csrf_enabled():
self.cookies = cookiejar.LWPCookieJar()
self.crumb = self._get_crumb() self.crumb = self._get_crumb()
# Get list of installed plugins # Get list of installed plugins
@ -332,7 +336,8 @@ class JenkinsPlugin(object):
# Get the URL data # Get the URL data
try: try:
response, info = fetch_url( response, info = fetch_url(
self.module, url, timeout=self.timeout, **kwargs) self.module, url, timeout=self.timeout, cookies=self.cookies,
headers=self.crumb, **kwargs)
if info['status'] != 200: if info['status'] != 200:
self.module.fail_json(msg=msg_status, details=info['msg']) self.module.fail_json(msg=msg_status, details=info['msg'])
@ -405,7 +410,6 @@ class JenkinsPlugin(object):
script_data = { script_data = {
'script': install_script 'script': install_script
} }
script_data.update(self.crumb)
data = urlencode(script_data) data = urlencode(script_data)
# Send the installation request # Send the installation request
@ -691,14 +695,12 @@ class JenkinsPlugin(object):
def _pm_query(self, action, msg): def _pm_query(self, action, msg):
url = "%s/pluginManager/plugin/%s/%s" % ( url = "%s/pluginManager/plugin/%s/%s" % (
self.params['url'], self.params['name'], action) self.params['url'], self.params['name'], action)
data = urlencode(self.crumb)
# Send the request # Send the request
self._get_url_data( self._get_url_data(
url, url,
msg_status="Plugin not found. %s" % url, msg_status="Plugin not found. %s" % url,
msg_exception="%s has failed." % msg, msg_exception="%s has failed." % msg)
data=data)
def main(): def main():

View file

@ -105,6 +105,7 @@ output:
import json import json
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.six.moves import http_cookiejar as cookiejar
from ansible.module_utils.six.moves.urllib.parse import urlencode from ansible.module_utils.six.moves.urllib.parse import urlencode
from ansible.module_utils.urls import fetch_url from ansible.module_utils.urls import fetch_url
from ansible.module_utils._text import to_native from ansible.module_utils._text import to_native
@ -121,10 +122,11 @@ def is_csrf_protection_enabled(module):
return json.loads(content).get('useCrumbs', False) return json.loads(content).get('useCrumbs', False)
def get_crumb(module): def get_crumb(module, cookies):
resp, info = fetch_url(module, resp, info = fetch_url(module,
module.params['url'] + '/crumbIssuer/api/json', module.params['url'] + '/crumbIssuer/api/json',
method='GET') method='GET',
cookies=cookies)
if info["status"] != 200: if info["status"] != 200:
module.fail_json(msg="HTTP error " + str(info["status"]) + " " + info["msg"], output='') module.fail_json(msg="HTTP error " + str(info["status"]) + " " + info["msg"], output='')
@ -163,8 +165,10 @@ def main():
script_contents = module.params['script'] script_contents = module.params['script']
headers = {} headers = {}
cookies = None
if is_csrf_protection_enabled(module): if is_csrf_protection_enabled(module):
crumb = get_crumb(module) cookies = cookiejar.LWPCookieJar()
crumb = get_crumb(module, cookies)
headers = {crumb['crumbRequestField']: crumb['crumb']} headers = {crumb['crumbRequestField']: crumb['crumb']}
resp, info = fetch_url(module, resp, info = fetch_url(module,
@ -172,7 +176,8 @@ def main():
data=urlencode({'script': script_contents}), data=urlencode({'script': script_contents}),
headers=headers, headers=headers,
method="POST", method="POST",
timeout=module.params['timeout']) timeout=module.params['timeout'],
cookies=cookies)
if info["status"] != 200: if info["status"] != 200:
module.fail_json(msg="HTTP error " + str(info["status"]) + " " + info["msg"], output='') module.fail_json(msg="HTTP error " + str(info["status"]) + " " + info["msg"], output='')