Skip to content

Commit 9098343

Browse files
authored
Merge pull request #195 from mawinter69/JENKINS-68841
[JENKINS-68841] fix node ownership permission problem
2 parents 2af05b0 + 775fc3a commit 9098343

File tree

8 files changed

+388
-18
lines changed

8 files changed

+388
-18
lines changed

pom.xml

+23-5
Original file line numberDiff line numberDiff line change
@@ -83,20 +83,38 @@
8383
<groupId>io.jenkins.plugins</groupId>
8484
<artifactId>caffeine-api</artifactId>
8585
</dependency>
86-
<dependency>
87-
<groupId>org.jenkins-ci.plugins</groupId>
88-
<artifactId>cloudbees-folder</artifactId>
89-
<scope>test</scope>
90-
</dependency>
9186
<dependency>
9287
<groupId>io.jenkins</groupId>
9388
<artifactId>configuration-as-code</artifactId>
9489
<optional>true</optional>
9590
</dependency>
91+
<dependency>
92+
<groupId>org.jenkins-ci.plugins</groupId>
93+
<artifactId>cloudbees-folder</artifactId>
94+
<scope>test</scope>
95+
</dependency>
9696
<dependency>
9797
<groupId>io.jenkins.configuration-as-code</groupId>
9898
<artifactId>test-harness</artifactId>
9999
<scope>test</scope>
100100
</dependency>
101+
<dependency>
102+
<groupId>com.synopsys.jenkinsci</groupId>
103+
<artifactId>ownership</artifactId>
104+
<version>0.13.0</version>
105+
<scope>test</scope>
106+
</dependency>
107+
<dependency>
108+
<groupId>org.apache.commons</groupId>
109+
<artifactId>commons-lang3</artifactId>
110+
<version>3.12.0</version>
111+
<scope>test</scope>
112+
</dependency>
113+
<dependency>
114+
<groupId>org.jenkins-ci.plugins</groupId>
115+
<artifactId>authorize-project</artifactId>
116+
<version>1.4.0</version>
117+
<scope>test</scope>
118+
</dependency>
101119
</dependencies>
102120
</project>

src/main/java/com/michelin/cio/hudson/plugins/rolestrategy/RoleBasedAuthorizationStrategy.java

+7-2
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,6 @@
7171
import java.util.Map;
7272
import java.util.Set;
7373
import java.util.SortedMap;
74-
import java.util.logging.Level;
75-
import java.util.logging.Logger;
7674
import java.util.regex.Pattern;
7775
import java.util.regex.PatternSyntaxException;
7876

@@ -183,6 +181,13 @@ public ACL getACL(@NonNull AbstractItem project) {
183181
}
184182

185183

