JIRA REST API User Impersonation

18 Jun, 2017 | 5 minutes read

JIRA is one of the best, if not the best, task management systems on the market. Besides JIRA’s powerful out-of-the-box features, the whole product has a rich add-on (plugin) mechanism that can help you customize many JIRA aspects.

JIRA exposes REST API as well allowing building rich ecosystems that support automation of many tasks, creating Issues for example. Sometimes in the systems that act on behalf of many users, there is a need to impersonate the user. In other words, a third-party system will authenticate and access JIRA REST API through a service user account, but each Issue that will be created shall have an arbitrary reporter that matches some other JIRA user.

The impersonation can be achieved by writing a JIRA plugin for that. There are already ready-to-use plugins for that purpose in the Atlassian Market. The purpose of this article is to demonstrate the power of the JIRA plugin mechanism and how easily we can build the aforementioned impersonation functionality.

Atlassian has an excellent documentation and “Hello World” tutorial that demonstrates how to install Atlassian SDK and build a simple plugin:

Create Hello World Plugin Project

In this article, I will assume that you’ve already downloaded the Atlassian SDK.

So let’s begin.

The first step is creating the plugin project by executing the following command:

atlas-create-jira-plugin

I’ve answered the wizard’s questions as:

Define value for groupId: : com.interworks.labs.jira

Define value for artifactId: : impersonation

Define value for version:  1.0.0-SNAPSHOT: :

Define value for package:  com.interworks.labs.jira: :

The wizard creates the plugin project in the directory named after your artifact ID: impersonation.

Navigate to the impersonation folder and open Maven’s pom.xml. Change the organization’s name and URL. Add a meaningful description. For example:

<organization>
    <name>InterWorks</name>
   <url>interworks.com.mk</url>
</organization>

<name>impersonation</name>
<description>Plugin that provides servlet filter for user impersonation in the REST API calls</description>
<packaging>atlassian-plugin</packaging>

Execute the module creation command in the plugin root folder:

atlas-create-jira-plugin-module

Choose the option 20 (Servlet Filter).

The wizard asks some questions. I’ve entered the following values:

Enter New Classname MyServletFilter: : ImpersonationServletFilter

Enter Package Name com.interworks.labs.jira.servlet.filter: :

Show Advanced Setup? (Y/y/N/n) N: : N

Add Another Plugin Module? (Y/y/N/n) N: : N

The wizard will modify the file atlassian-plugin.xml file in the folder src/main/resources. Open that file and change the url-pattern in the servlet filter in order to match the REST API endpoint:

servlet-filter name="Impersonation Servlet Filter" i18n-name-key="impersonation-servlet-filter.name" key="impersonation-servlet-filter" class="com.interworks.labs.jira.servlet.filter.ImpersonationServletFilter" location="before-dispatch" weight="100">

    <description key="impersonation-servlet-filter.description">The Impersonation Servlet Filter Plugin </description>

    <url-pattern>/rest/api/*</url-pattern>

</servlet-filter>

The plugin is almost done :-).

Open the class ImpersonationServletFilter and implement the filter as:

ImpersonationServletFileter.java

package com.interworks.labs.jira.servlet.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.atlassian.jira.component.ComponentAccessor;
import com.atlassian.jira.permission.GlobalPermissionKey;
import com.atlassian.jira.security.GlobalPermissionManager;
import com.atlassian.jira.security.JiraAuthenticationContext;
import com.atlassian.jira.user.ApplicationUser;
import com.atlassian.jira.user.util.UserManager;

public class ImpersonationServletFilter implements Filter {
    
    private static final Logger log = LoggerFactory.getLogger(ImpersonationServletFilter.class);

    public void init(FilterConfig filterConfig) throws ServletException {
    }

    public void destroy() {
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        JiraAuthenticationContext authContext = ComponentAccessor.getJiraAuthenticationContext();
        GlobalPermissionManager globalPermissionManager = ComponentAccessor.getGlobalPermissionManager();
        
        ApplicationUser origUser = authContext.getLoggedInUser();
        
        if (origUser != null && globalPermissionManager.hasPermission(GlobalPermissionKey.ADMINISTER, origUser)) {
            try {
                String impersonateUser = httpServletRequest.getHeader("iw-impersonate");
                if (impersonateUser != null) {
                    UserManager userManager = ComponentAccessor.getUserManager();
                    ApplicationUser appUser = userManager.getUserByName(impersonateUser);
                    if (appUser != null && appUser.isActive()) {
                        authContext.setLoggedInUser(appUser);
                        log.info("User % impersonated as %s!", origUser.getUsername(), appUser.getUsername());
                    }
                }

                chain.doFilter(request, response);
            } finally {
                if (origUser != null)
                    authContext.setLoggedInUser(origUser);
            }
        } else {
            chain.doFilter(request, response);
        }

    }
}

In the plugin’s root folder run the following command that will build the plugin and start the development of the JIRA instance with the plugin installed:

atlas-run

Once the JIRA server is up and running, navigate your browser to the following URL:

http://localhost:2990/jira

Login with the following credentials: Username=’admin’, Password=’admin’

This is your first login, so choose the Language and the optional Avatar. After that, press the button Create new Project. Choose the Project management type. Enter the name of the project: Test. Select the cog (top right settings icon) and select User Management after that.

Create 2 new users by pressing the button Create User. I’ve picked the following usernames: Alice and Bob.

Ok, let’s test the functionality!

In an arbitrary folder of your choice create the following JSON payload file that contains the Issue.

issue.json

{

    "fields": {

       "project":

       {

          "key": "TEST"

       },

       "summary": "Test Issue",

       "description": "Creating of an issue using project keys and issue type names using the REST API",

       "issuetype": {

          "name": "Task"

       }

   }
}

In the same folder execute the following command first:

curl -D- -u admin:admin -X POST --data @issue.json -H "Content-Type: application/json" http://localhost:2990/jira/rest/api/2/issue/

The first issue is created.

After that execute the following commands that insert the HTTP header iw-impersonate which contains the name of the user to be impersonated:

curl -D- -u admin:admin -X POST --data @issue.json -H "Content-Type: application/json" -H "iw-impersonate: alice" http://localhost:2990/jira/rest/api/2/issue/

curl -D- -u admin:admin -X POST --data @issue.json -H "Content-Type: application/json" -H "iw-impersonate: bob" http://localhost:2990/jira/rest/api/2/issue/

Note in the JIRA dashboard that the issue reporter is no more admin, but rather Alice and Bob.

The plugin enforces that the authenticated user must be a JIRA Administrator. Otherwise, the impersonation is skipped.

You can adjust the plugin code in order to meet your needs regarding the permission logic, but in essence, that will be a trivial task. The main goal has been achieved, third-party systems can integrate with JIRA and act on behalf of existing JIRA users.