JIRA REST API User Impersonation
Marjan Sterjev
IT Engineer | CISSP | CCSP | CEH (Master): research | learn | do | MENTOR
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 rich add-on (plugin) mechanism that can help you customize many JIRA aspects.
JIRA exposes REST API as well that allows 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, third party system will authenticate and access JIRA REST API through service user account, but each Issue that will created shall have arbitrary reporter that matches some other JIRA user.
The impersonation can be achieved by writing JIRA plugin for that. There are ready to use plugins for that purpose on the Atlassian Market. The purpose of this article is demonstrating the power of the JIRA plugin mechanism and how easy 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 the Maven’s pom.xml. Change the organization’s name and URL. Add 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 development JIRA instance with the plugin installed:
atlas-run
Once the JIRA server is up and running, navigate your browser to the following URL:
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" https://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 that 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 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.
4年多的研发团队管理经验,从零打造?效、多?化的开发团队,结合 10+年的?线、全?命周期的研发经验,使我能够充满信?的指导、带领团队成员完成?个个业务、技术挑战
3 年Nice. I'll try this in JIRA server rest apis.
Software Engineering leader | Team Builder | Mentor/Coach | Engineer | Building high performing, happy software engineering teams through empathy and empowerment
7 年Great article, thank you! Is there any way to do this same thing for a Jira Cloud instance?
Enterprise Sales and Bid Management (automotive/ multi-industry) | Pythonista, Jira Guru & DIY Smart Home Tinkerer
7 年Impressive hack! Your trick is to use a custom HTTP header which will be processed by the servlet filter and switch the user before REST call is processed. This is something new to learn for me. :) However, I find it difficult to create a practical case where this will be legitimately useful - because acting on behalf of users is not a good security practice. In most organizations this is prohibited by information security policy.
Co-Founder & CIO em Paytech
7 年Tiago Struck
#AtlassianCreator & Atlassian Certified Administration Expert
7 年Very good!