Skip to content

Commit

Permalink
[OJ-12496] Add bearer token authorization for Jira Server
Browse files Browse the repository at this point in the history
In cases where a customer has disabled basic authentication on Jira
server (see
[here](https://confluence.atlassian.com/enterprise/disabling-basic-authentication-1044776464.html))
we want to allow using `Authorization: Bearer <token>` headers
directly instead of forcing all users to use basic auth. This is a bit
hacky (I would like to get this integrated upstream, PR for that
coming soon) but will allow customers to use bearer tokens in the
short term.

Tested this locally against a Jira server running in Docker on which I
disabled basic authentication and against the Jellyfish cloud Jira
instance, both of which worked.
  • Loading branch information
ametzger committed Oct 19, 2021
1 parent 41dbbbd commit b00cc65
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 11 deletions.
17 changes: 17 additions & 0 deletions docs/_setups/jira-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
title: Jira Setup
---

Before you begin, determine which credential setup you will need to use. Most of the time, you will want to set up credentials using basic auth. If you are running Jira server (i.e. not Jira cloud) and have [disabled basic authentication](https://confluence.atlassian.com/enterprise/disabling-basic-authentication-1044776464.html), you will want to use bearer token auth. If you aren't sure, use basic auth.

## Basic Auth

1. Add the following section to your environment variable file. This is the same file mentioned in step 3 above. Adding the following variables allows the agent to access your Jira data:
<p class="code-block"><code>
JIRA_USERNAME=...<br/>
Expand All @@ -13,3 +17,16 @@ title: Jira Setup
3. The value for `JIRA_PASSWORD` will vary. If you are using Jira server, enter the password for the user specified with `JIRA_USERNAME`. If you are using Jira Cloud, create a personal API token, following the instructions [here](https://support.atlassian.com/atlassian-account/docs/manage-api-tokens-for-your-atlassian-account/).

4. Populate the appropriate values for your Jira configuration in the `example.yml` file you copied above from step 1. This is [this](https://github.com/Jellyfish-AI/jf_agent/blob/master/example.yml#L13-L111) section of the yml file. Follow the instructions provided in the yml file.

## Bearer Token Auth

1. Create a user in your Jira server that has read access to all projects you would like the agent to ingest. If you have already created such a user, open a browser while logged in as said user.

2. Retrieve a Personal Access Token for the user from step 2 following [this guide](https://confluence.atlassian.com/enterprise/using-personal-access-tokens-1026032365.html).

3. Add the following section to your environment variable file. This is the same file mentioned in step 3 above. Adding the following variables allows the agent to access your Jira data:
<p class="code-block"><code>
JIRA_BEARER_TOKEN=...<br/>
</code></p>

4. Populate the appropriate values for your Jira configuration in the `example.yml` file you copied above from step 1. This is [this](https://github.com/Jellyfish-AI/jf_agent/blob/master/example.yml#L13-L111) section of the yml file. Follow the instructions provided in the yml file.
32 changes: 26 additions & 6 deletions jf_agent/jf_jira/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,35 @@


def _get_raw_jira_connection(config, creds, max_retries=3):
jira_conn = JIRA(
server=config.jira_url,
basic_auth=(creds.jira_username, creds.jira_password),
max_retries=max_retries,
options={
kwargs = {
'server': config.jira_url,
'max_retries': max_retries,
'options': {
'agile_rest_path': GreenHopperResource.AGILE_BASE_REST_PATH,
'verify': not config.skip_ssl_verification,
},
)
}
if creds.jira_username and creds.jira_password:
kwargs['basic_auth'] = (creds.jira_username, creds.jira_password)
elif creds.jira_bearer_token:
# HACK(asm,2021-10-18): This is copypasta from
# https://github.com/pycontribs/jira/blob/df8a6a9879b48083ba940ef9b00d6543bcea5015/jira/client.py#L307-L315
# I would like to get bearer token support merged upstream,
# however this is a short term fix to enable customers who
# have already disabled basic authentication.
kwargs['options']['headers'] = {
'Authorization': f'Bearer {creds.jira_bearer_token}',
'Cache-Control': 'no-cache',
'Content-Type': 'application/json',
'Accept': 'application/json',
'X-Atlassian-Token': 'no-check',
}
else:
raise RuntimeError(
'No valid Jira credentials found! Check your JIRA_USERNAME, JIRA_PASSWORD, or JIRA_BEARER_TOKEN environment variables.'
)

jira_conn = JIRA(**kwargs)

jira_conn._session.headers[
'User-Agent'
Expand Down
21 changes: 16 additions & 5 deletions jf_agent/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,9 @@ def main():
print('Validating configuration...')

# Check for Jira credentials
if config.jira_url and creds.jira_username and creds.jira_password:
if config.jira_url and (
(creds.jira_username and creds.jira_password) or creds.jira_bearer_token
):
validate_jira(config, creds)
else:
print("\nNo Jira URL or credentials provided, skipping Jira validation...")
Expand Down Expand Up @@ -189,7 +191,13 @@ def main():

UserProvidedCreds = namedtuple(
'UserProvidedCreds',
['jellyfish_api_token', 'jira_username', 'jira_password', 'git_instance_to_creds'],
[
'jellyfish_api_token',
'jira_username',
'jira_password',
'jira_bearer_token',
'git_instance_to_creds',
],
)

JellyfishEndpointInfo = namedtuple('JellyfishEndpointInfo', ['jira_info', 'git_instance_info'])
Expand Down Expand Up @@ -248,21 +256,24 @@ def obtain_creds(config):

jira_username = os.environ.get('JIRA_USERNAME', None)
jira_password = os.environ.get('JIRA_PASSWORD', None)
jira_bearer_token = os.environ.get('JIRA_BEARER_TOKEN', None)

# obtain git slug to credentials
git_instance_to_creds = {
git_config.git_instance_slug: _get_git_instance_to_creds(git_config)
for git_config in config.git_configs
}

if config.jira_url and not (jira_username and jira_password):
jira_username_pass_missing = bool(not (jira_username and jira_password))
jira_bearer_token_missing = bool(not jira_bearer_token)
if config.jira_url and jira_username_pass_missing and jira_bearer_token_missing:
print(
'ERROR: Jira credentials not found. Set environment variables JIRA_USERNAME and JIRA_PASSWORD.'
'ERROR: Jira credentials not found. Set environment variables JIRA_USERNAME and JIRA_PASSWORD or JIRA_BEARER_TOKEN.'
)
raise BadConfigException()

return UserProvidedCreds(
jellyfish_api_token, jira_username, jira_password, git_instance_to_creds
jellyfish_api_token, jira_username, jira_password, jira_bearer_token, git_instance_to_creds
)


Expand Down

0 comments on commit b00cc65

Please sign in to comment.