Skip to content

Instantly share code, notes, and snippets.

@ojacques
Last active July 22, 2021 11:27
Show Gist options
  • Save ojacques/fa696af3c296d9c4f049801daee52b4f to your computer and use it in GitHub Desktop.
Save ojacques/fa696af3c296d9c4f049801daee52b4f to your computer and use it in GitHub Desktop.
JIRA Oauth dance and API
import base64
import urllib
#import tlslite
from tlslite.utils import keyfactory
import oauth2 as oauth
class SignatureMethod_RSA_SHA1(oauth.SignatureMethod):
name = 'RSA-SHA1'
def signing_base(self, request, consumer, token):
if not hasattr(request, 'normalized_url') or request.normalized_url is None:
raise ValueError("Base URL for request is not set.")
sig = (
oauth.escape(request.method),
oauth.escape(request.normalized_url),
oauth.escape(request.get_normalized_parameters()),
)
key = '%s&' % oauth.escape(consumer.secret)
if token:
key += oauth.escape(token.secret)
raw = '&'.join(sig)
return key, raw
def sign(self, request, consumer, token):
"""Builds the base signature string."""
key, raw = self.signing_base(request, consumer, token)
with open('jira_privatekey.pem', 'r') as f:
data = f.read()
privateKeyString = data.strip()
privatekey = keyfactory.parsePrivateKey(privateKeyString)
signature = privatekey.hashAndSign(bytes(raw,"utf-8"))
# return base64.b64encode(bytes(signature,"utf-8"))
return base64.b64encode(signature)
consumer_key = 'OauthKey'
consumer_secret = 'dont_care'
request_token_url = 'https://jira-url/plugins/servlet/oauth/request-token'
access_token_url = 'https://jira-url/plugins/servlet/oauth/access-token'
authorize_url = 'https://jira-url/plugins/servlet/oauth/authorize'
data_url = 'https://jira-url/rest/api/2/user/search?username=jdoe'
consumer = oauth.Consumer(consumer_key, consumer_secret)
client = oauth.Client(consumer)
# Lets try to access a JIRA issue (BULK-1). We should get a 401.
#resp, content = client.request(data_url, "GET")
#if resp['status'] != '401':
# raise Exception("Should have no access!")
consumer = oauth.Consumer(consumer_key, consumer_secret)
client = oauth.Client(consumer)
client.set_signature_method(SignatureMethod_RSA_SHA1())
# Step 1: Get a request token. This is a temporary token that is used for
# having the user authorize an access token and to sign the request to obtain
# said access token.
resp, content = client.request(request_token_url, "POST")
if resp['status'] != '200':
raise Exception("Invalid response %s: %s" % (resp['status'], content))
print(content)
request_token = dict(urllib.parse.parse_qsl(content.decode('utf-8')))
print("Request Token:")
print(" - oauth_token = %s" % request_token['oauth_token'])
print(" - oauth_token_secret = %s" % request_token['oauth_token_secret'])
print
# Step 2: Redirect to the provider. Since this is a CLI script we do not
# redirect. In a web application you would redirect the user to the URL
# below.
print("Go to the following link in your browser:")
print("%s?oauth_token=%s" % (authorize_url, request_token['oauth_token']))
print
# After the user has granted access to you, the consumer, the provider will
# redirect you to whatever URL you have told them to redirect to. You can
# usually define this in the oauth_callback argument as well.
accepted = 'n'
while accepted.lower() == 'n':
accepted = input('Have you authorized me? (y/n) ')
# oauth_verifier = raw_input('What is the PIN? ')
# Step 3: Once the consumer has redirected the user back to the oauth_callback
# URL you can request the access token the user has approved. You use the
# request token to sign this request. After this is done you throw away the
# request token and use the access token returned. You should store this
# access token somewhere safe, like a database, for future use.
token = oauth.Token(request_token['oauth_token'],
request_token['oauth_token_secret'])
#token.set_verifier(oauth_verifier)
client = oauth.Client(consumer, token)
client.set_signature_method(SignatureMethod_RSA_SHA1())
resp, content = client.request(access_token_url, "POST")
print(content)
access_token = dict(urllib.parse.parse_qsl(content.decode('utf-8')))
print( "Access Token:")
print(" - oauth_token = %s" % access_token['oauth_token'])
print(" - oauth_token_secret = %s" % access_token['oauth_token_secret'])
print
print("You may now access protected resources using the access tokens above.")
print
# Now lets try to access the same issue again with the access token. We should get a 200!
accessToken = oauth.Token(access_token['oauth_token'], access_token['oauth_token_secret'])
client = oauth.Client(consumer, accessToken)
client.set_signature_method(SignatureMethod_RSA_SHA1())
resp, content = client.request(data_url, "GET")
print(content)
if resp['status'] != '200':
raise Exception("Should have access!")

The original Python code from Atlassian to issue REST requests after Oauth authentication is outdated and does not work. This Python code is Python 3 compatible and has been tested to work against Jira on prem servers.

Pre-requisites

  • Create Key pairs:
openssl genrsa -out jira_privatekey.pem 1024
openssl req -newkey rsa:1024 -x509 -key jira_privatekey.pem -out jira_publickey.cer -days 365
openssl pkcs8 -topk8 -nocrypt -in jira_privatekey.pem -out jira_privatekey.pcks8
openssl x509 -pubkey -noout -in jira_publickey.cer  > jira_publickey.pem
  • jira_privatekey.pem is used in the Python script
  • jira_publickey.pem is used when creating the JIRA application link "public key" field
  • As a JIRA administrator, create a new "Application link"
    • Use OauthKey as "Consumer Key"
    • Use jira_publickey.pem content as "public key" field
  • On Python client side:
pip install tlslite-ng oauth2 urllib

Running

  • Run: python3 auth.py
  • Authorize through the link provided
  • See the result of the data_url

Using JIRA Python library

One can also use the JIRA Python library.

The following Python code will read from a .env file the various settings / tokens and run a JQL query.

Install the pre-requisites:

pip install jira python-dotenv

Create your .env file (don't forget to add it to your .gitignore) and fill the details retrieved by auth.py:

JIRA_ACCESS_TOKEN=
JIRA_ACCESS_TOKEN_SECRET=
JIRA_CONSUMER_KEY=
JIRA_KEY_FILE=jira_privatekey.pem
JIRA_URL=https://jira-url

Use this Python script:

from jira import JIRA
from dotenv import load_dotenv
import os

# Load .env file
load_dotenv()


key_cert_data = None
with open(os.getenv("JIRA_KEY_FILE"), 'r') as key_cert_file:
    key_cert_data = key_cert_file.read()

oauth_dict = {
    'access_token': os.getenv("JIRA_ACCESS_TOKEN"),
    'access_token_secret': os.getenv("JIRA_ACCESS_TOKEN_SECRET"),
    'consumer_key': os.getenv("JIRA_CONSUMER_KEY"),
    'key_cert': key_cert_data
}
jira = JIRA(os.getenv("JIRA_URL"), oauth=oauth_dict)

all_proj_issues_but_mine = jira.search_issues('project=PROJ and assignee != currentUser()')
print(all_proj_issues_but_mine)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment