-
Notifications
You must be signed in to change notification settings - Fork 41
Added bearer token support #159
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
/* | ||
* Copyright (C) 2022 Atlassian | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package org.jenkinsci.plugins.JiraTestResultReporter; | ||
|
||
import com.atlassian.httpclient.api.Request; | ||
import com.atlassian.jira.rest.client.api.AuthenticationHandler; | ||
|
||
/** | ||
* Handler for Bearer (Token) authentication | ||
*/ | ||
public class BearerAuthenticationHandler implements AuthenticationHandler { | ||
|
||
private static final String AUTHORIZATION_HEADER = "Authorization"; | ||
|
||
private final String token; | ||
|
||
public BearerAuthenticationHandler(String token) { | ||
this.token = token; | ||
} | ||
|
||
@Override | ||
public void configure(Request.Builder builder) { | ||
builder.setHeader(AUTHORIZATION_HEADER, "Bearer " + token); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -70,10 +70,10 @@ | |
*/ | ||
public class JiraTestDataPublisher extends TestDataPublisher { | ||
|
||
public static final boolean DEBUG = false; | ||
public static final boolean DEBUG = false; | ||
|
||
/** Attachments obtained from junit-attachments plugin indexed by className and test method name **/ | ||
private Map<String, Map<String, List<String>>> attachments = new HashMap<>(); | ||
private Map<String, Map<String, List<String>>> attachments = new HashMap<>(); | ||
|
||
/** | ||
* Getter for the configured fields | ||
|
@@ -110,7 +110,7 @@ | |
public boolean getAutoUnlinkIssue() { | ||
return JobConfigMapping.getInstance().getAutoUnlinkIssue(getJobName()); | ||
} | ||
|
||
/** | ||
* Getter for list of attachments by test method identified by its classname and name | ||
* @param className | ||
|
@@ -122,12 +122,12 @@ | |
return Collections.emptyList(); | ||
} | ||
Map<String, List<String>> attachmentsByClassname = this.attachments.get(className); | ||
if (!attachmentsByClassname.containsKey(name)) { | ||
if (!attachmentsByClassname.containsKey(name)) { | ||
return Collections.emptyList(); | ||
} | ||
return attachmentsByClassname.get(name); | ||
} | ||
|
||
/** | ||
* Getter for the project associated with this publisher | ||
* @return | ||
|
@@ -146,12 +146,12 @@ | |
private JobConfigMapping.JobConfigEntry getJobConfig() { | ||
return jobConfig; | ||
} | ||
|
||
@CheckForNull | ||
public boolean getAdditionalAttachments() { | ||
return jobConfig.getAdditionalAttachments(); | ||
} | ||
|
||
@DataBoundSetter | ||
public void setAdditionalAttachments(boolean additionalAttachments) { | ||
JiraUtils.log(String.format("Additional attachments field configured as %s", additionalAttachments)); | ||
|
@@ -166,7 +166,7 @@ | |
.withConfigs(this.jobConfig.getConfigs()) | ||
.build(); | ||
} | ||
|
||
|
||
/** | ||
* Constructor | ||
|
@@ -178,8 +178,8 @@ | |
* @param autoUnlinkIssue | ||
* @param overrideResolvedIssues | ||
*/ | ||
@DataBoundConstructor | ||
public JiraTestDataPublisher(List<AbstractFields> configs, String projectKey, String issueType, | ||
@DataBoundConstructor | ||
public JiraTestDataPublisher(List<AbstractFields> configs, String projectKey, String issueType, | ||
boolean autoRaiseIssue, boolean autoResolveIssue, boolean autoUnlinkIssue, boolean overrideResolvedIssues) { | ||
|
||
long defaultIssueType; | ||
|
@@ -222,12 +222,12 @@ | |
* @throws IOException | ||
* @throws InterruptedException | ||
*/ | ||
@Override | ||
public TestResultAction.Data contributeTestData(Run<?, ?> run, @Nonnull FilePath workspace, Launcher launcher, | ||
@Override | ||
public TestResultAction.Data contributeTestData(Run<?, ?> run, @Nonnull FilePath workspace, Launcher launcher, | ||
TaskListener listener, TestResult testResult) | ||
throws IOException, InterruptedException { | ||
EnvVars envVars = run.getEnvironment(listener); | ||
|
||
EnvVars envVars = run.getEnvironment(listener); | ||
Job job = run.getParent(); | ||
Job project; | ||
if (job instanceof MatrixConfiguration) { | ||
|
@@ -316,7 +316,7 @@ | |
break; | ||
} | ||
} | ||
|
||
if (!transitionExecuted) { | ||
listener.getLogger().println("Could not find transition to resolve issue " + issueKey); | ||
} | ||
|
@@ -386,7 +386,7 @@ | |
* Getter for the Descriptor | ||
* @return singleton instance of the Descriptor | ||
*/ | ||
@Override | ||
@Override | ||
public JiraTestDataPublisherDescriptor getDescriptor() { | ||
return (JiraTestDataPublisherDescriptor) Jenkins.getInstance().getDescriptorOrDie(getClass()); | ||
} | ||
|
@@ -400,8 +400,8 @@ | |
} | ||
|
||
@Symbol("jiraTestResultReporter") | ||
@Extension | ||
public static class JiraTestDataPublisherDescriptor extends Descriptor<TestDataPublisher> { | ||
@Extension | ||
public static class JiraTestDataPublisherDescriptor extends Descriptor<TestDataPublisher> { | ||
/** | ||
* Constructor | ||
* loads the serialized descriptor from the previous run | ||
|
@@ -430,88 +430,101 @@ | |
private transient JiraRestClient restClient; | ||
private transient JiraRestClientExtension restClientExtension; | ||
private transient MetadataCache metadataCache = new MetadataCache(); | ||
private URI jiraUri = null; | ||
private String username = null; | ||
private Secret password = null; | ||
private URI jiraUri = null; | ||
private String username = null; | ||
private Secret password = null; | ||
private boolean useBearerAuth = false; | ||
private String defaultSummary; | ||
private String defaultDescription; | ||
|
||
|
||
public URI getJiraUri() { return jiraUri; } | ||
public String getUsername() { return username; } | ||
public Secret getPassword() { return password; } | ||
public boolean getUseBearerAuth() { return useBearerAuth; } | ||
public String getJiraUrl() { return jiraUri != null ? jiraUri.toString() : null; } | ||
public JiraRestClient getRestClient() { return restClient; } | ||
|
||
/** | ||
* Getter for the summary template | ||
* @return | ||
*/ | ||
public String getDefaultSummary() { | ||
return defaultSummary != null && !defaultSummary.equals("") ? defaultSummary : DEFAULT_SUMMARY; | ||
} | ||
|
||
/** | ||
* Getter for the description template | ||
* @return | ||
*/ | ||
public String getDefaultDescription() { | ||
return defaultDescription != null && !defaultDescription.equals("") ? defaultDescription : DEFAULT_DESCRIPTION; | ||
} | ||
|
||
/** | ||
* Getter for the statuses map, contains information about status category of each status | ||
* @return | ||
*/ | ||
public HashMap<String, FullStatus> getStatusesMap() { | ||
return statuses; | ||
} | ||
|
||
/** | ||
* Getter for the cache entry | ||
* @param projectKey | ||
* @param issueType | ||
* @return a metadata cache entry | ||
*/ | ||
public MetadataCache.CacheEntry getCacheEntry(String projectKey, String issueType) { | ||
return metadataCache.getCacheEntry(projectKey, issueType); | ||
} | ||
|
||
/** | ||
* Method for resolving transient objects after deserialization. Called by the JVM. | ||
* See Java documentation for more details. | ||
* @return this object | ||
*/ | ||
public Object readResolve() { | ||
if(jiraUri != null && username != null && password != null) { | ||
AsynchronousJiraRestClientFactory factory = new AsynchronousJiraRestClientFactory(); | ||
restClient = factory.createWithBasicHttpAuthentication(jiraUri, username, password.getPlainText()); | ||
restClientExtension = new JiraRestClientExtension(jiraUri, | ||
new AsynchronousHttpClientFactory().createClient(jiraUri, new BasicHttpAuthenticationHandler(username, password.getPlainText()))); | ||
if(useBearerAuth) | ||
{ | ||
BearerAuthenticationHandler handler = new BearerAuthenticationHandler(password.getPlainText()); | ||
restClient= factory.create(jiraUri, handler); | ||
|
||
restClientExtension = new JiraRestClientExtension(jiraUri, | ||
new AsynchronousHttpClientFactory().createClient(jiraUri, new BearerAuthenticationHandler(password.getPlainText()))); | ||
} | ||
else | ||
{ | ||
restClient = factory.createWithBasicHttpAuthentication(jiraUri, username, password.getPlainText()); | ||
restClientExtension = new JiraRestClientExtension(jiraUri, | ||
new AsynchronousHttpClientFactory().createClient(jiraUri, new BasicHttpAuthenticationHandler(username, password.getPlainText()))); | ||
} | ||
tryCreatingStatusToCategoryMap(); | ||
} | ||
return this; | ||
} | ||
|
||
/** | ||
* Getter for the display name | ||
* @return | ||
Check warning on line 511 in src/main/java/org/jenkinsci/plugins/JiraTestResultReporter/JiraTestDataPublisher.java
|
||
*/ | ||
@Override | ||
public String getDisplayName() { | ||
return "JiraTestResultReporter"; | ||
} | ||
@Override | ||
public String getDisplayName() { | ||
return "JiraTestResultReporter"; | ||
} | ||
|
||
/** | ||
* Method for obtaining the global configurations (global.jelly), when save/apply is clicked | ||
* @param req current request | ||
* @param json form in json format | ||
* @return | ||
Check warning on line 522 in src/main/java/org/jenkinsci/plugins/JiraTestResultReporter/JiraTestDataPublisher.java
|
||
* @throws FormException | ||
Check warning on line 523 in src/main/java/org/jenkinsci/plugins/JiraTestResultReporter/JiraTestDataPublisher.java
|
||
*/ | ||
@Override | ||
public boolean configure(StaplerRequest req, JSONObject json) | ||
throws FormException { | ||
@Override | ||
public boolean configure(StaplerRequest req, JSONObject json) | ||
throws FormException { | ||
|
||
try { | ||
jiraUri = new URI(json.getString("jiraUrl")); | ||
|
@@ -521,27 +534,40 @@ | |
|
||
|
||
username = json.getString("username"); | ||
password = Secret.fromString(json.getString("password")); | ||
password = Secret.fromString(json.getString("password")); | ||
useBearerAuth = json.getBoolean("useBearerAuth"); | ||
defaultSummary = json.getString("summary"); | ||
defaultDescription = json.getString("description"); | ||
|
||
if (json.getString("jiraUrl").equals("") | ||
|| json.getString("username").equals("") | ||
|| json.getString("password").equals("")) { | ||
useBearerAuth=false; | ||
restClient = null; | ||
restClientExtension = null; | ||
save(); | ||
return true; | ||
} | ||
|
||
AsynchronousJiraRestClientFactory factory = new AsynchronousJiraRestClientFactory(); | ||
restClient = factory.createWithBasicHttpAuthentication(jiraUri, username, password.getPlainText()); | ||
restClientExtension = new JiraRestClientExtension(jiraUri, | ||
new AsynchronousHttpClientFactory().createClient(jiraUri, new BasicHttpAuthenticationHandler(username, password.getPlainText()))); | ||
if(useBearerAuth) | ||
{ | ||
BearerAuthenticationHandler handler = new BearerAuthenticationHandler(password.getPlainText()); | ||
restClient= factory.create(jiraUri, handler); | ||
|
||
restClientExtension = new JiraRestClientExtension(jiraUri, | ||
new AsynchronousHttpClientFactory().createClient(jiraUri, new BearerAuthenticationHandler(password.getPlainText()))); | ||
} | ||
else | ||
{ | ||
restClient = factory.createWithBasicHttpAuthentication(jiraUri, username, password.getPlainText()); | ||
restClientExtension = new JiraRestClientExtension(jiraUri, | ||
new AsynchronousHttpClientFactory().createClient(jiraUri, new BasicHttpAuthenticationHandler(username, password.getPlainText()))); | ||
} | ||
tryCreatingStatusToCategoryMap(); | ||
save(); | ||
save(); | ||
return super.configure(req, json); | ||
} | ||
} | ||
|
||
/** | ||
* method for creating the status category map, if the Jira server knows about categories | ||
|
@@ -580,15 +606,17 @@ | |
|
||
/** | ||
* Validation for the global configuration, called when Validate Settings is clicked (global.jelly) | ||
* @param jiraUrl | ||
Check warning on line 609 in src/main/java/org/jenkinsci/plugins/JiraTestResultReporter/JiraTestDataPublisher.java
|
||
* @param username | ||
Check warning on line 610 in src/main/java/org/jenkinsci/plugins/JiraTestResultReporter/JiraTestDataPublisher.java
|
||
* @param password | ||
Check warning on line 611 in src/main/java/org/jenkinsci/plugins/JiraTestResultReporter/JiraTestDataPublisher.java
|
||
* @param useBearerAuth | ||
Check warning on line 612 in src/main/java/org/jenkinsci/plugins/JiraTestResultReporter/JiraTestDataPublisher.java
|
||
* @return | ||
Check warning on line 613 in src/main/java/org/jenkinsci/plugins/JiraTestResultReporter/JiraTestDataPublisher.java
|
||
*/ | ||
@RequirePOST | ||
public FormValidation doValidateGlobal(@QueryParameter String jiraUrl, | ||
@QueryParameter String username, | ||
@QueryParameter String password | ||
@QueryParameter String password, | ||
@QueryParameter boolean useBearerAuth | ||
) { | ||
|
||
Jenkins.get().checkPermission(Jenkins.ADMINISTER); | ||
|
@@ -602,7 +630,17 @@ | |
// JIRA does not offer ways to validate username and password, so we try to query some server | ||
// metadata, to see if the configured user is authorized on this server | ||
AsynchronousJiraRestClientFactory factory = new AsynchronousJiraRestClientFactory(); | ||
JiraRestClient restClient = factory.createWithBasicHttpAuthentication(uri, username, pass.getPlainText()); | ||
JiraRestClient restClient; | ||
if(useBearerAuth) | ||
{ | ||
BearerAuthenticationHandler handler = new BearerAuthenticationHandler(pass.getPlainText()); | ||
restClient= factory.create(uri, handler); | ||
} | ||
else | ||
{ | ||
restClient = factory.createWithBasicHttpAuthentication(uri, username, pass.getPlainText()); | ||
} | ||
|
||
MetadataRestClient client = restClient.getMetadataClient(); | ||
Promise<ServerInfo> serverInfoPromise = client.getServerInfo(); | ||
ServerInfo serverInfo = serverInfoPromise.claim(); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
<div> | ||
Uses password data as input for Bearer token, PAT(personal access token) access to enterprise jira (https://confluence.atlassian.com/enterprise/using-personal-access-tokens-1026032365.html). | ||
Bearer authentication is only supported in Jira Server, for Jira Cloud leave this unchecked | ||
</div> |
Check warning
Code scanning / Jenkins Security Scan
Jenkins: Plaintext password storage Warning
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Token here is transferred to jira server. The input therefore comes from the password field which is stored to disk.