Skip to content

Commit

Permalink
Only trigger if token matches #55
Browse files Browse the repository at this point in the history
  • Loading branch information
tomasbjerre committed Apr 7, 2018
1 parent fc5306c commit 23afc4b
Show file tree
Hide file tree
Showing 6 changed files with 235 additions and 21 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Changelog of Generic Webhook Plugin.
**BDD**


[e273e1ee8ba0446](https://github.com/jenkinsci/generic-webhook-trigger-plugin/commit/e273e1ee8ba0446) Tomas Bjerre *2018-04-01 17:54:51*
[fc5306ce0fe9a71](https://github.com/jenkinsci/generic-webhook-trigger-plugin/commit/fc5306ce0fe9a71) Tomas Bjerre *2018-04-03 18:15:38*

**Issue template**

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ If a node is selected, then all leafs in that node will be contributed. If a lea

When using the plugin in several jobs, you will have the same URL trigger all jobs. If you want to trigger only a certain job you can:

* Use the `token`-parameter have different tokens for different jobs. **Don't combine it with any other authentication!** Using only the token means only jobs with that exact token will be visible for that request. This will increase performance and reduce responses of each invocation.
* Use the `token`-parameter have different tokens for different jobs. Using only the token means only jobs with that exact token will be visible for that request. This will increase performance and reduce responses of each invocation.
* Or, add some request parameter (or header, or post content) and use the **regexp filter** to trigger only if that parameter has a specific value.


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.IOUtils;
import org.jenkinsci.plugins.gwt.jobfinder.JobFinder;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.StaplerRequest;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,37 +1,44 @@
package org.jenkinsci.plugins.gwt;
package org.jenkinsci.plugins.gwt.jobfinder;

import static com.google.common.base.Strings.isNullOrEmpty;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.acegisecurity.context.SecurityContext;
import org.acegisecurity.context.SecurityContextHolder;
import org.jenkinsci.plugins.gwt.FoundJob;
import org.jenkinsci.plugins.gwt.GenericTrigger;

import com.google.common.annotations.VisibleForTesting;

import hudson.security.ACL;
import hudson.triggers.Trigger;
import hudson.triggers.TriggerDescriptor;
import jenkins.model.Jenkins;
import jenkins.model.ParameterizedJobMixIn.ParameterizedJob;

public final class JobFinder {

private JobFinder() {}

private static JobFinderImpersonater jobFinderImpersonater = new JobFinderImpersonater();

@VisibleForTesting
static void setJobFinderImpersonater(final JobFinderImpersonater jobFinderImpersonater) {
JobFinder.jobFinderImpersonater = jobFinderImpersonater;
}

public static List<FoundJob> findAllJobsWithTrigger(final String givenToken) {

final List<FoundJob> found = new ArrayList<>();

List<ParameterizedJob> candidateProjects = getAllParameterizedJobs();
List<ParameterizedJob> candidateProjects = getAllParameterizedJobs(givenToken);
for (final ParameterizedJob candidateJob : candidateProjects) {
final GenericTrigger genericTriggerOpt = findGenericTrigger(candidateJob.getTriggers());
if (genericTriggerOpt != null) {
found.add(new FoundJob(candidateJob.getFullName(), genericTriggerOpt));
}
}

candidateProjects = getAllParameterizedJobsByImpersonation();
candidateProjects = jobFinderImpersonater.getAllParameterizedJobsByImpersonation();
for (final ParameterizedJob candidateJob : candidateProjects) {
if (!isIncluded(candidateJob.getFullName(), found)
&& authenticationTokenMatches(candidateJob, givenToken)) {
Expand All @@ -54,36 +61,43 @@ private static boolean isIncluded(final String searchFor, final List<FoundJob> i
return false;
}

private static List<ParameterizedJob> getAllParameterizedJobs() {
private static List<ParameterizedJob> getAllParameterizedJobs(final String givenToken) {
final List<ParameterizedJob> candidateProjects =
Jenkins.getInstance().getAllItems(ParameterizedJob.class);
return candidateProjects;
jobFinderImpersonater.getAllParameterizedJobs();

final List<ParameterizedJob> candidateProjectsWithoutToken = new ArrayList<>();
for (final ParameterizedJob candidate : candidateProjects) {
if (authenticationTokenMatches(candidate, givenToken)) {
candidateProjectsWithoutToken.add(candidate);
}
}
return candidateProjectsWithoutToken;
}

@SuppressWarnings("deprecation")
private static boolean authenticationTokenMatches(
final ParameterizedJob candidateJob, final String givenToken) {
final hudson.model.BuildAuthorizationToken authToken = candidateJob.getAuthToken();

final boolean jobHasAuthToken = authToken != null && !isNullOrEmpty(authToken.getToken());
if (jobHasAuthToken && givenToken != null) {
final boolean jobHasAuthToken = jobHasAuthToken(authToken);
final boolean authTokenWasGiven = !isNullOrEmpty(givenToken);
if (jobHasAuthToken && authTokenWasGiven) {
final boolean authTokenMatchesQueryToken = authToken.getToken().equals(givenToken);
if (authTokenMatchesQueryToken) {
return true;
} else {
return false;
}
}
if (!jobHasAuthToken && !authTokenWasGiven) {
return true;
}
return false;
}

private static List<ParameterizedJob> getAllParameterizedJobsByImpersonation() {
// Impersinate to get all jobs even without read grants
final SecurityContext orig = ACL.impersonate(ACL.SYSTEM);
final List<ParameterizedJob> jobs = getAllParameterizedJobs();
// Return to previous authentication context
SecurityContextHolder.setContext(orig);
return jobs;
@SuppressWarnings("deprecation")
private static boolean jobHasAuthToken(final hudson.model.BuildAuthorizationToken authToken) {
return authToken != null && !isNullOrEmpty(authToken.getToken());
}

private static GenericTrigger findGenericTrigger(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.jenkinsci.plugins.gwt.jobfinder;

import java.util.List;

import org.acegisecurity.context.SecurityContext;
import org.acegisecurity.context.SecurityContextHolder;

import hudson.security.ACL;
import jenkins.model.Jenkins;
import jenkins.model.ParameterizedJobMixIn.ParameterizedJob;

public class JobFinderImpersonater {
public List<ParameterizedJob> getAllParameterizedJobs() {
return getAllParameterizedJobs(false);
}

public List<ParameterizedJob> getAllParameterizedJobsByImpersonation() {
return getAllParameterizedJobs(true);
}

private List<ParameterizedJob> getAllParameterizedJobs(final boolean impersonate) {
if (impersonate) {
// Impersinate to get all jobs even without read grants
final SecurityContext orig = ACL.impersonate(ACL.SYSTEM);
final List<ParameterizedJob> jobs = Jenkins.getInstance().getAllItems(ParameterizedJob.class);
// Return to previous authentication context
SecurityContextHolder.setContext(orig);
return jobs;
}
return Jenkins.getInstance().getAllItems(ParameterizedJob.class);
}
}
167 changes: 167 additions & 0 deletions src/test/java/org/jenkinsci/plugins/gwt/jobfinder/JobFinderTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
package org.jenkinsci.plugins.gwt.jobfinder;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

import org.jenkinsci.plugins.gwt.FoundJob;
import org.jenkinsci.plugins.gwt.GenericTrigger;
import org.junit.Before;
import org.junit.Test;

import hudson.model.BuildAuthorizationToken;
import hudson.triggers.Trigger;
import hudson.triggers.TriggerDescriptor;
import jenkins.model.ParameterizedJobMixIn.ParameterizedJob;

public class JobFinderTest {
private final AtomicInteger atomicInteger = new AtomicInteger(0);
private List<ParameterizedJob> allParameterizedJobs;
private List<ParameterizedJob> allParameterizedJobsByImpersonation;
private final ParameterizedJob job1WithNoToken = createJob("");
private final ParameterizedJob job2WithNoToken = createJob("");
private final ParameterizedJob job3WithTokenAbc = createJob("ABC");
private final ParameterizedJob job4WithTokenAbc = createJob("ABC");
private final ParameterizedJob job5WithTokenDef = createJob("DEF");
private final ParameterizedJob job6WithTokenDef = createJob("DEF");

@Before
public void before() {
allParameterizedJobs = new ArrayList<>();
allParameterizedJobsByImpersonation = new ArrayList<>();
final JobFinderImpersonater jobFinderImpersonater =
new JobFinderImpersonater() {
@Override
public List<ParameterizedJob> getAllParameterizedJobs() {
return allParameterizedJobs;
}

@Override
public List<ParameterizedJob> getAllParameterizedJobsByImpersonation() {
return allParameterizedJobsByImpersonation;
}
};
allParameterizedJobs.add(job1WithNoToken);
allParameterizedJobsByImpersonation.add(job2WithNoToken);
allParameterizedJobs.add(job3WithTokenAbc);
allParameterizedJobsByImpersonation.add(job4WithTokenAbc);
allParameterizedJobs.add(job5WithTokenDef);
allParameterizedJobsByImpersonation.add(job6WithTokenDef);

JobFinder.setJobFinderImpersonater(jobFinderImpersonater);
}

@SuppressWarnings("deprecation")
private ParameterizedJob createJob(final String token) {
final ParameterizedJob mock = mock(ParameterizedJob.class);
when(mock.getAuthToken()) //
.thenReturn(new BuildAuthorizationToken(token));
when(mock.getFullName()) //
.thenReturn("name-" + atomicInteger.incrementAndGet());
final Map<TriggerDescriptor, Trigger<?>> triggers = new HashMap<>();
final TriggerDescriptor typeDescr = mock(TriggerDescriptor.class);
final Trigger<?> genericTrigger = new GenericTrigger(null, null, null, null, null);
triggers.put(typeDescr, genericTrigger);
when(mock.getTriggers()) //
.thenReturn(triggers);

return mock;
}

private List<String> findAllJobs(final String givenToken) {
final List<FoundJob> foundJobs = JobFinder.findAllJobsWithTrigger(givenToken);
final List<String> names = new ArrayList<>();
for (final FoundJob found : foundJobs) {
names.add(found.getFullName());
}
Collections.sort(names);
return names;
}

@Test
public void testThatJobsWithoutTokenIsFoundWhenNoTokenSupplied() {
final String givenToken = "";

final List<String> actual = findAllJobs(givenToken);

assertThat(actual) //
.containsExactly(job1WithNoToken.getFullName(), job2WithNoToken.getFullName());
}

@Test
public void testThatJobsWithoutTokenIsNotFoundWhenTokenSupplied() {
final String givenToken = "some-token";

final List<String> actual = findAllJobs(givenToken);

assertThat(actual) //
.isEmpty();
}

@Test
public void testThatJobsWithTokenIsFoundWhenTokenSuppliedAndMatches() {
final String givenToken = "ABC";

final List<String> actual = findAllJobs(givenToken);

assertThat(actual) //
.containsExactly(job3WithTokenAbc.getFullName(), job4WithTokenAbc.getFullName());
}

@Test
public void testThatJobsWithTokenIsFoundWhenOtherTokenSuppliedAndMatches() {
final String givenToken = "DEF";

final List<String> actual = findAllJobs(givenToken);

assertThat(actual) //
.containsExactly(job5WithTokenDef.getFullName(), job6WithTokenDef.getFullName());
}

@Test
public void testThatJobsOnlyInImpersonateIsFound() {
allParameterizedJobs.clear();
allParameterizedJobsByImpersonation.clear();
allParameterizedJobsByImpersonation.add(job1WithNoToken);
final String givenToken = "";

final List<String> actual = findAllJobs(givenToken);

assertThat(actual) //
.containsExactly(job1WithNoToken.getFullName());
}

@Test
public void testThatJobsOnlyNotInImpersonateIsFound() {
allParameterizedJobs.clear();
allParameterizedJobs.add(job1WithNoToken);
allParameterizedJobsByImpersonation.clear();
final String givenToken = "";

final List<String> actual = findAllJobs(givenToken);

assertThat(actual) //
.containsExactly(job1WithNoToken.getFullName());
}

@Test
public void testThatJobsInBothImpersonateAndNotIsFound() {
allParameterizedJobs.clear();
allParameterizedJobs.add(job1WithNoToken);
allParameterizedJobsByImpersonation.clear();
allParameterizedJobsByImpersonation.add(job1WithNoToken);
final String givenToken = "";

final List<String> actual = findAllJobs(givenToken);

assertThat(actual) //
.containsExactly(job1WithNoToken.getFullName());
}
}

0 comments on commit 23afc4b

Please sign in to comment.