fix: replaces aesh with picocli (#28276)
* fix: replaces aesh with picocli closes: #28275 Signed-off-by: Steve Hawkins <shawkins@redhat.com> * fix: replaces aesh with picocli closes: #28275 Signed-off-by: Steve Hawkins <shawkins@redhat.com> --------- Signed-off-by: Steve Hawkins <shawkins@redhat.com>
This commit is contained in:
parent
b428e7dc81
commit
58398d1f69
31 changed files with 933 additions and 1591 deletions
|
@ -69,9 +69,9 @@ Options `cache`, `cache-stack`, and `cache-config-file` are no longer build opti
|
|||
This eliminates the need to execute the build phase and rebuild your image due to them.
|
||||
Be aware that they will not be recognized during the `build` phase, so you need to remove them.
|
||||
|
||||
= kcadm Changes
|
||||
= kcadm and kcreg changes
|
||||
|
||||
How kcadm parses and handles options and parameters has changed. Error messages from usage errors, the wrong option or parameter, may be slightly different than previous versions. Also usage errors will have an exit code of 2 instead of 1.
|
||||
How kcadm and kcreg parse and handle options and parameters has changed. Error messages from usage errors, the wrong option or parameter, may be slightly different than previous versions. Also usage errors will have an exit code of 2 instead of 1.
|
||||
|
||||
= Removing custom user attribute indexes
|
||||
|
||||
|
|
|
@ -31,8 +31,12 @@
|
|||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jboss.aesh</groupId>
|
||||
<artifactId>aesh</artifactId>
|
||||
<groupId>info.picocli</groupId>
|
||||
<artifactId>picocli</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-admin-cli</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
package org.keycloak.client.registration.cli;
|
||||
|
||||
import org.keycloak.client.registration.cli.common.EndpointType;
|
||||
|
||||
import picocli.CommandLine.ITypeConverter;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||
*/
|
||||
public class EndpointTypeConverter implements ITypeConverter<EndpointType> {
|
||||
|
||||
@Override
|
||||
public EndpointType convert(String value) throws Exception {
|
||||
return EndpointType.of(value);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package org.keycloak.client.registration.cli;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||
*/
|
||||
public class Globals {
|
||||
|
||||
public static boolean dumpTrace = false;
|
||||
|
||||
public static boolean help = false;
|
||||
|
||||
}
|
|
@ -1,21 +1,16 @@
|
|||
package org.keycloak.client.registration.cli;
|
||||
|
||||
import org.jboss.aesh.console.AeshConsoleBuilder;
|
||||
import org.jboss.aesh.console.AeshConsoleImpl;
|
||||
import org.jboss.aesh.console.Prompt;
|
||||
import org.jboss.aesh.console.command.registry.AeshCommandRegistryBuilder;
|
||||
import org.jboss.aesh.console.command.registry.CommandRegistry;
|
||||
import org.jboss.aesh.console.settings.Settings;
|
||||
import org.jboss.aesh.console.settings.SettingsBuilder;
|
||||
import org.keycloak.client.registration.cli.aesh.AeshEnhancer;
|
||||
import org.keycloak.client.registration.cli.aesh.ValveInputStream;
|
||||
import org.keycloak.client.registration.cli.aesh.Globals;
|
||||
import org.keycloak.client.admin.cli.ExecutionExceptionHandler;
|
||||
import org.keycloak.client.admin.cli.ShortErrorMessageHandler;
|
||||
import org.keycloak.client.registration.cli.commands.KcRegCmd;
|
||||
import org.keycloak.client.registration.cli.util.ClassLoaderUtil;
|
||||
import org.keycloak.client.registration.cli.util.OsUtil;
|
||||
import org.keycloak.common.crypto.CryptoIntegration;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
import picocli.CommandLine;
|
||||
import picocli.CommandLine.Model.CommandSpec;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||
|
@ -32,54 +27,20 @@ public class KcRegMain {
|
|||
|
||||
CryptoIntegration.init(cl);
|
||||
|
||||
Globals.stdin = new ValveInputStream();
|
||||
CommandLine cli = createCommandLine();
|
||||
int exitCode = cli.execute(args);
|
||||
System.exit(exitCode);
|
||||
}
|
||||
|
||||
Settings settings = new SettingsBuilder()
|
||||
.logging(false)
|
||||
.readInputrc(false)
|
||||
.disableCompletion(true)
|
||||
.disableHistory(true)
|
||||
.enableAlias(false)
|
||||
.enableExport(false)
|
||||
.inputStream(Globals.stdin)
|
||||
.create();
|
||||
public static CommandLine createCommandLine() {
|
||||
CommandSpec spec = CommandSpec.forAnnotatedObject(new KcRegCmd()).name(OsUtil.CMD);
|
||||
|
||||
CommandRegistry registry = new AeshCommandRegistryBuilder()
|
||||
.command(KcRegCmd.class)
|
||||
.create();
|
||||
CommandLine cmd = new CommandLine(spec);
|
||||
|
||||
AeshConsoleImpl console = (AeshConsoleImpl) new AeshConsoleBuilder()
|
||||
.settings(settings)
|
||||
.commandRegistry(registry)
|
||||
.prompt(new Prompt(""))
|
||||
.create();
|
||||
cmd.setExecutionExceptionHandler(new ExecutionExceptionHandler());
|
||||
cmd.setParameterExceptionHandler(new ShortErrorMessageHandler());
|
||||
cmd.setErr(new PrintWriter(System.err, true));
|
||||
|
||||
AeshEnhancer.enhance(console);
|
||||
|
||||
// work around parser issues with quotes and brackets
|
||||
ArrayList<String> arguments = new ArrayList<>();
|
||||
arguments.add("kcreg");
|
||||
arguments.addAll(Arrays.asList(args));
|
||||
Globals.args = arguments;
|
||||
|
||||
StringBuilder b = new StringBuilder();
|
||||
for (String s : args) {
|
||||
// quote if necessary
|
||||
boolean needQuote = false;
|
||||
needQuote = s.indexOf(' ') != -1 || s.indexOf('\"') != -1 || s.indexOf('\'') != -1;
|
||||
b.append(' ');
|
||||
if (needQuote) {
|
||||
b.append('\'');
|
||||
}
|
||||
b.append(s);
|
||||
if (needQuote) {
|
||||
b.append('\'');
|
||||
}
|
||||
}
|
||||
console.setEcho(false);
|
||||
|
||||
console.execute("kcreg" + b.toString());
|
||||
|
||||
console.start();
|
||||
return cmd;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,101 +0,0 @@
|
|||
package org.keycloak.client.registration.cli.aesh;
|
||||
|
||||
import org.jboss.aesh.cl.parser.OptionParserException;
|
||||
import org.jboss.aesh.cl.result.ResultHandler;
|
||||
import org.jboss.aesh.console.AeshConsoleCallback;
|
||||
import org.jboss.aesh.console.AeshConsoleImpl;
|
||||
import org.jboss.aesh.console.ConsoleOperation;
|
||||
import org.jboss.aesh.console.command.CommandNotFoundException;
|
||||
import org.jboss.aesh.console.command.CommandResult;
|
||||
import org.jboss.aesh.console.command.container.CommandContainer;
|
||||
import org.jboss.aesh.console.command.container.CommandContainerResult;
|
||||
import org.jboss.aesh.console.command.invocation.AeshCommandInvocation;
|
||||
import org.jboss.aesh.console.command.invocation.AeshCommandInvocationProvider;
|
||||
import org.jboss.aesh.parser.AeshLine;
|
||||
import org.jboss.aesh.parser.ParserStatus;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
class AeshConsoleCallbackImpl extends AeshConsoleCallback {
|
||||
|
||||
private final AeshConsoleImpl console;
|
||||
private CommandResult result;
|
||||
|
||||
AeshConsoleCallbackImpl(AeshConsoleImpl aeshConsole) {
|
||||
this.console = aeshConsole;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public int execute(ConsoleOperation output) throws InterruptedException {
|
||||
if (output != null && output.getBuffer().trim().length() > 0) {
|
||||
ResultHandler resultHandler = null;
|
||||
//AeshLine aeshLine = Parser.findAllWords(output.getBuffer());
|
||||
AeshLine aeshLine = new AeshLine(output.getBuffer(), Globals.args, ParserStatus.OK, "");
|
||||
try (CommandContainer commandContainer = getCommand(output, aeshLine)) {
|
||||
resultHandler = commandContainer.getParser().getProcessedCommand().getResultHandler();
|
||||
CommandContainerResult ccResult =
|
||||
commandContainer.executeCommand(aeshLine, console.getInvocationProviders(), console.getAeshContext(),
|
||||
new AeshCommandInvocationProvider().enhanceCommandInvocation(
|
||||
new AeshCommandInvocation(console,
|
||||
output.getControlOperator(), output.getPid(), this)));
|
||||
|
||||
result = ccResult.getCommandResult();
|
||||
|
||||
if(result == CommandResult.SUCCESS && resultHandler != null)
|
||||
resultHandler.onSuccess();
|
||||
else if(resultHandler != null)
|
||||
resultHandler.onFailure(result);
|
||||
|
||||
if (result == CommandResult.FAILURE) {
|
||||
// we assume the command has already output any error messages
|
||||
System.exit(1);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
console.stop();
|
||||
|
||||
if (e instanceof OptionParserException) {
|
||||
System.err.println("Unknown command: " + aeshLine.getWords().get(0));
|
||||
} else {
|
||||
System.err.println(e.getMessage());
|
||||
}
|
||||
if (Globals.dumpTrace) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
// empty line
|
||||
else if (output != null) {
|
||||
result = CommandResult.FAILURE;
|
||||
}
|
||||
else {
|
||||
//stop();
|
||||
result = CommandResult.FAILURE;
|
||||
}
|
||||
|
||||
if (result == CommandResult.SUCCESS) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
private CommandContainer getCommand(ConsoleOperation output, AeshLine aeshLine) throws CommandNotFoundException {
|
||||
Method m;
|
||||
try {
|
||||
m = console.getClass().getDeclaredMethod("getCommand", AeshLine.class, String.class);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new RuntimeException("Unexpected error: ", e);
|
||||
}
|
||||
|
||||
m.setAccessible(true);
|
||||
|
||||
try {
|
||||
return (CommandContainer) m.invoke(console, aeshLine, output.getBuffer());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Unexpected error: ", e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
package org.keycloak.client.registration.cli.aesh;
|
||||
|
||||
import org.jboss.aesh.console.AeshConsoleImpl;
|
||||
import org.jboss.aesh.console.Console;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||
*/
|
||||
public class AeshEnhancer {
|
||||
|
||||
public static void enhance(AeshConsoleImpl console) {
|
||||
try {
|
||||
Globals.stdin.setConsole(console);
|
||||
|
||||
Field field = AeshConsoleImpl.class.getDeclaredField("console");
|
||||
field.setAccessible(true);
|
||||
Console internalConsole = (Console) field.get(console);
|
||||
internalConsole.setConsoleCallback(new AeshConsoleCallbackImpl(console));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to install Aesh enhancement", e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
package org.keycloak.client.registration.cli.aesh;
|
||||
|
||||
import org.jboss.aesh.cl.converter.Converter;
|
||||
import org.jboss.aesh.cl.validator.OptionValidatorException;
|
||||
import org.jboss.aesh.console.command.converter.ConverterInvocation;
|
||||
import org.keycloak.client.registration.cli.common.EndpointType;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||
*/
|
||||
public class EndpointTypeConverter implements Converter<EndpointType, ConverterInvocation> {
|
||||
|
||||
@Override
|
||||
public EndpointType convert(ConverterInvocation converterInvocation) throws OptionValidatorException {
|
||||
return EndpointType.of(converterInvocation.getInput());
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
package org.keycloak.client.registration.cli.aesh;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||
*/
|
||||
public class Globals {
|
||||
|
||||
public static boolean dumpTrace = false;
|
||||
|
||||
public static ValveInputStream stdin;
|
||||
|
||||
public static List<String> args;
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
package org.keycloak.client.registration.cli.aesh;
|
||||
|
||||
import org.jboss.aesh.console.AeshConsoleImpl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
/**
|
||||
* This stream blocks and waits, until there is some stream in the queue.
|
||||
* It reads all streams from the queue, and then blocks until it receives more.
|
||||
*/
|
||||
public class ValveInputStream extends InputStream {
|
||||
|
||||
private BlockingQueue<InputStream> queue = new LinkedBlockingQueue<>(10);
|
||||
|
||||
private InputStream current;
|
||||
|
||||
private AeshConsoleImpl console;
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
if (current == null) {
|
||||
try {
|
||||
current = queue.take();
|
||||
} catch (InterruptedException e) {
|
||||
throw new InterruptedIOException("Signalled to exit");
|
||||
}
|
||||
}
|
||||
int c = current.read();
|
||||
if (c == -1) {
|
||||
//current = null;
|
||||
if (console != null) {
|
||||
console.stop();
|
||||
}
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* For some reason AeshInputStream wants to do blocking read of whole buffers, which for stdin
|
||||
* results in blocked input.
|
||||
*/
|
||||
@Override
|
||||
public int read(byte b[], int off, int len) throws IOException {
|
||||
int c = read();
|
||||
if (c == -1) {
|
||||
return c;
|
||||
}
|
||||
b[off] = (byte) c;
|
||||
return 1;
|
||||
}
|
||||
|
||||
public void setInputStream(InputStream is) {
|
||||
if (queue.contains(is)) {
|
||||
return;
|
||||
}
|
||||
queue.add(is);
|
||||
}
|
||||
|
||||
public void setConsole(AeshConsoleImpl console) {
|
||||
this.console = console;
|
||||
}
|
||||
|
||||
public boolean isStdinAvailable() {
|
||||
return console.isRunning();
|
||||
}
|
||||
}
|
|
@ -1,7 +1,5 @@
|
|||
package org.keycloak.client.registration.cli.commands;
|
||||
|
||||
import org.jboss.aesh.cl.Option;
|
||||
import org.jboss.aesh.console.command.invocation.CommandInvocation;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.client.registration.cli.config.ConfigData;
|
||||
import org.keycloak.client.registration.cli.config.ConfigHandler;
|
||||
|
@ -14,6 +12,8 @@ import org.keycloak.client.registration.cli.util.IoUtil;
|
|||
|
||||
import java.io.File;
|
||||
|
||||
import picocli.CommandLine.Option;
|
||||
|
||||
import static org.keycloak.client.registration.cli.config.FileConfigHandler.setConfigFile;
|
||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.checkAuthInfo;
|
||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.checkServerInfo;
|
||||
|
@ -26,58 +26,55 @@ public abstract class AbstractAuthOptionsCmd extends AbstractGlobalOptionsCmd {
|
|||
|
||||
static final String DEFAULT_CLIENT = "admin-cli";
|
||||
|
||||
@Option(name = "config", description = "Path to the config file (~/.keycloak/kcreg.config by default)", hasValue = true)
|
||||
@Option(names = "--config", description = "Path to the config file (~/.keycloak/kcreg.config by default)")
|
||||
protected String config;
|
||||
|
||||
@Option(name = "no-config", description = "No configuration file should be used, no authentication info is loaded or saved", hasValue = false)
|
||||
@Option(names = "--no-config", description = "No configuration file should be used, no authentication info is loaded or saved")
|
||||
protected boolean noconfig;
|
||||
|
||||
@Option(name = "server", description = "Server endpoint url (e.g. 'http://localhost:8080')", hasValue = true)
|
||||
@Option(names = "--server", description = "Server endpoint url (e.g. 'http://localhost:8080')")
|
||||
protected String server;
|
||||
|
||||
@Option(name = "realm", description = "Realm name to authenticate against", hasValue = true)
|
||||
@Option(names = "--realm", description = "Realm name to authenticate against")
|
||||
protected String realm;
|
||||
|
||||
@Option(name = "client", description = "Realm name to authenticate against", hasValue = true)
|
||||
@Option(names = "--client", description = "Realm name to authenticate against")
|
||||
protected String clientId;
|
||||
|
||||
@Option(name = "user", description = "Username to login with", hasValue = true)
|
||||
@Option(names = "--user", description = "Username to login with")
|
||||
protected String user;
|
||||
|
||||
@Option(name = "password", description = "Password to login with (prompted for if not specified and --user is used)", hasValue = true)
|
||||
@Option(names = "--password", description = "Password to login with (prompted for if not specified and --user is used)")
|
||||
protected String password;
|
||||
|
||||
@Option(name = "secret", description = "Secret to authenticate the client (prompted for if no --user or --keystore is specified)", hasValue = true)
|
||||
@Option(names = "--secret", description = "Secret to authenticate the client (prompted for if no --user or --keystore is specified)")
|
||||
protected String secret;
|
||||
|
||||
@Option(name = "keystore", description = "Path to a keystore containing private key", hasValue = true)
|
||||
@Option(names = "--keystore", description = "Path to a keystore containing private key")
|
||||
protected String keystore;
|
||||
|
||||
@Option(name = "storepass", description = "Keystore password (prompted for if not specified and --keystore is used)", hasValue = true)
|
||||
@Option(names = "--storepass", description = "Keystore password (prompted for if not specified and --keystore is used)")
|
||||
protected String storePass;
|
||||
|
||||
@Option(name = "keypass", description = "Key password (prompted for if not specified and --keystore is used without --storepass, \n otherwise defaults to keystore password)", hasValue = true)
|
||||
@Option(names = "--keypass", description = "Key password (prompted for if not specified and --keystore is used without --storepass, \n otherwise defaults to keystore password)")
|
||||
protected String keyPass;
|
||||
|
||||
@Option(name = "alias", description = "Alias of the key inside a keystore (defaults to the value of ClientId)", hasValue = true)
|
||||
@Option(names = "--alias", description = "Alias of the key inside a keystore (defaults to the value of ClientId)")
|
||||
protected String alias;
|
||||
|
||||
@Option(name = "truststore", description = "Path to a truststore", hasValue = true)
|
||||
@Option(names = "--truststore", description = "Path to a truststore")
|
||||
protected String trustStore;
|
||||
|
||||
@Option(name = "trustpass", description = "Truststore password (prompted for if not specified and --truststore is used)", hasValue = true)
|
||||
@Option(names = "--trustpass", description = "Truststore password (prompted for if not specified and --truststore is used)")
|
||||
protected String trustPass;
|
||||
|
||||
@Option(name = "insecure", description = "Turns off TLS validation", hasValue = false)
|
||||
@Option(names = "--insecure", description = "Turns off TLS validation")
|
||||
protected boolean insecure;
|
||||
|
||||
@Option(shortName = 't', name = "token", description = "Initial / Registration access token to use)", hasValue = true)
|
||||
@Option(names = {"-t", "--token"}, description = "Initial / Registration access token to use)")
|
||||
protected String token;
|
||||
|
||||
protected void initFromParent(AbstractAuthOptionsCmd parent) {
|
||||
|
||||
super.initFromParent(parent);
|
||||
|
||||
noconfig = parent.noconfig;
|
||||
config = parent.config;
|
||||
server = parent.server;
|
||||
|
@ -110,12 +107,11 @@ public abstract class AbstractAuthOptionsCmd extends AbstractGlobalOptionsCmd {
|
|||
token == null && config == null;
|
||||
}
|
||||
|
||||
protected void processGlobalOptions() {
|
||||
|
||||
super.processGlobalOptions();
|
||||
@Override
|
||||
protected void processOptions() {
|
||||
|
||||
if (config != null && noconfig) {
|
||||
throw new RuntimeException("Options --config and --no-config are mutually exclusive");
|
||||
throw new IllegalArgumentException("Options --config and --no-config are mutually exclusive");
|
||||
}
|
||||
|
||||
if (!noconfig) {
|
||||
|
@ -130,7 +126,7 @@ public abstract class AbstractAuthOptionsCmd extends AbstractGlobalOptionsCmd {
|
|||
}
|
||||
}
|
||||
|
||||
protected void setupTruststore(ConfigData configData, CommandInvocation invocation ) {
|
||||
protected void setupTruststore(ConfigData configData) {
|
||||
|
||||
if (!configData.getServerUrl().startsWith("https:")) {
|
||||
return;
|
||||
|
@ -147,7 +143,7 @@ public abstract class AbstractAuthOptionsCmd extends AbstractGlobalOptionsCmd {
|
|||
pass = configData.getTrustpass();
|
||||
}
|
||||
if (pass == null) {
|
||||
pass = IoUtil.readSecret("Enter truststore password: ", invocation);
|
||||
pass = IoUtil.readSecret("Enter truststore password: ");
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -162,7 +158,7 @@ public abstract class AbstractAuthOptionsCmd extends AbstractGlobalOptionsCmd {
|
|||
}
|
||||
}
|
||||
|
||||
protected ConfigData ensureAuthInfo(ConfigData config, CommandInvocation commandInvocation) {
|
||||
protected ConfigData ensureAuthInfo(ConfigData config) {
|
||||
|
||||
if (requiresLogin()) {
|
||||
// make sure current handler is in-memory handler
|
||||
|
@ -178,7 +174,7 @@ public abstract class AbstractAuthOptionsCmd extends AbstractGlobalOptionsCmd {
|
|||
ConfigCredentialsCmd login = new ConfigCredentialsCmd();
|
||||
login.initFromParent(this);
|
||||
login.init(config);
|
||||
login.process(commandInvocation);
|
||||
login.process();
|
||||
|
||||
// this must be executed before finally block which restores config handler
|
||||
return loadConfig();
|
||||
|
@ -237,6 +233,7 @@ public abstract class AbstractAuthOptionsCmd extends AbstractGlobalOptionsCmd {
|
|||
rdata.setGrantTypeForAuthentication(grantTypeForAuthentication);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void checkUnsupportedOptions(String ... options) {
|
||||
if (options.length % 2 != 0) {
|
||||
throw new IllegalArgumentException("Even number of argument required");
|
||||
|
|
|
@ -1,38 +1,37 @@
|
|||
package org.keycloak.client.registration.cli.commands;
|
||||
|
||||
import org.jboss.aesh.cl.Option;
|
||||
import org.jboss.aesh.console.command.Command;
|
||||
import org.keycloak.client.registration.cli.aesh.Globals;
|
||||
import org.keycloak.client.registration.cli.Globals;
|
||||
|
||||
import picocli.CommandLine;
|
||||
import picocli.CommandLine.Option;
|
||||
|
||||
import static org.keycloak.client.registration.cli.util.IoUtil.printOut;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||
*/
|
||||
public abstract class AbstractGlobalOptionsCmd implements Command {
|
||||
public abstract class AbstractGlobalOptionsCmd implements Runnable {
|
||||
|
||||
@Option(shortName = 'x', description = "Print full stack trace when exiting with error", hasValue = false)
|
||||
protected boolean dumpTrace;
|
||||
|
||||
@Option(name = "help", description = "Print command specific help", hasValue = false)
|
||||
protected boolean help;
|
||||
|
||||
protected void initFromParent(AbstractGlobalOptionsCmd parent) {
|
||||
dumpTrace = parent.dumpTrace;
|
||||
help = parent.help;
|
||||
@Option(names = "--help",
|
||||
description = "Print command specific help")
|
||||
public void setHelp(boolean help) {
|
||||
Globals.help = help;
|
||||
}
|
||||
|
||||
protected void processGlobalOptions() {
|
||||
@Option(names = "-x",
|
||||
description = "Print full stack trace when exiting with error")
|
||||
public void setDumpTrace(boolean dumpTrace) {
|
||||
Globals.dumpTrace = dumpTrace;
|
||||
}
|
||||
|
||||
protected boolean printHelp() {
|
||||
if (help || nothingToDo()) {
|
||||
protected void printHelpIfNeeded() {
|
||||
if (Globals.help) {
|
||||
printOut(help());
|
||||
return true;
|
||||
System.exit(CommandLine.ExitCode.OK);
|
||||
} else if (nothingToDo()) {
|
||||
printOut(help());
|
||||
System.exit(CommandLine.ExitCode.USAGE);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean nothingToDo() {
|
||||
|
@ -42,4 +41,47 @@ public abstract class AbstractGlobalOptionsCmd implements Command {
|
|||
protected String help() {
|
||||
return KcRegCmd.usage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
printHelpIfNeeded();
|
||||
|
||||
checkUnsupportedOptions(getUnsupportedOptions());
|
||||
|
||||
processOptions();
|
||||
|
||||
process();
|
||||
}
|
||||
|
||||
protected String[] getUnsupportedOptions() {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
protected void processOptions() {
|
||||
|
||||
}
|
||||
|
||||
protected void process() {
|
||||
|
||||
}
|
||||
|
||||
protected void checkUnsupportedOptions(String ... options) {
|
||||
if (options.length % 2 != 0) {
|
||||
throw new IllegalArgumentException("Even number of argument required");
|
||||
}
|
||||
|
||||
for (int i = 0; i < options.length; i++) {
|
||||
String name = options[i];
|
||||
String value = options[++i];
|
||||
|
||||
if (value != null) {
|
||||
throw new IllegalArgumentException("Unsupported option: " + name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static String booleanOptionForCheck(boolean value) {
|
||||
return value ? "true" : null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,11 +1,5 @@
|
|||
package org.keycloak.client.registration.cli.commands;
|
||||
|
||||
import org.jboss.aesh.cl.Arguments;
|
||||
import org.jboss.aesh.cl.CommandDefinition;
|
||||
import org.jboss.aesh.cl.Option;
|
||||
import org.jboss.aesh.console.command.CommandException;
|
||||
import org.jboss.aesh.console.command.CommandResult;
|
||||
import org.jboss.aesh.console.command.invocation.CommandInvocation;
|
||||
import org.keycloak.client.registration.cli.common.AttributeKey;
|
||||
import org.keycloak.client.registration.cli.common.EndpointType;
|
||||
import org.keycloak.client.registration.cli.util.ReflectionUtil;
|
||||
|
@ -19,9 +13,13 @@ import java.lang.reflect.Field;
|
|||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import picocli.CommandLine;
|
||||
import picocli.CommandLine.Command;
|
||||
import picocli.CommandLine.Option;
|
||||
import picocli.CommandLine.Parameters;
|
||||
|
||||
import static org.keycloak.client.registration.cli.util.OsUtil.CMD;
|
||||
import static org.keycloak.client.registration.cli.util.OsUtil.PROMPT;
|
||||
import static org.keycloak.client.registration.cli.util.ReflectionUtil.getAttributeListWithJSonTypes;
|
||||
|
@ -32,97 +30,78 @@ import static org.keycloak.client.registration.cli.util.ReflectionUtil.isMapType
|
|||
/**
|
||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||
*/
|
||||
@CommandDefinition(name = "attrs", description = "[ATTRIBUTE] [--endpoint TYPE]")
|
||||
@Command(name = "attrs", description = "[ATTRIBUTE] [--endpoint TYPE]")
|
||||
public class AttrsCmd extends AbstractGlobalOptionsCmd {
|
||||
|
||||
@Option(shortName = 'e', name = "endpoint", description = "Endpoint type to use", hasValue = true)
|
||||
CommandLine.Model.CommandSpec spec;
|
||||
|
||||
@Option(names = {"-e", "--endpoint"}, description = "Endpoint type to use")
|
||||
protected String endpoint;
|
||||
|
||||
@Arguments
|
||||
protected List<String> args;
|
||||
|
||||
@Parameters(arity = "0..1")
|
||||
protected String attr;
|
||||
|
||||
@Override
|
||||
public CommandResult execute(CommandInvocation commandInvocation) throws CommandException, InterruptedException {
|
||||
try {
|
||||
processGlobalOptions();
|
||||
protected void process() {
|
||||
EndpointType regType = EndpointType.DEFAULT;
|
||||
PrintStream out = System.out;
|
||||
|
||||
if (printHelp()) {
|
||||
return CommandResult.SUCCESS;
|
||||
if (endpoint != null) {
|
||||
regType = EndpointType.of(endpoint);
|
||||
}
|
||||
|
||||
Class type = regType == EndpointType.DEFAULT ? ClientRepresentation.class : (regType == EndpointType.OIDC ? OIDCClientRepresentation.class : null);
|
||||
if (type == null) {
|
||||
throw new IllegalArgumentException("Endpoint not supported: " + regType);
|
||||
}
|
||||
AttributeKey key = attr == null ? new AttributeKey() : new AttributeKey(attr);
|
||||
|
||||
Field f = ReflectionUtil.resolveField(type, key);
|
||||
String ts = f != null ? ReflectionUtil.getTypeString(null, f) : null;
|
||||
|
||||
if (f == null) {
|
||||
out.printf("Attributes for %s format:\n", regType.getEndpoint());
|
||||
|
||||
LinkedHashMap<String, String> items = getAttributeListWithJSonTypes(type, key);
|
||||
for (Map.Entry<String, String> item : items.entrySet()) {
|
||||
out.printf(" %-40s %s\n", item.getKey(), item.getValue());
|
||||
}
|
||||
|
||||
EndpointType regType = EndpointType.DEFAULT;
|
||||
PrintStream out = commandInvocation.getShell().out();
|
||||
} else {
|
||||
out.printf("%-40s %s", attr, ts);
|
||||
boolean eol = false;
|
||||
|
||||
if (endpoint != null) {
|
||||
regType = EndpointType.of(endpoint);
|
||||
}
|
||||
|
||||
if (args != null) {
|
||||
if (args.size() > 1) {
|
||||
throw new IllegalArgumentException("Invalid option: " + args.get(1));
|
||||
}
|
||||
attr = args.get(0);
|
||||
}
|
||||
|
||||
Class type = regType == EndpointType.DEFAULT ? ClientRepresentation.class : (regType == EndpointType.OIDC ? OIDCClientRepresentation.class : null);
|
||||
if (type == null) {
|
||||
throw new IllegalArgumentException("Endpoint not supported: " + regType);
|
||||
}
|
||||
AttributeKey key = attr == null ? new AttributeKey() : new AttributeKey(attr);
|
||||
|
||||
Field f = ReflectionUtil.resolveField(type, key);
|
||||
String ts = f != null ? ReflectionUtil.getTypeString(null, f) : null;
|
||||
|
||||
if (f == null) {
|
||||
out.printf("Attributes for %s format:\n", regType.getEndpoint());
|
||||
|
||||
LinkedHashMap<String, String> items = getAttributeListWithJSonTypes(type, key);
|
||||
for (Map.Entry<String, String> item : items.entrySet()) {
|
||||
out.printf(" %-40s %s\n", item.getKey(), item.getValue());
|
||||
}
|
||||
|
||||
} else {
|
||||
out.printf("%-40s %s", attr, ts);
|
||||
boolean eol = false;
|
||||
|
||||
Type t = f.getGenericType();
|
||||
if (isListType(f.getType()) && t instanceof ParameterizedType) {
|
||||
t = ((ParameterizedType) t).getActualTypeArguments()[0];
|
||||
if (!isBasicType(t) && t instanceof Class) {
|
||||
eol = true;
|
||||
System.out.printf(", where value is:\n", ts);
|
||||
LinkedHashMap<String, String> items = ReflectionUtil.getAttributeListWithJSonTypes((Class) t, null);
|
||||
for (Map.Entry<String, String> item : items.entrySet()) {
|
||||
out.printf(" %-36s %s\n", item.getKey(), item.getValue());
|
||||
}
|
||||
}
|
||||
} else if (isMapType(f.getType()) && t instanceof ParameterizedType) {
|
||||
t = ((ParameterizedType) t).getActualTypeArguments()[1];
|
||||
if (!isBasicType(t) && t instanceof Class) {
|
||||
eol = true;
|
||||
out.printf(", where value is:\n", ts);
|
||||
LinkedHashMap<String, String> items = ReflectionUtil.getAttributeListWithJSonTypes((Class) t, null);
|
||||
for (Map.Entry<String, String> item : items.entrySet()) {
|
||||
out.printf(" %-36s %s\n", item.getKey(), item.getValue());
|
||||
}
|
||||
Type t = f.getGenericType();
|
||||
if (isListType(f.getType()) && t instanceof ParameterizedType) {
|
||||
t = ((ParameterizedType) t).getActualTypeArguments()[0];
|
||||
if (!isBasicType(t) && t instanceof Class) {
|
||||
eol = true;
|
||||
out.printf(", where value is:\n", ts);
|
||||
LinkedHashMap<String, String> items = ReflectionUtil.getAttributeListWithJSonTypes((Class) t, null);
|
||||
for (Map.Entry<String, String> item : items.entrySet()) {
|
||||
out.printf(" %-36s %s\n", item.getKey(), item.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
if (!eol) {
|
||||
// add end of line
|
||||
out.println();
|
||||
} else if (isMapType(f.getType()) && t instanceof ParameterizedType) {
|
||||
t = ((ParameterizedType) t).getActualTypeArguments()[1];
|
||||
if (!isBasicType(t) && t instanceof Class) {
|
||||
eol = true;
|
||||
out.printf(", where value is:\n", ts);
|
||||
LinkedHashMap<String, String> items = ReflectionUtil.getAttributeListWithJSonTypes((Class) t, null);
|
||||
for (Map.Entry<String, String> item : items.entrySet()) {
|
||||
out.printf(" %-36s %s\n", item.getKey(), item.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return CommandResult.SUCCESS;
|
||||
|
||||
} finally {
|
||||
commandInvocation.stop();
|
||||
if (!eol) {
|
||||
// add end of line
|
||||
out.println();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String help() {
|
||||
return usage();
|
||||
}
|
||||
|
|
|
@ -17,80 +17,36 @@
|
|||
|
||||
package org.keycloak.client.registration.cli.commands;
|
||||
|
||||
import org.jboss.aesh.cl.Arguments;
|
||||
import org.jboss.aesh.cl.GroupCommandDefinition;
|
||||
import org.jboss.aesh.console.command.CommandException;
|
||||
import org.jboss.aesh.console.command.Command;
|
||||
import org.jboss.aesh.console.command.CommandResult;
|
||||
import org.jboss.aesh.console.command.invocation.CommandInvocation;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.List;
|
||||
|
||||
import picocli.CommandLine.Command;
|
||||
|
||||
import static org.keycloak.client.registration.cli.util.OsUtil.CMD;
|
||||
import static org.keycloak.client.registration.cli.util.OsUtil.EOL;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||
*/
|
||||
|
||||
@GroupCommandDefinition(name = "config", description = "COMMAND [ARGUMENTS]", groupCommands = {ConfigCredentialsCmd.class} )
|
||||
public class ConfigCmd extends AbstractAuthOptionsCmd implements Command {
|
||||
@Command(name = "config", description = "COMMAND [ARGUMENTS]", subcommands = {
|
||||
ConfigCredentialsCmd.class,
|
||||
ConfigInitialTokenCmd.class,
|
||||
ConfigRegistrationTokenCmd.class,
|
||||
ConfigTruststoreCmd.class
|
||||
})
|
||||
public class ConfigCmd extends AbstractAuthOptionsCmd {
|
||||
|
||||
@Arguments
|
||||
protected List<String> args;
|
||||
@Override
|
||||
protected void process() {
|
||||
|
||||
|
||||
public CommandResult execute(CommandInvocation commandInvocation) throws CommandException, InterruptedException {
|
||||
try {
|
||||
if (args != null && args.size() > 0) {
|
||||
String cmd = args.get(0);
|
||||
switch (cmd) {
|
||||
case "credentials": {
|
||||
ConfigCredentialsCmd command = new ConfigCredentialsCmd();
|
||||
command.initFromParent(this);
|
||||
return command.execute(commandInvocation);
|
||||
}
|
||||
case "truststore": {
|
||||
ConfigTruststoreCmd command = new ConfigTruststoreCmd();
|
||||
command.initFromParent(this);
|
||||
return command.execute(commandInvocation);
|
||||
}
|
||||
case "initial-token": {
|
||||
ConfigInitialTokenCmd command = new ConfigInitialTokenCmd();
|
||||
command.initFromParent(this);
|
||||
return command.execute(commandInvocation);
|
||||
}
|
||||
case "registration-token": {
|
||||
ConfigRegistrationTokenCmd command = new ConfigRegistrationTokenCmd();
|
||||
command.initFromParent(this);
|
||||
return command.execute(commandInvocation);
|
||||
}
|
||||
default: {
|
||||
if (printHelp()) {
|
||||
return help ? CommandResult.SUCCESS : CommandResult.FAILURE;
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown sub-command: " + cmd + suggestHelp());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (printHelp()) {
|
||||
return help ? CommandResult.SUCCESS : CommandResult.FAILURE;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Sub-command required by '" + CMD + " config' - one of: 'credentials', 'truststore', 'initial-token', 'registration-token'");
|
||||
|
||||
} finally {
|
||||
commandInvocation.stop();
|
||||
}
|
||||
}
|
||||
|
||||
protected String suggestHelp() {
|
||||
return EOL + "Try '" + CMD + " help config' for more information";
|
||||
@Override
|
||||
protected boolean nothingToDo() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String help() {
|
||||
return usage();
|
||||
}
|
||||
|
|
|
@ -1,10 +1,5 @@
|
|||
package org.keycloak.client.registration.cli.commands;
|
||||
|
||||
import org.jboss.aesh.cl.CommandDefinition;
|
||||
import org.jboss.aesh.console.command.Command;
|
||||
import org.jboss.aesh.console.command.CommandException;
|
||||
import org.jboss.aesh.console.command.CommandResult;
|
||||
import org.jboss.aesh.console.command.invocation.CommandInvocation;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.client.registration.cli.config.ConfigData;
|
||||
import org.keycloak.client.registration.cli.config.RealmConfigData;
|
||||
|
@ -16,6 +11,8 @@ import java.io.PrintWriter;
|
|||
import java.io.StringWriter;
|
||||
import java.net.URL;
|
||||
|
||||
import picocli.CommandLine.Command;
|
||||
|
||||
import static org.keycloak.client.registration.cli.util.AuthUtil.getAuthTokens;
|
||||
import static org.keycloak.client.registration.cli.util.AuthUtil.getAuthTokensByJWT;
|
||||
import static org.keycloak.client.registration.cli.util.AuthUtil.getAuthTokensBySecret;
|
||||
|
@ -26,15 +23,14 @@ import static org.keycloak.client.registration.cli.util.ConfigUtil.saveTokens;
|
|||
import static org.keycloak.client.registration.cli.util.IoUtil.printErr;
|
||||
import static org.keycloak.client.registration.cli.util.IoUtil.readSecret;
|
||||
import static org.keycloak.client.registration.cli.util.OsUtil.CMD;
|
||||
import static org.keycloak.client.registration.cli.util.OsUtil.EOL;
|
||||
import static org.keycloak.client.registration.cli.util.OsUtil.OS_ARCH;
|
||||
import static org.keycloak.client.registration.cli.util.OsUtil.PROMPT;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||
*/
|
||||
@CommandDefinition(name = "credentials", description = "--server SERVER_URL --realm REALM [ARGUMENTS]")
|
||||
public class ConfigCredentialsCmd extends AbstractAuthOptionsCmd implements Command {
|
||||
@Command(name = "credentials", description = "--server SERVER_URL --realm REALM [ARGUMENTS]")
|
||||
public class ConfigCredentialsCmd extends AbstractAuthOptionsCmd {
|
||||
|
||||
private int sigLifetime = 600;
|
||||
|
||||
|
@ -60,24 +56,9 @@ public class ConfigCredentialsCmd extends AbstractAuthOptionsCmd implements Comm
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public CommandResult execute(CommandInvocation commandInvocation) throws CommandException, InterruptedException {
|
||||
try {
|
||||
if (printHelp()) {
|
||||
return help ? CommandResult.SUCCESS : CommandResult.FAILURE;
|
||||
}
|
||||
|
||||
checkUnsupportedOptions("--no-config", booleanOptionForCheck(noconfig));
|
||||
|
||||
processGlobalOptions();
|
||||
|
||||
return process(commandInvocation);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IllegalArgumentException(e.getMessage() + suggestHelp(), e);
|
||||
} finally {
|
||||
commandInvocation.stop();
|
||||
}
|
||||
protected String[] getUnsupportedOptions() {
|
||||
return new String[] {"--no-config", booleanOptionForCheck(noconfig)};
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -85,8 +66,8 @@ public class ConfigCredentialsCmd extends AbstractAuthOptionsCmd implements Comm
|
|||
return noOptions();
|
||||
}
|
||||
|
||||
public CommandResult process(CommandInvocation commandInvocation) throws CommandException, InterruptedException {
|
||||
|
||||
@Override
|
||||
protected void process() {
|
||||
// check server
|
||||
if (server == null) {
|
||||
throw new IllegalArgumentException("Required option not specified: --server");
|
||||
|
@ -113,18 +94,18 @@ public class ConfigCredentialsCmd extends AbstractAuthOptionsCmd implements Comm
|
|||
|
||||
// if user was set there needs to be a password so we can authenticate
|
||||
if (password == null) {
|
||||
password = readSecret("Enter password: ", commandInvocation);
|
||||
password = readSecret("Enter password: ");
|
||||
}
|
||||
// if secret was set to be read from stdin, then ask for it
|
||||
if ("-".equals(secret) && keystore == null) {
|
||||
secret = readSecret("Enter client secret: ", commandInvocation);
|
||||
secret = readSecret("Enter client secret: ");
|
||||
}
|
||||
} else if (keystore != null || secret != null || clientSet) {
|
||||
grantTypeForAuthentication = OAuth2Constants.CLIENT_CREDENTIALS;
|
||||
printErr("Logging into " + server + " as " + "service-account-" + clientId + " of realm " + realm);
|
||||
if (keystore == null) {
|
||||
if (secret == null) {
|
||||
secret = readSecret("Enter client secret: ", commandInvocation);
|
||||
secret = readSecret("Enter client secret: ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -139,8 +120,8 @@ public class ConfigCredentialsCmd extends AbstractAuthOptionsCmd implements Comm
|
|||
}
|
||||
|
||||
if (storePass == null) {
|
||||
storePass = readSecret("Enter keystore password: ", commandInvocation);
|
||||
keyPass = readSecret("Enter key password: ", commandInvocation);
|
||||
storePass = readSecret("Enter keystore password: ");
|
||||
keyPass = readSecret("Enter key password: ");
|
||||
}
|
||||
|
||||
if (keyPass == null) {
|
||||
|
@ -163,10 +144,10 @@ public class ConfigCredentialsCmd extends AbstractAuthOptionsCmd implements Comm
|
|||
config.setServerUrl(server);
|
||||
config.setRealm(realm);
|
||||
});
|
||||
return CommandResult.SUCCESS;
|
||||
return;
|
||||
}
|
||||
|
||||
setupTruststore(copyWithServerInfo(loadConfig()), commandInvocation);
|
||||
setupTruststore(copyWithServerInfo(loadConfig()));
|
||||
|
||||
// now use the token endpoint to retrieve access token, and refresh token
|
||||
AccessTokenResponse tokens = signedRequestToken != null ?
|
||||
|
@ -179,14 +160,9 @@ public class ConfigCredentialsCmd extends AbstractAuthOptionsCmd implements Comm
|
|||
|
||||
// save tokens to config file
|
||||
saveTokens(tokens, server, realm, clientId, signedRequestToken, sigExpiresAt, secret, grantTypeForAuthentication);
|
||||
|
||||
return CommandResult.SUCCESS;
|
||||
}
|
||||
|
||||
protected String suggestHelp() {
|
||||
return EOL + "Try '" + CMD + " help config credentials' for more information";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String help() {
|
||||
return usage();
|
||||
}
|
||||
|
|
|
@ -1,107 +1,45 @@
|
|||
package org.keycloak.client.registration.cli.commands;
|
||||
|
||||
import org.jboss.aesh.cl.CommandDefinition;
|
||||
import org.jboss.aesh.console.command.Command;
|
||||
import org.jboss.aesh.console.command.CommandException;
|
||||
import org.jboss.aesh.console.command.CommandResult;
|
||||
import org.jboss.aesh.console.command.invocation.CommandInvocation;
|
||||
import org.keycloak.client.registration.cli.config.RealmConfigData;
|
||||
import org.keycloak.client.registration.cli.util.IoUtil;
|
||||
import org.keycloak.client.registration.cli.util.ParseUtil;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import picocli.CommandLine.Command;
|
||||
import picocli.CommandLine.Option;
|
||||
import picocli.CommandLine.Parameters;
|
||||
|
||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_STRING;
|
||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.saveMergeConfig;
|
||||
import static org.keycloak.client.registration.cli.util.IoUtil.warnfOut;
|
||||
import static org.keycloak.client.registration.cli.util.OsUtil.CMD;
|
||||
import static org.keycloak.client.registration.cli.util.OsUtil.EOL;
|
||||
import static org.keycloak.client.registration.cli.util.OsUtil.OS_ARCH;
|
||||
import static org.keycloak.client.registration.cli.util.OsUtil.PROMPT;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||
*/
|
||||
@CommandDefinition(name = "initial-token", description = "[--server SERVER] --realm REALM [--delete | TOKEN] [ARGUMENTS]")
|
||||
public class ConfigInitialTokenCmd extends AbstractAuthOptionsCmd implements Command {
|
||||
|
||||
private ConfigCmd parent;
|
||||
@Command(name = "initial-token", description = "[--server SERVER] --realm REALM [--delete | TOKEN] [ARGUMENTS]")
|
||||
public class ConfigInitialTokenCmd extends AbstractAuthOptionsCmd {
|
||||
|
||||
@Option(names = {"-d", "--delete"}, description = "Indicates that initial access token should be removed")
|
||||
private boolean delete;
|
||||
@Option(names = {"-k", "--keep-domain"}, description = "Don't overwrite default server and realm")
|
||||
private boolean keepDomain;
|
||||
|
||||
|
||||
protected void initFromParent(ConfigCmd parent) {
|
||||
this.parent = parent;
|
||||
super.initFromParent(parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandResult execute(CommandInvocation commandInvocation) throws CommandException, InterruptedException {
|
||||
try {
|
||||
if (printHelp()) {
|
||||
return help ? CommandResult.SUCCESS : CommandResult.FAILURE;
|
||||
}
|
||||
|
||||
return process(commandInvocation);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IllegalArgumentException(e.getMessage() + suggestHelp(), e);
|
||||
} finally {
|
||||
commandInvocation.stop();
|
||||
}
|
||||
}
|
||||
@Parameters(arity = "0..1")
|
||||
private String token;
|
||||
|
||||
@Override
|
||||
protected boolean nothingToDo() {
|
||||
return noOptions() && parent.args.size() == 1;
|
||||
return noOptions() && token == null && !delete && !keepDomain;
|
||||
}
|
||||
|
||||
public CommandResult process(CommandInvocation commandInvocation) throws CommandException, InterruptedException {
|
||||
|
||||
List<String> args = new ArrayList<>();
|
||||
|
||||
Iterator<String> it = parent.args.iterator();
|
||||
// skip the first argument 'initial-token'
|
||||
it.next();
|
||||
|
||||
while (it.hasNext()) {
|
||||
String arg = it.next();
|
||||
switch (arg) {
|
||||
case "-d":
|
||||
case "--delete": {
|
||||
delete = true;
|
||||
break;
|
||||
}
|
||||
case "-k":
|
||||
case "--keep-domain": {
|
||||
keepDomain = true;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
args.add(arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (args.size() > 1) {
|
||||
throw new IllegalArgumentException("Invalid option: " + args.get(1));
|
||||
}
|
||||
|
||||
String token = args.size() == 1 ? args.get(0) : null;
|
||||
|
||||
if (realm == null) {
|
||||
throw new IllegalArgumentException("Realm not specified");
|
||||
}
|
||||
|
||||
if (token != null && token.startsWith("-")) {
|
||||
warnfOut(ParseUtil.TOKEN_OPTION_WARN, token);
|
||||
}
|
||||
|
||||
checkUnsupportedOptions(
|
||||
@Override
|
||||
protected String[] getUnsupportedOptions() {
|
||||
return new String[] {
|
||||
"--client", clientId,
|
||||
"--user", user,
|
||||
"--password", password,
|
||||
|
@ -112,15 +50,24 @@ public class ConfigInitialTokenCmd extends AbstractAuthOptionsCmd implements Com
|
|||
"--alias", alias,
|
||||
"--truststore", trustStore,
|
||||
"--trustpass", keyPass,
|
||||
"--no-config", booleanOptionForCheck(noconfig));
|
||||
"--no-config", booleanOptionForCheck(noconfig)};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void process() {
|
||||
if (realm == null) {
|
||||
throw new IllegalArgumentException("Realm not specified");
|
||||
}
|
||||
|
||||
if (token != null && token.startsWith("-")) {
|
||||
warnfOut(ParseUtil.TOKEN_OPTION_WARN, token);
|
||||
}
|
||||
|
||||
if (!delete && token == null) {
|
||||
token = IoUtil.readSecret("Enter Initial Access Token: ", commandInvocation);
|
||||
token = IoUtil.readSecret("Enter Initial Access Token: ");
|
||||
}
|
||||
|
||||
// now update the config
|
||||
processGlobalOptions();
|
||||
|
||||
String initialToken = token;
|
||||
saveMergeConfig(config -> {
|
||||
|
@ -138,14 +85,9 @@ public class ConfigInitialTokenCmd extends AbstractAuthOptionsCmd implements Com
|
|||
rdata.setInitialToken(initialToken);
|
||||
}
|
||||
});
|
||||
|
||||
return CommandResult.SUCCESS;
|
||||
}
|
||||
|
||||
protected String suggestHelp() {
|
||||
return EOL + "Try '" + CMD + " help config initial-token' for more information";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String help() {
|
||||
return usage();
|
||||
}
|
||||
|
|
|
@ -1,91 +1,40 @@
|
|||
package org.keycloak.client.registration.cli.commands;
|
||||
|
||||
import org.jboss.aesh.cl.CommandDefinition;
|
||||
import org.jboss.aesh.console.command.Command;
|
||||
import org.jboss.aesh.console.command.CommandException;
|
||||
import org.jboss.aesh.console.command.CommandResult;
|
||||
import org.jboss.aesh.console.command.invocation.CommandInvocation;
|
||||
import org.keycloak.client.registration.cli.config.RealmConfigData;
|
||||
import org.keycloak.client.registration.cli.util.IoUtil;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import picocli.CommandLine.Command;
|
||||
import picocli.CommandLine.Option;
|
||||
import picocli.CommandLine.Parameters;
|
||||
|
||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_STRING;
|
||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.saveMergeConfig;
|
||||
import static org.keycloak.client.registration.cli.util.OsUtil.CMD;
|
||||
import static org.keycloak.client.registration.cli.util.OsUtil.EOL;
|
||||
import static org.keycloak.client.registration.cli.util.OsUtil.OS_ARCH;
|
||||
import static org.keycloak.client.registration.cli.util.OsUtil.PROMPT;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||
*/
|
||||
@CommandDefinition(name = "registration-token", description = "[--server SERVER] --realm REALM --client CLIENT [--delete | TOKEN] [ARGUMENTS]")
|
||||
public class ConfigRegistrationTokenCmd extends AbstractAuthOptionsCmd implements Command {
|
||||
|
||||
private ConfigCmd parent;
|
||||
@Command(name = "registration-token", description = "[--server SERVER] --realm REALM --client CLIENT [--delete | TOKEN] [ARGUMENTS]")
|
||||
public class ConfigRegistrationTokenCmd extends AbstractAuthOptionsCmd {
|
||||
|
||||
@Option(names = {"-d", "--delete"}, description = "Indicates that initial access token should be removed")
|
||||
private boolean delete;
|
||||
|
||||
|
||||
protected void initFromParent(ConfigCmd parent) {
|
||||
this.parent = parent;
|
||||
super.initFromParent(parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandResult execute(CommandInvocation commandInvocation) throws CommandException, InterruptedException {
|
||||
try {
|
||||
if (printHelp()) {
|
||||
return help ? CommandResult.SUCCESS : CommandResult.FAILURE;
|
||||
}
|
||||
|
||||
return process(commandInvocation);
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IllegalArgumentException(e.getMessage() + suggestHelp(), e);
|
||||
} finally {
|
||||
commandInvocation.stop();
|
||||
}
|
||||
}
|
||||
@Parameters(arity = "0..1")
|
||||
private String token;
|
||||
|
||||
@Override
|
||||
protected boolean nothingToDo() {
|
||||
return noOptions() && parent.args.size() == 1;
|
||||
return noOptions() && token == null && !delete;
|
||||
}
|
||||
|
||||
public CommandResult process(CommandInvocation commandInvocation) throws CommandException, InterruptedException {
|
||||
|
||||
List<String> args = new ArrayList<>();
|
||||
|
||||
Iterator<String> it = parent.args.iterator();
|
||||
// skip the first argument 'registration-token'
|
||||
it.next();
|
||||
|
||||
while (it.hasNext()) {
|
||||
String arg = it.next();
|
||||
switch (arg) {
|
||||
case "-d":
|
||||
case "--delete": {
|
||||
delete = true;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
args.add(arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (args.size() > 1) {
|
||||
throw new IllegalArgumentException("Invalid option: " + args.get(1));
|
||||
}
|
||||
|
||||
String token = args.size() == 1 ? args.get(0) : null;
|
||||
|
||||
@Override
|
||||
protected void process() {
|
||||
if (server == null) {
|
||||
throw new IllegalArgumentException("Required option not specified: --server");
|
||||
}
|
||||
|
@ -112,11 +61,10 @@ public class ConfigRegistrationTokenCmd extends AbstractAuthOptionsCmd implement
|
|||
|
||||
|
||||
if (!delete && token == null) {
|
||||
token = IoUtil.readSecret("Enter Registration Access Token: ", commandInvocation);
|
||||
token = IoUtil.readSecret("Enter Registration Access Token: ");
|
||||
}
|
||||
|
||||
// now update the config
|
||||
processGlobalOptions();
|
||||
|
||||
String registrationToken = token;
|
||||
saveMergeConfig(config -> {
|
||||
|
@ -129,14 +77,9 @@ public class ConfigRegistrationTokenCmd extends AbstractAuthOptionsCmd implement
|
|||
config.ensureRealmConfigData(server, realm).getClients().put(clientId, registrationToken);
|
||||
}
|
||||
});
|
||||
|
||||
return CommandResult.SUCCESS;
|
||||
}
|
||||
|
||||
protected String suggestHelp() {
|
||||
return EOL + "Try '" + CMD + " help config registration-token' for more information";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String help() {
|
||||
return usage();
|
||||
}
|
||||
|
|
|
@ -16,96 +16,41 @@
|
|||
*/
|
||||
package org.keycloak.client.registration.cli.commands;
|
||||
|
||||
import org.jboss.aesh.cl.CommandDefinition;
|
||||
import org.jboss.aesh.console.command.Command;
|
||||
import org.jboss.aesh.console.command.CommandException;
|
||||
import org.jboss.aesh.console.command.CommandResult;
|
||||
import org.jboss.aesh.console.command.invocation.CommandInvocation;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import picocli.CommandLine.Command;
|
||||
import picocli.CommandLine.Option;
|
||||
import picocli.CommandLine.Parameters;
|
||||
|
||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_STRING;
|
||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.saveMergeConfig;
|
||||
import static org.keycloak.client.registration.cli.util.IoUtil.readSecret;
|
||||
import static org.keycloak.client.registration.cli.util.OsUtil.CMD;
|
||||
import static org.keycloak.client.registration.cli.util.OsUtil.EOL;
|
||||
import static org.keycloak.client.registration.cli.util.OsUtil.OS_ARCH;
|
||||
import static org.keycloak.client.registration.cli.util.OsUtil.PROMPT;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||
*/
|
||||
@CommandDefinition(name = "truststore", description = "PATH [ARGUMENTS]")
|
||||
public class ConfigTruststoreCmd extends AbstractAuthOptionsCmd implements Command {
|
||||
|
||||
private ConfigCmd parent;
|
||||
@Command(name = "truststore", description = "PATH [ARGUMENTS]")
|
||||
public class ConfigTruststoreCmd extends AbstractAuthOptionsCmd {
|
||||
|
||||
@Option(names = {"-d", "--delete"}, description = "Indicates that initial access token should be removed")
|
||||
private boolean delete;
|
||||
|
||||
|
||||
protected void initFromParent(ConfigCmd parent) {
|
||||
this.parent = parent;
|
||||
super.initFromParent(parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandResult execute(CommandInvocation commandInvocation) throws CommandException, InterruptedException {
|
||||
try {
|
||||
if (printHelp()) {
|
||||
return help ? CommandResult.SUCCESS : CommandResult.FAILURE;
|
||||
}
|
||||
|
||||
return process(commandInvocation);
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IllegalArgumentException(e.getMessage() + suggestHelp(), e);
|
||||
} finally {
|
||||
commandInvocation.stop();
|
||||
}
|
||||
}
|
||||
@Parameters(arity = "0..1")
|
||||
private String truststorePath;
|
||||
|
||||
@Override
|
||||
protected boolean nothingToDo() {
|
||||
return noOptions() && parent.args.size() == 1;
|
||||
return noOptions() && truststorePath == null && !delete;
|
||||
}
|
||||
|
||||
public CommandResult process(CommandInvocation commandInvocation) throws CommandException, InterruptedException {
|
||||
|
||||
List<String> args = new ArrayList<>();
|
||||
|
||||
Iterator<String> it = parent.args.iterator();
|
||||
// skip the first argument 'truststore'
|
||||
it.next();
|
||||
|
||||
while (it.hasNext()) {
|
||||
String arg = it.next();
|
||||
switch (arg) {
|
||||
case "-d":
|
||||
case "--delete": {
|
||||
delete = true;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
args.add(arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (args.size() > 1) {
|
||||
throw new IllegalArgumentException("Invalid option: " + args.get(1));
|
||||
}
|
||||
|
||||
String truststore = null;
|
||||
if (args.size() > 0) {
|
||||
truststore = args.get(0);
|
||||
}
|
||||
|
||||
checkUnsupportedOptions("--server", server,
|
||||
@Override
|
||||
protected String[] getUnsupportedOptions() {
|
||||
return new String[] {"--server", server,
|
||||
"--realm", realm,
|
||||
"--client", clientId,
|
||||
"--user", user,
|
||||
|
@ -115,33 +60,35 @@ public class ConfigTruststoreCmd extends AbstractAuthOptionsCmd implements Comma
|
|||
"--keystore", keystore,
|
||||
"--keypass", keyPass,
|
||||
"--alias", alias,
|
||||
"--no-config", booleanOptionForCheck(noconfig));
|
||||
"--no-config", booleanOptionForCheck(noconfig)};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void process() {
|
||||
// now update the config
|
||||
processGlobalOptions();
|
||||
|
||||
String store;
|
||||
String pass;
|
||||
|
||||
if (!delete) {
|
||||
|
||||
if (truststore == null) {
|
||||
if (truststorePath == null) {
|
||||
throw new IllegalArgumentException("No truststore specified");
|
||||
}
|
||||
|
||||
if (!new File(truststore).isFile()) {
|
||||
throw new RuntimeException("Truststore file not found: " + truststore);
|
||||
if (!new File(truststorePath).isFile()) {
|
||||
throw new RuntimeException("Truststore file not found: " + truststorePath);
|
||||
}
|
||||
|
||||
if ("-".equals(trustPass)) {
|
||||
trustPass = readSecret("Enter truststore password: ", commandInvocation);
|
||||
trustPass = readSecret("Enter truststore password: ");
|
||||
}
|
||||
|
||||
store = truststore;
|
||||
store = truststorePath;
|
||||
pass = trustPass;
|
||||
|
||||
} else {
|
||||
if (truststore != null) {
|
||||
if (truststorePath != null) {
|
||||
throw new IllegalArgumentException("Option --delete is mutually exclusive with specifying a TRUSTSTORE");
|
||||
}
|
||||
if (trustPass != null) {
|
||||
|
@ -155,14 +102,9 @@ public class ConfigTruststoreCmd extends AbstractAuthOptionsCmd implements Comma
|
|||
config.setTruststore(store);
|
||||
config.setTrustpass(pass);
|
||||
});
|
||||
|
||||
return CommandResult.SUCCESS;
|
||||
}
|
||||
|
||||
protected String suggestHelp() {
|
||||
return EOL + "Try '" + CMD + " help config truststore' for more information";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String help() {
|
||||
return usage();
|
||||
}
|
||||
|
|
|
@ -17,19 +17,11 @@
|
|||
|
||||
package org.keycloak.client.registration.cli.commands;
|
||||
|
||||
import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException;
|
||||
import org.jboss.aesh.cl.Arguments;
|
||||
import org.jboss.aesh.cl.CommandDefinition;
|
||||
import org.jboss.aesh.cl.Option;
|
||||
import org.jboss.aesh.console.command.Command;
|
||||
import org.jboss.aesh.console.command.CommandException;
|
||||
import org.jboss.aesh.console.command.CommandResult;
|
||||
import org.jboss.aesh.console.command.invocation.CommandInvocation;
|
||||
import org.keycloak.client.registration.cli.aesh.EndpointTypeConverter;
|
||||
import org.keycloak.client.registration.cli.EndpointTypeConverter;
|
||||
import org.keycloak.client.registration.cli.common.AttributeOperation;
|
||||
import org.keycloak.client.registration.cli.config.ConfigData;
|
||||
import org.keycloak.client.registration.cli.common.CmdStdinContext;
|
||||
import org.keycloak.client.registration.cli.common.EndpointType;
|
||||
import org.keycloak.client.registration.cli.config.ConfigData;
|
||||
import org.keycloak.client.registration.cli.util.HttpUtil;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.oidc.OIDCClientRepresentation;
|
||||
|
@ -39,183 +31,155 @@ import java.io.IOException;
|
|||
import java.io.InputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException;
|
||||
|
||||
import picocli.CommandLine.Command;
|
||||
import picocli.CommandLine.Option;
|
||||
|
||||
import static org.keycloak.client.registration.cli.common.AttributeOperation.Type.SET;
|
||||
import static org.keycloak.client.registration.cli.common.EndpointType.DEFAULT;
|
||||
import static org.keycloak.client.registration.cli.common.EndpointType.OIDC;
|
||||
import static org.keycloak.client.registration.cli.common.EndpointType.SAML2;
|
||||
import static org.keycloak.client.registration.cli.util.AuthUtil.ensureToken;
|
||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_STRING;
|
||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.credentialsAvailable;
|
||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.loadConfig;
|
||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.saveMergeConfig;
|
||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.setRegistrationToken;
|
||||
import static org.keycloak.client.registration.cli.util.HttpUtil.doPost;
|
||||
import static org.keycloak.client.registration.cli.util.HttpUtil.getExpectedContentType;
|
||||
import static org.keycloak.client.registration.cli.util.IoUtil.printErr;
|
||||
import static org.keycloak.client.registration.cli.util.IoUtil.printOut;
|
||||
import static org.keycloak.client.registration.cli.util.IoUtil.readFully;
|
||||
import static org.keycloak.client.registration.cli.util.IoUtil.readSecret;
|
||||
import static org.keycloak.client.registration.cli.util.OsUtil.CMD;
|
||||
import static org.keycloak.client.registration.cli.util.OsUtil.EOL;
|
||||
import static org.keycloak.client.registration.cli.util.OsUtil.OS_ARCH;
|
||||
import static org.keycloak.client.registration.cli.util.OsUtil.PROMPT;
|
||||
import static org.keycloak.client.registration.cli.util.ParseUtil.mergeAttributes;
|
||||
import static org.keycloak.client.registration.cli.util.ParseUtil.parseFileOrStdin;
|
||||
import static org.keycloak.client.registration.cli.util.AuthUtil.ensureToken;
|
||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.setRegistrationToken;
|
||||
import static org.keycloak.client.registration.cli.util.HttpUtil.doPost;
|
||||
import static org.keycloak.client.registration.cli.util.IoUtil.printOut;
|
||||
import static org.keycloak.client.registration.cli.util.ParseUtil.parseKeyVal;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||
*/
|
||||
@CommandDefinition(name = "create", description = "[ARGUMENTS]")
|
||||
public class CreateCmd extends AbstractAuthOptionsCmd implements Command {
|
||||
@Command(name = "create", description = "[ARGUMENTS]")
|
||||
public class CreateCmd extends AbstractAuthOptionsCmd {
|
||||
|
||||
@Option(shortName = 'i', name = "clientId", description = "After creation only print clientId to standard output", hasValue = false)
|
||||
@Option(names = {"-i", "--clientId"}, description = "After creation only print clientId to standard output")
|
||||
protected boolean returnClientId = false;
|
||||
|
||||
@Option(shortName = 'e', name = "endpoint", description = "Endpoint type / document format to use - one of: 'default', 'oidc', 'saml2'",
|
||||
hasValue = true, converter = EndpointTypeConverter.class)
|
||||
@Option(names = {"-e", "--endpoint"}, description = "Endpoint type / document format to use - one of: 'default', 'oidc', 'saml2'", converter = EndpointTypeConverter.class)
|
||||
protected EndpointType regType;
|
||||
|
||||
@Option(shortName = 'f', name = "file", description = "Read object from file or standard input if FILENAME is set to '-'", hasValue = true)
|
||||
@Option(names = {"-f", "--file"}, description = "Read object from file or standard input if FILENAME is set to '-'")
|
||||
protected String file;
|
||||
|
||||
@Option(shortName = 'o', name = "output", description = "After creation output the new client configuration to standard output", hasValue = false)
|
||||
@Option(names = {"-o", "--output"}, description = "After creation output the new client configuration to standard output")
|
||||
protected boolean outputClient = false;
|
||||
|
||||
@Option(shortName = 'c', name = "compressed", description = "Don't pretty print the output", hasValue = false)
|
||||
@Option(names = {"-c", "--compressed"}, description = "Don't pretty print the output")
|
||||
protected boolean compressed = false;
|
||||
|
||||
//@OptionGroup(shortName = 's', name = "set", description = "Set attribute to the specified value")
|
||||
//Map<String, String> attributes = new LinkedHashMap<>();
|
||||
@Option(names = {"-s", "--set"}, description = "Set a specific attribute NAME to a specified value VALUE")
|
||||
List<String> rawSets = new ArrayList<>();
|
||||
|
||||
@Arguments
|
||||
protected List<String> args;
|
||||
List<AttributeOperation> attrs = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public CommandResult execute(CommandInvocation commandInvocation) throws CommandException, InterruptedException {
|
||||
protected void processOptions() {
|
||||
super.processOptions();
|
||||
|
||||
List<AttributeOperation> attrs = new LinkedList<>();
|
||||
for (String set : rawSets) {
|
||||
String[] keyVal = parseKeyVal(set);
|
||||
attrs.add(new AttributeOperation(SET, keyVal[0], keyVal[1]));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void process() {
|
||||
if (file == null && attrs.size() == 0) {
|
||||
throw new IllegalArgumentException("No file nor attribute values specified");
|
||||
}
|
||||
|
||||
if (outputClient && returnClientId) {
|
||||
throw new IllegalArgumentException("Options -o and -i are mutually exclusive");
|
||||
}
|
||||
|
||||
// if --token is specified read it
|
||||
if ("-".equals(token)) {
|
||||
token = readSecret("Enter Initial Access Token: ");
|
||||
}
|
||||
|
||||
CmdStdinContext ctx = new CmdStdinContext();
|
||||
if (file != null) {
|
||||
ctx = parseFileOrStdin(file, regType);
|
||||
}
|
||||
|
||||
if (ctx.getEndpointType() == null) {
|
||||
regType = regType != null ? regType : DEFAULT;
|
||||
ctx.setEndpointType(regType);
|
||||
} else if (regType != null && ctx.getEndpointType() != regType) {
|
||||
throw new RuntimeException("Requested endpoint type not compatible with detected configuration format: " + ctx.getEndpointType());
|
||||
}
|
||||
|
||||
if (attrs.size() > 0) {
|
||||
ctx = mergeAttributes(ctx, attrs);
|
||||
}
|
||||
|
||||
String contentType = getExpectedContentType(ctx.getEndpointType());
|
||||
|
||||
ConfigData config = loadConfig();
|
||||
config = copyWithServerInfo(config);
|
||||
|
||||
if (token == null) {
|
||||
// if initial token is not set, try use the one from configuration
|
||||
token = config.sessionRealmConfigData().getInitialToken();
|
||||
}
|
||||
|
||||
setupTruststore(config);
|
||||
|
||||
String auth = token;
|
||||
if (auth == null) {
|
||||
config = ensureAuthInfo(config);
|
||||
config = copyWithServerInfo(config);
|
||||
if (credentialsAvailable(config)) {
|
||||
auth = ensureToken(config);
|
||||
}
|
||||
}
|
||||
|
||||
auth = auth != null ? "Bearer " + auth : null;
|
||||
|
||||
final String server = config.getServerUrl();
|
||||
final String realm = config.getRealm();
|
||||
|
||||
InputStream response = doPost(server + "/realms/" + realm + "/clients-registrations/" + ctx.getEndpointType().getEndpoint(),
|
||||
contentType, HttpUtil.APPLICATION_JSON, ctx.getContent(), auth);
|
||||
|
||||
try {
|
||||
if (printHelp()) {
|
||||
return help ? CommandResult.SUCCESS : CommandResult.FAILURE;
|
||||
if (ctx.getEndpointType() == DEFAULT || ctx.getEndpointType() == SAML2) {
|
||||
ClientRepresentation client = JsonSerialization.readValue(response, ClientRepresentation.class);
|
||||
outputResult(client.getClientId(), client);
|
||||
|
||||
saveMergeConfig(cfg -> {
|
||||
setRegistrationToken(cfg.ensureRealmConfigData(server, realm), client.getClientId(), client.getRegistrationAccessToken());
|
||||
});
|
||||
} else if (ctx.getEndpointType() == OIDC) {
|
||||
OIDCClientRepresentation client = JsonSerialization.readValue(response, OIDCClientRepresentation.class);
|
||||
outputResult(client.getClientId(), client);
|
||||
|
||||
saveMergeConfig(cfg -> {
|
||||
setRegistrationToken(cfg.ensureRealmConfigData(server, realm), client.getClientId(), client.getRegistrationAccessToken());
|
||||
});
|
||||
} else {
|
||||
printOut("Response from server: " + readFully(response));
|
||||
}
|
||||
|
||||
processGlobalOptions();
|
||||
|
||||
if (args != null) {
|
||||
Iterator<String> it = args.iterator();
|
||||
while (it.hasNext()) {
|
||||
String option = it.next();
|
||||
switch (option) {
|
||||
case "-s":
|
||||
case "--set": {
|
||||
if (!it.hasNext()) {
|
||||
throw new IllegalArgumentException("Option " + option + " requires a value");
|
||||
}
|
||||
String[] keyVal = parseKeyVal(it.next());
|
||||
attrs.add(new AttributeOperation(SET, keyVal[0], keyVal[1]));
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new IllegalArgumentException("Unsupported option: " + option);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (file == null && attrs.size() == 0) {
|
||||
throw new IllegalArgumentException("No file nor attribute values specified");
|
||||
}
|
||||
|
||||
if (outputClient && returnClientId) {
|
||||
throw new IllegalArgumentException("Options -o and -i are mutually exclusive");
|
||||
}
|
||||
|
||||
// if --token is specified read it
|
||||
if ("-".equals(token)) {
|
||||
token = readSecret("Enter Initial Access Token: ", commandInvocation);
|
||||
}
|
||||
|
||||
CmdStdinContext ctx = new CmdStdinContext();
|
||||
if (file != null) {
|
||||
ctx = parseFileOrStdin(file, regType);
|
||||
}
|
||||
|
||||
if (ctx.getEndpointType() == null) {
|
||||
regType = regType != null ? regType : DEFAULT;
|
||||
ctx.setEndpointType(regType);
|
||||
} else if (regType != null && ctx.getEndpointType() != regType) {
|
||||
throw new RuntimeException("Requested endpoint type not compatible with detected configuration format: " + ctx.getEndpointType());
|
||||
}
|
||||
|
||||
if (attrs.size() > 0) {
|
||||
ctx = mergeAttributes(ctx, attrs);
|
||||
}
|
||||
|
||||
String contentType = getExpectedContentType(ctx.getEndpointType());
|
||||
|
||||
ConfigData config = loadConfig();
|
||||
config = copyWithServerInfo(config);
|
||||
|
||||
if (token == null) {
|
||||
// if initial token is not set, try use the one from configuration
|
||||
token = config.sessionRealmConfigData().getInitialToken();
|
||||
}
|
||||
|
||||
setupTruststore(config, commandInvocation);
|
||||
|
||||
String auth = token;
|
||||
if (auth == null) {
|
||||
config = ensureAuthInfo(config, commandInvocation);
|
||||
config = copyWithServerInfo(config);
|
||||
if (credentialsAvailable(config)) {
|
||||
auth = ensureToken(config);
|
||||
}
|
||||
}
|
||||
|
||||
auth = auth != null ? "Bearer " + auth : null;
|
||||
|
||||
final String server = config.getServerUrl();
|
||||
final String realm = config.getRealm();
|
||||
|
||||
InputStream response = doPost(server + "/realms/" + realm + "/clients-registrations/" + ctx.getEndpointType().getEndpoint(),
|
||||
contentType, HttpUtil.APPLICATION_JSON, ctx.getContent(), auth);
|
||||
|
||||
try {
|
||||
if (ctx.getEndpointType() == DEFAULT || ctx.getEndpointType() == SAML2) {
|
||||
ClientRepresentation client = JsonSerialization.readValue(response, ClientRepresentation.class);
|
||||
outputResult(client.getClientId(), client);
|
||||
|
||||
saveMergeConfig(cfg -> {
|
||||
setRegistrationToken(cfg.ensureRealmConfigData(server, realm), client.getClientId(), client.getRegistrationAccessToken());
|
||||
});
|
||||
} else if (ctx.getEndpointType() == OIDC) {
|
||||
OIDCClientRepresentation client = JsonSerialization.readValue(response, OIDCClientRepresentation.class);
|
||||
outputResult(client.getClientId(), client);
|
||||
|
||||
saveMergeConfig(cfg -> {
|
||||
setRegistrationToken(cfg.ensureRealmConfigData(server, realm), client.getClientId(), client.getRegistrationAccessToken());
|
||||
});
|
||||
} else {
|
||||
printOut("Response from server: " + readFully(response));
|
||||
}
|
||||
} catch (UnrecognizedPropertyException e) {
|
||||
throw new RuntimeException("Failed to process HTTP reponse - " + e.getMessage(), e);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to process HTTP response", e);
|
||||
}
|
||||
|
||||
return CommandResult.SUCCESS;
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IllegalArgumentException(e.getMessage() + suggestHelp(), e);
|
||||
} finally {
|
||||
commandInvocation.stop();
|
||||
} catch (UnrecognizedPropertyException e) {
|
||||
throw new RuntimeException("Failed to process HTTP reponse - " + e.getMessage(), e);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to process HTTP response", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -235,13 +199,10 @@ public class CreateCmd extends AbstractAuthOptionsCmd implements Command {
|
|||
|
||||
@Override
|
||||
protected boolean nothingToDo() {
|
||||
return noOptions() && regType == null && file == null && (args == null || args.size() == 0);
|
||||
}
|
||||
|
||||
protected String suggestHelp() {
|
||||
return EOL + "Try '" + CMD + " help create' for more information";
|
||||
return noOptions() && regType == null && file == null && rawSets.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String help() {
|
||||
return usage();
|
||||
}
|
||||
|
|
|
@ -17,17 +17,14 @@
|
|||
|
||||
package org.keycloak.client.registration.cli.commands;
|
||||
|
||||
import org.jboss.aesh.cl.Arguments;
|
||||
import org.jboss.aesh.cl.CommandDefinition;
|
||||
import org.jboss.aesh.console.command.CommandException;
|
||||
import org.jboss.aesh.console.command.CommandResult;
|
||||
import org.jboss.aesh.console.command.invocation.CommandInvocation;
|
||||
import org.keycloak.client.registration.cli.config.ConfigData;
|
||||
import org.keycloak.client.registration.cli.util.ParseUtil;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.List;
|
||||
|
||||
import picocli.CommandLine.Command;
|
||||
import picocli.CommandLine.Parameters;
|
||||
|
||||
import static org.keycloak.client.registration.cli.util.AuthUtil.ensureToken;
|
||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_STRING;
|
||||
|
@ -39,92 +36,68 @@ import static org.keycloak.client.registration.cli.util.HttpUtil.doDelete;
|
|||
import static org.keycloak.client.registration.cli.util.HttpUtil.urlencode;
|
||||
import static org.keycloak.client.registration.cli.util.IoUtil.warnfErr;
|
||||
import static org.keycloak.client.registration.cli.util.OsUtil.CMD;
|
||||
import static org.keycloak.client.registration.cli.util.OsUtil.EOL;
|
||||
import static org.keycloak.client.registration.cli.util.OsUtil.PROMPT;
|
||||
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||
*/
|
||||
@CommandDefinition(name = "delete", description = "CLIENT [GLOBAL_OPTIONS]")
|
||||
@Command(name = "delete", description = "CLIENT [GLOBAL_OPTIONS]")
|
||||
public class DeleteCmd extends AbstractAuthOptionsCmd {
|
||||
|
||||
@Arguments
|
||||
private List<String> args;
|
||||
@Parameters(arity = "0..1")
|
||||
String clientId;
|
||||
|
||||
@Override
|
||||
public CommandResult execute(CommandInvocation commandInvocation) throws CommandException, InterruptedException {
|
||||
try {
|
||||
if (printHelp()) {
|
||||
return help ? CommandResult.SUCCESS : CommandResult.FAILURE;
|
||||
}
|
||||
|
||||
processGlobalOptions();
|
||||
|
||||
if (args == null || args.isEmpty()) {
|
||||
throw new IllegalArgumentException("CLIENT not specified");
|
||||
}
|
||||
|
||||
if (args.size() > 1) {
|
||||
throw new IllegalArgumentException("Invalid option: " + args.get(1));
|
||||
}
|
||||
|
||||
String clientId = args.get(0);
|
||||
|
||||
if (clientId.startsWith("-")) {
|
||||
warnfErr(ParseUtil.CLIENT_OPTION_WARN, clientId);
|
||||
}
|
||||
|
||||
String regType = "default";
|
||||
|
||||
ConfigData config = loadConfig();
|
||||
config = copyWithServerInfo(config);
|
||||
|
||||
if (token == null) {
|
||||
// if registration access token is not set via -t, try use the one from configuration
|
||||
token = getRegistrationToken(config.sessionRealmConfigData(), clientId);
|
||||
}
|
||||
|
||||
setupTruststore(config, commandInvocation);
|
||||
|
||||
String auth = token;
|
||||
if (auth == null) {
|
||||
config = ensureAuthInfo(config, commandInvocation);
|
||||
config = copyWithServerInfo(config);
|
||||
if (credentialsAvailable(config)) {
|
||||
auth = ensureToken(config);
|
||||
}
|
||||
}
|
||||
|
||||
auth = auth != null ? "Bearer " + auth : null;
|
||||
|
||||
|
||||
final String server = config.getServerUrl();
|
||||
final String realm = config.getRealm();
|
||||
|
||||
doDelete(server + "/realms/" + realm + "/clients-registrations/" + regType + "/" + urlencode(clientId), auth);
|
||||
|
||||
saveMergeConfig(cfg -> {
|
||||
cfg.ensureRealmConfigData(server, realm).getClients().remove(clientId);
|
||||
});
|
||||
return CommandResult.SUCCESS;
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IllegalArgumentException(e.getMessage() + suggestHelp(), e);
|
||||
} finally {
|
||||
commandInvocation.stop();
|
||||
protected void process() {
|
||||
if (clientId == null) {
|
||||
throw new IllegalArgumentException("CLIENT not specified");
|
||||
}
|
||||
|
||||
if (clientId.startsWith("-")) {
|
||||
warnfErr(ParseUtil.CLIENT_OPTION_WARN, clientId);
|
||||
}
|
||||
|
||||
String regType = "default";
|
||||
|
||||
ConfigData config = loadConfig();
|
||||
config = copyWithServerInfo(config);
|
||||
|
||||
if (token == null) {
|
||||
// if registration access token is not set via -t, try use the one from configuration
|
||||
token = getRegistrationToken(config.sessionRealmConfigData(), clientId);
|
||||
}
|
||||
|
||||
setupTruststore(config);
|
||||
|
||||
String auth = token;
|
||||
if (auth == null) {
|
||||
config = ensureAuthInfo(config);
|
||||
config = copyWithServerInfo(config);
|
||||
if (credentialsAvailable(config)) {
|
||||
auth = ensureToken(config);
|
||||
}
|
||||
}
|
||||
|
||||
auth = auth != null ? "Bearer " + auth : null;
|
||||
|
||||
|
||||
final String server = config.getServerUrl();
|
||||
final String realm = config.getRealm();
|
||||
|
||||
doDelete(server + "/realms/" + realm + "/clients-registrations/" + regType + "/" + urlencode(clientId), auth);
|
||||
|
||||
saveMergeConfig(cfg -> {
|
||||
cfg.ensureRealmConfigData(server, realm).getClients().remove(clientId);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nothingToDo() {
|
||||
return noOptions() && (args == null || args.size() == 0);
|
||||
}
|
||||
|
||||
protected String suggestHelp() {
|
||||
return EOL + "Try '" + CMD + " help delete' for more information";
|
||||
return noOptions() && clientId == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String help() {
|
||||
return usage();
|
||||
}
|
||||
|
|
|
@ -17,12 +17,6 @@
|
|||
|
||||
package org.keycloak.client.registration.cli.commands;
|
||||
|
||||
import org.jboss.aesh.cl.Arguments;
|
||||
import org.jboss.aesh.cl.CommandDefinition;
|
||||
import org.jboss.aesh.cl.Option;
|
||||
import org.jboss.aesh.console.command.CommandException;
|
||||
import org.jboss.aesh.console.command.CommandResult;
|
||||
import org.jboss.aesh.console.command.invocation.CommandInvocation;
|
||||
import org.keycloak.client.registration.cli.config.ConfigData;
|
||||
import org.keycloak.client.registration.cli.common.EndpointType;
|
||||
import org.keycloak.client.registration.cli.util.ParseUtil;
|
||||
|
@ -35,7 +29,10 @@ import java.io.IOException;
|
|||
import java.io.InputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.List;
|
||||
|
||||
import picocli.CommandLine.Command;
|
||||
import picocli.CommandLine.Option;
|
||||
import picocli.CommandLine.Parameters;
|
||||
|
||||
import static org.keycloak.client.registration.cli.util.AuthUtil.ensureToken;
|
||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_STRING;
|
||||
|
@ -51,142 +48,118 @@ import static org.keycloak.client.registration.cli.util.IoUtil.warnfErr;
|
|||
import static org.keycloak.client.registration.cli.util.IoUtil.printOut;
|
||||
import static org.keycloak.client.registration.cli.util.IoUtil.readFully;
|
||||
import static org.keycloak.client.registration.cli.util.OsUtil.CMD;
|
||||
import static org.keycloak.client.registration.cli.util.OsUtil.EOL;
|
||||
import static org.keycloak.client.registration.cli.util.OsUtil.PROMPT;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||
*/
|
||||
@CommandDefinition(name = "get", description = "[ARGUMENTS]")
|
||||
@Command(name = "get", description = "[ARGUMENTS]")
|
||||
public class GetCmd extends AbstractAuthOptionsCmd {
|
||||
|
||||
@Option(shortName = 'c', name = "compressed", description = "Print full stack trace when exiting with error", hasValue = false)
|
||||
@Option(names = {"-c", "--compressed"}, description = "Print full stack trace when exiting with error")
|
||||
private boolean compressed = false;
|
||||
|
||||
@Option(shortName = 'e', name = "endpoint", description = "Endpoint type to use", hasValue = true)
|
||||
@Option(names = {"-e", "--endpoint"}, description = "Endpoint type to use")
|
||||
private String endpoint;
|
||||
|
||||
@Arguments
|
||||
private List<String> args;
|
||||
@Parameters(arity = "0..1")
|
||||
String clientId;
|
||||
|
||||
@Override
|
||||
public CommandResult execute(CommandInvocation commandInvocation) throws CommandException, InterruptedException {
|
||||
protected void process() {
|
||||
if (clientId == null) {
|
||||
throw new IllegalArgumentException("CLIENT not specified");
|
||||
}
|
||||
|
||||
EndpointType regType = endpoint != null ? EndpointType.of(endpoint) : EndpointType.DEFAULT;
|
||||
|
||||
|
||||
if (clientId.startsWith("-")) {
|
||||
warnfErr(ParseUtil.CLIENT_OPTION_WARN, clientId);
|
||||
}
|
||||
|
||||
ConfigData config = loadConfig();
|
||||
config = copyWithServerInfo(config);
|
||||
|
||||
if (token == null) {
|
||||
// if registration access token is not set via -t, try use the one from configuration
|
||||
token = getRegistrationToken(config.sessionRealmConfigData(), clientId);
|
||||
}
|
||||
|
||||
setupTruststore(config);
|
||||
|
||||
String auth = token;
|
||||
if (auth == null) {
|
||||
config = ensureAuthInfo(config);
|
||||
config = copyWithServerInfo(config);
|
||||
if (credentialsAvailable(config)) {
|
||||
auth = ensureToken(config);
|
||||
}
|
||||
}
|
||||
|
||||
auth = auth != null ? "Bearer " + auth : null;
|
||||
|
||||
|
||||
final String server = config.getServerUrl();
|
||||
final String realm = config.getRealm();
|
||||
|
||||
InputStream response = doGet(server + "/realms/" + realm + "/clients-registrations/" + regType.getEndpoint() + "/" + urlencode(clientId),
|
||||
APPLICATION_JSON, auth);
|
||||
|
||||
try {
|
||||
if (printHelp()) {
|
||||
return help ? CommandResult.SUCCESS : CommandResult.FAILURE;
|
||||
}
|
||||
String json = readFully(response);
|
||||
Object result = null;
|
||||
|
||||
processGlobalOptions();
|
||||
switch (regType) {
|
||||
case DEFAULT: {
|
||||
ClientRepresentation client = JsonSerialization.readValue(json, ClientRepresentation.class);
|
||||
result = client;
|
||||
|
||||
if (args == null || args.isEmpty()) {
|
||||
throw new IllegalArgumentException("CLIENT not specified");
|
||||
}
|
||||
saveMergeConfig(cfg -> {
|
||||
setRegistrationToken(cfg.ensureRealmConfigData(server, realm), client.getClientId(), client.getRegistrationAccessToken());
|
||||
});
|
||||
break;
|
||||
}
|
||||
case OIDC: {
|
||||
OIDCClientRepresentation client = JsonSerialization.readValue(json, OIDCClientRepresentation.class);
|
||||
result = client;
|
||||
|
||||
if (args.size() > 1) {
|
||||
throw new IllegalArgumentException("Invalid option: " + args.get(1));
|
||||
}
|
||||
|
||||
String clientId = args.get(0);
|
||||
EndpointType regType = endpoint != null ? EndpointType.of(endpoint) : EndpointType.DEFAULT;
|
||||
|
||||
|
||||
if (clientId.startsWith("-")) {
|
||||
warnfErr(ParseUtil.CLIENT_OPTION_WARN, clientId);
|
||||
}
|
||||
|
||||
ConfigData config = loadConfig();
|
||||
config = copyWithServerInfo(config);
|
||||
|
||||
if (token == null) {
|
||||
// if registration access token is not set via -t, try use the one from configuration
|
||||
token = getRegistrationToken(config.sessionRealmConfigData(), clientId);
|
||||
}
|
||||
|
||||
setupTruststore(config, commandInvocation);
|
||||
|
||||
String auth = token;
|
||||
if (auth == null) {
|
||||
config = ensureAuthInfo(config, commandInvocation);
|
||||
config = copyWithServerInfo(config);
|
||||
if (credentialsAvailable(config)) {
|
||||
auth = ensureToken(config);
|
||||
saveMergeConfig(cfg -> {
|
||||
setRegistrationToken(cfg.ensureRealmConfigData(server, realm), client.getClientId(), client.getRegistrationAccessToken());
|
||||
});
|
||||
break;
|
||||
}
|
||||
case INSTALL: {
|
||||
result = JsonSerialization.readValue(json, AdapterConfig.class);
|
||||
break;
|
||||
}
|
||||
case SAML2: {
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new RuntimeException("Unexpected type: " + regType);
|
||||
}
|
||||
}
|
||||
|
||||
auth = auth != null ? "Bearer " + auth : null;
|
||||
|
||||
|
||||
final String server = config.getServerUrl();
|
||||
final String realm = config.getRealm();
|
||||
|
||||
InputStream response = doGet(server + "/realms/" + realm + "/clients-registrations/" + regType.getEndpoint() + "/" + urlencode(clientId),
|
||||
APPLICATION_JSON, auth);
|
||||
|
||||
try {
|
||||
String json = readFully(response);
|
||||
Object result = null;
|
||||
|
||||
switch (regType) {
|
||||
case DEFAULT: {
|
||||
ClientRepresentation client = JsonSerialization.readValue(json, ClientRepresentation.class);
|
||||
result = client;
|
||||
|
||||
saveMergeConfig(cfg -> {
|
||||
setRegistrationToken(cfg.ensureRealmConfigData(server, realm), client.getClientId(), client.getRegistrationAccessToken());
|
||||
});
|
||||
break;
|
||||
}
|
||||
case OIDC: {
|
||||
OIDCClientRepresentation client = JsonSerialization.readValue(json, OIDCClientRepresentation.class);
|
||||
result = client;
|
||||
|
||||
saveMergeConfig(cfg -> {
|
||||
setRegistrationToken(cfg.ensureRealmConfigData(server, realm), client.getClientId(), client.getRegistrationAccessToken());
|
||||
});
|
||||
break;
|
||||
}
|
||||
case INSTALL: {
|
||||
result = JsonSerialization.readValue(json, AdapterConfig.class);
|
||||
break;
|
||||
}
|
||||
case SAML2: {
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new RuntimeException("Unexpected type: " + regType);
|
||||
}
|
||||
}
|
||||
|
||||
if (!compressed && result != null) {
|
||||
json = JsonSerialization.writeValueAsPrettyString(result);
|
||||
}
|
||||
|
||||
printOut(json);
|
||||
|
||||
//} catch (UnrecognizedPropertyException e) {
|
||||
// throw new RuntimeException("Failed to parse returned JSON - " + e.getMessage(), e);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to process HTTP response", e);
|
||||
if (!compressed && result != null) {
|
||||
json = JsonSerialization.writeValueAsPrettyString(result);
|
||||
}
|
||||
return CommandResult.SUCCESS;
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IllegalArgumentException(e.getMessage() + suggestHelp(), e);
|
||||
} finally {
|
||||
commandInvocation.stop();
|
||||
printOut(json);
|
||||
|
||||
//} catch (UnrecognizedPropertyException e) {
|
||||
// throw new RuntimeException("Failed to parse returned JSON - " + e.getMessage(), e);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to process HTTP response", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nothingToDo() {
|
||||
return noOptions() && endpoint == null && (args == null || args.size() == 0);
|
||||
}
|
||||
|
||||
protected String suggestHelp() {
|
||||
return EOL + "Try '" + CMD + " help get' for more information";
|
||||
return noOptions() && endpoint == null && clientId == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String help() {
|
||||
return usage();
|
||||
}
|
||||
|
|
|
@ -1,90 +1,80 @@
|
|||
package org.keycloak.client.registration.cli.commands;
|
||||
|
||||
import org.jboss.aesh.cl.Arguments;
|
||||
import org.jboss.aesh.cl.CommandDefinition;
|
||||
import org.jboss.aesh.console.command.Command;
|
||||
import org.jboss.aesh.console.command.CommandException;
|
||||
import org.jboss.aesh.console.command.CommandResult;
|
||||
import org.jboss.aesh.console.command.invocation.CommandInvocation;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import picocli.CommandLine.Command;
|
||||
import picocli.CommandLine.Parameters;
|
||||
|
||||
import static org.keycloak.client.registration.cli.util.IoUtil.printOut;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||
*/
|
||||
@CommandDefinition(name = "help", description = "This help")
|
||||
public class HelpCmd implements Command {
|
||||
@Command(name = "help", description = "This help")
|
||||
public class HelpCmd implements Runnable {
|
||||
|
||||
@Arguments
|
||||
@Parameters
|
||||
private List<String> args;
|
||||
|
||||
@Override
|
||||
public CommandResult execute(CommandInvocation commandInvocation) throws CommandException, InterruptedException {
|
||||
try {
|
||||
if (args == null || args.size() == 0) {
|
||||
printOut(KcRegCmd.usage());
|
||||
} else {
|
||||
outer:
|
||||
switch (args.get(0)) {
|
||||
case "config": {
|
||||
if (args.size() > 1) {
|
||||
switch (args.get(1)) {
|
||||
case "credentials": {
|
||||
printOut(ConfigCredentialsCmd.usage());
|
||||
break outer;
|
||||
}
|
||||
case "initial-token": {
|
||||
printOut(ConfigInitialTokenCmd.usage());
|
||||
break outer;
|
||||
}
|
||||
case "registration-token": {
|
||||
printOut(ConfigRegistrationTokenCmd.usage());
|
||||
break outer;
|
||||
}
|
||||
case "truststore": {
|
||||
printOut(ConfigTruststoreCmd.usage());
|
||||
break outer;
|
||||
}
|
||||
public void run() {
|
||||
if (args == null || args.size() == 0) {
|
||||
printOut(KcRegCmd.usage());
|
||||
} else {
|
||||
outer:
|
||||
switch (args.get(0)) {
|
||||
case "config": {
|
||||
if (args.size() > 1) {
|
||||
switch (args.get(1)) {
|
||||
case "credentials": {
|
||||
printOut(ConfigCredentialsCmd.usage());
|
||||
break outer;
|
||||
}
|
||||
case "initial-token": {
|
||||
printOut(ConfigInitialTokenCmd.usage());
|
||||
break outer;
|
||||
}
|
||||
case "registration-token": {
|
||||
printOut(ConfigRegistrationTokenCmd.usage());
|
||||
break outer;
|
||||
}
|
||||
case "truststore": {
|
||||
printOut(ConfigTruststoreCmd.usage());
|
||||
break outer;
|
||||
}
|
||||
}
|
||||
printOut(ConfigCmd.usage());
|
||||
break;
|
||||
}
|
||||
case "create": {
|
||||
printOut(CreateCmd.usage());
|
||||
break;
|
||||
}
|
||||
case "get": {
|
||||
printOut(GetCmd.usage());
|
||||
break;
|
||||
}
|
||||
case "update": {
|
||||
printOut(UpdateCmd.usage());
|
||||
break;
|
||||
}
|
||||
case "delete": {
|
||||
printOut(DeleteCmd.usage());
|
||||
break;
|
||||
}
|
||||
case "attrs": {
|
||||
printOut(AttrsCmd.usage());
|
||||
break;
|
||||
}
|
||||
case "update-token": {
|
||||
printOut(UpdateTokenCmd.usage());
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new RuntimeException("Unknown command: " + args.get(0));
|
||||
}
|
||||
printOut(ConfigCmd.usage());
|
||||
break;
|
||||
}
|
||||
case "create": {
|
||||
printOut(CreateCmd.usage());
|
||||
break;
|
||||
}
|
||||
case "get": {
|
||||
printOut(GetCmd.usage());
|
||||
break;
|
||||
}
|
||||
case "update": {
|
||||
printOut(UpdateCmd.usage());
|
||||
break;
|
||||
}
|
||||
case "delete": {
|
||||
printOut(DeleteCmd.usage());
|
||||
break;
|
||||
}
|
||||
case "attrs": {
|
||||
printOut(AttrsCmd.usage());
|
||||
break;
|
||||
}
|
||||
case "update-token": {
|
||||
printOut(UpdateTokenCmd.usage());
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new IllegalArgumentException("Unknown command: " + args.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
return CommandResult.SUCCESS;
|
||||
} finally {
|
||||
commandInvocation.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,14 +16,11 @@
|
|||
*/
|
||||
package org.keycloak.client.registration.cli.commands;
|
||||
|
||||
import org.jboss.aesh.cl.GroupCommandDefinition;
|
||||
import org.jboss.aesh.console.command.CommandException;
|
||||
import org.jboss.aesh.console.command.CommandResult;
|
||||
import org.jboss.aesh.console.command.invocation.CommandInvocation;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
|
||||
import picocli.CommandLine.Command;
|
||||
|
||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_STRING;
|
||||
import static org.keycloak.client.registration.cli.util.IoUtil.printOut;
|
||||
import static org.keycloak.client.registration.cli.util.OsUtil.CMD;
|
||||
|
@ -33,27 +30,30 @@ import static org.keycloak.client.registration.cli.util.OsUtil.PROMPT;
|
|||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||
*/
|
||||
|
||||
@GroupCommandDefinition(name = "kcreg", description = "COMMAND [ARGUMENTS]", groupCommands = {
|
||||
HelpCmd.class, ConfigCmd.class, CreateCmd.class, UpdateCmd.class, GetCmd.class, DeleteCmd.class, AttrsCmd.class, UpdateTokenCmd.class} )
|
||||
@Command(name = "kcreg",
|
||||
header = {
|
||||
"Keycloak - Open Source Identity and Access Management",
|
||||
"",
|
||||
"Find more information at: https://www.keycloak.org/docs/latest"
|
||||
},
|
||||
description = {
|
||||
"%nCOMMAND [ARGUMENTS]"
|
||||
},
|
||||
subcommands = {
|
||||
HelpCmd.class,
|
||||
ConfigCmd.class,
|
||||
CreateCmd.class,
|
||||
GetCmd.class,
|
||||
UpdateCmd.class,
|
||||
DeleteCmd.class,
|
||||
AttrsCmd.class,
|
||||
UpdateTokenCmd.class
|
||||
})
|
||||
public class KcRegCmd extends AbstractGlobalOptionsCmd {
|
||||
|
||||
//@Arguments
|
||||
//private List<String> args;
|
||||
|
||||
|
||||
@Override
|
||||
public CommandResult execute(CommandInvocation commandInvocation) throws CommandException, InterruptedException {
|
||||
try {
|
||||
// if --help was requested then status is SUCCESS
|
||||
// if not we print help anyway, but status is FAILURE
|
||||
if (printHelp()) {
|
||||
return CommandResult.SUCCESS;
|
||||
} else {
|
||||
printOut(usage());
|
||||
return CommandResult.FAILURE;
|
||||
}
|
||||
} finally {
|
||||
commandInvocation.stop();
|
||||
}
|
||||
protected boolean nothingToDo() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static String usage() {
|
||||
|
|
|
@ -18,13 +18,13 @@
|
|||
package org.keycloak.client.registration.cli.commands;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParseException;
|
||||
import org.jboss.aesh.cl.Arguments;
|
||||
import org.jboss.aesh.cl.CommandDefinition;
|
||||
import org.jboss.aesh.cl.Option;
|
||||
import org.jboss.aesh.console.command.CommandException;
|
||||
import org.jboss.aesh.console.command.CommandResult;
|
||||
import org.jboss.aesh.console.command.invocation.CommandInvocation;
|
||||
import org.keycloak.client.registration.cli.aesh.EndpointTypeConverter;
|
||||
|
||||
import picocli.CommandLine.ArgGroup;
|
||||
import picocli.CommandLine.Command;
|
||||
import picocli.CommandLine.Option;
|
||||
import picocli.CommandLine.Parameters;
|
||||
|
||||
import org.keycloak.client.registration.cli.EndpointTypeConverter;
|
||||
import org.keycloak.client.registration.cli.common.AttributeOperation;
|
||||
import org.keycloak.client.registration.cli.config.ConfigData;
|
||||
import org.keycloak.client.registration.cli.common.CmdStdinContext;
|
||||
|
@ -39,7 +39,7 @@ import java.io.IOException;
|
|||
import java.io.InputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.Iterator;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -62,7 +62,6 @@ import static org.keycloak.client.registration.cli.util.IoUtil.warnfErr;
|
|||
import static org.keycloak.client.registration.cli.util.IoUtil.readFully;
|
||||
import static org.keycloak.client.registration.cli.util.HttpUtil.APPLICATION_JSON;
|
||||
import static org.keycloak.client.registration.cli.util.OsUtil.CMD;
|
||||
import static org.keycloak.client.registration.cli.util.OsUtil.EOL;
|
||||
import static org.keycloak.client.registration.cli.util.OsUtil.PROMPT;
|
||||
import static org.keycloak.client.registration.cli.util.ParseUtil.mergeAttributes;
|
||||
import static org.keycloak.client.registration.cli.util.ParseUtil.parseFileOrStdin;
|
||||
|
@ -71,258 +70,231 @@ import static org.keycloak.client.registration.cli.util.ParseUtil.parseKeyVal;
|
|||
/**
|
||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||
*/
|
||||
@CommandDefinition(name = "update", description = "CLIENT_ID [ARGUMENTS]")
|
||||
@Command(name = "update", description = "CLIENT_ID [ARGUMENTS]")
|
||||
public class UpdateCmd extends AbstractAuthOptionsCmd {
|
||||
|
||||
@Option(shortName = 'e', name = "endpoint", description = "Endpoint type to use - one of: 'default', 'oidc'", hasValue = true, converter = EndpointTypeConverter.class)
|
||||
@Option(names = {"-e", "--endpoint"}, description = "Endpoint type to use - one of: 'default', 'oidc'", converter = EndpointTypeConverter.class)
|
||||
private EndpointType regType = null;
|
||||
|
||||
//@GroupOption(shortName = 's', name = "set", description = "Set specific attribute to a specified value", hasValue = true)
|
||||
//private List<String> attributes = new ArrayList<>();
|
||||
|
||||
@Option(shortName = 'f', name = "file", description = "Use the file or standard input if '-' is specified", hasValue = true)
|
||||
@Option(names = {"-f", "--file"}, description = "Use the file or standard input if '-' is specified")
|
||||
private String file = null;
|
||||
|
||||
@Option(shortName = 'm', name = "merge", description = "Merge new values with existing configuration on the server", hasValue = false)
|
||||
private boolean mergeMode = true;
|
||||
@Option(names = {"-m", "--merge"}, description = "Merge new values with existing configuration on the server")
|
||||
private boolean mergeMode = false;
|
||||
|
||||
@Option(shortName = 'o', name = "output", description = "After update output the new client configuration", hasValue = false)
|
||||
@Option(names = {"-o", "--output"}, description = "After update output the new client configuration")
|
||||
private boolean outputClient = false;
|
||||
|
||||
@Option(shortName = 'c', name = "compressed", description = "Don't pretty print the output", hasValue = false)
|
||||
@Option(names = {"-c", "--compressed"}, description = "Don't pretty print the output")
|
||||
private boolean compressed = false;
|
||||
|
||||
@Arguments
|
||||
private List<String> args;
|
||||
@Parameters(arity = "0..1")
|
||||
String clientId;
|
||||
|
||||
// to maintain relative positions of set and delete operations
|
||||
static class AttributeOperations {
|
||||
@Option(names = {"-s", "--set"}, required = true) String set;
|
||||
@Option(names = {"-d", "--delete"}, required = true) String delete;
|
||||
}
|
||||
|
||||
@ArgGroup(exclusive = true, multiplicity = "0..*")
|
||||
List<AttributeOperations> rawAttributeOperations = new ArrayList<>();
|
||||
|
||||
List<AttributeOperation> attrs = new LinkedList<>();
|
||||
|
||||
@Override
|
||||
public CommandResult execute(CommandInvocation commandInvocation) throws CommandException, InterruptedException {
|
||||
protected void processOptions() {
|
||||
super.processOptions();
|
||||
|
||||
List<AttributeOperation> attrs = new LinkedList<>();
|
||||
|
||||
try {
|
||||
if (printHelp()) {
|
||||
return help ? CommandResult.SUCCESS : CommandResult.FAILURE;
|
||||
for (AttributeOperations entry : rawAttributeOperations) {
|
||||
if (entry.delete != null) {
|
||||
attrs.add(new AttributeOperation(DELETE, entry.delete));
|
||||
} else {
|
||||
String[] keyVal = parseKeyVal(entry.set);
|
||||
attrs.add(new AttributeOperation(SET, keyVal[0], keyVal[1]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
processGlobalOptions();
|
||||
@Override
|
||||
protected void process() {
|
||||
if (clientId == null) {
|
||||
throw new IllegalArgumentException("CLIENT_ID not specified");
|
||||
}
|
||||
|
||||
String clientId = null;
|
||||
if (clientId.startsWith("-")) {
|
||||
warnfErr(ParseUtil.CLIENT_OPTION_WARN, clientId);
|
||||
}
|
||||
|
||||
if (args != null) {
|
||||
Iterator<String> it = args.iterator();
|
||||
if (!it.hasNext()) {
|
||||
throw new IllegalArgumentException("CLIENT_ID not specified");
|
||||
}
|
||||
if (file == null && attrs.size() == 0) {
|
||||
throw new IllegalArgumentException("No file nor attribute values specified");
|
||||
}
|
||||
|
||||
clientId = it.next();
|
||||
// We have several options for update:
|
||||
//
|
||||
// A) if a file is specified, then we can overwrite server state with that file
|
||||
// (that's the normal flow - get and save locally, edit, post to server)
|
||||
//
|
||||
// update my_client -f new_client.json
|
||||
//
|
||||
// B) if a file is specified, and overrides are specified, then we override the file values with those from command line
|
||||
// (that allows us to have a local file as a template, it's also batch job friendly)
|
||||
//
|
||||
// update my_client -s public=true -s enableDirectGrants=false -f new_client.json
|
||||
//
|
||||
// C) if no file is specified, then we can fetch the client definition from server, apply changes to it, and post it back
|
||||
// (again a batch job friendly mode)
|
||||
//
|
||||
// update my_client -s public=true -s enableDirectGrants=false
|
||||
//
|
||||
// This is merge mode by default - if --merge is additionally specified, it is ignored
|
||||
//
|
||||
// D) if a file is specified, then we can merge the file with current state on the server
|
||||
// (that is similar to previous mode except that the overrides are also taken from a file)
|
||||
//
|
||||
// update my_client --merge -f new_client.json
|
||||
// update my_client --merge -s public=true -s enableDirectGrants=false -f new_client.json
|
||||
//
|
||||
// We could also support environment variables in input file, and apply them before parsing it.
|
||||
//
|
||||
// One problem - what if it is SAML XML? No problem as we don't support update for SAML - only create.
|
||||
//
|
||||
if (file == null && attrs.size() > 0) {
|
||||
mergeMode = true;
|
||||
}
|
||||
|
||||
if (clientId.startsWith("-")) {
|
||||
warnfErr(ParseUtil.CLIENT_OPTION_WARN, clientId);
|
||||
}
|
||||
CmdStdinContext ctx = new CmdStdinContext();
|
||||
if (file != null) {
|
||||
ctx = parseFileOrStdin(file, regType);
|
||||
regType = ctx.getEndpointType();
|
||||
}
|
||||
|
||||
while (it.hasNext()) {
|
||||
String option = it.next();
|
||||
switch (option) {
|
||||
case "-s":
|
||||
case "--set": {
|
||||
if (!it.hasNext()) {
|
||||
throw new IllegalArgumentException("Option " + option + " requires a value");
|
||||
}
|
||||
String[] keyVal = parseKeyVal(it.next());
|
||||
attrs.add(new AttributeOperation(SET, keyVal[0], keyVal[1]));
|
||||
break;
|
||||
}
|
||||
case "-d":
|
||||
case "--delete": {
|
||||
attrs.add(new AttributeOperation(DELETE, it.next()));
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new IllegalArgumentException("Unsupported option: " + option);
|
||||
}
|
||||
if (regType == null) {
|
||||
regType = DEFAULT;
|
||||
ctx.setEndpointType(regType);
|
||||
} else if (regType != DEFAULT && regType != OIDC) {
|
||||
throw new RuntimeException("Update not supported for endpoint type: " + regType.getEndpoint());
|
||||
}
|
||||
|
||||
// initialize config only after reading from stdin,
|
||||
// to allow proper operation when piping 'get' - which consumes the old
|
||||
// registration access token, and saves the new one to the config
|
||||
ConfigData config = loadConfig();
|
||||
config = copyWithServerInfo(config);
|
||||
|
||||
final String server = config.getServerUrl();
|
||||
final String realm = config.getRealm();
|
||||
|
||||
if (token == null) {
|
||||
// if registration access token is not set via --token, see if it's in the body of any input file
|
||||
// but first see if it's overridden by --set, or maybe deliberately muted via -d registrationAccessToken
|
||||
boolean processed = false;
|
||||
for (AttributeOperation op: attrs) {
|
||||
if ("registrationAccessToken".equals(op.getKey().toString())) {
|
||||
processed = true;
|
||||
if (op.getType() == AttributeOperation.Type.SET) {
|
||||
token = op.getValue();
|
||||
}
|
||||
// otherwise it's delete - meaning it should stay null
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (file == null && attrs.size() == 0) {
|
||||
throw new IllegalArgumentException("No file nor attribute values specified");
|
||||
if (!processed) {
|
||||
token = ctx.getRegistrationAccessToken();
|
||||
}
|
||||
}
|
||||
|
||||
// We have several options for update:
|
||||
//
|
||||
// A) if a file is specified, then we can overwrite server state with that file
|
||||
// (that's the normal flow - get and save locally, edit, post to server)
|
||||
//
|
||||
// update my_client -f new_client.json
|
||||
//
|
||||
// B) if a file is specified, and overrides are specified, then we override the file values with those from command line
|
||||
// (that allows us to have a local file as a template, it's also batch job friendly)
|
||||
//
|
||||
// update my_client -s public=true -s enableDirectGrants=false -f new_client.json
|
||||
//
|
||||
// C) if no file is specified, then we can fetch the client definition from server, apply changes to it, and post it back
|
||||
// (again a batch job friendly mode)
|
||||
//
|
||||
// update my_client -s public=true -s enableDirectGrants=false
|
||||
//
|
||||
// This is merge mode by default - if --merge is additionally specified, it is ignored
|
||||
//
|
||||
// D) if a file is specified, then we can merge the file with current state on the server
|
||||
// (that is similar to previous mode except that the overrides are also taken from a file)
|
||||
//
|
||||
// update my_client --merge -f new_client.json
|
||||
// update my_client --merge -s public=true -s enableDirectGrants=false -f new_client.json
|
||||
//
|
||||
// We could also support environment variables in input file, and apply them before parsing it.
|
||||
//
|
||||
// One problem - what if it is SAML XML? No problem as we don't support update for SAML - only create.
|
||||
//
|
||||
if (file == null && attrs.size() > 0) {
|
||||
mergeMode = true;
|
||||
}
|
||||
if (token == null) {
|
||||
// if registration access token is not set, try use the one from configuration
|
||||
token = getRegistrationToken(config.sessionRealmConfigData(), clientId);
|
||||
}
|
||||
|
||||
CmdStdinContext ctx = new CmdStdinContext();
|
||||
if (file != null) {
|
||||
ctx = parseFileOrStdin(file, regType);
|
||||
regType = ctx.getEndpointType();
|
||||
}
|
||||
setupTruststore(config);
|
||||
|
||||
if (regType == null) {
|
||||
regType = DEFAULT;
|
||||
ctx.setEndpointType(regType);
|
||||
} else if (regType != DEFAULT && regType != OIDC) {
|
||||
throw new RuntimeException("Update not supported for endpoint type: " + regType.getEndpoint());
|
||||
}
|
||||
|
||||
// initialize config only after reading from stdin,
|
||||
// to allow proper operation when piping 'get' - which consumes the old
|
||||
// registration access token, and saves the new one to the config
|
||||
ConfigData config = loadConfig();
|
||||
String auth = token;
|
||||
if (auth == null) {
|
||||
config = ensureAuthInfo(config);
|
||||
config = copyWithServerInfo(config);
|
||||
|
||||
final String server = config.getServerUrl();
|
||||
final String realm = config.getRealm();
|
||||
|
||||
if (token == null) {
|
||||
// if registration access token is not set via --token, see if it's in the body of any input file
|
||||
// but first see if it's overridden by --set, or maybe deliberately muted via -d registrationAccessToken
|
||||
boolean processed = false;
|
||||
for (AttributeOperation op: attrs) {
|
||||
if ("registrationAccessToken".equals(op.getKey().toString())) {
|
||||
processed = true;
|
||||
if (op.getType() == AttributeOperation.Type.SET) {
|
||||
token = op.getValue();
|
||||
}
|
||||
// otherwise it's delete - meaning it should stay null
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!processed) {
|
||||
token = ctx.getRegistrationAccessToken();
|
||||
}
|
||||
if (credentialsAvailable(config)) {
|
||||
auth = ensureToken(config);
|
||||
}
|
||||
}
|
||||
|
||||
if (token == null) {
|
||||
// if registration access token is not set, try use the one from configuration
|
||||
token = getRegistrationToken(config.sessionRealmConfigData(), clientId);
|
||||
}
|
||||
|
||||
setupTruststore(config, commandInvocation);
|
||||
|
||||
String auth = token;
|
||||
if (auth == null) {
|
||||
config = ensureAuthInfo(config, commandInvocation);
|
||||
config = copyWithServerInfo(config);
|
||||
if (credentialsAvailable(config)) {
|
||||
auth = ensureToken(config);
|
||||
}
|
||||
}
|
||||
|
||||
auth = auth != null ? "Bearer " + auth : null;
|
||||
auth = auth != null ? "Bearer " + auth : null;
|
||||
|
||||
|
||||
if (mergeMode) {
|
||||
InputStream response = doGet(server + "/realms/" + realm + "/clients-registrations/" + regType.getEndpoint() + "/" + urlencode(clientId),
|
||||
APPLICATION_JSON, auth);
|
||||
if (mergeMode) {
|
||||
InputStream response = doGet(server + "/realms/" + realm + "/clients-registrations/" + regType.getEndpoint() + "/" + urlencode(clientId),
|
||||
APPLICATION_JSON, auth);
|
||||
|
||||
String json = readFully(response);
|
||||
String json = readFully(response);
|
||||
|
||||
CmdStdinContext ctxremote = new CmdStdinContext();
|
||||
ctxremote.setContent(json);
|
||||
ctxremote.setEndpointType(regType);
|
||||
try {
|
||||
|
||||
if (regType == DEFAULT) {
|
||||
ctxremote.setClient(JsonSerialization.readValue(json, ClientRepresentation.class));
|
||||
token = ctxremote.getClient().getRegistrationAccessToken();
|
||||
} else if (regType == OIDC) {
|
||||
ctxremote.setOidcClient(JsonSerialization.readValue(json, OIDCClientRepresentation.class));
|
||||
token = ctxremote.getOidcClient().getRegistrationAccessToken();
|
||||
}
|
||||
} catch (JsonParseException e) {
|
||||
throw new RuntimeException("Not a valid JSON document. " + e.getMessage(), e);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Not a valid JSON document", e);
|
||||
}
|
||||
|
||||
// we have to use registration access token retrieved from previous operation
|
||||
// that ensures optimistic locking semantics
|
||||
if (token != null) {
|
||||
// we use auth with doPost later
|
||||
auth = "Bearer " + token;
|
||||
|
||||
String newToken = token;
|
||||
String clientToUpdate = clientId;
|
||||
saveMergeConfig(cfg -> {
|
||||
setRegistrationToken(cfg.ensureRealmConfigData(server, realm), clientToUpdate, newToken);
|
||||
});
|
||||
}
|
||||
|
||||
// merge local representation over remote one
|
||||
if (ctx.getClient() != null) {
|
||||
ReflectionUtil.merge(ctx.getClient(), ctxremote.getClient());
|
||||
} else if (ctx.getOidcClient() != null) {
|
||||
ReflectionUtil.merge(ctx.getOidcClient(), ctxremote.getOidcClient());
|
||||
}
|
||||
ctx = ctxremote;
|
||||
}
|
||||
|
||||
if (attrs.size() > 0) {
|
||||
ctx = mergeAttributes(ctx, attrs);
|
||||
}
|
||||
|
||||
// now update
|
||||
InputStream response = doPut(server + "/realms/" + realm + "/clients-registrations/" + regType.getEndpoint() + "/" + urlencode(clientId),
|
||||
APPLICATION_JSON, APPLICATION_JSON, ctx.getContent(), auth);
|
||||
CmdStdinContext ctxremote = new CmdStdinContext();
|
||||
ctxremote.setContent(json);
|
||||
ctxremote.setEndpointType(regType);
|
||||
try {
|
||||
|
||||
if (regType == DEFAULT) {
|
||||
ClientRepresentation clirep = JsonSerialization.readValue(response, ClientRepresentation.class);
|
||||
outputResult(clirep);
|
||||
token = clirep.getRegistrationAccessToken();
|
||||
ctxremote.setClient(JsonSerialization.readValue(json, ClientRepresentation.class));
|
||||
token = ctxremote.getClient().getRegistrationAccessToken();
|
||||
} else if (regType == OIDC) {
|
||||
OIDCClientRepresentation clirep = JsonSerialization.readValue(response, OIDCClientRepresentation.class);
|
||||
outputResult(clirep);
|
||||
token = clirep.getRegistrationAccessToken();
|
||||
ctxremote.setOidcClient(JsonSerialization.readValue(json, OIDCClientRepresentation.class));
|
||||
token = ctxremote.getOidcClient().getRegistrationAccessToken();
|
||||
}
|
||||
} catch (JsonParseException e) {
|
||||
throw new RuntimeException("Not a valid JSON document. " + e.getMessage(), e);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Not a valid JSON document", e);
|
||||
}
|
||||
|
||||
// we have to use registration access token retrieved from previous operation
|
||||
// that ensures optimistic locking semantics
|
||||
if (token != null) {
|
||||
// we use auth with doPost later
|
||||
auth = "Bearer " + token;
|
||||
|
||||
String newToken = token;
|
||||
String clientToUpdate = clientId;
|
||||
saveMergeConfig(cfg -> {
|
||||
setRegistrationToken(cfg.ensureRealmConfigData(server, realm), clientToUpdate, newToken);
|
||||
});
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to process HTTP response", e);
|
||||
}
|
||||
|
||||
return CommandResult.SUCCESS;
|
||||
// merge local representation over remote one
|
||||
if (ctx.getClient() != null) {
|
||||
ReflectionUtil.merge(ctx.getClient(), ctxremote.getClient());
|
||||
} else if (ctx.getOidcClient() != null) {
|
||||
ReflectionUtil.merge(ctx.getOidcClient(), ctxremote.getOidcClient());
|
||||
}
|
||||
ctx = ctxremote;
|
||||
}
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IllegalArgumentException(e.getMessage() + suggestHelp(), e);
|
||||
} finally {
|
||||
commandInvocation.stop();
|
||||
if (attrs.size() > 0) {
|
||||
ctx = mergeAttributes(ctx, attrs);
|
||||
}
|
||||
|
||||
// now update
|
||||
InputStream response = doPut(server + "/realms/" + realm + "/clients-registrations/" + regType.getEndpoint() + "/" + urlencode(clientId),
|
||||
APPLICATION_JSON, APPLICATION_JSON, ctx.getContent(), auth);
|
||||
try {
|
||||
if (regType == DEFAULT) {
|
||||
ClientRepresentation clirep = JsonSerialization.readValue(response, ClientRepresentation.class);
|
||||
outputResult(clirep);
|
||||
token = clirep.getRegistrationAccessToken();
|
||||
} else if (regType == OIDC) {
|
||||
OIDCClientRepresentation clirep = JsonSerialization.readValue(response, OIDCClientRepresentation.class);
|
||||
outputResult(clirep);
|
||||
token = clirep.getRegistrationAccessToken();
|
||||
}
|
||||
|
||||
String newToken = token;
|
||||
String clientToUpdate = clientId;
|
||||
saveMergeConfig(cfg -> {
|
||||
setRegistrationToken(cfg.ensureRealmConfigData(server, realm), clientToUpdate, newToken);
|
||||
});
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to process HTTP response", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -338,13 +310,10 @@ public class UpdateCmd extends AbstractAuthOptionsCmd {
|
|||
|
||||
@Override
|
||||
protected boolean nothingToDo() {
|
||||
return noOptions() && regType == null && file == null && (args == null || args.size() == 0);
|
||||
}
|
||||
|
||||
protected String suggestHelp() {
|
||||
return EOL + "Try '" + CMD + " help update' for more information";
|
||||
return noOptions() && regType == null && file == null && rawAttributeOperations.isEmpty() && clientId == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String help() {
|
||||
return usage();
|
||||
}
|
||||
|
|
|
@ -18,11 +18,10 @@
|
|||
package org.keycloak.client.registration.cli.commands;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import org.jboss.aesh.cl.Arguments;
|
||||
import org.jboss.aesh.cl.CommandDefinition;
|
||||
import org.jboss.aesh.console.command.CommandException;
|
||||
import org.jboss.aesh.console.command.CommandResult;
|
||||
import org.jboss.aesh.console.command.invocation.CommandInvocation;
|
||||
|
||||
import picocli.CommandLine.Command;
|
||||
import picocli.CommandLine.Parameters;
|
||||
|
||||
import org.keycloak.client.registration.cli.config.ConfigData;
|
||||
import org.keycloak.client.registration.cli.util.ParseUtil;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
|
@ -45,105 +44,82 @@ import static org.keycloak.client.registration.cli.util.HttpUtil.doPost;
|
|||
import static org.keycloak.client.registration.cli.util.IoUtil.printOut;
|
||||
import static org.keycloak.client.registration.cli.util.IoUtil.warnfOut;
|
||||
import static org.keycloak.client.registration.cli.util.OsUtil.CMD;
|
||||
import static org.keycloak.client.registration.cli.util.OsUtil.EOL;
|
||||
import static org.keycloak.client.registration.cli.util.OsUtil.PROMPT;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||
*/
|
||||
@CommandDefinition(name = "update-token", description = "CLIENT [ARGUMENTS]")
|
||||
@Command(name = "update-token", description = "CLIENT [ARGUMENTS]")
|
||||
public class UpdateTokenCmd extends AbstractAuthOptionsCmd {
|
||||
|
||||
@Arguments
|
||||
private List<String> args;
|
||||
@Parameters(arity = "0..1")
|
||||
String clientId;
|
||||
|
||||
@Override
|
||||
public CommandResult execute(CommandInvocation commandInvocation) throws CommandException, InterruptedException {
|
||||
protected void process() {
|
||||
if (clientId == null) {
|
||||
throw new IllegalArgumentException("CLIENT not specified");
|
||||
}
|
||||
|
||||
if (clientId.startsWith("-")) {
|
||||
warnfOut(ParseUtil.CLIENT_OPTION_WARN, clientId);
|
||||
}
|
||||
|
||||
ConfigData config = loadConfig();
|
||||
config = copyWithServerInfo(config);
|
||||
setupTruststore(config);
|
||||
|
||||
config = ensureAuthInfo(config);
|
||||
String auth = ensureToken(config);
|
||||
|
||||
String cid = null;
|
||||
|
||||
final String server = config.getServerUrl();
|
||||
final String realm = config.getRealm();
|
||||
|
||||
// first we need to get id of the client with client_id == clientId
|
||||
InputStream response = doGet(server + "/admin/realms/" + realm + "/clients", APPLICATION_JSON, "Bearer " + auth);
|
||||
try {
|
||||
List<ClientRepresentation> clients = JsonSerialization.readValue(response, new TypeReference<List<ClientRepresentation>>() {});
|
||||
for (ClientRepresentation client: clients) {
|
||||
if (clientId.equals(client.getClientId())) {
|
||||
cid = client.getId();
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to process response from server", e);
|
||||
}
|
||||
|
||||
if (cid == null) {
|
||||
throw new RuntimeException("No client found for: " + clientId);
|
||||
}
|
||||
|
||||
response = doPost(server + "/admin/realms/" + realm + "/clients/" + cid + "/registration-access-token",
|
||||
APPLICATION_JSON, APPLICATION_JSON, null, "Bearer " + auth);
|
||||
|
||||
try {
|
||||
if (printHelp()) {
|
||||
return help ? CommandResult.SUCCESS : CommandResult.FAILURE;
|
||||
ClientRepresentation client = JsonSerialization.readValue(response, ClientRepresentation.class);
|
||||
|
||||
if (noconfig) {
|
||||
// output to stdout
|
||||
printOut(client.getRegistrationAccessToken());
|
||||
} else {
|
||||
saveMergeConfig(cfg -> {
|
||||
setRegistrationToken(cfg.ensureRealmConfigData(server, realm), client.getClientId(), client.getRegistrationAccessToken());
|
||||
});
|
||||
}
|
||||
|
||||
processGlobalOptions();
|
||||
|
||||
if (args == null || args.isEmpty()) {
|
||||
throw new IllegalArgumentException("CLIENT not specified");
|
||||
}
|
||||
|
||||
String clientId = args.get(0);
|
||||
|
||||
if (clientId.startsWith("-")) {
|
||||
warnfOut(ParseUtil.CLIENT_OPTION_WARN, clientId);
|
||||
}
|
||||
|
||||
ConfigData config = loadConfig();
|
||||
config = copyWithServerInfo(config);
|
||||
setupTruststore(config, commandInvocation);
|
||||
|
||||
config = ensureAuthInfo(config, commandInvocation);
|
||||
String auth = ensureToken(config);
|
||||
|
||||
String cid = null;
|
||||
|
||||
final String server = config.getServerUrl();
|
||||
final String realm = config.getRealm();
|
||||
|
||||
// first we need to get id of the client with client_id == clientId
|
||||
InputStream response = doGet(server + "/admin/realms/" + realm + "/clients", APPLICATION_JSON, "Bearer " + auth);
|
||||
try {
|
||||
List<ClientRepresentation> clients = JsonSerialization.readValue(response, new TypeReference<List<ClientRepresentation>>() {});
|
||||
for (ClientRepresentation client: clients) {
|
||||
if (clientId.equals(client.getClientId())) {
|
||||
cid = client.getId();
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to process response from server", e);
|
||||
}
|
||||
|
||||
if (cid == null) {
|
||||
throw new RuntimeException("No client found for: " + clientId);
|
||||
}
|
||||
|
||||
response = doPost(server + "/admin/realms/" + realm + "/clients/" + cid + "/registration-access-token",
|
||||
APPLICATION_JSON, APPLICATION_JSON, null, "Bearer " + auth);
|
||||
|
||||
try {
|
||||
ClientRepresentation client = JsonSerialization.readValue(response, ClientRepresentation.class);
|
||||
|
||||
if (noconfig) {
|
||||
// output to stdout
|
||||
printOut(client.getRegistrationAccessToken());
|
||||
} else {
|
||||
saveMergeConfig(cfg -> {
|
||||
setRegistrationToken(cfg.ensureRealmConfigData(server, realm), client.getClientId(), client.getRegistrationAccessToken());
|
||||
});
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to process response from server", e);
|
||||
}
|
||||
|
||||
//System.out.println("Token updated for client " + clientId);
|
||||
return CommandResult.SUCCESS;
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IllegalArgumentException(e.getMessage() + suggestHelp(), e);
|
||||
} finally {
|
||||
commandInvocation.stop();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to process response from server", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean nothingToDo() {
|
||||
return noOptions() && (args == null || args.size() == 0);
|
||||
}
|
||||
|
||||
protected String suggestHelp() {
|
||||
return EOL + "Try '" + CMD + " help update-token' for more information";
|
||||
return noOptions() && clientId == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String help() {
|
||||
return usage();
|
||||
}
|
||||
|
|
|
@ -17,14 +17,7 @@
|
|||
|
||||
package org.keycloak.client.registration.cli.util;
|
||||
|
||||
import org.jboss.aesh.console.AeshConsoleBufferBuilder;
|
||||
import org.jboss.aesh.console.AeshInputProcessorBuilder;
|
||||
import org.jboss.aesh.console.ConsoleBuffer;
|
||||
import org.jboss.aesh.console.InputProcessor;
|
||||
import org.jboss.aesh.console.Prompt;
|
||||
import org.jboss.aesh.console.command.invocation.CommandInvocation;
|
||||
import org.keycloak.client.registration.cli.aesh.Globals;
|
||||
|
||||
import java.io.Console;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
|
@ -50,7 +43,6 @@ import static java.nio.file.Files.createDirectories;
|
|||
import static java.nio.file.Files.createFile;
|
||||
import static java.nio.file.Files.isDirectory;
|
||||
import static java.nio.file.Files.isRegularFile;
|
||||
import static org.keycloak.client.registration.cli.util.OsUtil.OS_ARCH;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||
|
@ -81,43 +73,14 @@ public class IoUtil {
|
|||
}
|
||||
}
|
||||
|
||||
public static String readSecret(String prompt, CommandInvocation invocation) {
|
||||
|
||||
// TODO Windows hack - masking not working on Windows
|
||||
char maskChar = OS_ARCH.isWindows() ? 0 : '*';
|
||||
ConsoleBuffer consoleBuffer = new AeshConsoleBufferBuilder()
|
||||
.shell(invocation.getShell())
|
||||
.prompt(new Prompt(prompt, maskChar))
|
||||
.create();
|
||||
InputProcessor inputProcessor = new AeshInputProcessorBuilder()
|
||||
.consoleBuffer(consoleBuffer)
|
||||
.create();
|
||||
|
||||
consoleBuffer.displayPrompt();
|
||||
|
||||
// activate stdin
|
||||
Globals.stdin.setInputStream(System.in);
|
||||
|
||||
String result;
|
||||
try {
|
||||
do {
|
||||
result = inputProcessor.parseOperation(invocation.getInput());
|
||||
} while (result == null);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("^C", e);
|
||||
public static String readSecret(String prompt) {
|
||||
Console cons;
|
||||
char[] passwd;
|
||||
if ((cons = System.console()) != null &&
|
||||
(passwd = cons.readPassword("%s", prompt)) != null) {
|
||||
return new String(passwd);
|
||||
}
|
||||
/*
|
||||
if (!Globals.stdin.isStdinAvailable()) {
|
||||
try {
|
||||
return readLine(new InputStreamReader(System.in));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Standard input not available");
|
||||
}
|
||||
}
|
||||
*/
|
||||
// Windows hack - get rid of any \n
|
||||
result = result.replaceAll("\\n", "");
|
||||
return result;
|
||||
throw new RuntimeException("Console is not active, or no password provided");
|
||||
}
|
||||
|
||||
public static String readFully(InputStream is) {
|
||||
|
|
6
pom.xml
6
pom.xml
|
@ -80,7 +80,6 @@
|
|||
<jboss.as.subsystem.test.version>7.5.22.Final-redhat-1</jboss.as.subsystem.test.version>
|
||||
|
||||
<!-- Versions used mostly for Undertow server, aligned with WildFly -->
|
||||
<jboss.aesh.version>0.66.19</jboss.aesh.version>
|
||||
<apache.httpcomponents.version>4.5.14</apache.httpcomponents.version>
|
||||
<jboss.dmr.version>1.5.1.Final</jboss.dmr.version>
|
||||
|
||||
|
@ -948,11 +947,6 @@
|
|||
<artifactId>pax-web-spi</artifactId>
|
||||
<version>${pax.web.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.aesh</groupId>
|
||||
<artifactId>aesh</artifactId>
|
||||
<version>${jboss.aesh.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- keycloak -->
|
||||
<dependency>
|
||||
|
|
|
@ -20,27 +20,27 @@ public class KcRegConfigTest extends AbstractRegCliTest {
|
|||
@Test
|
||||
public void testRegistrationToken() throws IOException {
|
||||
|
||||
FileConfigHandler handler = initCustomConfigFile();
|
||||
initCustomConfigFile();
|
||||
|
||||
try (TempFileResource configFile = new TempFileResource(handler.getConfigFile())) {
|
||||
try (TempFileResource configFile = new TempFileResource(FileConfigHandler.getConfigFile())) {
|
||||
|
||||
// without --server
|
||||
KcRegExec exe = execute("config registration-token --config '" + configFile.getName() + "' ");
|
||||
assertExitCodeAndStreamSizes(exe, 1, 0, 2);
|
||||
assertExitCodeAndStreamSizes(exe, 2, 0, 2);
|
||||
Assert.assertEquals("error message", "Required option not specified: --server", exe.stderrLines().get(0));
|
||||
Assert.assertEquals("try help", "Try '" + CMD + " help config registration-token' for more information", exe.stderrLines().get(1));
|
||||
Assert.assertEquals("try help", "Try '" + CMD + " config registration-token --help' for more information on the available options.", exe.stderrLines().get(1));
|
||||
|
||||
// without --realm
|
||||
exe = execute("config registration-token --config '" + configFile.getName() + "' --server http://localhost:8080/auth");
|
||||
assertExitCodeAndStreamSizes(exe, 1, 0, 2);
|
||||
assertExitCodeAndStreamSizes(exe, 2, 0, 2);
|
||||
Assert.assertEquals("error message", "Required option not specified: --realm", exe.stderrLines().get(0));
|
||||
Assert.assertEquals("try help", "Try '" + CMD + " help config registration-token' for more information", exe.stderrLines().get(1));
|
||||
Assert.assertEquals("try help", "Try '" + CMD + " config registration-token --help' for more information on the available options.", exe.stderrLines().get(1));
|
||||
|
||||
// without --client
|
||||
exe = execute("config registration-token --config '" + configFile.getName() + "' --server http://localhost:8080/auth --realm test");
|
||||
assertExitCodeAndStreamSizes(exe, 1, 0, 2);
|
||||
assertExitCodeAndStreamSizes(exe, 2, 0, 2);
|
||||
Assert.assertEquals("error message", "Required option not specified: --client", exe.stderrLines().get(0));
|
||||
Assert.assertEquals("try help", "Try '" + CMD + " help config registration-token' for more information", exe.stderrLines().get(1));
|
||||
Assert.assertEquals("try help", "Try '" + CMD + " config registration-token --help' for more information on the available options.", exe.stderrLines().get(1));
|
||||
|
||||
// specify token on cmdline
|
||||
exe = execute("config registration-token --config '" + configFile.getName() + "' --server http://localhost:8080/auth --realm test --client my_client NEWTOKEN");
|
||||
|
@ -75,14 +75,14 @@ public class KcRegConfigTest extends AbstractRegCliTest {
|
|||
public void testNoConfigOption() throws IOException {
|
||||
|
||||
KcRegExec exe = execute("config registration-token --no-config --server http://localhost:8080/auth --realm test --client my_client --delete");
|
||||
assertExitCodeAndStreamSizes(exe, 1, 0, 2);
|
||||
assertExitCodeAndStreamSizes(exe, 2, 0, 2);
|
||||
Assert.assertEquals("stderr first line", "Unsupported option: --no-config", exe.stderrLines().get(0));
|
||||
Assert.assertEquals("try help", "Try '" + CMD + " help config registration-token' for more information", exe.stderrLines().get(1));
|
||||
Assert.assertEquals("try help", "Try '" + CMD + " config registration-token --help' for more information on the available options.", exe.stderrLines().get(1));
|
||||
|
||||
exe = execute("config initial-token --no-config --server http://localhost:8080/auth --realm test --delete");
|
||||
assertExitCodeAndStreamSizes(exe, 1, 0, 2);
|
||||
assertExitCodeAndStreamSizes(exe, 2, 0, 2);
|
||||
Assert.assertEquals("stderr first line", "Unsupported option: --no-config", exe.stderrLines().get(0));
|
||||
Assert.assertEquals("try help", "Try '" + CMD + " help config initial-token' for more information", exe.stderrLines().get(1));
|
||||
Assert.assertEquals("try help", "Try '" + CMD + " config initial-token --help' for more information on the available options.", exe.stderrLines().get(1));
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ public class KcRegTest extends AbstractRegCliTest {
|
|||
*/
|
||||
KcRegExec exe = execute("");
|
||||
|
||||
assertExitCodeAndStdErrSize(exe, 1, 0);
|
||||
assertExitCodeAndStdErrSize(exe, 2, 0);
|
||||
|
||||
List<String> lines = exe.stdoutLines();
|
||||
Assert.assertTrue("stdout output not empty", lines.size() > 0);
|
||||
|
@ -49,51 +49,51 @@ public class KcRegTest extends AbstractRegCliTest {
|
|||
* Test commands without arguments
|
||||
*/
|
||||
exe = execute("config");
|
||||
assertExitCodeAndStreamSizes(exe, 1, 0, 1);
|
||||
assertExitCodeAndStreamSizes(exe, 2, 8, 0);
|
||||
Assert.assertEquals("error message",
|
||||
"Sub-command required by '" + CMD + " config' - one of: 'credentials', 'truststore', 'initial-token', 'registration-token'",
|
||||
exe.stderrLines().get(0));
|
||||
"Usage: kcreg.sh config SUB_COMMAND [ARGUMENTS]",
|
||||
exe.stdoutLines().get(0));
|
||||
|
||||
exe = execute("config credentials");
|
||||
assertExitCodeAndStdErrSize(exe, 1, 0);
|
||||
assertExitCodeAndStdErrSize(exe, 2, 0);
|
||||
Assert.assertTrue("help message returned", exe.stdoutLines().size() > 10);
|
||||
Assert.assertEquals("help message", "Usage: " + CMD + " config credentials --server SERVER_URL --realm REALM [ARGUMENTS]", exe.stdoutLines().get(0));
|
||||
|
||||
exe = execute("config initial-token");
|
||||
assertExitCodeAndStdErrSize(exe, 1, 0);
|
||||
assertExitCodeAndStdErrSize(exe, 2, 0);
|
||||
Assert.assertTrue("help message returned", exe.stdoutLines().size() > 10);
|
||||
Assert.assertEquals("help message", "Usage: " + CMD + " config initial-token --server SERVER --realm REALM [--delete | TOKEN] [ARGUMENTS]", exe.stdoutLines().get(0));
|
||||
|
||||
exe = execute("config registration-token");
|
||||
assertExitCodeAndStdErrSize(exe, 1, 0);
|
||||
assertExitCodeAndStdErrSize(exe, 2, 0);
|
||||
Assert.assertTrue("help message returned", exe.stdoutLines().size() > 10);
|
||||
Assert.assertEquals("help message", "Usage: " + CMD + " config registration-token --server SERVER --realm REALM --client CLIENT [--delete | TOKEN] [ARGUMENTS]", exe.stdoutLines().get(0));
|
||||
|
||||
exe = execute("config truststore");
|
||||
assertExitCodeAndStdErrSize(exe, 1, 0);
|
||||
assertExitCodeAndStdErrSize(exe, 2, 0);
|
||||
Assert.assertTrue("help message returned", exe.stdoutLines().size() > 10);
|
||||
Assert.assertEquals("help message", "Usage: " + CMD + " config truststore [TRUSTSTORE | --delete] [--trustpass PASSWORD] [ARGUMENTS]", exe.stdoutLines().get(0));
|
||||
|
||||
exe = execute("create");
|
||||
assertExitCodeAndStdErrSize(exe, 1, 0);
|
||||
assertExitCodeAndStdErrSize(exe, 2, 0);
|
||||
Assert.assertTrue("help message returned", exe.stdoutLines().size() > 10);
|
||||
Assert.assertEquals("help message", "Usage: " + CMD + " create [ARGUMENTS]", exe.stdoutLines().get(0));
|
||||
//Assert.assertEquals("error message", "No file nor attribute values specified", exe.stderrLines().get(0));
|
||||
|
||||
exe = execute("get");
|
||||
assertExitCodeAndStdErrSize(exe, 1, 0);
|
||||
assertExitCodeAndStdErrSize(exe, 2, 0);
|
||||
Assert.assertTrue("help message returned", exe.stdoutLines().size() > 10);
|
||||
Assert.assertEquals("help message", "Usage: " + CMD + " get CLIENT [ARGUMENTS]", exe.stdoutLines().get(0));
|
||||
//Assert.assertEquals("error message", "CLIENT not specified", exe.stderrLines().get(0));
|
||||
|
||||
exe = execute("update");
|
||||
assertExitCodeAndStdErrSize(exe, 1, 0);
|
||||
assertExitCodeAndStdErrSize(exe, 2, 0);
|
||||
Assert.assertTrue("help message returned", exe.stdoutLines().size() > 10);
|
||||
Assert.assertEquals("help message", "Usage: " + CMD + " update CLIENT [ARGUMENTS]", exe.stdoutLines().get(0));
|
||||
//Assert.assertEquals("error message", "No file nor attribute values specified", exe.stderrLines().get(0));
|
||||
|
||||
exe = execute("delete");
|
||||
assertExitCodeAndStdErrSize(exe, 1, 0);
|
||||
assertExitCodeAndStdErrSize(exe, 2, 0);
|
||||
Assert.assertTrue("help message returned", exe.stdoutLines().size() > 10);
|
||||
Assert.assertEquals("help message", "Usage: " + CMD + " delete CLIENT [ARGUMENTS]", exe.stdoutLines().get(0));
|
||||
//Assert.assertEquals("error message", "CLIENT not specified", exe.stderrLines().get(0));
|
||||
|
@ -104,7 +104,7 @@ public class KcRegTest extends AbstractRegCliTest {
|
|||
Assert.assertEquals("first line", "Attributes for default format:", exe.stdoutLines().get(0));
|
||||
|
||||
exe = execute("update-token");
|
||||
assertExitCodeAndStdErrSize(exe, 1, 0);
|
||||
assertExitCodeAndStdErrSize(exe, 2, 0);
|
||||
Assert.assertTrue("help message returned", exe.stdoutLines().size() > 10);
|
||||
Assert.assertEquals("help message", "Usage: " + CMD + " update-token CLIENT [ARGUMENTS]", exe.stdoutLines().get(0));
|
||||
//Assert.assertEquals("error message", "CLIENT not specified", exe.stderrLines().get(0));
|
||||
|
@ -188,8 +188,8 @@ public class KcRegTest extends AbstractRegCliTest {
|
|||
*/
|
||||
KcRegExec exe = execute("nonexistent");
|
||||
|
||||
assertExitCodeAndStreamSizes(exe, 1, 0, 1);
|
||||
Assert.assertEquals("stderr first line", "Unknown command: nonexistent", exe.stderrLines().get(0));
|
||||
assertExitCodeAndStreamSizes(exe, 2, 0, 3);
|
||||
Assert.assertEquals("stderr first line", "Unmatched argument at index 0: 'nonexistent'", exe.stderrLines().get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -199,8 +199,8 @@ public class KcRegTest extends AbstractRegCliTest {
|
|||
*/
|
||||
KcRegExec exe = execute("--nonexistent");
|
||||
|
||||
assertExitCodeAndStreamSizes(exe, 1, 0, 1);
|
||||
Assert.assertEquals("stderr first line", "Unknown command: --nonexistent", exe.stderrLines().get(0));
|
||||
assertExitCodeAndStreamSizes(exe, 2, 0, 2);
|
||||
Assert.assertEquals("stderr first line", "Unknown option: '--nonexistent'", exe.stderrLines().get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -211,9 +211,9 @@ public class KcRegTest extends AbstractRegCliTest {
|
|||
|
||||
KcRegExec exe = execute("get my_client --nonexistent");
|
||||
|
||||
assertExitCodeAndStreamSizes(exe, 1, 0, 2);
|
||||
Assert.assertEquals("stderr first line", "Invalid option: --nonexistent", exe.stderrLines().get(0));
|
||||
Assert.assertEquals("try help", "Try '" + CMD + " help get' for more information", exe.stderrLines().get(1));
|
||||
assertExitCodeAndStreamSizes(exe, 2, 0, 3);
|
||||
Assert.assertEquals("stderr first line", "Unknown option: '--nonexistent'", exe.stderrLines().get(0));
|
||||
Assert.assertEquals("try help", "Try '" + CMD + " get --help' for more information on the available options.", exe.stderrLines().get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -233,9 +233,9 @@ public class KcRegTest extends AbstractRegCliTest {
|
|||
*/
|
||||
KcRegExec exe = execute("config credentials --realm master --user admin --password admin");
|
||||
|
||||
assertExitCodeAndStreamSizes(exe, 1, 0, 2);
|
||||
assertExitCodeAndStreamSizes(exe, 2, 0, 2);
|
||||
Assert.assertEquals("stderr first line", "Required option not specified: --server", exe.stderrLines().get(0));
|
||||
Assert.assertEquals("try help", "Try '" + CMD + " help config credentials' for more information", exe.stderrLines().get(1));
|
||||
Assert.assertEquals("try help", "Try '" + CMD + " config credentials --help' for more information on the available options.", exe.stderrLines().get(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -245,9 +245,9 @@ public class KcRegTest extends AbstractRegCliTest {
|
|||
*/
|
||||
KcRegExec exe = execute("config credentials --server " + serverUrl + " --user admin --password admin");
|
||||
|
||||
assertExitCodeAndStreamSizes(exe, 1, 0, 2);
|
||||
assertExitCodeAndStreamSizes(exe, 2, 0, 2);
|
||||
Assert.assertEquals("stderr first line", "Required option not specified: --realm", exe.stderrLines().get(0));
|
||||
Assert.assertEquals("try help", "Try '" + CMD + " help config credentials' for more information", exe.stderrLines().get(1));
|
||||
Assert.assertEquals("try help", "Try '" + CMD + " config credentials --help' for more information on the available options.", exe.stderrLines().get(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -257,9 +257,9 @@ public class KcRegTest extends AbstractRegCliTest {
|
|||
*/
|
||||
KcRegExec exe = KcRegExec.execute("config credentials --no-config --server " + serverUrl + " --realm master --user admin --password admin");
|
||||
|
||||
assertExitCodeAndStreamSizes(exe, 1, 0, 2);
|
||||
assertExitCodeAndStreamSizes(exe, 2, 0, 2);
|
||||
Assert.assertEquals("stderr first line", "Unsupported option: --no-config", exe.stderrLines().get(0));
|
||||
Assert.assertEquals("try help", "Try '" + CMD + " help config credentials' for more information", exe.stderrLines().get(1));
|
||||
Assert.assertEquals("try help", "Try '" + CMD + " config credentials --help' for more information on the available options.", exe.stderrLines().get(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -6,6 +6,7 @@ import org.keycloak.client.registration.cli.config.ConfigData;
|
|||
import org.keycloak.client.registration.cli.config.FileConfigHandler;
|
||||
import org.keycloak.client.registration.cli.util.OsUtil;
|
||||
import org.keycloak.testsuite.cli.KcRegExec;
|
||||
import org.keycloak.testsuite.util.OAuthClient;
|
||||
import org.keycloak.testsuite.util.TempFileResource;
|
||||
|
||||
import java.io.File;
|
||||
|
@ -29,9 +30,9 @@ public class KcRegTruststoreTest extends AbstractRegCliTest {
|
|||
|
||||
KcRegExec exe = execute("config truststore --no-config '" + truststore.getAbsolutePath() + "'");
|
||||
|
||||
assertExitCodeAndStreamSizes(exe, 1, 0, 2);
|
||||
assertExitCodeAndStreamSizes(exe, 2, 0, 2);
|
||||
Assert.assertEquals("stderr first line", "Unsupported option: --no-config", exe.stderrLines().get(0));
|
||||
Assert.assertEquals("try help", "Try '" + OsUtil.CMD + " help config truststore' for more information", exe.stderrLines().get(1));
|
||||
Assert.assertEquals("try help", "Try '" + OsUtil.CMD + " config truststore --help' for more information on the available options.", exe.stderrLines().get(1));
|
||||
|
||||
// only run the rest of this test if ssl protected keycloak server is available
|
||||
if (!AUTH_SERVER_SSL_REQUIRED) {
|
||||
|
@ -39,9 +40,9 @@ public class KcRegTruststoreTest extends AbstractRegCliTest {
|
|||
return;
|
||||
}
|
||||
|
||||
FileConfigHandler handler = initCustomConfigFile();
|
||||
initCustomConfigFile();
|
||||
|
||||
try (TempFileResource configFile = new TempFileResource(handler.getConfigFile())) {
|
||||
try (TempFileResource configFile = new TempFileResource(FileConfigHandler.getConfigFile())) {
|
||||
|
||||
if (runIntermittentlyFailingTests()) {
|
||||
// configure truststore
|
||||
|
@ -52,7 +53,7 @@ public class KcRegTruststoreTest extends AbstractRegCliTest {
|
|||
|
||||
// perform authentication against server - asks for password, then for truststore password
|
||||
exe = KcRegExec.newBuilder()
|
||||
.argsLine("config credentials --server " + oauth.AUTH_SERVER_ROOT + " --realm test --user user1" +
|
||||
.argsLine("config credentials --server " + OAuthClient.AUTH_SERVER_ROOT + " --realm test --user user1" +
|
||||
" --config '" + configFile.getName() + "'")
|
||||
.executeAsync();
|
||||
|
||||
|
@ -72,7 +73,7 @@ public class KcRegTruststoreTest extends AbstractRegCliTest {
|
|||
|
||||
// perform authentication against server - asks for password, then for truststore password
|
||||
exe = KcRegExec.newBuilder()
|
||||
.argsLine("config credentials --server " + oauth.AUTH_SERVER_ROOT + " --realm test --user user1" +
|
||||
.argsLine("config credentials --server " + OAuthClient.AUTH_SERVER_ROOT + " --realm test --user user1" +
|
||||
" --config '" + configFile.getName() + "'")
|
||||
.executeAsync();
|
||||
|
||||
|
@ -99,17 +100,17 @@ public class KcRegTruststoreTest extends AbstractRegCliTest {
|
|||
assertExitCodeAndStreamSizes(exe, 0, 0, 0);
|
||||
|
||||
exe = execute("config truststore --delete '" + truststore.getAbsolutePath() + "'");
|
||||
assertExitCodeAndStreamSizes(exe, 1, 0, 2);
|
||||
assertExitCodeAndStreamSizes(exe, 2, 0, 2);
|
||||
Assert.assertEquals("incompatible", "Option --delete is mutually exclusive with specifying a TRUSTSTORE", exe.stderrLines().get(0));
|
||||
Assert.assertEquals("try help", "Try '" + CMD + " help config truststore' for more information", exe.stderrLines().get(1));
|
||||
Assert.assertEquals("try help", "Try '" + CMD + " config truststore --help' for more information on the available options.", exe.stderrLines().get(1));
|
||||
|
||||
exe = execute("config truststore --delete --trustpass secret");
|
||||
assertExitCodeAndStreamSizes(exe, 1, 0, 2);
|
||||
assertExitCodeAndStreamSizes(exe, 2, 0, 2);
|
||||
Assert.assertEquals("no truststore error", "Options --trustpass and --delete are mutually exclusive", exe.stderrLines().get(0));
|
||||
Assert.assertEquals("try help", "Try '" + CMD + " help config truststore' for more information", exe.stderrLines().get(1));
|
||||
Assert.assertEquals("try help", "Try '" + CMD + " config truststore --help' for more information on the available options.", exe.stderrLines().get(1));
|
||||
|
||||
FileConfigHandler cfghandler = new FileConfigHandler();
|
||||
cfghandler.setConfigFile(DEFAULT_CONFIG_FILE_PATH);
|
||||
FileConfigHandler.setConfigFile(DEFAULT_CONFIG_FILE_PATH);
|
||||
ConfigData config = cfghandler.loadConfig();
|
||||
Assert.assertNull("truststore null", config.getTruststore());
|
||||
Assert.assertNull("trustpass null", config.getTrustpass());
|
||||
|
|
|
@ -26,7 +26,7 @@ public class KcRegUpdateTest extends AbstractRegCliTest {
|
|||
|
||||
FileConfigHandler handler = initCustomConfigFile();
|
||||
|
||||
try (TempFileResource configFile = new TempFileResource(handler.getConfigFile())) {
|
||||
try (TempFileResource configFile = new TempFileResource(FileConfigHandler.getConfigFile())) {
|
||||
|
||||
final String realm = "test";
|
||||
|
||||
|
@ -91,9 +91,9 @@ public class KcRegUpdateTest extends AbstractRegCliTest {
|
|||
// check that using an invalid attribute key is not ignored
|
||||
exe = execute("update my_client --nonexisting --config '" + configFile.getName() + "'");
|
||||
|
||||
assertExitCodeAndStreamSizes(exe, 1, 0, 2);
|
||||
Assert.assertEquals("error message", "Unsupported option: --nonexisting", exe.stderrLines().get(0));
|
||||
Assert.assertEquals("try help", "Try '" + CMD + " help update' for more information", exe.stderrLines().get(1));
|
||||
assertExitCodeAndStreamSizes(exe, 2, 0, 3);
|
||||
Assert.assertEquals("error message", "Unknown option: '--nonexisting'", exe.stderrLines().get(0));
|
||||
Assert.assertEquals("try help", "Try '" + CMD + " update --help' for more information on the available options.", exe.stderrLines().get(2));
|
||||
|
||||
|
||||
// try use incompatible endpoint
|
||||
|
|
Loading…
Reference in a new issue