Skip to content

Commit

Permalink
Merge pull request #195 from mawinter69/JENKINS-68841
Browse files Browse the repository at this point in the history
[JENKINS-68841] fix node ownership permission problem
  • Loading branch information
mawinter69 authored Jun 27, 2022
2 parents 2af05b0 + 775fc3a commit 9098343
Show file tree
Hide file tree
Showing 8 changed files with 388 additions and 18 deletions.
28 changes: 23 additions & 5 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -83,20 +83,38 @@
<groupId>io.jenkins.plugins</groupId>
<artifactId>caffeine-api</artifactId>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>cloudbees-folder</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.jenkins</groupId>
<artifactId>configuration-as-code</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>cloudbees-folder</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.jenkins.configuration-as-code</groupId>
<artifactId>test-harness</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.synopsys.jenkinsci</groupId>
<artifactId>ownership</artifactId>
<version>0.13.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>authorize-project</artifactId>
<version>1.4.0</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Builder> {
@SuppressWarnings("rawtypes")
@Override
public boolean isApplicable(Class<? extends AbstractProject> jobType) {
return true;
}

@Override
public String getDisplayName() {
return "AuthorizationCheckBuilder";
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
* @author Oleg Nenashev
* @since 2.11
*/
public class RoleStrategyTest {
public class ConfigurationAsCodeTest {

@Rule
public JenkinsConfiguredWithCodeRule j = new JenkinsConfiguredWithCodeRule();
Expand Down
Original file line number Diff line number Diff line change
@@ -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));
}

}
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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);
}
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?xml version='1.1' encoding='UTF-8'?>
<hudson>
<disabledAdministrativeMonitors>
<string>jenkins.security.QueueItemAuthenticatorMonitor</string>
</disabledAdministrativeMonitors>
<version>2.303.3</version>
<numExecutors>2</numExecutors>
<mode>NORMAL</mode>
<useSecurity>true</useSecurity>
<authorizationStrategy class="com.michelin.cio.hudson.plugins.rolestrategy.RoleBasedAuthorizationStrategy">
<roleMap type="globalRoles">
<role name="admin" pattern=".*">
<permissions>
<permission>hudson.model.Hudson.Administer</permission>
</permissions>
<assignedSIDs>
<sid>admin</sid>
</assignedSIDs>
</role>
<role name="builder" pattern=".*">
<permissions>
<permission>hudson.model.Hudson.Read</permission>
<permission>hudson.model.Item.Read</permission>
<permission>hudson.model.Item.Build</permission>
</permissions>
<assignedSIDs>
<sid>authenticated</sid>
</assignedSIDs>
</role>
</roleMap>
<roleMap type="slaveRoles">
<role name="agentbuilder" pattern="TestAgent">
<permissions>
<permission>hudson.model.Computer.Build</permission>
</permissions>
<assignedSIDs>
<sid>tester</sid>
</assignedSIDs>
</role>
</roleMap>
</authorizationStrategy>
</hudson>
Loading

0 comments on commit 9098343

Please sign in to comment.