Authenticating Against Atlassian Cloud

The request on the Atlassian Forums that caught my eye last night was a request to return all Jira Cloud attachments with a certain extension.   Ordinarily it would be easy enough to cite ScriptRunner as the solution to this, but the user included data residency concerns in his initial post.

My solution to this was to write him a Python script to provide simple reporting on issues with certain extension types.    Most anything that the rest API can accomplish can be accomplished with Python; you don’t HAVE to have ScriptRunner.

The hardest part of working with external scripts is figuring out the authorization scheme. Once you’ve got that figured out, the rest is just the same REST API interaction that you’d get with UniREST and ScriptRunner for cloud.

Authorizing the Script

First, read the instructions: https://developer.atlassian.com/cloud/jira/platform/basic-auth-for-rest-apis/ 

Then:

1. Generate an API token: https://id.atlassian.com/manage-profile/security/api-tokens

2. Go to https://www.base64encode.net/ (or figure out the Python module to do the encoding)

3. Base-64 encode a string in exactly this format: youratlassianlogin:APIToken.   
If your email is john@adaptamist.com and the API token you generated is:

ATATT3xFfGF0nH_KSeZZkb_WbwJgi131SCo9N-ztA3SAySIK5w3qo9hdrxhqHZAZvimLHxbMA7ZmeYRMMNR

 

Then the string you base-64 encode is:

john@adaptamist.com:ATATT3xFfGF0nH_KSeZZkb_WbwJgi131SCo9N-ztA3SAySIK5w3qo9hdrxhqHZAZvimLHxbMA7ZmeYRMMNR

 

Do not forget the colon between the two pieces.

 

The website will spit out a single string of encoded text that looks like this: a21jY2xlYW5AYWQQhcHRhdmlzdC5jb206QVRBVFQzeEZmR0YwbkhfS1NlWlprYl9XYndKZ2kxMzFTQ285Ti16dEEzU0F5U0lLNXczcW85a

 

4. Stick the encoded string into the header variable like so:

headers = {

'Authorization': 'Basic a21jY2xlYW5AYWQQhcHRhdmlzdC5jb206QVRBVFQzeEZmR0YwbkhfS1NlWlprYl9XYndKZ2kxMzFTQ285Ti16dEEzU0F5U0lLNXczcW85a',

'Content-Type': 'application/json',

'Accept': 'application/json',

}

Note that there’s nothing between the encoded string and the word “Basic”, and that both are treated as a single string.

Here’s an example of the script actually using this authorization scheme

 

import requests
 
max_results = 100
start_at = 0
attachment_arr = []
still_paginating = True
 
yourDomain = "<domain>"
 
headers = {
    'Authorization': 'Basic <base-64 encoded string>',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
}
 
 
# Define the global variables
 
while still_paginating:
    # We need to paginate through the results
    # We're iterating by 100 results at a time
    
    response = requests.get(f"https://{yourDomain}.atlassian.net/rest/api/3/search?jql=project%20is%20not%20EMPTY&maxResults={max_results}&startAt={start_at}",
                            headers=headers)
                            
   #print(response.content)
    issue_keys = response.json().get("issues")
    # We start by returning all of the issues in the instance with a JQL search
    
    for issue in issue_keys:
        # Next, we're iterating through each result (issue) that was returned
        
        issue_key = issue.get("key")
        issue_response = requests.get(f"https://{yourDomain}.atlassian.net/rest/api/3/issue/{issue_key}",
                                      headers=headers)
                                      
        #print(issue_response.content)
        issue_data = issue_response.json()
        # We query the system for more information about the issue in question
        
        attachments = issue_data.get("fields", {}).get("attachment", [])
        for attachment in attachments:
            # Specifically, we're after the ID of any attachment on the issue
            
            attachment_id = attachment.get("id")
            attachment_response = requests.get(f"https://{yourDomain}.atlassian.net/rest/api/3/attachment/{attachment_id}",
                                               headers=headers)
                                                
            #print(attachment_response.content)
            attachment_data = attachment_response.json()
            # Once we have the ID of the attachment, we can use that ID to get more information about the attachment
            
            filename = attachment_data.get("filename")
            if filename and (".csv" in filename or ".xlsx" in filename):
                attachment_arr.append(f"Issue {issue_key} has an attachment: {filename}")
    
    if len(issue_keys) < max_results:
        still_paginating = False
    # Finally, we check to see if we're still paginating
    # If the total number of results is less than the total number of maximum possible results,
    # we must be at the end of the line and can stop paginating by terminating the loop  start_at += 100
    start_at += 100
print(attachment_arr)
# Print the results

 

 

 

Leave a Reply

Your email address will not be published. Required fields are marked *