diff --git a/pom.xml b/pom.xml
index e96dacdb..5b4c8f51 100644
--- a/pom.xml
+++ b/pom.xml
@@ -83,20 +83,38 @@
io.jenkins.plugins
caffeine-api
-
- org.jenkins-ci.plugins
- cloudbees-folder
- test
-
io.jenkins
configuration-as-code
true
+
+ org.jenkins-ci.plugins
+ cloudbees-folder
+ test
+
io.jenkins.configuration-as-code
test-harness
test
+
+ com.synopsys.jenkinsci
+ ownership
+ 0.13.0
+ test
+
+
+ org.apache.commons
+ commons-lang3
+ 3.12.0
+ test
+
+
+ org.jenkins-ci.plugins
+ authorize-project
+ 1.4.0
+ test
+
diff --git a/src/main/java/com/michelin/cio/hudson/plugins/rolestrategy/RoleBasedAuthorizationStrategy.java b/src/main/java/com/michelin/cio/hudson/plugins/rolestrategy/RoleBasedAuthorizationStrategy.java
index 67281aa5..dbb77ef9 100644
--- a/src/main/java/com/michelin/cio/hudson/plugins/rolestrategy/RoleBasedAuthorizationStrategy.java
+++ b/src/main/java/com/michelin/cio/hudson/plugins/rolestrategy/RoleBasedAuthorizationStrategy.java
@@ -71,8 +71,6 @@
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
-import java.util.logging.Level;
-import java.util.logging.Logger;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
@@ -183,6 +181,13 @@ public ACL getACL(@NonNull AbstractItem project) {
}
+ @Override
+ @NonNull
+ public ACL getACL(@NonNull Computer computer) {
+ return agentRoles.newMatchingRoleMap(computer.getName()).getACL(RoleType.Slave, computer)
+ .newInheritingACL(getRootACL());
+ }
+
@Override
@NonNull
public ACL getACL(@NonNull Node node) {
diff --git a/src/test/java/org/jenkinsci/plugins/rolestrategy/AuthorizeProjectTest.java b/src/test/java/org/jenkinsci/plugins/rolestrategy/AuthorizeProjectTest.java
new file mode 100644
index 00000000..537354a6
--- /dev/null
+++ b/src/test/java/org/jenkinsci/plugins/rolestrategy/AuthorizeProjectTest.java
@@ -0,0 +1,125 @@
+package org.jenkinsci.plugins.rolestrategy;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.nullValue;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.concurrent.TimeUnit;
+
+import org.jenkinsci.plugins.authorizeproject.AuthorizeProjectProperty;
+import org.jenkinsci.plugins.authorizeproject.ProjectQueueItemAuthenticator;
+import org.jenkinsci.plugins.authorizeproject.strategy.TriggeringUsersAuthorizationStrategy;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.jvnet.hudson.test.JenkinsRule;
+import org.jvnet.hudson.test.JenkinsRule.DummySecurityRealm;
+import org.jvnet.hudson.test.recipes.LocalData;
+import org.springframework.security.core.Authentication;
+
+import hudson.Launcher;
+import hudson.model.AbstractBuild;
+import hudson.model.AbstractProject;
+import hudson.model.BuildListener;
+import hudson.model.Cause;
+import hudson.model.FreeStyleBuild;
+import hudson.model.FreeStyleProject;
+import hudson.model.Slave;
+import hudson.model.User;
+import hudson.model.Queue.Item;
+import hudson.model.Queue.WaitingItem;
+import hudson.security.ACL;
+import hudson.security.ACLContext;
+import hudson.tasks.BuildStepDescriptor;
+import hudson.tasks.Builder;
+import jenkins.model.Jenkins;
+import jenkins.security.QueueItemAuthenticatorConfiguration;
+
+public class AuthorizeProjectTest
+{
+
+ @Rule
+ public JenkinsRule j = new JenkinsRule();
+
+ private Slave n;
+ private FreeStyleProject p;
+ private AuthorizationCheckBuilder checker;
+
+ @Before
+ @LocalData
+ public void setup() throws Exception {
+ Authentication a = j.jenkins.getAuthentication2();
+ QueueItemAuthenticatorConfiguration.get().getAuthenticators().add(new ProjectQueueItemAuthenticator(Collections.emptyMap()));
+ n = j.createSlave("TestAgent", null, null);
+ j.waitOnline(n);
+ p = j.createFreeStyleProject();
+ p.setAssignedNode(n);
+ p.addProperty(new AuthorizeProjectProperty(new TriggeringUsersAuthorizationStrategy()));
+ checker = new AuthorizationCheckBuilder();
+ p.getBuildersList().add(checker);
+ DummySecurityRealm sr = j.createDummySecurityRealm();
+ j.jenkins.setSecurityRealm(sr);
+ }
+
+ @Test
+ @LocalData
+ public void agentBuildPermissionsAllowsToBuildOnAgent() throws Exception {
+ try (ACLContext c = ACL.as(User.getById("tester", true))) {
+ p.scheduleBuild2(0, new Cause.UserIdCause());
+ }
+ j.waitUntilNoActivity();
+ FreeStyleBuild b = p.getLastBuild();
+ assertThat(b, is(not(nullValue())));
+ j.assertBuildStatusSuccess(b);
+ assertThat(checker.userName, is("tester"));
+ }
+
+ @Test
+ @LocalData
+ public void missingAgentBuildPermissionsBlockBuild() throws Exception {
+ try (ACLContext c = ACL.as(User.getById("reader", true))) {
+ p.scheduleBuild2(0, new Cause.UserIdCause());
+ }
+ TimeUnit.SECONDS.sleep(15);
+ Item qi = p.getQueueItem();
+ assertThat(qi.getCauseOfBlockage().toString(), containsString("‘reader’ lacks permission to run on ‘TestAgent’"));
+ }
+
+ public static class AuthorizationCheckBuilder extends Builder {
+
+ // "transient" is required for exclusion from serialization - see https://jenkins.io/redirect/class-filter/
+ public transient String userName = null;
+
+ @Override
+ public boolean prebuild(AbstractBuild, ?> build, BuildListener listener) {
+ userName = null;
+ return true;
+ }
+
+ @Override
+ public boolean perform(AbstractBuild, ?> build, Launcher launcher,
+ BuildListener listener) throws InterruptedException, IOException {
+ userName = Jenkins.getAuthentication2().getName();
+ return true;
+ }
+
+ public static class DescriptorImpl extends BuildStepDescriptor {
+ @SuppressWarnings("rawtypes")
+ @Override
+ public boolean isApplicable(Class extends AbstractProject> jobType) {
+ return true;
+ }
+
+ @Override
+ public String getDisplayName() {
+ return "AuthorizationCheckBuilder";
+ }
+ }
+ }
+}
diff --git a/src/test/java/org/jenkinsci/plugins/rolestrategy/RoleStrategyTest.java b/src/test/java/org/jenkinsci/plugins/rolestrategy/ConfigurationAsCodeTest.java
similarity index 99%
rename from src/test/java/org/jenkinsci/plugins/rolestrategy/RoleStrategyTest.java
rename to src/test/java/org/jenkinsci/plugins/rolestrategy/ConfigurationAsCodeTest.java
index adf00e56..771dc578 100644
--- a/src/test/java/org/jenkinsci/plugins/rolestrategy/RoleStrategyTest.java
+++ b/src/test/java/org/jenkinsci/plugins/rolestrategy/ConfigurationAsCodeTest.java
@@ -40,7 +40,7 @@
* @author Oleg Nenashev
* @since 2.11
*/
-public class RoleStrategyTest {
+public class ConfigurationAsCodeTest {
@Rule
public JenkinsConfiguredWithCodeRule j = new JenkinsConfiguredWithCodeRule();
diff --git a/src/test/java/org/jenkinsci/plugins/rolestrategy/OwnershipTest.java b/src/test/java/org/jenkinsci/plugins/rolestrategy/OwnershipTest.java
new file mode 100644
index 00000000..2da4d1ab
--- /dev/null
+++ b/src/test/java/org/jenkinsci/plugins/rolestrategy/OwnershipTest.java
@@ -0,0 +1,91 @@
+package org.jenkinsci.plugins.rolestrategy;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.oneOf;
+
+import java.net.URL;
+import java.util.Collections;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.jvnet.hudson.test.JenkinsRule.WebClient;
+
+import com.gargoylesoftware.htmlunit.HttpMethod;
+import com.gargoylesoftware.htmlunit.WebRequest;
+import com.gargoylesoftware.htmlunit.WebResponse;
+import com.gargoylesoftware.htmlunit.html.HtmlPage;
+import com.gargoylesoftware.htmlunit.util.NameValuePair;
+import com.synopsys.arc.jenkins.plugins.ownership.OwnershipDescription;
+import com.synopsys.arc.jenkins.plugins.ownership.nodes.OwnerNodeProperty;
+
+import hudson.model.Node;
+import io.jenkins.plugins.casc.misc.ConfiguredWithCode;
+import io.jenkins.plugins.casc.misc.JenkinsConfiguredWithCodeRule;
+
+public class OwnershipTest
+{
+ @Rule
+ public JenkinsConfiguredWithCodeRule j = new JenkinsConfiguredWithCodeRule();
+
+ @Test
+ @ConfiguredWithCode("OwnershipTest.yml")
+ public void currentUserIsPrimaryOwnerGrantsPermissions() throws Exception {
+ Node n = j.createOnlineSlave();
+ n.getNodeProperties().add(new OwnerNodeProperty(n, new OwnershipDescription(true, "nodePrimaryTester", null)));
+ String nodeUrl = n.toComputer().getUrl();
+
+ WebClient wc = j.createWebClient();
+ wc.withThrowExceptionOnFailingStatusCode(false);
+ wc.login("nodePrimaryTester", "nodePrimaryTester");
+ HtmlPage managePage = wc.withThrowExceptionOnFailingStatusCode(false).goTo(String.format("%sconfigure", nodeUrl));
+ assertThat(managePage.getWebResponse().getStatusCode(), is(200));
+ URL testUrl = wc.createCrumbedUrl(String.format("%sdisconnect", nodeUrl));
+ WebRequest request = new WebRequest(testUrl, HttpMethod.POST);
+ NameValuePair param = new NameValuePair("offlineMessage", "Disconnect for Test");
+ request.setRequestParameters(Collections.singletonList(param));
+ WebResponse response = wc.loadWebResponse(request);
+ assertThat(response.getStatusCode(), is(200));
+
+ testUrl = wc.createCrumbedUrl(String.format("%slaunchSlaveAgent", nodeUrl));
+ request = new WebRequest(testUrl, HttpMethod.POST);
+ response = wc.loadWebResponse(request);
+ assertThat(response.getStatusCode(), is(oneOf(200, 302)));
+
+ testUrl = wc.createCrumbedUrl(String.format("%sdoDelete", nodeUrl));
+ request = new WebRequest(testUrl, HttpMethod.POST);
+ response = wc.loadWebResponse(request);
+ assertThat(response.getStatusCode(), is(200));
+ }
+
+ @Test
+ @ConfiguredWithCode("OwnershipTest.yml")
+ public void currentUserIsSecondaryOwnerGrantsPermissions() throws Exception {
+ Node n = j.createOnlineSlave();
+ n.getNodeProperties().add(new OwnerNodeProperty(n, new OwnershipDescription(true, "nodePrimaryTester", Collections.singleton("nodeSecondaryTester"))));
+ String nodeUrl = n.toComputer().getUrl();
+
+ WebClient wc = j.createWebClient();
+ wc.withThrowExceptionOnFailingStatusCode(false);
+ wc.login("nodeSecondaryTester", "nodeSecondaryTester");
+ HtmlPage managePage = wc.withThrowExceptionOnFailingStatusCode(false).goTo(String.format("%sconfigure", nodeUrl));
+ assertThat(managePage.getWebResponse().getStatusCode(), is(200));
+ URL testUrl = wc.createCrumbedUrl(String.format("%sdisconnect", nodeUrl));
+ WebRequest request = new WebRequest(testUrl, HttpMethod.POST);
+ NameValuePair param = new NameValuePair("offlineMessage", "Disconnect for Test");
+ request.setRequestParameters(Collections.singletonList(param));
+ WebResponse response = wc.loadWebResponse(request);
+ assertThat(response.getStatusCode(), is(200));
+
+ testUrl = wc.createCrumbedUrl(String.format("%slaunchSlaveAgent", nodeUrl));
+ request = new WebRequest(testUrl, HttpMethod.POST);
+ response = wc.loadWebResponse(request);
+ assertThat(response.getStatusCode(), is(oneOf(200, 302)));
+
+ testUrl = wc.createCrumbedUrl(String.format("%sdoDelete", nodeUrl));
+ request = new WebRequest(testUrl, HttpMethod.POST);
+ response = wc.loadWebResponse(request);
+ assertThat(response.getStatusCode(), is(200));
+ }
+
+}
diff --git a/src/test/java/org/jenkinsci/plugins/rolestrategy/RoleAssignmentTest.java b/src/test/java/org/jenkinsci/plugins/rolestrategy/RoleAssignmentTest.java
index 24072c74..769ceb10 100644
--- a/src/test/java/org/jenkinsci/plugins/rolestrategy/RoleAssignmentTest.java
+++ b/src/test/java/org/jenkinsci/plugins/rolestrategy/RoleAssignmentTest.java
@@ -1,11 +1,10 @@
package org.jenkinsci.plugins.rolestrategy;
import hudson.model.User;
+import hudson.security.ACL;
+import hudson.security.ACLContext;
import hudson.security.Permission;
-import org.acegisecurity.Authentication;
-import org.acegisecurity.context.SecurityContext;
-import org.acegisecurity.context.SecurityContextHolder;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -25,14 +24,9 @@ public void initSecurityRealm() {
@LocalData
@Test public void testRoleAssignment() {
- SecurityContext seccon = SecurityContextHolder.getContext();
- Authentication orig = seccon.getAuthentication();
- seccon.setAuthentication(User.getById("alice", true).impersonate());
- try {
+ try (ACLContext c = ACL.as(User.getById("alice", true))) {
assertTrue(j.jenkins.hasPermission(Permission.READ));
- } finally {
- seccon.setAuthentication(orig);
- }
+ }
}
}
diff --git a/src/test/resources/org/jenkinsci/plugins/rolestrategy/AuthorizeProjectTest/config.xml b/src/test/resources/org/jenkinsci/plugins/rolestrategy/AuthorizeProjectTest/config.xml
new file mode 100644
index 00000000..f070fca4
--- /dev/null
+++ b/src/test/resources/org/jenkinsci/plugins/rolestrategy/AuthorizeProjectTest/config.xml
@@ -0,0 +1,42 @@
+
+
+
+ jenkins.security.QueueItemAuthenticatorMonitor
+
+ 2.303.3
+ 2
+ NORMAL
+ true
+
+
+
+
+ hudson.model.Hudson.Administer
+
+
+ admin
+
+
+
+
+ hudson.model.Hudson.Read
+ hudson.model.Item.Read
+ hudson.model.Item.Build
+
+
+ authenticated
+
+
+
+
+
+
+ hudson.model.Computer.Build
+
+
+ tester
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/resources/org/jenkinsci/plugins/rolestrategy/OwnershipTest.yml b/src/test/resources/org/jenkinsci/plugins/rolestrategy/OwnershipTest.yml
new file mode 100644
index 00000000..4d3e3c49
--- /dev/null
+++ b/src/test/resources/org/jenkinsci/plugins/rolestrategy/OwnershipTest.yml
@@ -0,0 +1,95 @@
+jenkins:
+ authorizationStrategy:
+ roleBased:
+ roles:
+ agents:
+ - assignments:
+ - "authenticated"
+ name: "@CurrentUserIsPrimaryOwner"
+ pattern: ".*"
+ description: "User is Primary Owner"
+ permissions:
+ - "Manage ownership/Nodes"
+ - "Agent/Provision"
+ - "Agent/Configure"
+ - "Agent/ExtendedRead"
+ - "Agent/Delete"
+ - "Agent/Connect"
+ - "Agent/Build"
+ - "Agent/Disconnect"
+ - assignments:
+ - "authenticated"
+ name: "@CurrentUserIsOwner"
+ pattern: ".*"
+ description: "User is Owner"
+ permissions:
+ - "Manage ownership/Nodes"
+ - "Agent/Provision"
+ - "Agent/Configure"
+ - "Agent/ExtendedRead"
+ - "Agent/Delete"
+ - "Agent/Connect"
+ - "Agent/Build"
+ - "Agent/Disconnect"
+ global:
+ - assignments:
+ - "admin"
+ name: "admin"
+ pattern: ".*"
+ description: "Admin Users"
+ permissions:
+ - "Overall/Administer"
+ - assignments:
+ - "authenticated"
+ name: "reader"
+ pattern: ".*"
+ permissions:
+ - "Overall/Read"
+ items:
+ - assignments:
+ - "authenticated"
+ name: "@CurrentUserIsOwner"
+ pattern: ".*"
+ description: "User is Owner"
+ permissions:
+ - "Job/Move"
+ - "Job/Build"
+ - "Job/Create"
+ - "Job/Discover"
+ - "Job/Read"
+ - "Job/Cancel"
+ - "Job/ExtendedRead"
+ - "Job/Delete"
+ - "Job/Configure"
+ - "Job/Workspace"
+ - "Job/ViewStatus" # System for test
+ - assignments:
+ - "authenticated"
+ name: "@CurrentUserIsPrimaryOwner"
+ pattern: ".*"
+ description: "User is Primary Owner"
+ permissions:
+ - "Job/Move"
+ - "Job/Build"
+ - "Job/Create"
+ - "Job/Discover"
+ - "Manage ownership/Jobs"
+ - "Job/Read"
+ - "Job/Cancel"
+ - "Job/ExtendedRead"
+ - "Job/Delete"
+ - "Job/Configure"
+ - "Job/Workspace"
+ - "Job/ViewStatus" # System for test
+
+ securityRealm:
+ local:
+ allowsSignup: false
+ users:
+ - id: "admin"
+ password: "1234"
+ - id: "nodePrimaryTester"
+ password: "nodePrimaryTester"
+ - id: "nodeSecondaryTester"
+ password: "nodeSecondaryTester"
+