184+
@Override
185+
@NonNull
186+
public ACL getACL(@NonNull Computer computer) {
187+
return agentRoles.newMatchingRoleMap(computer.getName()).getACL(RoleType.Slave, computer)
188+
.newInheritingACL(getRootACL());
189+
}
190+
186191
@Override
187192
@NonNull
188193
public ACL getACL(@NonNull Node node) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
package org.jenkinsci.plugins.rolestrategy;
2+
3+
import static org.hamcrest.MatcherAssert.assertThat;
4+
import static org.hamcrest.Matchers.contains;
5+
import static org.hamcrest.Matchers.containsString;
6+
import static org.hamcrest.Matchers.instanceOf;
7+
import static org.hamcrest.Matchers.is;
8+
import static org.hamcrest.Matchers.not;
9+
import static org.hamcrest.Matchers.nullValue;
10+
11+
import java.io.IOException;
12+
import java.util.Collections;
13+
import java.util.concurrent.TimeUnit;
14+
15+
import org.jenkinsci.plugins.authorizeproject.AuthorizeProjectProperty;
16+
import org.jenkinsci.plugins.authorizeproject.ProjectQueueItemAuthenticator;
17+
import org.jenkinsci.plugins.authorizeproject.strategy.TriggeringUsersAuthorizationStrategy;
18+
import org.junit.Before;
19+
import org.junit.Rule;
20+
import org.junit.Test;
21+
import org.jvnet.hudson.test.JenkinsRule;
22+
import org.jvnet.hudson.test.JenkinsRule.DummySecurityRealm;
23+
import org.jvnet.hudson.test.recipes.LocalData;
24+
import org.springframework.security.core.Authentication;
25+
26+
import hudson.Launcher;
27+
import hudson.model.AbstractBuild;
28+
import hudson.model.AbstractProject;
29+
import hudson.model.BuildListener;
30+
import hudson.model.Cause;
31+
import hudson.model.FreeStyleBuild;
32+
import hudson.model.FreeStyleProject;
33+
import hudson.model.Slave;
34+
import hudson.model.User;
35+
import hudson.model.Queue.Item;
36+
import hudson.model.Queue.WaitingItem;
37+
import hudson.security.ACL;
38+
import hudson.security.ACLContext;
39+
import hudson.tasks.BuildStepDescriptor;
40+
import hudson.tasks.Builder;
41+
import jenkins.model.Jenkins;
42+
import jenkins.security.QueueItemAuthenticatorConfiguration;
43+
44+
public class AuthorizeProjectTest
45+
{
46+
47+
@Rule
48+
public JenkinsRule j = new JenkinsRule();
49+
50+
private Slave n;
51+
private FreeStyleProject p;
52+
private AuthorizationCheckBuilder checker;
53+
54+
@Before
55+
@LocalData
56+
public void setup() throws Exception {
57+
Authentication a = j.jenkins.getAuthentication2();
58+
QueueItemAuthenticatorConfiguration.get().getAuthenticators().add(new ProjectQueueItemAuthenticator(Collections.emptyMap()));
59+
n = j.createSlave("TestAgent", null, null);
60+
j.waitOnline(n);
61+
p = j.createFreeStyleProject();
62+
p.setAssignedNode(n);
63+
p.addProperty(new AuthorizeProjectProperty(new TriggeringUsersAuthorizationStrategy()));
64+
checker = new AuthorizationCheckBuilder();
65+
p.getBuildersList().add(checker);
66+
DummySecurityRealm sr = j.createDummySecurityRealm();
67+
j.jenkins.setSecurityRealm(sr);
68+
}
69+
70+
@Test
71+
@LocalData
72+
public void agentBuildPermissionsAllowsToBuildOnAgent() throws Exception {
73+
try (ACLContext c = ACL.as(User.getById("tester", true))) {
74+
p.scheduleBuild2(0, new Cause.UserIdCause());
75+
}
76+
j.waitUntilNoActivity();
77+
FreeStyleBuild b = p.getLastBuild();
78+
assertThat(b, is(not(nullValue())));
79+
j.assertBuildStatusSuccess(b);
80+
assertThat(checker.userName, is("tester"));
81+
}
82+
83+
@Test
84+
@LocalData
85+
public void missingAgentBuildPermissionsBlockBuild() throws Exception {
86+
try (ACLContext c = ACL.as(User.getById("reader", true))) {
87+
p.scheduleBuild2(0, new Cause.UserIdCause());
88+
}
89+
TimeUnit.SECONDS.sleep(15);
90+
Item qi = p.getQueueItem();
91+
assertThat(qi.getCauseOfBlockage().toString(), containsString("‘reader’ lacks permission to run on ‘TestAgent’"));
92+
}
93+
94+
public static class AuthorizationCheckBuilder extends Builder {
95+
96+
// "transient" is required for exclusion from serialization - see https://jenkins.io/redirect/class-filter/
97+
public transient String userName = null;
98+
99+
@Override
100+
public boolean prebuild(AbstractBuild<?, ?> build, BuildListener listener) {
101+
userName = null;
102+
return true;
103+
}
104+
105+
@Override
106+
public boolean perform(AbstractBuild<?, ?> build, Launcher launcher,
107+
BuildListener listener) throws InterruptedException, IOException {
108+
userName = Jenkins.getAuthentication2().getName();
109+
return true;
110+
}
111+
112+
public static class DescriptorImpl extends BuildStepDescriptor<Builder> {
113+
@SuppressWarnings("rawtypes")
114+
@Override
115+
public boolean isApplicable(Class<? extends AbstractProject> jobType) {
116+
return true;
117+
}
118+
119+
@Override
120+
public String getDisplayName() {
121+
return "AuthorizationCheckBuilder";
122+
}
123+
}
124+
}
125+
}

