Skip to content

Commit

Permalink
Add support for API tokens and generic cleanup and improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
- committed Feb 19, 2019
1 parent adcc485 commit 05242df
Show file tree
Hide file tree
Showing 9 changed files with 140 additions and 70 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,20 @@
*******************************************************************************/
package com.synopsys.protecode.sc.jenkins;

import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.common.StandardCredentials;
import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
import com.cloudbees.plugins.credentials.domains.URIRequirementBuilder;
import com.synopsys.protecode.sc.jenkins.interfaces.ProtecodeScApi;
import com.synopsys.protecode.sc.jenkins.interfaces.ProtecodeScServicesApi;
import com.synopsys.protecode.sc.jenkins.utils.UtilitiesJenkins;
import hudson.model.Run;
import java.net.URL;
import java.util.Collections;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import okhttp3.*;
import okhttp3.logging.HttpLoggingInterceptor;
import org.jenkinsci.plugins.plaincredentials.StringCredentials;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

Expand Down Expand Up @@ -54,10 +57,17 @@ public static ProtecodeScServicesApi serviceBackend(URL url, boolean checkCertif
* Main entry point for building a backend implementation in run-time.
* @param credentialsId the identifier for the credentials to be used.
* @param url The url which points to the protecode-sc instance.
* @param run The context for getting the credentials
* @param checkCertificate whether or not to check the server certificate.
* @return the backend to use while communicating to the server
*/
public static ProtecodeScApi backend(String credentialsId, URL url, boolean checkCertificate) {
public static ProtecodeScApi backend(
String credentialsId,
URL url,
Run run,
boolean checkCertificate
) {
// TODO: Add a debug flag or something
// HOW TO LOG
// Leave these here for convenience of debugging. They bleed memory _badly_ though
// HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
Expand All @@ -68,34 +78,23 @@ public static ProtecodeScApi backend(String credentialsId, URL url, boolean chec
{
Request originalRequest = chain.request();

StandardUsernamePasswordCredentials credentials
= UtilitiesJenkins.getCredentials(url, credentialsId);

// Right now we can't provide credentials "as is" to protecode so we need to extract to
// contents
String protecodeScUser = credentials.getUsername();
String protecodeScPass = credentials.getPassword().getPlainText();

Request.Builder builder = originalRequest.newBuilder()
.header(
"Authorization",
Credentials.basic(protecodeScUser, protecodeScPass)
)
.addHeader("User-Agent", Configuration.CLIENT_NAME)
.addHeader("Connection", "close");

builder.header("Authorization", authenticationString(credentialsId, run, url));

Request newRequest = builder.build();
return chain.proceed(newRequest);
}
).readTimeout(Configuration.TIMEOUT_SECONDS, TimeUnit.SECONDS)
.connectTimeout(Configuration.TIMEOUT_SECONDS, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
// .addInterceptor(interceptor)
//.addInterceptor(interceptor)
.build();
// TODO: Write interceptor for checking is the error 429 (too many requests) and handle that in
// a nice fashion.


okHttpClient.dispatcher().setMaxRequests(Configuration.MAX_REQUESTS_TO_PROTECODE);
LOGGER.log(Level.ALL, "Max simultaneous requests to protecode limited to: {0}",
okHttpClient.dispatcher().getMaxRequests());
Expand All @@ -109,6 +108,46 @@ public static ProtecodeScApi backend(String credentialsId, URL url, boolean chec
return retrofit.create(ProtecodeScApi.class);
}

/**
* Method returns authentication string based on the credentials type.
*
* @param credentialsId the identifier for the credentials to be used.
* @param url The url which points to the protecode-sc instance.
* @param run The context for getting the credentials
* @return The string to use with authorization header
*/
private static String authenticationString(String credentialsId, Run<?, ?> run, URL url) {
StandardCredentials credentials = CredentialsProvider.findCredentialById(
credentialsId,
StandardCredentials.class,
run,
URIRequirementBuilder.fromUri(url.toExternalForm()).build()
);

String authenticationString;

if (credentials instanceof StandardUsernamePasswordCredentials) {
LOGGER.fine("using credentials");
authenticationString = Credentials.basic(
((StandardUsernamePasswordCredentials) credentials).getUsername(),
((StandardUsernamePasswordCredentials) credentials).getPassword().getPlainText()
);
} else if (credentials instanceof StringCredentials) {
LOGGER.fine("using API key");
authenticationString = "Bearer " + ((StringCredentials) credentials).getSecret();
} else {
return "";
}
return authenticationString;
}

/**
* Add possible CipherSuites, connection specs and connection linked items which are completely server
* specific here.
*
* @param checkCertificate whether to enforce certificate
* @return the http client
*/
private static OkHttpClient.Builder httpClientBuilder(boolean checkCertificate) {
if (checkCertificate) {
LOGGER.log(Level.INFO, "Checking certificates");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@

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.StandardUsernamePasswordCredentials;
import com.cloudbees.plugins.credentials.domains.HostnameRequirement;

import hudson.Extension;
Expand Down Expand Up @@ -151,7 +151,7 @@ public Object readResolve() {
return this;
}

private ProtecodeScService service() {
private ProtecodeScService service(Run<?, ?> run) {
// TODO: Add check that service is ok. Write http interceptor for okhttp

// TODO: Is this needed?
Expand All @@ -163,21 +163,24 @@ private ProtecodeScService service() {
|| !getDescriptor().getProtecodeScHost().equals(storedHost.toExternalForm())
|| getDescriptor().isDontCheckCert() != storedDontCheckCertificate
) {
LOGGER.finer("Making new protecode http connection service");
LOGGER.finer("Making new " + Configuration.TOOL_NAME + " http connection service");
storedHost = new URL(getDescriptor().getProtecodeScHost());
storedDontCheckCertificate = getDescriptor().isDontCheckCert();

// TODO: Make credentials provider since we don't want to provide the run context to parts which
// shouldn't be jenkins libs linked.
service = new ProtecodeScService(
credentialsId,
storedHost,
run,
!getDescriptor().isDontCheckCert()
);
// TODO: Add username password check
// Call some API which should always work and see if it doesn't return an error
}
} catch (MalformedURLException e) {
LOGGER.warning("No URL given for Protecode SC ");
listener.error("Cannot read Protecode SC URL, please make sure it has been set in the Jenkins"
LOGGER.warning("No URL given for " + Configuration.TOOL_NAME);
listener.error("Cannot read " + Configuration.TOOL_NAME + " URL, please make sure it has been set in the Jenkins"
+ " configuration page.");
// TODO: Add prebuild
}
Expand All @@ -187,7 +190,7 @@ private ProtecodeScService service() {
@Override
public void perform(Run<?, ?> run, FilePath workspace, Launcher launcher, TaskListener listener)
throws InterruptedException, IOException {
// TODO add try - catch (Interrupted) to call abort scan in protecode sc (remember to throw the
// TODO add try - catch (Interrupted) to call abort scan in BDBA (remember to throw the
// same exception upward)
LOGGER.finer("Perform() with run object");
this.listener = listener;
Expand All @@ -197,7 +200,7 @@ public void perform(Run<?, ?> run, FilePath workspace, Launcher launcher, TaskLi
@Override
public boolean perform(AbstractBuild<?, ?> build, Launcher launcher,
BuildListener listener) throws InterruptedException, IOException {
// TODO add try - catch (Interrupted) to call abort scan in protecode sc (remember to throw the
// TODO add try - catch (Interrupted) to call abort scan in BDBA (remember to throw the
// same exception upward)
LOGGER.finer("Perform() with build object");
this.listener = listener;
Expand Down Expand Up @@ -229,9 +232,9 @@ public boolean doPerform(Run<?, ?> run, FilePath workspace)
BuildVerdict verdict = new BuildVerdict(failIfVulns);

// use shortened word to distinguish from possibly null service
ProtecodeScService serv = service();
ProtecodeScService serv = service(run);
if (serv == null) {
listener.error("Cannot connect to Protecode SC"); // TODO use consoler also
listener.error("Cannot connect to " + Configuration.TOOL_NAME); // TODO use consoler also
run.setResult(Result.FAILURE);
return false;
}
Expand All @@ -241,10 +244,11 @@ public boolean doPerform(Run<?, ?> run, FilePath workspace)
boolean forceDontZip = this.dontZipFiles
&& !UtilitiesGeneral.isPublicHost(getDescriptor().getProtecodeScHost());
if (this.dontZipFiles) {
console.log("'Dont zip' is chosen, but since this build is done against a Synopsys hosted Protecode SC "
+ "instance, this option is ignored.");
console.log("'Dont zip' is chosen, but since this build is done against a Synopsys hosted "
+ Configuration.TOOL_NAME + "instance, this option is ignored.");
}

// TOTO: Make Scanner not linked to Jenkins.
Scanner scanner = new Scanner(
verdict,
protecodeScGroup,
Expand All @@ -270,21 +274,21 @@ public boolean doPerform(Run<?, ?> run, FilePath workspace)
// There needs to be a possiblity to just end the phase after the files are transfered.
Optional<List<FileResult>> resultOp = scanner.doPerform();
if (verdict.getFilesFound() == 0) {
LOGGER.info("No files found, ending Protecode SC phase.");
console.log("No files found, ending Protecode SC phase.");
LOGGER.info("No files found, ending " + Configuration.TOOL_NAME + " phase.");
console.log("No files found, ending " + Configuration.TOOL_NAME + " phase.");
run.setResult(Result.SUCCESS);
return true;
}
if(endAfterSendingFiles) {
LOGGER.info("Files sent, ending Protecode SC phase due to configuration.");
LOGGER.info("Files sent, ending " + Configuration.TOOL_NAME + " phase due to configuration.");
console.log("Files sent, ending phase.");
run.setResult(Result.SUCCESS);
return true;
}
results = resultOp.get();
} catch (IOException ioe) {
listener.error("Could not send files to Protecode-SC: " + ioe);
verdict.setError("Could not send files to Protecode-SC");
listener.error("Could not send files to " + Configuration.TOOL_NAME + ": " + ioe);
verdict.setError("Could not send files to " + Configuration.TOOL_NAME);
if (results.isEmpty()) {
return false;
} // otherwise carry on, might get something
Expand Down Expand Up @@ -313,8 +317,9 @@ public boolean doPerform(Run<?, ?> run, FilePath workspace)
console.printReportString(results);
listener.fatalError(verdict.verdictStr());
run.setResult(Result.FAILURE);
} else {
console.log("NO vulnerabilities found.");
}
console.log("NO vulnerabilities found.");
} else {
if (!verdict.verdict()) {
console.printReportString(results);
Expand All @@ -326,7 +331,7 @@ public boolean doPerform(Run<?, ?> run, FilePath workspace)
run.setResult(Result.SUCCESS);
}

console.log("Protecode SC plugin end");
console.log(Configuration.TOOL_NAME + " plugin end");
// TODO: Use perhaps unstable also
return buildStatus;
}
Expand Down Expand Up @@ -383,14 +388,22 @@ public ListBoxModel doFillCredentialsIdItems(@AncestorInPath Item context) {
// - this might be impossible in this scope
// https://groups.google.com/forum/?hl=en#!searchin/jenkinsci-dev/store$20configuration|sort:date/jenkinsci-dev/-DosteCUiu8/18-HvlAsAAAJ
StandardListBoxModel result = new StandardListBoxModel();
// TODO: use non-deprectated
result.withEmptySelection();
result.withMatching(
CredentialsMatchers.anyOf(CredentialsMatchers.instanceOf(
StandardUsernamePasswordCredentials.class)),
CredentialsMatchers.anyOf(
CredentialsMatchers.instanceOf(
// TODO: Perhaps too wide
StandardCredentials.class
)
),
CredentialsProvider.lookupCredentials(
StandardUsernamePasswordCredentials.class, context,
// TODO: Perhaps too wide
StandardCredentials.class, context,
ACL.SYSTEM,
new HostnameRequirement(protecodeScHost)));
new HostnameRequirement(protecodeScHost)
)
);
return result;
}

Expand Down

0 comments on commit 05242df

Please sign in to comment.