|
4 | 4 | import com.gargoylesoftware.htmlunit.Page;
|
5 | 5 | import com.gargoylesoftware.htmlunit.WebRequest;
|
6 | 6 | import com.gargoylesoftware.htmlunit.WebResponse;
|
| 7 | +import com.google.common.collect.Lists; |
7 | 8 | import hudson.Functions;
|
| 9 | +import hudson.Launcher; |
| 10 | +import hudson.model.Item; |
| 11 | +import hudson.model.User; |
8 | 12 | import hudson.remoting.Channel;
|
9 | 13 | import hudson.remoting.ChannelBuilder;
|
| 14 | +import hudson.util.StreamTaskListener; |
| 15 | +import java.io.File; |
| 16 | +import java.io.IOException; |
10 | 17 | import java.net.HttpURLConnection;
|
11 | 18 | import java.net.URL;
|
12 | 19 | import java.util.ArrayList;
|
| 20 | +import java.util.Arrays; |
13 | 21 | import java.util.List;
|
14 | 22 | import java.util.UUID;
|
15 | 23 | import java.util.concurrent.ExecutorService;
|
16 | 24 | import java.util.concurrent.Executors;
|
| 25 | +import jenkins.model.Jenkins; |
| 26 | +import jenkins.security.ApiTokenProperty; |
| 27 | +import org.apache.commons.io.FileUtils; |
17 | 28 | import org.codehaus.groovy.runtime.Security218;
|
18 | 29 | import static org.hamcrest.Matchers.containsString;
|
19 | 30 | import static org.junit.Assert.*;
|
20 | 31 | import org.junit.Rule;
|
21 | 32 | import org.junit.Test;
|
| 33 | +import org.junit.rules.TemporaryFolder; |
22 | 34 | import org.jvnet.hudson.test.Issue;
|
23 | 35 | import org.jvnet.hudson.test.JenkinsRule;
|
| 36 | +import org.jvnet.hudson.test.MockAuthorizationStrategy; |
24 | 37 | import org.jvnet.hudson.test.recipes.PresetData;
|
25 | 38 | import org.jvnet.hudson.test.recipes.PresetData.DataSet;
|
26 | 39 |
|
27 | 40 | public class CLIActionTest {
|
28 | 41 | @Rule
|
29 | 42 | public JenkinsRule j = new JenkinsRule();
|
30 | 43 |
|
| 44 | + @Rule |
| 45 | + public TemporaryFolder tmp = new TemporaryFolder(); |
| 46 | + |
31 | 47 | private ExecutorService pool;
|
32 | 48 |
|
33 | 49 | /**
|
@@ -102,4 +118,76 @@ public void serveCliActionToAnonymousUserWithAnonymousUserWithPermissions() thro
|
102 | 118 | wc.goTo("cli");
|
103 | 119 | }
|
104 | 120 |
|
| 121 | + @Issue({"JENKINS-12543", "JENKINS-41745"}) |
| 122 | + @Test |
| 123 | + public void authentication() throws Exception { |
| 124 | + File jar = tmp.newFile("jenkins-cli.jar"); |
| 125 | + FileUtils.copyURLToFile(j.jenkins.getJnlpJars("jenkins-cli.jar").getURL(), jar); |
| 126 | + j.jenkins.setSecurityRealm(j.createDummySecurityRealm()); |
| 127 | + j.jenkins.setAuthorizationStrategy(new MockAuthorizationStrategy().grant(Jenkins.ADMINISTER).everywhere().to("admin")); |
| 128 | + j.createFreeStyleProject("p"); |
| 129 | + // CLICommand with @Argument: |
| 130 | + assertExitCode(3, false, jar, "-remoting", "get-job", "p"); // IllegalArgumentException from GenericItemOptionHandler |
| 131 | + assertExitCode(3, false, jar, "get-job", "p"); // ditto under new protocol |
| 132 | + assertExitCode(3, false, jar, "-remoting", "get-job", "--username", "admin", "--password", "admin", "p"); // JENKINS-12543: too late |
| 133 | + assertExitCode(3, false, jar, "get-job", "--username", "admin", "--password", "admin", "p"); // same |
| 134 | + assertExitCode(0, false, jar, "-remoting", "login", "--username", "admin", "--password", "admin"); |
| 135 | + try { |
| 136 | + assertExitCode(3, false, jar, "-remoting", "get-job", "p"); // ClientAuthenticationCache also used too late |
| 137 | + } finally { |
| 138 | + assertExitCode(0, false, jar, "-remoting", "logout"); |
| 139 | + } |
| 140 | + assertExitCode(3, true, jar, "-remoting", "get-job", "p"); // does not work with API tokens |
| 141 | + assertExitCode(0, true, jar, "get-job", "p"); // but does under new protocol |
| 142 | + // @CLIMethod: |
| 143 | + assertExitCode(6, false, jar, "-remoting", "disable-job", "p"); // AccessDeniedException from CLIRegisterer? |
| 144 | + assertExitCode(6, false, jar, "disable-job", "p"); |
| 145 | + assertExitCode(0, false, jar, "-remoting", "disable-job", "--username", "admin", "--password", "admin", "p"); // works from CliAuthenticator |
| 146 | + assertExitCode(0, false, jar, "disable-job", "--username", "admin", "--password", "admin", "p"); |
| 147 | + assertExitCode(0, false, jar, "-remoting", "login", "--username", "admin", "--password", "admin"); |
| 148 | + try { |
| 149 | + assertExitCode(0, false, jar, "-remoting", "disable-job", "p"); // or from ClientAuthenticationCache |
| 150 | + } finally { |
| 151 | + assertExitCode(0, false, jar, "-remoting", "logout"); |
| 152 | + } |
| 153 | + assertExitCode(6, true, jar, "-remoting", "disable-job", "p"); |
| 154 | + assertExitCode(0, true, jar, "disable-job", "p"); |
| 155 | + // If we have anonymous read access, then the situation is simpler. |
| 156 | + j.jenkins.setAuthorizationStrategy(new MockAuthorizationStrategy().grant(Jenkins.ADMINISTER).everywhere().to("admin").grant(Jenkins.READ, Item.READ).everywhere().toEveryone()); |
| 157 | + assertExitCode(6, false, jar, "-remoting", "get-job", "p"); // AccessDeniedException from AbstractItem.writeConfigDotXml |
| 158 | + assertExitCode(6, false, jar, "get-job", "p"); |
| 159 | + assertExitCode(0, false, jar, "-remoting", "get-job", "--username", "admin", "--password", "admin", "p"); |
| 160 | + assertExitCode(0, false, jar, "get-job", "--username", "admin", "--password", "admin", "p"); |
| 161 | + assertExitCode(0, false, jar, "-remoting", "login", "--username", "admin", "--password", "admin"); |
| 162 | + try { |
| 163 | + assertExitCode(0, false, jar, "-remoting", "get-job", "p"); |
| 164 | + } finally { |
| 165 | + assertExitCode(0, false, jar, "-remoting", "logout"); |
| 166 | + } |
| 167 | + assertExitCode(6, true, jar, "-remoting", "get-job", "p"); // does not work with API tokens |
| 168 | + assertExitCode(0, true, jar, "get-job", "p"); // but does under new protocol |
| 169 | + assertExitCode(6, false, jar, "-remoting", "disable-job", "p"); // AccessDeniedException from AbstractProject.doDisable |
| 170 | + assertExitCode(6, false, jar, "disable-job", "p"); |
| 171 | + assertExitCode(0, false, jar, "-remoting", "disable-job", "--username", "admin", "--password", "admin", "p"); |
| 172 | + assertExitCode(0, false, jar, "disable-job", "--username", "admin", "--password", "admin", "p"); |
| 173 | + assertExitCode(0, false, jar, "-remoting", "login", "--username", "admin", "--password", "admin"); |
| 174 | + try { |
| 175 | + assertExitCode(0, false, jar, "-remoting", "disable-job", "p"); |
| 176 | + } finally { |
| 177 | + assertExitCode(0, false, jar, "-remoting", "logout"); |
| 178 | + } |
| 179 | + assertExitCode(6, true, jar, "-remoting", "disable-job", "p"); |
| 180 | + assertExitCode(0, true, jar, "disable-job", "p"); |
| 181 | + } |
| 182 | + |
| 183 | + private void assertExitCode(int code, boolean useApiToken, File jar, String... args) throws IOException, InterruptedException { |
| 184 | + String url = j.getURL().toString(); |
| 185 | + if (useApiToken) { |
| 186 | + url = url.replace("://localhost:", "://admin:" + User.get("admin").getProperty(ApiTokenProperty.class).getApiToken() + "@localhost:"); |
| 187 | + } |
| 188 | + List<String> commands = Lists.newArrayList("java", "-jar", jar.getAbsolutePath(), "-s", url, /* not covering SSH keys in this test */ "-noKeyAuth"); |
| 189 | + commands.addAll(Arrays.asList(args)); |
| 190 | + assertEquals(code, new Launcher.LocalLauncher(StreamTaskListener.fromStderr()).launch().cmds(commands).stdout(System.out).stderr(System.err).join()); |
| 191 | + } |
| 192 | + |
105 | 193 | }
|
0 commit comments