Skip to content
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

Feature/token #187

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions hugo/content/getting-started/config/authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ lastmodifierdisplayname = "Naresh Rayapati"

## Authentication

This plugin supports both Basic and OAuth, OAuth is preferred over the Basic authentication.
This plugin supports Basic Authentication, OAuth, and Personal Access Tokens.
OAuth is preferred over the Basic authentication.

### Basic Authentication

Expand Down Expand Up @@ -39,11 +40,11 @@ This plugin supports both Basic and OAuth, OAuth is preferred over the Basic aut

{{% alert theme="info" %}} Please take note that above screenshot missing **ReadTimeout(ms)** {{% /alert %}}

### Use Credential Plugin for basic authentication
### Use Credential Plugin for Password or Personal Access Token Authentication

* Goto **Manage Jenkins > Configure System > JIRA Steps > Add Site > Choose Credential**.
* Name = Jira Site Name
* URL = Jira Site URL
* Credentials = Choose a stored credential
* Credentials = Choose a stored credential (Username with password for password-based authentication, or Secret text for Personal Access Token)

![Credential](https://raw.githubusercontent.com/jenkinsci/jira-steps-plugin/master/hugo/static/images/jira_site_credential.png)
21 changes: 21 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
<assertj-core.version>3.24.2</assertj-core.version>
<google.oauth.version>1.34.1</google.oauth.version>
<lombok.version>1.18.24</lombok.version>
<okhttp3.version>4.12.0</okhttp3.version>
<retrofit2.version>2.9.0</retrofit2.version>
<signpost.oauth.version>2.1.1</signpost.oauth.version>
<maven.javadoc.skip>true</maven.javadoc.skip>
Expand Down Expand Up @@ -76,6 +77,11 @@
<type>pom</type>
<version>1763.v092b_8980a_f5e</version>
</dependency>
<dependency>
<artifactId>kotlin-stdlib-jdk8</artifactId>
<groupId>org.jetbrains.kotlin</groupId>
<version>1.9.10</version>
</dependency>
</dependencies>
</dependencyManagement>

Expand All @@ -92,6 +98,10 @@
<artifactId>credentials</artifactId>
<groupId>org.jenkins-ci.plugins</groupId>
</dependency>
<dependency>
<artifactId>plain-credentials</artifactId>
<groupId>org.jenkins-ci.plugins</groupId>
</dependency>
<dependency>
<artifactId>script-security</artifactId>
<groupId>org.jenkins-ci.plugins</groupId>
Expand All @@ -112,6 +122,11 @@
<groupId>com.squareup.retrofit2</groupId>
<version>${retrofit2.version}</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>${okhttp3.version}</version>
</dependency>
<dependency>
<artifactId>signpost-core</artifactId>
<groupId>oauth.signpost</groupId>
Expand Down Expand Up @@ -152,6 +167,12 @@
<version>${assertj-core.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>mockwebserver</artifactId>
<version>${okhttp3.version}</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
25 changes: 18 additions & 7 deletions src/main/java/org/thoughtslive/jenkins/plugins/jira/Site.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
import com.cloudbees.plugins.credentials.CredentialsMatcher;
import com.cloudbees.plugins.credentials.CredentialsMatchers;
import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.common.StandardCredentials;
import com.cloudbees.plugins.credentials.common.StandardListBoxModel;
import com.cloudbees.plugins.credentials.common.StandardUsernameCredentials;
import com.cloudbees.plugins.credentials.common.UsernamePasswordCredentials;
import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
import com.cloudbees.plugins.credentials.domains.DomainRequirement;
import com.cloudbees.plugins.credentials.domains.URIRequirementBuilder;
import hudson.Extension;
Expand All @@ -32,6 +33,7 @@
import lombok.extern.java.Log;
import org.acegisecurity.Authentication;
import org.apache.commons.lang.StringUtils;
import org.jenkinsci.plugins.plaincredentials.StringCredentials;
import org.kohsuke.stapler.AncestorInPath;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;
Expand Down Expand Up @@ -191,9 +193,15 @@ public FormValidation doCheckCredentialsId(@AncestorInPath Item item,
}

List<DomainRequirement> domainRequirements = URIRequirementBuilder.fromUri(url).build();
if (CredentialsProvider.listCredentials(StandardUsernameCredentials.class, item,
getAuthentication(item), domainRequirements, CredentialsMatchers.withId(credentialsId))
.isEmpty()) {
boolean credentialsIsUsernameCredentials = !CredentialsProvider.listCredentials(
StandardUsernameCredentials.class, item, getAuthentication(item), domainRequirements,
CredentialsMatchers.withId(credentialsId)
).isEmpty();
boolean credentialsIsTokenCredentials = !CredentialsProvider.listCredentials(
StringCredentials.class, item, getAuthentication(item), domainRequirements,
CredentialsMatchers.withId(credentialsId)
).isEmpty();
if (!credentialsIsUsernameCredentials && !credentialsIsTokenCredentials) {
return FormValidation.error(Messages.Site_invalidCredentialsId());
}
return FormValidation.ok();
Expand All @@ -220,13 +228,16 @@ public ListBoxModel doFillCredentialsIdItems(final @AncestorInPath Item item,
Authentication authentication = getAuthentication(item);
List<DomainRequirement> domainRequirements = URIRequirementBuilder.fromUri(url).build();
CredentialsMatcher always = CredentialsMatchers.always();
Class type = UsernamePasswordCredentials.class;
Class<? extends StandardCredentials> usernamePasswordType = StandardUsernamePasswordCredentials.class;
Class<? extends StandardCredentials> tokenType = StringCredentials.class;

result.includeEmptyValue();
if (item != null) {
result.includeMatchingAs(authentication, item, type, domainRequirements, always);
result.includeMatchingAs(authentication, item, usernamePasswordType, domainRequirements, always);
result.includeMatchingAs(authentication, item, tokenType, domainRequirements, always);
} else {
result.includeMatchingAs(authentication, Jenkins.get(), type, domainRequirements, always);
result.includeMatchingAs(authentication, Jenkins.get(), usernamePasswordType, domainRequirements, always);
result.includeMatchingAs(authentication, Jenkins.get(), tokenType, domainRequirements, always);
}
return result;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
import java.util.HashMap;
import java.util.Map;
import oauth.signpost.http.HttpRequest;
import okhttp3.MediaType;
import okhttp3.Request;
import okhttp3.RequestBody;
import okio.Buffer;

public class RequestAdapter implements HttpRequest {
Expand All @@ -27,9 +29,10 @@ public Map<String, String> getAllHeaders() {

@Override
public String getContentType() {
if (request.body() != null) {
return (request.body().contentType() != null) ? request.body().contentType().toString()
: null;
RequestBody body = request.body();
if (body != null) {
MediaType contentType = body.contentType();
return (contentType != null) ? contentType.toString() : null;
}
return null;
}
Expand All @@ -41,11 +44,12 @@ public String getHeader(String key) {

@Override
public InputStream getMessagePayload() throws IOException {
if (request.body() == null) {
RequestBody body = request.body();
if (body == null) {
return null;
}
Buffer buf = new Buffer();
request.body().writeTo(buf);
body.writeTo(buf);
return buf.inputStream();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
package org.thoughtslive.jenkins.plugins.jira.login;

import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.common.StandardCredentials;
import com.cloudbees.plugins.credentials.common.StandardUsernameCredentials;
import com.cloudbees.plugins.credentials.common.UsernamePasswordCredentials;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.security.ACL;
import java.io.IOException;
import java.util.Base64;
import java.util.Collections;
import java.util.stream.Stream;

import jenkins.model.Jenkins;
import oauth.signpost.exception.OAuthException;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;

import org.jenkinsci.plugins.plaincredentials.StringCredentials;
import org.jenkinsci.plugins.plaincredentials.impl.StringCredentialsImpl;
import org.thoughtslive.jenkins.plugins.jira.Messages;
import org.thoughtslive.jenkins.plugins.jira.Site;

Expand Down Expand Up @@ -53,21 +59,33 @@ public Response intercept(Interceptor.Chain chain) throws IOException {
throw new IOException("Error signing request with OAuth.", e);
}
} else if (Site.LoginType.CREDENTIAL.name().equalsIgnoreCase(jiraSite.getLoginType())) {
StandardUsernameCredentials credentialsId = null;
StandardCredentials credentials = null;
// credentials is saved in global configuration, there is no context there during test connection so SYSTEM access is used
credentialsId = CredentialsProvider.lookupCredentials(StandardUsernameCredentials.class,
Jenkins.get(), ACL.SYSTEM, Collections.emptyList()) //
.stream() //
credentials = Stream.concat(
CredentialsProvider.lookupCredentials(StandardCredentials.class, Jenkins.get(), ACL.SYSTEM, Collections.emptyList()) //
.stream(),
CredentialsProvider.lookupCredentials(StringCredentials.class, Jenkins.get(), ACL.SYSTEM, Collections.emptyList()) //
.stream()
)
.filter(c -> c.getId().equals(jiraSite.getCredentialsId())) //
.findFirst() //
.orElseThrow(() -> new IllegalStateException(Messages.Site_invalidCredentialsId()));
String credentials = credentialsId.getUsername();
if (credentialsId instanceof UsernamePasswordCredentials) {
credentials +=
":" + ((UsernamePasswordCredentials) credentialsId).getPassword().getPlainText();
final String encodedHeader;
if (credentials instanceof UsernamePasswordCredentials) {
UsernamePasswordCredentials usernamePasswordCredentials = (UsernamePasswordCredentials) credentials;
String username = usernamePasswordCredentials.getUsername();
String password = usernamePasswordCredentials.getPassword().getPlainText();
String userPass = username + ":" + password;
encodedHeader = "Basic " + new String(Base64.getEncoder().encode(userPass.getBytes()));
} else if (credentials instanceof StringCredentialsImpl) {
StringCredentialsImpl stringCredentials = (StringCredentialsImpl) credentials;
encodedHeader = "Bearer " + stringCredentials.getSecret().getPlainText();
} else {
throw new IllegalArgumentException(String.format(
"Credentials %s has unsupported type %s. Only UsernamePasswordCredentials and StringCredentials are supported.",
jiraSite.getCredentialsId(), credentials.getClass().getCanonicalName()
));
}
String encodedHeader =
"Basic " + new String(Base64.getEncoder().encode(credentials.getBytes()));
Request requestWithAuthorization = chain.request().newBuilder()
.addHeader("Authorization", encodedHeader).build();
return chain.proceed(requestWithAuthorization);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ public JiraService(final Site jiraSite) {
InetSocketAddress proxyAddr = new InetSocketAddress(proxyConfiguration.name,
proxyConfiguration.port);
Authenticator proxyAuthenticator = (route, response) -> {
String credential = Credentials.basic(proxyConfiguration.getUserName(),
proxyConfiguration.getPassword());
String proxyUserName = (proxyConfiguration.getUserName() != null ? proxyConfiguration.getUserName() : "");
String credential = Credentials.basic(proxyUserName, proxyConfiguration.getPassword());
return response.request().newBuilder()
.header("Proxy-Authorization", credential)
.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ public class AddCommentStep extends BasicJiraStep {
private static final long serialVersionUID = 8523118063993121080L;

@Getter
private final String idOrKey;
public final String idOrKey;

@Deprecated
@Getter
private final String comment;
public final String comment;

@Getter
@DataBoundSetter
private Object input;
public Object input;

@Deprecated
@DataBoundConstructor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ public class AddWatcherStep extends BasicJiraStep {
private static final long serialVersionUID = 6417829072320454268L;

@Getter
private final String idOrKey;
public final String idOrKey;

@Getter
private final String userName;
public final String userName;

@DataBoundConstructor
public AddWatcherStep(final String idOrKey, final String userName) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ public class AssignIssueStep extends BasicJiraStep {
private static final long serialVersionUID = -7552691123209663987L;

@Getter
private final String idOrKey;
public final String idOrKey;

@Getter
private final String userName;
public final String userName;

@Getter
private final String accountId;
public final String accountId;

@DataBoundConstructor
public AssignIssueStep(final String idOrKey, final String userName, final String accountId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,19 @@ public class AssignableUserSearchStep extends BasicJiraStep {
private static final long serialVersionUID = -7754102811625753132L;

@Getter
private String project;
public String project;

@Getter
private String issueKey;
public String issueKey;
@Getter
@DataBoundSetter
private String queryStr;
public String queryStr;
@Getter
@DataBoundSetter
private int startAt = 0;
public int startAt = 0;
@Getter
@DataBoundSetter
private int maxResults = 1000;
public int maxResults = 1000;

@DataBoundConstructor
public AssignableUserSearchStep(final String project, final String issueKey) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,19 @@ public abstract class BasicJiraStep extends Step implements Serializable {

@Getter
@DataBoundSetter
private String site;
public String site;

@Getter
@DataBoundSetter
private boolean failOnError = true;
public boolean failOnError = true;

@Getter
@DataBoundSetter
private boolean auditLog = true;
public boolean auditLog = true;

@Getter
@DataBoundSetter
private Map<String, String> queryParams = new HashMap<>();
public Map<String, String> queryParams = new HashMap<>();

@Extension
public static class JiraWhitelist extends ProxyWhitelist {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public class DeleteAttachmentStep extends BasicJiraStep {
private static final long serialVersionUID = -4661648934764886451L;

@Getter
private final String id;
public final String id;

@DataBoundConstructor
public DeleteAttachmentStep(final String id) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public class DeleteIssueLinkStep extends BasicJiraStep {

private static final long serialVersionUID = -4252560961571411897L;
@Getter
private final String id;
public final String id;

@DataBoundConstructor
public DeleteIssueLinkStep(final String id) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ public class DeleteIssueRemoteLinkStep extends BasicJiraStep {
private static final long serialVersionUID = 3529709240318435576L;

@Getter
private final String idOrKey;
public final String idOrKey;

@Getter
private final String linkId;
public final String linkId;

@DataBoundConstructor
public DeleteIssueRemoteLinkStep(final String idOrKey, final String linkId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ public class DeleteIssueRemoteLinksStep extends BasicJiraStep {
private static final long serialVersionUID = 3529709240318435576L;

@Getter
private final String idOrKey;
public final String idOrKey;

@Getter
private final String globalId;
public final String globalId;

@DataBoundConstructor
public DeleteIssueRemoteLinksStep(final String idOrKey, final String globalId) {
Expand Down
Loading