src/test/java/org/jenkinsci/plugins/rolestrategy/RoleStrategyTest.java src/test/java/org/jenkinsci/plugins/rolestrategy/ConfigurationAsCodeTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
* @author Oleg Nenashev
4141
* @since 2.11
4242
*/
43-
public class RoleStrategyTest {
43+
public class ConfigurationAsCodeTest {
4444

4545
@Rule
4646
public JenkinsConfiguredWithCodeRule j = new JenkinsConfiguredWithCodeRule();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package org.jenkinsci.plugins.rolestrategy;
2+
3+
import static org.hamcrest.MatcherAssert.assertThat;
4+
import static org.hamcrest.Matchers.is;
5+
import static org.hamcrest.Matchers.oneOf;
6+
7+
import java.net.URL;
8+
import java.util.Collections;
9+
10+
import org.junit.Rule;
11+
import org.junit.Test;
12+
import org.jvnet.hudson.test.JenkinsRule.WebClient;
13+
14+
import com.gargoylesoftware.htmlunit.HttpMethod;
15+
import com.gargoylesoftware.htmlunit.WebRequest;
16+
import com.gargoylesoftware.htmlunit.WebResponse;
17+
import com.gargoylesoftware.htmlunit.html.HtmlPage;
18+
import com.gargoylesoftware.htmlunit.util.NameValuePair;
19+
import com.synopsys.arc.jenkins.plugins.ownership.OwnershipDescription;
20+
import com.synopsys.arc.jenkins.plugins.ownership.nodes.OwnerNodeProperty;
21+
22+
import hudson.model.Node;
23+
import io.jenkins.plugins.casc.misc.ConfiguredWithCode;
24+
import io.jenkins.plugins.casc.misc.JenkinsConfiguredWithCodeRule;
25+
26+
public class OwnershipTest
27+
{
28+
@Rule
29+
public JenkinsConfiguredWithCodeRule j = new JenkinsConfiguredWithCodeRule();
30+
31+
@Test
32+
@ConfiguredWithCode("OwnershipTest.yml")
33+
public void currentUserIsPrimaryOwnerGrantsPermissions() throws Exception {
34+
Node n = j.createOnlineSlave();
35+
n.getNodeProperties().add(new OwnerNodeProperty(n, new OwnershipDescription(true, "nodePrimaryTester", null)));
36+
String nodeUrl = n.toComputer().getUrl();
37+
38+
WebClient wc = j.createWebClient();
39+
wc.withThrowExceptionOnFailingStatusCode(false);
40+
wc.login("nodePrimaryTester", "nodePrimaryTester");
41+
HtmlPage managePage = wc.withThrowExceptionOnFailingStatusCode(false).goTo(String.format("%sconfigure", nodeUrl));
42+
assertThat(managePage.getWebResponse().getStatusCode(), is(200));
43+
URL testUrl = wc.createCrumbedUrl(String.format("%sdisconnect", nodeUrl));
44+
WebRequest request = new WebRequest(testUrl, HttpMethod.POST);
45+
NameValuePair param = new NameValuePair("offlineMessage", "Disconnect for Test");
46+
request.setRequestParameters(Collections.singletonList(param));
47+
WebResponse response = wc.loadWebResponse(request);
48+
assertThat(response.getStatusCode(), is(200));
49+
50+
testUrl = wc.createCrumbedUrl(String.format("%slaunchSlaveAgent", nodeUrl));
51+
request = new WebRequest(testUrl, HttpMethod.POST);
52+
response = wc.loadWebResponse(request);
53+
assertThat(response.getStatusCode(), is(oneOf(200, 302)));
54+
55+
testUrl = wc.createCrumbedUrl(String.format("%sdoDelete", nodeUrl));
56+
request = new WebRequest(testUrl, HttpMethod.POST);
57+
response = wc.loadWebResponse(request);
58+
assertThat(response.getStatusCode(), is(200));
59+
}
60+
61+
@Test
62+
@ConfiguredWithCode("OwnershipTest.yml")
63+
public void currentUserIsSecondaryOwnerGrantsPermissions() throws Exception {
64+
Node n = j.createOnlineSlave();
65+
n.getNodeProperties().add(new OwnerNodeProperty(n, new OwnershipDescription(true, "nodePrimaryTester", Collections.singleton("nodeSecondaryTester"))));
66+
String nodeUrl = n.toComputer().getUrl();
67+
68+
WebClient wc = j.createWebClient();
69+
wc.withThrowExceptionOnFailingStatusCode(false);
70+
wc.login("nodeSecondaryTester", "nodeSecondaryTester");
71+
HtmlPage managePage = wc.withThrowExceptionOnFailingStatusCode(false).goTo(String.format("%sconfigure", nodeUrl));
72+
assertThat(managePage.getWebResponse().getStatusCode(), is(200));
73+
URL testUrl = wc.createCrumbedUrl(String.format("%sdisconnect", nodeUrl));
74+
WebRequest request = new WebRequest(testUrl, HttpMethod.POST);
75+
NameValuePair param = new NameValuePair("offlineMessage", "Disconnect for Test");
76+
request.setRequestParameters(Collections.singletonList(param));
77+
WebResponse response = wc.loadWebResponse(request);
78+
assertThat(response.getStatusCode(), is(200));
79+
80+
testUrl = wc.createCrumbedUrl(String.format("%slaunchSlaveAgent", nodeUrl));
81+
request = new WebRequest(testUrl, HttpMethod.POST);
82+
response = wc.loadWebResponse(request);
83+
assertThat(response.getStatusCode(), is(oneOf(200, 302)));
84+
85+
testUrl = wc.createCrumbedUrl(String.format("%sdoDelete", nodeUrl));
86+
request = new WebRequest(testUrl, HttpMethod.POST);
87+
response = wc.loadWebResponse(request);
88+
assertThat(response.getStatusCode(), is(200));
89+
}
90+
91+
}

src/test/java/org/jenkinsci/plugins/rolestrategy/RoleAssignmentTest.java

+4-10
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
package org.jenkinsci.plugins.rolestrategy;
22

33
import hudson.model.User;
4+
import hudson.security.ACL;
5+
import hudson.security.ACLContext;
46
import hudson.security.Permission;
57

6-
import org.acegisecurity.Authentication;
7-
import org.acegisecurity.context.SecurityContext;
8-
import org.acegisecurity.context.SecurityContextHolder;
98
import org.junit.Before;
109
import org.junit.Rule;
1110
import org.junit.Test;
@@ -25,14 +24,9 @@ public void initSecurityRealm() {
2524

2625
@LocalData
2726
@Test public void testRoleAssignment() {
28-
SecurityContext seccon = SecurityContextHolder.getContext();
29-
Authentication orig = seccon.getAuthentication();
30-
seccon.setAuthentication(User.getById("alice", true).impersonate());
31-
try {
27+
try (ACLContext c = ACL.as(User.getById("alice", true))) {
3228
assertTrue(j.jenkins.hasPermission(Permission.READ));
33-
} finally {
34-
seccon.setAuthentication(orig);
35-
}
29+
}
3630
}
3731

3832
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?xml version='1.1' encoding='UTF-8'?>
2+
<hudson>
3+
<disabledAdministrativeMonitors>
4+
<string>jenkins.security.QueueItemAuthenticatorMonitor</string>
5+
</disabledAdministrativeMonitors>
6+
<version>2.303.3</version>
7+
<numExecutors>2</numExecutors>
8+
<mode>NORMAL</mode>
9+
<useSecurity>true</useSecurity>
10+
<authorizationStrategy class="com.michelin.cio.hudson.plugins.rolestrategy.RoleBasedAuthorizationStrategy">
11+
<roleMap type="globalRoles">
12+
<role name="admin" pattern=".*">
13+
<permissions>
14+
<permission>hudson.model.Hudson.Administer</permission>
15+
</permissions>
16+
<assignedSIDs>
17+
<sid>admin</sid>
18+
</assignedSIDs>
19+
</role>
20+
<role name="builder" pattern=".*">
21+
<permissions>
22+
<permission>hudson.model.Hudson.Read</permission>
23+
<permission>hudson.model.Item.Read</permission>
24+
<permission>hudson.model.Item.Build</permission>
25+
</permissions>
26+
<assignedSIDs>
27+
<sid>authenticated</sid>
28+
</assignedSIDs>
29+
</role>
30+
</roleMap>
31+
<roleMap type="slaveRoles">
32+
<role name="agentbuilder" pattern="TestAgent">
33+
<permissions>
34+
<permission>hudson.model.Computer.Build</permission>
35+
</permissions>
36+
<assignedSIDs>
37+
<sid>tester</sid>
38+
</assignedSIDs>
39+
</role>
40+
</roleMap>
41+
</authorizationStrategy>
42+
</hudson>

0 commit comments

Comments
 (0)