mirror of
https://github.com/jenkinsci/jenkins.git
synced 2026-11-06 08:36:01 +00:00
[JENKINS-53792] Env vars to configure CLI -auth option (#3653)
* [JENKINS-53792] Add env vars for auth option * [JENKINS-53792] Fix encoding after first commit * [JENKINS-53792] Better readability * [JENKINS-53792] Refactor and use the ApiToken * [JENKINS-53792] Fix encoding
This commit is contained in:
1 parent
e0ed9a86e0
commit
e998a7808f
7 files changed
+292
-9
No files matched your search
@@ -81,6 +81,10 @@
|
||||
<artifactId>annotations</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-lang</groupId>
|
||||
<artifactId>commons-lang</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -77,6 +77,7 @@ import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import static java.util.logging.Level.*;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
/**
|
||||
* CLI entry point to Jenkins.
|
||||
@@ -452,6 +453,10 @@ public class CLI implements AutoCloseable {
|
||||
|
||||
String user = null;
|
||||
String auth = null;
|
||||
|
||||
String userIdEnv = System.getenv("JENKINS_USER_ID");
|
||||
String tokenEnv = System.getenv("JENKINS_API_TOKEN");
|
||||
|
||||
boolean strictHostKey = false;
|
||||
|
||||
while(!args.isEmpty()) {
|
||||
@@ -563,6 +568,17 @@ public class CLI implements AutoCloseable {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (auth == null) {
|
||||
// -auth option not set
|
||||
if (StringUtils.isNotBlank(userIdEnv) && StringUtils.isNotBlank(tokenEnv)) {
|
||||
auth = StringUtils.defaultString(userIdEnv).concat(":").concat(StringUtils.defaultString(tokenEnv));
|
||||
} else if (StringUtils.isNotBlank(userIdEnv) || StringUtils.isNotBlank(tokenEnv)) {
|
||||
printUsage(Messages.CLI_BadAuth());
|
||||
return -1;
|
||||
} // Otherwise, none credentials were set
|
||||
|
||||
}
|
||||
|
||||
if (!url.endsWith("/")) {
|
||||
url += '/';
|
||||
}
|
||||
|
||||
@@ -13,10 +13,12 @@ CLI.Usage=Jenkins CLI\n\
|
||||
-strictHostKey : request strict host key checking (for use with -ssh)\n\
|
||||
-logger FINE : enable detailed logging from the client\n\
|
||||
-auth [ USER:SECRET | @FILE ] : specify username and either password or API token (or load from them both from a file);\n\
|
||||
for use with -http, or -remoting but only when the JNLP agent port is disabled\n\
|
||||
for use with -http, or -remoting but only when the JNLP agent port is disabled.\n\
|
||||
Not necessary if JENKINS_USER_ID and JENKINS_API_TOKEN env vars are set\n\
|
||||
\n\
|
||||
The available commands depend on the server. Run the 'help' command to\n\
|
||||
see the list.
|
||||
CLI.NoURL=Neither -s nor the JENKINS_URL env var is specified.
|
||||
CLI.VersionMismatch=Version mismatch. This CLI cannot work with this Jenkins server.
|
||||
CLI.NoSuchFileExists=No such file exists: {0}
|
||||
CLI.BadAuth=The JENKINS_USER_ID and JENKINS_API_TOKEN env vars should be both set or left empty.
|
||||
@@ -1,10 +1,24 @@
|
||||
CLI.Usage=Jenkins CLI\n\
|
||||
Usar: java -jar jenkins-cli.jar [-s URL] command [opts...] args...\n\
|
||||
Options:\n\
|
||||
\ -s URL : dirección web (por defecto se usa la variable JENKINS_URL)\n\
|
||||
Uso: java -jar jenkins-cli.jar [-s URL] command [opts...] args...\n\
|
||||
Opciones:\n\
|
||||
-s URL : direcci\u00f3n web (por defecto se usa la variable JENKINS_URL)\n\
|
||||
-http : usa un protocolo plano sobre HTTP(S) (es el uso por defecto; excluyente con -ssh y -remoting)\n\
|
||||
-ssh : usa el protocolo SSH (requiere -user; el puerto SSH debe estar abierto en el servidor y el usuario debe tener registrada su clave publica)\n\
|
||||
-remoting : usa el protocolo deprecado de Remoting (siempre que est\u00e9 habilitado en el servidor; s\u00f3lo para compatibilidad con comandos heredados o legacy)\n\
|
||||
-i KEY : clave privada SSH usada para autenticaci\u00f3n (usado con -ssh o -remoting)\n\
|
||||
-p HOST:PORT : host y puerto para el uso de proxy HTTPS. Ver https://jenkins.io/redirect/cli-https-proxy-tunnel\n\
|
||||
-noCertificateCheck : elude por completo la verificaci\u00f3n del certificado HTTPS. Usar con precauci\u00f3n\n\
|
||||
-noKeyAuth : intenta no cargar la clave privada de autenticaci\u00f3n SSH. Presenta conflicto con -i\n\
|
||||
-user : especifica el usuario (se usa con -ssh)\n\
|
||||
-strictHostKey : solicita la comprobaci\u00f3n estricta de la clave del host (se usa con -ssh)\n\
|
||||
-logger FINE : habilita logs detallados en el cliente\n\
|
||||
-auth [ USER:SECRET | @FILE ] : especifica el usuario y contrase\u00f1a o API token (o los carga de un fichero);\n\
|
||||
se usa con -http o -remoting pero s\u00f3lo si el puerto del agente JNLP est\u00e1 deshabilitado.\n\
|
||||
No es necesario si las variables de entorno JENKINS_USER_ID y JENKINS_API_TOKEN se encuentran configuradas.\n\
|
||||
\n\
|
||||
La lista completa de comandos disponibles depende del servidor. Ejecuta\n\
|
||||
el comando ''help'' para ver la lista.
|
||||
CLI.NoURL=No se ha especificado el parámetro -s ni la variable JENKINS_URL
|
||||
CLI.VersionMismatch=La versión no coincide. Esta consola "CLI" no se puede usar en este Jenkins
|
||||
La lista de comandos disponibles depende del servidor. Ejecuta\n\
|
||||
el comando ''help'' para ver la lista completa.
|
||||
CLI.NoURL=No se ha especificado el par\u00e1metro -s ni la variable JENKINS_URL
|
||||
CLI.VersionMismatch=La versi\u00f3n no coincide. Esta consola "CLI" no se puede usar en este Jenkins
|
||||
CLI.NoSuchFileExists=El fichero {0} no existe.
|
||||
CLI.BadAuth=Ambas variables de entorno JENKINS_USER_ID y JENKINS_API_TOKEN deber\u00edan estar configuradas o mantenerse vac\u00edas.
|
||||
@@ -279,7 +279,6 @@ THE SOFTWARE.
|
||||
<dependency><!-- hudson doesn't use this directly, but some plugins wanted to use the latest -->
|
||||
<groupId>commons-lang</groupId>
|
||||
<artifactId>commons-lang</artifactId>
|
||||
<version>2.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-digester</groupId>
|
||||
|
||||
@@ -148,6 +148,12 @@ THE SOFTWARE.
|
||||
<version>2.4</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-lang</groupId>
|
||||
<artifactId>commons-lang</artifactId>
|
||||
<version>2.6</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
|
||||
@@ -0,0 +1,242 @@
|
||||
package hudson.cli;
|
||||
|
||||
import hudson.EnvVars;
|
||||
import hudson.Launcher;
|
||||
import hudson.model.User;
|
||||
import hudson.util.StreamTaskListener;
|
||||
import jenkins.model.Jenkins;
|
||||
import jenkins.security.ApiTokenProperty;
|
||||
import jenkins.security.apitoken.ApiTokenTestHelper;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.jvnet.hudson.test.JenkinsRule;
|
||||
import org.jvnet.hudson.test.MockAuthorizationStrategy;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class CLIEnvVarTest {
|
||||
|
||||
@Rule
|
||||
public JenkinsRule r = new JenkinsRule();
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder tmp = new TemporaryFolder();
|
||||
|
||||
private File home;
|
||||
private File jar;
|
||||
|
||||
@Before
|
||||
public void grabCliJar() throws IOException {
|
||||
home = tmp.newFolder();
|
||||
jar = tmp.newFile("jenkins-cli.jar");
|
||||
FileUtils.copyURLToFile(r.jenkins.getJnlpJars("jenkins-cli.jar").getURL(), jar);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSOptionWithoutJENKINS_URL() throws Exception {
|
||||
assertEquals(0, launch("java",
|
||||
"-Duser.home=" + home,
|
||||
"-jar", jar.getAbsolutePath(),
|
||||
"-s", r.getURL().toString(),
|
||||
"who-am-i")
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithoutSOptionAndWithoutJENKINS_URL() throws Exception {
|
||||
assertNotEquals(0, launch("java",
|
||||
"-Duser.home=" + home,
|
||||
"-jar", jar.getAbsolutePath(),
|
||||
"who-am-i")
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJENKINS_URLWithoutSOption() throws Exception {
|
||||
// Valid URL
|
||||
Map<String, String> envars = new HashMap<>();
|
||||
envars.put("JENKINS_URL", r.getURL().toString());
|
||||
assertEquals(0, launch(envars,
|
||||
"java",
|
||||
"-Duser.home=" + home,
|
||||
"-jar", jar.getAbsolutePath(),
|
||||
"who-am-i")
|
||||
);
|
||||
|
||||
// Invalid URL
|
||||
envars = new HashMap<>();
|
||||
envars.put("JENKINS_URL", "http://invalid-url");
|
||||
assertNotEquals(0, launch(envars,
|
||||
"java",
|
||||
"-Duser.home=" + home,
|
||||
"-jar", jar.getAbsolutePath(),
|
||||
"who-am-i")
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSOptionOverridesJENKINS_URL() throws Exception {
|
||||
Map<String, String> envars = new HashMap<>();
|
||||
envars.put("JENKINS_URL", "http://invalid-url");
|
||||
assertEquals(0, launch(envars,
|
||||
"java",
|
||||
"-Duser.home=" + home,
|
||||
"-jar", jar.getAbsolutePath(),
|
||||
"-s", r.getURL().toString(),
|
||||
"who-am-i")
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAuthOptionWithoutEnvVars() throws Exception {
|
||||
String token = getToken();
|
||||
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
|
||||
assertEquals(0, launch(Collections.emptyMap(), baos, null,
|
||||
"java",
|
||||
"-Duser.home=" + home,
|
||||
"-jar", jar.getAbsolutePath(),
|
||||
"-s", r.getURL().toString(),
|
||||
"-auth", String.format("%s:%s", "admin", token),
|
||||
"who-am-i")
|
||||
);
|
||||
assertThat(baos.toString(), containsString("Authenticated as: admin"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithoutEnvVarsAndWithoutAuthOption() throws Exception {
|
||||
r.jenkins.setSecurityRealm(r.createDummySecurityRealm());
|
||||
r.jenkins.setAuthorizationStrategy(new MockAuthorizationStrategy().grant(Jenkins.ADMINISTER).everywhere().to("admin"));
|
||||
|
||||
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
|
||||
assertEquals(0, launch(Collections.emptyMap(), baos, null,
|
||||
"java",
|
||||
"-Duser.home=" + home,
|
||||
"-jar", jar.getAbsolutePath(),
|
||||
"-s", r.getURL().toString(),
|
||||
"who-am-i")
|
||||
);
|
||||
assertThat(baos.toString(), containsString("Authenticated as: anonymous"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEnvVarsWithoutAuthOption() throws Exception {
|
||||
String token = getToken();
|
||||
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
|
||||
Map<String, String> envars = new HashMap<>();
|
||||
envars.put("JENKINS_USER_ID", "admin");
|
||||
envars.put("JENKINS_API_TOKEN", token);
|
||||
assertEquals(0, launch(envars, baos, null,
|
||||
"java",
|
||||
"-Duser.home=" + home,
|
||||
"-jar", jar.getAbsolutePath(),
|
||||
"-s", r.getURL().toString(),
|
||||
"who-am-i")
|
||||
);
|
||||
assertThat(baos.toString(), containsString("Authenticated as: admin"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnlyOneEnvVar() throws Exception {
|
||||
String token = getToken();
|
||||
|
||||
// only JENKINS_USER_ID
|
||||
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
|
||||
Map<String, String> envars = new HashMap<>();
|
||||
envars.put("JENKINS_USER_ID", "admin");
|
||||
assertNotEquals(0, launch(envars,
|
||||
"java",
|
||||
"-Duser.home=" + home,
|
||||
"-jar", jar.getAbsolutePath(),
|
||||
"-s", r.getURL().toString(),
|
||||
"who-am-i")
|
||||
);
|
||||
}
|
||||
|
||||
// only JENKINS_API_TOKEN
|
||||
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
|
||||
Map<String, String> envars = new HashMap<>();
|
||||
envars.put("JENKINS_API_TOKEN", token);
|
||||
assertNotEquals(0, launch(envars,
|
||||
"java",
|
||||
"-Duser.home=" + home,
|
||||
"-jar", jar.getAbsolutePath(),
|
||||
"-s", r.getURL().toString(),
|
||||
"who-am-i")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAuthOptionOverridesEnvVars() throws Exception {
|
||||
String token = getToken();
|
||||
|
||||
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
|
||||
Map<String, String> envars = new HashMap<>();
|
||||
envars.put("JENKINS_USER_ID", "other-user");
|
||||
envars.put("JENKINS_API_TOKEN", "other-user");
|
||||
assertEquals(0, launch(envars, baos, null,
|
||||
"java",
|
||||
"-Duser.home=" + home,
|
||||
"-jar", jar.getAbsolutePath(),
|
||||
"-s", r.getURL().toString(),
|
||||
"-auth", String.format("%s:%s", "admin", token),
|
||||
"who-am-i")
|
||||
);
|
||||
assertThat(baos.toString(), containsString("Authenticated as: admin"));
|
||||
}
|
||||
}
|
||||
|
||||
private String getToken() {
|
||||
ApiTokenTestHelper.enableLegacyBehavior();
|
||||
|
||||
r.jenkins.setSecurityRealm(r.createDummySecurityRealm());
|
||||
r.jenkins.setAuthorizationStrategy(new MockAuthorizationStrategy().grant(Jenkins.ADMINISTER).everywhere().to("admin"));
|
||||
|
||||
return User.get("admin").getProperty(ApiTokenProperty.class).getApiToken();
|
||||
}
|
||||
|
||||
private int launch(String... cmdArgs) throws Exception {
|
||||
return launch(Collections.emptyMap(), cmdArgs);
|
||||
}
|
||||
|
||||
private int launch(Map<String, String> envars, String... cmdArgs) throws Exception {
|
||||
return launch(envars, null, null, cmdArgs);
|
||||
}
|
||||
|
||||
private int launch(Map<String, String> envars, OutputStream out, OutputStream err, String... cmdArgs) throws Exception {
|
||||
if (out == null) {
|
||||
out = System.out;
|
||||
}
|
||||
|
||||
if (err == null) {
|
||||
err = System.err;
|
||||
}
|
||||
|
||||
return new Launcher.LocalLauncher(StreamTaskListener.fromStderr())
|
||||
.decorateByEnv(new EnvVars(envars))
|
||||
.launch()
|
||||
.cmds(cmdArgs)
|
||||
.stdout(out)
|
||||
.stderr(err)
|
||||
.join();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user