From cbc974274792f9ee1f2756cced906a105bf86abe Mon Sep 17 00:00:00 2001 From: Kambiz Aghaiepour Date: Thu, 9 Dec 2021 15:24:24 -0500 Subject: [PATCH] jira - Add support for Bearer token auth (#3838) * jira - Add support for Bearer token auth * jira - Add support for Bearer token auth * added changelog fragment Co-authored-by: Felix Fontein * fix indent issue * fix overindent * jira - Add support for Bearer token auth * jira - Add support for Bearer token auth * added changelog fragment * minor doc fix to be clearer. Be clear about the exclusivity between username and token as well as password and token. * Update changelogs/fragments/3838-jira-token.yaml Co-authored-by: Felix Fontein * Update plugins/modules/web_infrastructure/jira.py Co-authored-by: Felix Fontein * Update plugins/modules/web_infrastructure/jira.py Co-authored-by: Felix Fontein Co-authored-by: Felix Fontein --- changelogs/fragments/3838-jira-token.yaml | 2 + plugins/modules/web_infrastructure/jira.py | 57 ++++++++++++++++------ 2 files changed, 43 insertions(+), 16 deletions(-) create mode 100644 changelogs/fragments/3838-jira-token.yaml diff --git a/changelogs/fragments/3838-jira-token.yaml b/changelogs/fragments/3838-jira-token.yaml new file mode 100644 index 0000000000..21aadd5ab8 --- /dev/null +++ b/changelogs/fragments/3838-jira-token.yaml @@ -0,0 +1,2 @@ +minor_changes: + - jira - add support for Bearer token auth (https://github.com/ansible-collections/community.general/pull/3838). diff --git a/plugins/modules/web_infrastructure/jira.py b/plugins/modules/web_infrastructure/jira.py index 9cd86341a1..942a804556 100644 --- a/plugins/modules/web_infrastructure/jira.py +++ b/plugins/modules/web_infrastructure/jira.py @@ -36,15 +36,22 @@ options: username: type: str - required: true description: - The username to log-in with. + - Must be used with I(password). Mutually exclusive with I(token). password: type: str - required: true description: - The password to log-in with. + - Must be used with I(username). Mutually exclusive with I(token). + + token: + type: str + description: + - The personal access token to log-in with. + - Mutually exclusive with I(username) and I(password). + version_added: 4.2.0 project: type: str @@ -206,7 +213,7 @@ options: done. notes: - - "Currently this only works with basic-auth." + - "Currently this only works with basic-auth, or tokens." - "To use with JIRA Cloud, pass the login e-mail as the I(username) and the API token as I(password)." author: @@ -408,8 +415,9 @@ class JIRA(StateModuleHelper): choices=['attach', 'create', 'comment', 'edit', 'update', 'fetch', 'transition', 'link', 'search'], aliases=['command'], required=True ), - username=dict(type='str', required=True), - password=dict(type='str', required=True, no_log=True), + username=dict(type='str'), + password=dict(type='str', no_log=True), + token=dict(type='str', no_log=True), project=dict(type='str', ), summary=dict(type='str', ), description=dict(type='str', ), @@ -432,6 +440,17 @@ class JIRA(StateModuleHelper): validate_certs=dict(default=True, type='bool'), account_id=dict(type='str'), ), + mutually_exclusive=[ + ['username', 'token'], + ['password', 'token'], + ['assignee', 'account_id'], + ], + required_together=[ + ['username', 'password'], + ], + required_one_of=[ + ['username', 'token'], + ], required_if=( ('operation', 'attach', ['issue', 'attachment']), ('operation', 'create', ['project', 'issuetype', 'summary']), @@ -441,7 +460,6 @@ class JIRA(StateModuleHelper): ('operation', 'link', ['linktype', 'inwardissue', 'outwardissue']), ('operation', 'search', ['jql']), ), - mutually_exclusive=[('assignee', 'account_id')], supports_check_mode=False ) @@ -642,23 +660,30 @@ class JIRA(StateModuleHelper): if data and content_type == 'application/json': data = json.dumps(data) + headers = {} + if isinstance(additional_headers, dict): + headers = additional_headers.copy() + # NOTE: fetch_url uses a password manager, which follows the # standard request-then-challenge basic-auth semantics. However as # JIRA allows some unauthorised operations it doesn't necessarily # send the challenge, so the request occurs as the anonymous user, # resulting in unexpected results. To work around this we manually - # inject the basic-auth header up-front to ensure that JIRA treats + # inject the auth header up-front to ensure that JIRA treats # the requests as authorized for this user. - auth = to_text(base64.b64encode(to_bytes('{0}:{1}'.format(self.vars.username, self.vars.password), - errors='surrogate_or_strict'))) - headers = {} - if isinstance(additional_headers, dict): - headers = additional_headers.copy() - headers.update({ - "Content-Type": content_type, - "Authorization": "Basic %s" % auth, - }) + if self.vars.token is not None: + headers.update({ + "Content-Type": content_type, + "Authorization": "Bearer %s" % self.vars.token, + }) + else: + auth = to_text(base64.b64encode(to_bytes('{0}:{1}'.format(self.vars.username, self.vars.password), + errors='surrogate_or_strict'))) + headers.update({ + "Content-Type": content_type, + "Authorization": "Basic %s" % auth, + }) response, info = fetch_url( self.module, url, data=data, method=method, timeout=self.vars.timeout, headers=headers