task: refactor overlap between cli clients
also repackaging to more clearly delineate code roles closes: #28329 Signed-off-by: Steve Hawkins <shawkins@redhat.com>
This commit is contained in:
parent
808926b63e
commit
0be34d64e7
121 changed files with 1569 additions and 3927 deletions
|
@ -14,38 +14,46 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.keycloak.client.admin.cli.util;
|
package org.keycloak.client.admin.cli;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonParseException;
|
import org.keycloak.client.cli.common.AttributeOperation;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import org.keycloak.client.cli.util.AttributeException;
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
|
||||||
import org.keycloak.client.admin.cli.common.AttributeOperation;
|
|
||||||
import org.keycloak.client.admin.cli.common.CmdStdinContext;
|
|
||||||
import org.keycloak.util.JsonSerialization;
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.keycloak.client.admin.cli.util.IoUtil.readFileOrStdin;
|
import com.fasterxml.jackson.core.JsonParseException;
|
||||||
import static org.keycloak.client.admin.cli.util.ReflectionUtil.setAttributes;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
|
||||||
|
import static org.keycloak.client.admin.cli.ReflectionUtil.setAttributes;
|
||||||
|
import static org.keycloak.client.cli.util.IoUtil.readFileOrStdin;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
*/
|
*/
|
||||||
public class ParseUtil {
|
public class CmdStdinContext<T> {
|
||||||
|
|
||||||
public static String[] parseKeyVal(String keyval) {
|
private T result;
|
||||||
// we expect = as a separator
|
private String content;
|
||||||
int pos = keyval.indexOf("=");
|
|
||||||
if (pos <= 0) {
|
public CmdStdinContext() {}
|
||||||
throw new IllegalArgumentException("Invalid key=value parameter: [" + keyval + "]");
|
|
||||||
|
public T getResult() {
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
String [] parsed = new String[2];
|
public void setResult(T result) {
|
||||||
parsed[0] = keyval.substring(0, pos);
|
this.result = result;
|
||||||
parsed[1] = keyval.substring(pos+1);
|
}
|
||||||
|
|
||||||
return parsed;
|
public String getContent() {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContent(String content) {
|
||||||
|
this.content = content;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CmdStdinContext<JsonNode> parseFileOrStdin(String file) {
|
public static CmdStdinContext<JsonNode> parseFileOrStdin(String file) {
|
|
@ -17,45 +17,37 @@
|
||||||
package org.keycloak.client.admin.cli;
|
package org.keycloak.client.admin.cli;
|
||||||
|
|
||||||
import org.keycloak.client.admin.cli.commands.KcAdmCmd;
|
import org.keycloak.client.admin.cli.commands.KcAdmCmd;
|
||||||
import org.keycloak.client.admin.cli.util.ClassLoaderUtil;
|
import org.keycloak.client.cli.common.CommandState;
|
||||||
import org.keycloak.client.admin.cli.util.OsUtil;
|
import org.keycloak.client.cli.common.Globals;
|
||||||
import org.keycloak.common.crypto.CryptoIntegration;
|
import org.keycloak.client.cli.util.OsUtil;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
|
||||||
|
|
||||||
import picocli.CommandLine;
|
|
||||||
import picocli.CommandLine.Model.CommandSpec;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
*/
|
*/
|
||||||
public class KcAdmMain {
|
public class KcAdmMain {
|
||||||
|
|
||||||
|
public static final String DEFAULT_CONFIG_FILE_PATH = System.getProperty("user.home") + "/.keycloak/kcadm.config";
|
||||||
|
|
||||||
|
public static final String DEFAULT_CONFIG_FILE_STRING = OsUtil.OS_ARCH.isWindows() ? "%HOMEDRIVE%%HOMEPATH%\\.keycloak\\kcadm.config" : "~/.keycloak/kcadm.config";
|
||||||
|
|
||||||
|
public static final String CMD = OsUtil.OS_ARCH.isWindows() ? "kcadm.bat" : "kcadm.sh";
|
||||||
|
|
||||||
|
public static final CommandState COMMAND_STATE = new CommandState() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCommand() {
|
||||||
|
return CMD;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDefaultConfigFilePath() {
|
||||||
|
return DEFAULT_CONFIG_FILE_PATH;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
public static void main(String [] args) {
|
public static void main(String [] args) {
|
||||||
String libDir = System.getProperty("kc.lib.dir");
|
Globals.main(args, new KcAdmCmd(), CMD, DEFAULT_CONFIG_FILE_STRING);
|
||||||
if (libDir == null) {
|
|
||||||
throw new RuntimeException("System property kc.lib.dir needs to be set");
|
|
||||||
}
|
|
||||||
ClassLoader cl = ClassLoaderUtil.resolveClassLoader(libDir);
|
|
||||||
Thread.currentThread().setContextClassLoader(cl);
|
|
||||||
|
|
||||||
CryptoIntegration.init(cl);
|
|
||||||
|
|
||||||
CommandLine cli = createCommandLine();
|
|
||||||
int exitCode = cli.execute(args);
|
|
||||||
System.exit(exitCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CommandLine createCommandLine() {
|
|
||||||
CommandSpec spec = CommandSpec.forAnnotatedObject(new KcAdmCmd()).name(OsUtil.CMD);
|
|
||||||
|
|
||||||
CommandLine cmd = new CommandLine(spec);
|
|
||||||
|
|
||||||
cmd.setExecutionExceptionHandler(new ExecutionExceptionHandler());
|
|
||||||
cmd.setParameterExceptionHandler(new ShortErrorMessageHandler());
|
|
||||||
cmd.setErr(new PrintWriter(System.err, true));
|
|
||||||
|
|
||||||
return cmd;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,22 +14,23 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.keycloak.client.admin.cli.util;
|
package org.keycloak.client.admin.cli;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||||
import com.fasterxml.jackson.databind.node.NullNode;
|
import com.fasterxml.jackson.databind.node.NullNode;
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
import com.fasterxml.jackson.databind.node.TextNode;
|
import com.fasterxml.jackson.databind.node.TextNode;
|
||||||
import org.keycloak.client.admin.cli.common.AttributeKey;
|
|
||||||
import org.keycloak.client.admin.cli.common.AttributeOperation;
|
import org.keycloak.client.cli.common.AttributeKey;
|
||||||
|
import org.keycloak.client.cli.common.AttributeOperation;
|
||||||
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static org.keycloak.client.admin.cli.common.AttributeOperation.Type.SET;
|
import static org.keycloak.client.cli.common.AttributeOperation.Type.SET;
|
||||||
import static org.keycloak.client.admin.cli.util.OutputUtil.MAPPER;
|
import static org.keycloak.client.cli.util.OutputUtil.MAPPER;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
|
@ -16,252 +16,35 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.client.admin.cli.commands;
|
package org.keycloak.client.admin.cli.commands;
|
||||||
|
|
||||||
import org.keycloak.OAuth2Constants;
|
import org.keycloak.client.admin.cli.KcAdmMain;
|
||||||
import org.keycloak.client.admin.cli.config.ConfigData;
|
import org.keycloak.client.cli.common.BaseAuthOptionsCmd;
|
||||||
import org.keycloak.client.admin.cli.config.ConfigHandler;
|
import org.keycloak.client.cli.config.ConfigData;
|
||||||
import org.keycloak.client.admin.cli.config.FileConfigHandler;
|
|
||||||
import org.keycloak.client.admin.cli.config.InMemoryConfigHandler;
|
|
||||||
import org.keycloak.client.admin.cli.config.RealmConfigData;
|
|
||||||
import org.keycloak.client.admin.cli.util.ConfigUtil;
|
|
||||||
import org.keycloak.client.admin.cli.util.HttpUtil;
|
|
||||||
import org.keycloak.client.admin.cli.util.IoUtil;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
import picocli.CommandLine.Option;
|
import picocli.CommandLine.Option;
|
||||||
|
|
||||||
import static org.keycloak.client.admin.cli.config.FileConfigHandler.setConfigFile;
|
|
||||||
import static org.keycloak.client.admin.cli.util.ConfigUtil.DEFAULT_CLIENT;
|
|
||||||
import static org.keycloak.client.admin.cli.util.ConfigUtil.checkAuthInfo;
|
|
||||||
import static org.keycloak.client.admin.cli.util.ConfigUtil.checkServerInfo;
|
|
||||||
import static org.keycloak.client.admin.cli.util.ConfigUtil.loadConfig;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractAuthOptionsCmd extends AbstractGlobalOptionsCmd {
|
public abstract class AbstractAuthOptionsCmd extends BaseAuthOptionsCmd implements GlobalOptionsCmdHelper {
|
||||||
|
|
||||||
@Option(names = {"-a", "--admin-root"}, description = "URL of Admin REST endpoint root if not default - e.g. http://localhost:8080/admin")
|
@Option(names = {"-a", "--admin-root"}, description = "URL of Admin REST endpoint root if not default - e.g. http://localhost:8080/admin")
|
||||||
String adminRestRoot;
|
String adminRestRoot;
|
||||||
|
|
||||||
@Option(names = "--config", description = "Path to the config file (~/.keycloak/kcadm.config by default)")
|
|
||||||
String config;
|
|
||||||
|
|
||||||
@Option(names = "--no-config", description = "Don't use config file - no authentication info is loaded or saved")
|
|
||||||
boolean noconfig;
|
|
||||||
|
|
||||||
@Option(names = "--server", description = "Server endpoint url (e.g. 'http://localhost:8080')")
|
|
||||||
String server;
|
|
||||||
|
|
||||||
@Option(names = {"-r", "--target-realm"}, description = "Realm to target - when it's different than the realm we authenticate against")
|
@Option(names = {"-r", "--target-realm"}, description = "Realm to target - when it's different than the realm we authenticate against")
|
||||||
String targetRealm;
|
String targetRealm;
|
||||||
|
|
||||||
@Option(names = "--realm", description = "Realm name to authenticate against")
|
|
||||||
String realm;
|
|
||||||
|
|
||||||
@Option(names = "--client", description = "Realm name to authenticate against")
|
|
||||||
String clientId;
|
|
||||||
|
|
||||||
@Option(names = "--user", description = "Username to login with")
|
|
||||||
String user;
|
|
||||||
|
|
||||||
@Option(names = "--password", description = "Password to login with (prompted for if not specified and --user is used)")
|
|
||||||
String password;
|
|
||||||
|
|
||||||
@Option(names = "--secret", description = "Secret to authenticate the client (prompted for if no --user or --keystore is specified)")
|
|
||||||
String secret;
|
|
||||||
|
|
||||||
@Option(names = "--keystore", description = "Path to a keystore containing private key")
|
|
||||||
String keystore;
|
|
||||||
|
|
||||||
@Option(names = "--storepass", description = "Keystore password (prompted for if not specified and --keystore is used)")
|
|
||||||
String storePass;
|
|
||||||
|
|
||||||
@Option(names = "--keypass", description = "Key password (prompted for if not specified and --keystore is used without --storepass, \n otherwise defaults to keystore password)")
|
|
||||||
String keyPass;
|
|
||||||
|
|
||||||
@Option(names = "--alias", description = "Alias of the key inside a keystore (defaults to the value of ClientId)")
|
|
||||||
String alias;
|
|
||||||
|
|
||||||
@Option(names = "--truststore", description = "Path to a truststore")
|
|
||||||
String trustStore;
|
|
||||||
|
|
||||||
@Option(names = "--trustpass", description = "Truststore password (prompted for if not specified and --truststore is used)")
|
|
||||||
String trustPass;
|
|
||||||
|
|
||||||
@Option(names = "--insecure", description = "Turns off TLS validation")
|
|
||||||
boolean insecure;
|
|
||||||
|
|
||||||
@Option(names = "--token", description = "Token to use for invocations. With this option set, every other authentication option is ignored")
|
@Option(names = "--token", description = "Token to use for invocations. With this option set, every other authentication option is ignored")
|
||||||
String externalToken;
|
public void setToken(String token) {
|
||||||
|
this.externalToken = token;
|
||||||
protected void initFromParent(AbstractAuthOptionsCmd parent) {
|
|
||||||
noconfig = parent.noconfig;
|
|
||||||
config = parent.config;
|
|
||||||
server = parent.server;
|
|
||||||
realm = parent.realm;
|
|
||||||
clientId = parent.clientId;
|
|
||||||
user = parent.user;
|
|
||||||
password = parent.password;
|
|
||||||
secret = parent.secret;
|
|
||||||
keystore = parent.keystore;
|
|
||||||
storePass = parent.storePass;
|
|
||||||
keyPass = parent.keyPass;
|
|
||||||
alias = parent.alias;
|
|
||||||
trustStore = parent.trustStore;
|
|
||||||
trustPass = parent.trustPass;
|
|
||||||
externalToken = parent.externalToken;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void applyDefaultOptionValues() {
|
public AbstractAuthOptionsCmd() {
|
||||||
if (clientId == null) {
|
super(KcAdmMain.COMMAND_STATE);
|
||||||
clientId = DEFAULT_CLIENT;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean nothingToDo() {
|
|
||||||
return externalToken == null && server == null && realm == null && clientId == null && secret == null &&
|
|
||||||
user == null && password == null &&
|
|
||||||
keystore == null && storePass == null && keyPass == null && alias == null &&
|
|
||||||
trustStore == null && trustPass == null && config == null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected String getTargetRealm(ConfigData config) {
|
protected String getTargetRealm(ConfigData config) {
|
||||||
return targetRealm != null ? targetRealm : config.getRealm();
|
return targetRealm != null ? targetRealm : config.getRealm();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void processOptions() {
|
|
||||||
if (config != null && noconfig) {
|
|
||||||
throw new IllegalArgumentException("Options --config and --no-config are mutually exclusive");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!noconfig) {
|
|
||||||
setConfigFile(config != null ? config : ConfigUtil.DEFAULT_CONFIG_FILE_PATH);
|
|
||||||
ConfigUtil.setHandler(new FileConfigHandler());
|
|
||||||
} else {
|
|
||||||
InMemoryConfigHandler handler = new InMemoryConfigHandler();
|
|
||||||
ConfigData data = new ConfigData();
|
|
||||||
initConfigData(data);
|
|
||||||
handler.setConfigData(data);
|
|
||||||
ConfigUtil.setHandler(handler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void setupTruststore(ConfigData configData) {
|
|
||||||
|
|
||||||
if (!configData.getServerUrl().startsWith("https:")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String truststore = trustStore;
|
|
||||||
if (truststore == null) {
|
|
||||||
truststore = configData.getTruststore();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (truststore != null) {
|
|
||||||
String pass = trustPass;
|
|
||||||
if (pass == null) {
|
|
||||||
pass = configData.getTrustpass();
|
|
||||||
}
|
|
||||||
if (pass == null) {
|
|
||||||
pass = IoUtil.readSecret("Enter truststore password: ");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
HttpUtil.setTruststore(new File(truststore), pass);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException("Failed to load truststore: " + truststore, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (insecure) {
|
|
||||||
HttpUtil.setSkipCertificateValidation();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ConfigData ensureAuthInfo(ConfigData config) {
|
|
||||||
|
|
||||||
if (requiresLogin()) {
|
|
||||||
// make sure current handler is in-memory handler
|
|
||||||
// restore it at the end
|
|
||||||
ConfigHandler old = ConfigUtil.getHandler();
|
|
||||||
try {
|
|
||||||
// make sure all defaults are initialized after this point
|
|
||||||
applyDefaultOptionValues();
|
|
||||||
|
|
||||||
initConfigData(config);
|
|
||||||
ConfigUtil.setupInMemoryHandler(config);
|
|
||||||
|
|
||||||
ConfigCredentialsCmd login = new ConfigCredentialsCmd();
|
|
||||||
login.initFromParent(this);
|
|
||||||
login.init(config);
|
|
||||||
login.process();
|
|
||||||
|
|
||||||
// this must be executed before finally block which restores config handler
|
|
||||||
return loadConfig();
|
|
||||||
|
|
||||||
} catch (RuntimeException e) {
|
|
||||||
throw e;
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
} finally {
|
|
||||||
ConfigUtil.setHandler(old);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
checkAuthInfo(config);
|
|
||||||
|
|
||||||
// make sure all defaults are initialized after this point
|
|
||||||
applyDefaultOptionValues();
|
|
||||||
return loadConfig();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean requiresLogin() {
|
|
||||||
return externalToken == null && (user != null || password != null || secret != null || keystore != null
|
|
||||||
|| keyPass != null || storePass != null || alias != null);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ConfigData copyWithServerInfo(ConfigData config) {
|
|
||||||
|
|
||||||
ConfigData result = config.deepcopy();
|
|
||||||
|
|
||||||
if (server != null) {
|
|
||||||
result.setServerUrl(server);
|
|
||||||
}
|
|
||||||
if (realm != null) {
|
|
||||||
result.setRealm(realm);
|
|
||||||
}
|
|
||||||
if (externalToken != null) {
|
|
||||||
result.setExternalToken(externalToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
checkServerInfo(result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initConfigData(ConfigData data) {
|
|
||||||
if (server != null)
|
|
||||||
data.setServerUrl(server);
|
|
||||||
if (realm != null)
|
|
||||||
data.setRealm(realm);
|
|
||||||
if (trustStore != null)
|
|
||||||
data.setTruststore(trustStore);
|
|
||||||
if (externalToken != null) {
|
|
||||||
data.setExternalToken(externalToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
RealmConfigData rdata = data.sessionRealmConfigData();
|
|
||||||
if (clientId != null)
|
|
||||||
rdata.setClientId(clientId);
|
|
||||||
if (secret != null)
|
|
||||||
rdata.setSecret(secret);
|
|
||||||
String grantTypeForAuthentication = user == null ? OAuth2Constants.CLIENT_CREDENTIALS : OAuth2Constants.PASSWORD;
|
|
||||||
rdata.setGrantTypeForAuthentication(grantTypeForAuthentication);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,136 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
|
||||||
* and other contributors as indicated by the @author tags.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.keycloak.client.admin.cli.commands;
|
|
||||||
|
|
||||||
import org.keycloak.client.admin.cli.Globals;
|
|
||||||
import org.keycloak.client.admin.cli.util.FilterUtil;
|
|
||||||
import org.keycloak.client.admin.cli.util.ReturnFields;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
|
|
||||||
import picocli.CommandLine;
|
|
||||||
import picocli.CommandLine.Option;
|
|
||||||
|
|
||||||
import static org.keycloak.client.admin.cli.util.HttpUtil.normalize;
|
|
||||||
import static org.keycloak.client.admin.cli.util.IoUtil.printOut;
|
|
||||||
|
|
||||||
public abstract class AbstractGlobalOptionsCmd implements Runnable {
|
|
||||||
|
|
||||||
@Option(names = "--help",
|
|
||||||
description = "Print command specific help")
|
|
||||||
public void setHelp(boolean help) {
|
|
||||||
Globals.help = help;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Option(names = "-x",
|
|
||||||
description = "Print full stack trace when exiting with error")
|
|
||||||
public void setDumpTrace(boolean dumpTrace) {
|
|
||||||
Globals.dumpTrace = dumpTrace;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void printHelpIfNeeded() {
|
|
||||||
if (Globals.help) {
|
|
||||||
printOut(help());
|
|
||||||
System.exit(CommandLine.ExitCode.OK);
|
|
||||||
} else if (nothingToDo()) {
|
|
||||||
printOut(help());
|
|
||||||
System.exit(CommandLine.ExitCode.USAGE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean nothingToDo() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String help() {
|
|
||||||
return KcAdmCmd.usage();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String composeAdminRoot(String server) {
|
|
||||||
return normalize(server) + "admin";
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String extractTypeNameFromUri(String resourceUrl) {
|
|
||||||
String type = extractLastComponentOfUri(resourceUrl);
|
|
||||||
if (type.endsWith("s")) {
|
|
||||||
type = type.substring(0, type.length()-1);
|
|
||||||
}
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String extractLastComponentOfUri(String resourceUrl) {
|
|
||||||
int endPos = resourceUrl.endsWith("/") ? resourceUrl.length()-2 : resourceUrl.length()-1;
|
|
||||||
int pos = resourceUrl.lastIndexOf("/", endPos);
|
|
||||||
pos = pos == -1 ? 0 : pos;
|
|
||||||
return resourceUrl.substring(pos+1, endPos+1);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected JsonNode applyFieldFilter(ObjectMapper mapper, JsonNode rootNode, ReturnFields returnFields) {
|
|
||||||
// construct new JsonNode that satisfies filtering specified by returnFields
|
|
||||||
try {
|
|
||||||
return FilterUtil.copyFilteredObject(rootNode, returnFields);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException("Failed to apply fields filter", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -17,18 +17,18 @@
|
||||||
package org.keycloak.client.admin.cli.commands;
|
package org.keycloak.client.admin.cli.commands;
|
||||||
|
|
||||||
import org.apache.http.entity.ContentType;
|
import org.apache.http.entity.ContentType;
|
||||||
import org.keycloak.client.admin.cli.common.AttributeOperation;
|
import org.keycloak.client.admin.cli.CmdStdinContext;
|
||||||
import org.keycloak.client.admin.cli.common.CmdStdinContext;
|
import org.keycloak.client.admin.cli.ReflectionUtil;
|
||||||
import org.keycloak.client.admin.cli.config.ConfigData;
|
import org.keycloak.client.cli.common.AttributeOperation;
|
||||||
import org.keycloak.client.admin.cli.util.AccessibleBufferOutputStream;
|
import org.keycloak.client.cli.config.ConfigData;
|
||||||
import org.keycloak.client.admin.cli.util.Header;
|
import org.keycloak.client.cli.util.AccessibleBufferOutputStream;
|
||||||
import org.keycloak.client.admin.cli.util.Headers;
|
import org.keycloak.client.cli.util.Header;
|
||||||
import org.keycloak.client.admin.cli.util.HeadersBody;
|
import org.keycloak.client.cli.util.Headers;
|
||||||
import org.keycloak.client.admin.cli.util.HeadersBodyStatus;
|
import org.keycloak.client.cli.util.HeadersBody;
|
||||||
import org.keycloak.client.admin.cli.util.HttpUtil;
|
import org.keycloak.client.cli.util.HeadersBodyStatus;
|
||||||
import org.keycloak.client.admin.cli.util.OutputFormat;
|
import org.keycloak.client.cli.util.HttpUtil;
|
||||||
import org.keycloak.client.admin.cli.util.ReflectionUtil;
|
import org.keycloak.client.cli.util.OutputFormat;
|
||||||
import org.keycloak.client.admin.cli.util.ReturnFields;
|
import org.keycloak.client.cli.util.ReturnFields;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
|
@ -52,22 +52,19 @@ import picocli.CommandLine.ArgGroup;
|
||||||
import picocli.CommandLine.Option;
|
import picocli.CommandLine.Option;
|
||||||
import picocli.CommandLine.Parameters;
|
import picocli.CommandLine.Parameters;
|
||||||
|
|
||||||
import static org.keycloak.client.admin.cli.common.AttributeOperation.Type.DELETE;
|
import static org.keycloak.client.cli.common.AttributeOperation.Type.DELETE;
|
||||||
import static org.keycloak.client.admin.cli.common.AttributeOperation.Type.SET;
|
import static org.keycloak.client.cli.common.AttributeOperation.Type.SET;
|
||||||
import static org.keycloak.client.admin.cli.util.AuthUtil.ensureToken;
|
import static org.keycloak.client.cli.util.ConfigUtil.credentialsAvailable;
|
||||||
import static org.keycloak.client.admin.cli.util.ConfigUtil.credentialsAvailable;
|
import static org.keycloak.client.cli.util.ConfigUtil.loadConfig;
|
||||||
import static org.keycloak.client.admin.cli.util.ConfigUtil.loadConfig;
|
import static org.keycloak.client.cli.util.HttpUtil.checkSuccess;
|
||||||
import static org.keycloak.client.admin.cli.util.HttpUtil.checkSuccess;
|
import static org.keycloak.client.cli.util.HttpUtil.composeResourceUrl;
|
||||||
import static org.keycloak.client.admin.cli.util.HttpUtil.composeResourceUrl;
|
import static org.keycloak.client.cli.util.HttpUtil.doGet;
|
||||||
import static org.keycloak.client.admin.cli.util.HttpUtil.doGet;
|
import static org.keycloak.client.cli.util.IoUtil.copyStream;
|
||||||
import static org.keycloak.client.admin.cli.util.IoUtil.copyStream;
|
import static org.keycloak.client.cli.util.IoUtil.printErr;
|
||||||
import static org.keycloak.client.admin.cli.util.IoUtil.printErr;
|
import static org.keycloak.client.cli.util.IoUtil.printOut;
|
||||||
import static org.keycloak.client.admin.cli.util.IoUtil.printOut;
|
import static org.keycloak.client.cli.util.OutputUtil.MAPPER;
|
||||||
import static org.keycloak.client.admin.cli.util.OutputUtil.MAPPER;
|
import static org.keycloak.client.cli.util.OutputUtil.printAsCsv;
|
||||||
import static org.keycloak.client.admin.cli.util.OutputUtil.printAsCsv;
|
import static org.keycloak.client.cli.util.ParseUtil.parseKeyVal;
|
||||||
import static org.keycloak.client.admin.cli.util.ParseUtil.mergeAttributes;
|
|
||||||
import static org.keycloak.client.admin.cli.util.ParseUtil.parseFileOrStdin;
|
|
||||||
import static org.keycloak.client.admin.cli.util.ParseUtil.parseKeyVal;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
|
@ -208,7 +205,7 @@ public abstract class AbstractRequestCmd extends AbstractAuthOptionsCmd {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ctx = parseFileOrStdin(file);
|
ctx = CmdStdinContext.parseFileOrStdin(file);
|
||||||
}
|
}
|
||||||
} else if (body != null) {
|
} else if (body != null) {
|
||||||
content = new ByteArrayInputStream(body.getBytes(StandardCharsets.UTF_8));
|
content = new ByteArrayInputStream(body.getBytes(StandardCharsets.UTF_8));
|
||||||
|
@ -280,7 +277,7 @@ public abstract class AbstractRequestCmd extends AbstractAuthOptionsCmd {
|
||||||
throw new RuntimeException("Can't set attributes on content of type other than application/json");
|
throw new RuntimeException("Can't set attributes on content of type other than application/json");
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx = mergeAttributes(ctx, MAPPER.createObjectNode(), attrs);
|
ctx = CmdStdinContext.mergeAttributes(ctx, MAPPER.createObjectNode(), attrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (content == null && ctx.getContent() != null) {
|
if (content == null && ctx.getContent() != null) {
|
||||||
|
|
|
@ -21,12 +21,13 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
import picocli.CommandLine.Command;
|
import picocli.CommandLine.Command;
|
||||||
import picocli.CommandLine.Option;
|
import picocli.CommandLine.Option;
|
||||||
|
|
||||||
import org.keycloak.client.admin.cli.config.ConfigData;
|
import org.keycloak.client.admin.cli.KcAdmMain;
|
||||||
import org.keycloak.client.admin.cli.operations.ClientOperations;
|
import org.keycloak.client.admin.cli.operations.ClientOperations;
|
||||||
import org.keycloak.client.admin.cli.operations.GroupOperations;
|
import org.keycloak.client.admin.cli.operations.GroupOperations;
|
||||||
import org.keycloak.client.admin.cli.operations.RoleOperations;
|
import org.keycloak.client.admin.cli.operations.RoleOperations;
|
||||||
import org.keycloak.client.admin.cli.operations.LocalSearch;
|
import org.keycloak.client.admin.cli.operations.LocalSearch;
|
||||||
import org.keycloak.client.admin.cli.operations.UserOperations;
|
import org.keycloak.client.admin.cli.operations.UserOperations;
|
||||||
|
import org.keycloak.client.cli.config.ConfigData;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
|
@ -35,12 +36,10 @@ import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import static org.keycloak.client.admin.cli.util.AuthUtil.ensureToken;
|
import static org.keycloak.client.admin.cli.KcAdmMain.CMD;
|
||||||
import static org.keycloak.client.admin.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_STRING;
|
import static org.keycloak.client.cli.util.ConfigUtil.credentialsAvailable;
|
||||||
import static org.keycloak.client.admin.cli.util.ConfigUtil.credentialsAvailable;
|
import static org.keycloak.client.cli.util.ConfigUtil.loadConfig;
|
||||||
import static org.keycloak.client.admin.cli.util.ConfigUtil.loadConfig;
|
import static org.keycloak.client.cli.util.OsUtil.PROMPT;
|
||||||
import static org.keycloak.client.admin.cli.util.OsUtil.CMD;
|
|
||||||
import static org.keycloak.client.admin.cli.util.OsUtil.PROMPT;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
|
@ -290,7 +289,7 @@ public class AddRolesCmd extends AbstractAuthOptionsCmd {
|
||||||
out.println();
|
out.println();
|
||||||
out.println(" Global options:");
|
out.println(" Global options:");
|
||||||
out.println(" -x Print full stack trace when exiting with error");
|
out.println(" -x Print full stack trace when exiting with error");
|
||||||
out.println(" --config Path to the config file (" + DEFAULT_CONFIG_FILE_STRING + " by default)");
|
out.println(" --config Path to the config file (" + KcAdmMain.DEFAULT_CONFIG_FILE_STRING + " by default)");
|
||||||
out.println(" --no-config Don't use config file - no authentication info is loaded or saved");
|
out.println(" --no-config Don't use config file - no authentication info is loaded or saved");
|
||||||
out.println(" --token Token to use to invoke on Keycloak. Other credential may be ignored if this flag is set.");
|
out.println(" --token Token to use to invoke on Keycloak. Other credential may be ignored if this flag is set.");
|
||||||
out.println(" --truststore PATH Path to a truststore containing trusted certificates");
|
out.println(" --truststore PATH Path to a truststore containing trusted certificates");
|
||||||
|
|
|
@ -21,7 +21,7 @@ import java.io.StringWriter;
|
||||||
|
|
||||||
import picocli.CommandLine.Command;
|
import picocli.CommandLine.Command;
|
||||||
|
|
||||||
import static org.keycloak.client.admin.cli.util.OsUtil.CMD;
|
import static org.keycloak.client.admin.cli.KcAdmMain.CMD;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -16,237 +16,20 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.client.admin.cli.commands;
|
package org.keycloak.client.admin.cli.commands;
|
||||||
|
|
||||||
import org.keycloak.OAuth2Constants;
|
import org.keycloak.client.admin.cli.KcAdmMain;
|
||||||
import org.keycloak.client.admin.cli.config.ConfigData;
|
import org.keycloak.client.cli.common.BaseConfigCredentialsCmd;
|
||||||
import org.keycloak.client.admin.cli.config.RealmConfigData;
|
|
||||||
import org.keycloak.client.admin.cli.util.AuthUtil;
|
|
||||||
import org.keycloak.representations.AccessTokenResponse;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.PrintWriter;
|
|
||||||
import java.io.StringWriter;
|
|
||||||
import java.net.URL;
|
|
||||||
|
|
||||||
import picocli.CommandLine.Command;
|
import picocli.CommandLine.Command;
|
||||||
|
|
||||||
import static org.keycloak.client.admin.cli.util.AuthUtil.getAuthTokens;
|
|
||||||
import static org.keycloak.client.admin.cli.util.AuthUtil.getAuthTokensByJWT;
|
|
||||||
import static org.keycloak.client.admin.cli.util.AuthUtil.getAuthTokensBySecret;
|
|
||||||
import static org.keycloak.client.admin.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_STRING;
|
|
||||||
import static org.keycloak.client.admin.cli.util.ConfigUtil.getHandler;
|
|
||||||
import static org.keycloak.client.admin.cli.util.ConfigUtil.loadConfig;
|
|
||||||
import static org.keycloak.client.admin.cli.util.ConfigUtil.saveTokens;
|
|
||||||
import static org.keycloak.client.admin.cli.util.IoUtil.printErr;
|
|
||||||
import static org.keycloak.client.admin.cli.util.IoUtil.readSecret;
|
|
||||||
import static org.keycloak.client.admin.cli.util.OsUtil.CMD;
|
|
||||||
import static org.keycloak.client.admin.cli.util.OsUtil.OS_ARCH;
|
|
||||||
import static org.keycloak.client.admin.cli.util.OsUtil.PROMPT;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
*/
|
*/
|
||||||
@Command(name = "credentials", description = "--server SERVER_URL --realm REALM [ARGUMENTS]")
|
@Command(name = "credentials", description = "--server SERVER_URL --realm REALM [ARGUMENTS]")
|
||||||
public class ConfigCredentialsCmd extends AbstractAuthOptionsCmd {
|
public class ConfigCredentialsCmd extends BaseConfigCredentialsCmd {
|
||||||
|
|
||||||
private int sigLifetime = 600;
|
public ConfigCredentialsCmd() {
|
||||||
|
super(KcAdmMain.COMMAND_STATE);
|
||||||
public void init(ConfigData configData) {
|
|
||||||
if (server == null) {
|
|
||||||
server = configData.getServerUrl();
|
|
||||||
}
|
|
||||||
if (realm == null) {
|
|
||||||
realm = configData.getRealm();
|
|
||||||
}
|
|
||||||
if (trustStore == null) {
|
|
||||||
trustStore = configData.getTruststore();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RealmConfigData rdata = configData.getRealmConfigData(server, realm);
|
|
||||||
if (rdata == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (clientId == null) {
|
|
||||||
clientId = rdata.getClientId();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String[] getUnsupportedOptions() {
|
|
||||||
return new String[] {"--no-config", booleanOptionForCheck(noconfig)};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void process() {
|
|
||||||
// check server
|
|
||||||
if (server == null) {
|
|
||||||
throw new IllegalArgumentException("Required option not specified: --server");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
new URL(server);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException("Invalid server endpoint url: " + server, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (realm == null)
|
|
||||||
throw new IllegalArgumentException("Required option not specified: --realm");
|
|
||||||
|
|
||||||
String signedRequestToken = null;
|
|
||||||
boolean clientSet = clientId != null;
|
|
||||||
|
|
||||||
applyDefaultOptionValues();
|
|
||||||
String grantTypeForAuthentication = null;
|
|
||||||
|
|
||||||
if (user != null) {
|
|
||||||
grantTypeForAuthentication = OAuth2Constants.PASSWORD;
|
|
||||||
printErr("Logging into " + server + " as user " + user + " of realm " + realm);
|
|
||||||
|
|
||||||
// if user was set there needs to be a password so we can authenticate
|
|
||||||
if (password == null) {
|
|
||||||
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: ");
|
|
||||||
}
|
|
||||||
} 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: ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (keystore != null) {
|
|
||||||
if (secret != null) {
|
|
||||||
throw new IllegalArgumentException("Can't use both --keystore and --secret");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!new File(keystore).isFile()) {
|
|
||||||
throw new RuntimeException("No such keystore file: " + keystore);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (storePass == null) {
|
|
||||||
storePass = readSecret("Enter keystore password: ");
|
|
||||||
keyPass = readSecret("Enter key password: ");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (keyPass == null) {
|
|
||||||
keyPass = storePass;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (alias == null) {
|
|
||||||
alias = clientId;
|
|
||||||
}
|
|
||||||
|
|
||||||
String realmInfoUrl = server + "/realms/" + realm;
|
|
||||||
|
|
||||||
signedRequestToken = AuthUtil.getSignedRequestToken(keystore, storePass, keyPass,
|
|
||||||
alias, sigLifetime, clientId, realmInfoUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if only server and realm are set, just save config and be done
|
|
||||||
if (user == null && secret == null && keystore == null) {
|
|
||||||
getHandler().saveMergeConfig(config -> {
|
|
||||||
config.setServerUrl(server);
|
|
||||||
config.setRealm(realm);
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setupTruststore(copyWithServerInfo(loadConfig()));
|
|
||||||
|
|
||||||
// now use the token endpoint to retrieve access token, and refresh token
|
|
||||||
AccessTokenResponse tokens = signedRequestToken != null ?
|
|
||||||
getAuthTokensByJWT(server, realm, user, password, clientId, signedRequestToken) :
|
|
||||||
secret != null ?
|
|
||||||
getAuthTokensBySecret(server, realm, user, password, clientId, secret) :
|
|
||||||
getAuthTokens(server, realm, user, password, clientId);
|
|
||||||
|
|
||||||
Long sigExpiresAt = signedRequestToken == null ? null : System.currentTimeMillis() + sigLifetime * 1000;
|
|
||||||
|
|
||||||
// save tokens to config file
|
|
||||||
saveTokens(tokens, server, realm, clientId, signedRequestToken, sigExpiresAt, secret, grantTypeForAuthentication);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String help() {
|
|
||||||
return usage();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String usage() {
|
|
||||||
StringWriter sb = new StringWriter();
|
|
||||||
PrintWriter out = new PrintWriter(sb);
|
|
||||||
out.println("Usage: " + CMD + " config credentials --server SERVER_URL --realm REALM --user USER [--password PASSWORD] [ARGUMENTS]");
|
|
||||||
out.println(" " + CMD + " config credentials --server SERVER_URL --realm REALM --client CLIENT_ID [--secret SECRET] [ARGUMENTS]");
|
|
||||||
out.println(" " + CMD + " config credentials --server SERVER_URL --realm REALM --client CLIENT_ID [--keystore KEYSTORE] [ARGUMENTS]");
|
|
||||||
out.println();
|
|
||||||
out.println("Command to establish an authenticated client session with the server. There are many authentication");
|
|
||||||
out.println("options available, and it depends on server side client authentication configuration how client can or should authenticate.");
|
|
||||||
out.println("The information always required includes --server, and --realm. Then, --user and / or --client need to be used to authenticate.");
|
|
||||||
out.println("If --client is not provided it defaults to 'admin-cli'. The authentication options / requirements depend on how this client is configured.");
|
|
||||||
out.println();
|
|
||||||
out.println("If confidential client authentication is also configured, you may have to specify a client id, and client credentials in addition to");
|
|
||||||
out.println("user credentials. Client credentials are either a client secret, or a keystore information to use Signed JWT mechanism.");
|
|
||||||
out.println("If only client credentials are provided, and no user credentials, then the service account is used for login.");
|
|
||||||
out.println();
|
|
||||||
out.println("Arguments:");
|
|
||||||
out.println();
|
|
||||||
out.println(" Global options:");
|
|
||||||
out.println(" -x Print full stack trace when exiting with error");
|
|
||||||
out.println(" --config Path to a config file (" + DEFAULT_CONFIG_FILE_STRING + " by default)");
|
|
||||||
out.println(" --truststore PATH Path to a truststore containing trusted certificates");
|
|
||||||
out.println(" --trustpass PASSWORD Truststore password (prompted for if not specified and --truststore is used)");
|
|
||||||
out.println();
|
|
||||||
out.println(" Command specific options:");
|
|
||||||
out.println(" --server SERVER_URL Server endpoint url (e.g. 'http://localhost:8080')");
|
|
||||||
out.println(" --realm REALM Realm name to use");
|
|
||||||
out.println(" --user USER Username to login with");
|
|
||||||
out.println(" --password PASSWORD Password to login with (prompted for if not specified and --user is used)");
|
|
||||||
out.println(" --client CLIENT_ID ClientId used by this client tool ('admin-cli' by default)");
|
|
||||||
out.println(" --secret SECRET Secret to authenticate the client (prompted for if --client is specified, and no --keystore is specified)");
|
|
||||||
out.println(" --keystore PATH Path to a keystore containing private key");
|
|
||||||
out.println(" --storepass PASSWORD Keystore password (prompted for if not specified and --keystore is used)");
|
|
||||||
out.println(" --keypass PASSWORD Key password (prompted for if not specified and --keystore is used without --storepass,");
|
|
||||||
out.println(" otherwise defaults to keystore password)");
|
|
||||||
out.println(" --alias ALIAS Alias of the key inside a keystore (defaults to the value of ClientId)");
|
|
||||||
out.println();
|
|
||||||
out.println();
|
|
||||||
out.println("Examples:");
|
|
||||||
out.println();
|
|
||||||
out.println("Login as 'admin' user of 'master' realm to a local Keycloak server running on default port.");
|
|
||||||
out.println("You will be prompted for a password:");
|
|
||||||
out.println(" " + PROMPT + " " + CMD + " config credentials --server http://localhost:8080 --realm master --user admin");
|
|
||||||
out.println();
|
|
||||||
out.println("Login to Keycloak server at non-default endpoint passing the password via standard input:");
|
|
||||||
if (OS_ARCH.isWindows()) {
|
|
||||||
out.println(" " + PROMPT + " echo mypassword | " + CMD + " config credentials --server http://localhost:9080 --realm master --user admin");
|
|
||||||
} else {
|
|
||||||
out.println(" " + PROMPT + " " + CMD + " config credentials --server http://localhost:9080 --realm master --user admin << EOF");
|
|
||||||
out.println(" mypassword");
|
|
||||||
out.println(" EOF");
|
|
||||||
}
|
|
||||||
out.println();
|
|
||||||
out.println("Login specifying a password through command line:");
|
|
||||||
out.println(" " + PROMPT + " " + CMD + " config credentials --server http://localhost:9080 --realm master --user admin --password " + OS_ARCH.envVar("PASSWORD"));
|
|
||||||
out.println();
|
|
||||||
out.println("Login using a client service account of a custom client. You will be prompted for a client secret:");
|
|
||||||
out.println(" " + PROMPT + " " + CMD + " config credentials --server http://localhost:9080 --realm master --client reg-cli");
|
|
||||||
out.println();
|
|
||||||
out.println("Login using a client service account of a custom client, authenticating with signed JWT.");
|
|
||||||
out.println("You will be prompted for a keystore password, and a key password:");
|
|
||||||
out.println(" " + PROMPT + " " + CMD + " config credentials --server http://localhost:9080 --realm master --client reg-cli --keystore " + OS_ARCH.path("~/.keycloak/keystore.jks"));
|
|
||||||
out.println();
|
|
||||||
out.println("Login as 'user' while also authenticating a custom client with signed JWT.");
|
|
||||||
out.println("You will be prompted for a user password, a keystore password, and a key password:");
|
|
||||||
out.println(" " + PROMPT + " " + CMD + " config credentials --server http://localhost:9080 --realm master --user user --client reg-cli --keystore " + OS_ARCH.path("~/.keycloak/keystore.jks"));
|
|
||||||
out.println();
|
|
||||||
out.println();
|
|
||||||
out.println("Use '" + CMD + " help' for general information and a list of commands");
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,126 +16,19 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.client.admin.cli.commands;
|
package org.keycloak.client.admin.cli.commands;
|
||||||
|
|
||||||
import java.io.File;
|
import org.keycloak.client.admin.cli.KcAdmMain;
|
||||||
import java.io.PrintWriter;
|
import org.keycloak.client.cli.common.BaseConfigTruststoreCmd;
|
||||||
import java.io.StringWriter;
|
|
||||||
|
|
||||||
import picocli.CommandLine.Command;
|
import picocli.CommandLine.Command;
|
||||||
import picocli.CommandLine.Option;
|
|
||||||
import picocli.CommandLine.Parameters;
|
|
||||||
|
|
||||||
import static org.keycloak.client.admin.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_STRING;
|
|
||||||
import static org.keycloak.client.admin.cli.util.ConfigUtil.saveMergeConfig;
|
|
||||||
import static org.keycloak.client.admin.cli.util.IoUtil.readSecret;
|
|
||||||
import static org.keycloak.client.admin.cli.util.OsUtil.CMD;
|
|
||||||
import static org.keycloak.client.admin.cli.util.OsUtil.OS_ARCH;
|
|
||||||
import static org.keycloak.client.admin.cli.util.OsUtil.PROMPT;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
*/
|
*/
|
||||||
@Command(name = "truststore", description = "PATH [ARGUMENTS]")
|
@Command(name = "truststore", description = "PATH [ARGUMENTS]")
|
||||||
public class ConfigTruststoreCmd extends AbstractAuthOptionsCmd {
|
public class ConfigTruststoreCmd extends BaseConfigTruststoreCmd {
|
||||||
|
|
||||||
@Parameters(arity = "0..1")
|
public ConfigTruststoreCmd() {
|
||||||
private String store;
|
super(KcAdmMain.COMMAND_STATE);
|
||||||
|
|
||||||
@Option(names = {"-d", "--delete"}, description = "Remove truststore configuration")
|
|
||||||
private boolean delete;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean nothingToDo() {
|
|
||||||
return super.nothingToDo() && store == null && !delete;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String[] getUnsupportedOptions() {
|
|
||||||
return new String[] {"--server", server,
|
|
||||||
"--realm", realm,
|
|
||||||
"--client", clientId,
|
|
||||||
"--user", user,
|
|
||||||
"--password", password,
|
|
||||||
"--secret", secret,
|
|
||||||
"--truststore", trustStore,
|
|
||||||
"--keystore", keystore,
|
|
||||||
"--keypass", keyPass,
|
|
||||||
"--alias", alias,
|
|
||||||
"--no-config", booleanOptionForCheck(noconfig)};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void process() {
|
|
||||||
String pass;
|
|
||||||
|
|
||||||
if (!delete) {
|
|
||||||
|
|
||||||
if (store == null) {
|
|
||||||
throw new IllegalArgumentException("No truststore specified");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!new File(store).isFile()) {
|
|
||||||
throw new RuntimeException("Truststore file not found: " + store);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ("-".equals(trustPass)) {
|
|
||||||
trustPass = readSecret("Enter truststore password: ");
|
|
||||||
}
|
|
||||||
|
|
||||||
pass = trustPass;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
if (store != null) {
|
|
||||||
throw new IllegalArgumentException("Option --delete is mutually exclusive with specifying a TRUSTSTORE");
|
|
||||||
}
|
|
||||||
if (trustPass != null) {
|
|
||||||
throw new IllegalArgumentException("Options --trustpass and --delete are mutually exclusive");
|
|
||||||
}
|
|
||||||
pass = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
saveMergeConfig(config -> {
|
|
||||||
config.setTruststore(store);
|
|
||||||
config.setTrustpass(pass);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String help() {
|
|
||||||
return usage();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String usage() {
|
|
||||||
StringWriter sb = new StringWriter();
|
|
||||||
PrintWriter out = new PrintWriter(sb);
|
|
||||||
out.println("Usage: " + CMD + " config truststore [TRUSTSTORE | --delete] [--trustpass PASSWORD] [ARGUMENTS]");
|
|
||||||
out.println();
|
|
||||||
out.println("Command to configure a global truststore to use when using https to connect to Keycloak server.");
|
|
||||||
out.println();
|
|
||||||
out.println("Arguments:");
|
|
||||||
out.println();
|
|
||||||
out.println(" Global options:");
|
|
||||||
out.println(" -x Print full stack trace when exiting with error");
|
|
||||||
out.println(" --config Path to the config file (" + DEFAULT_CONFIG_FILE_STRING + " by default)");
|
|
||||||
out.println();
|
|
||||||
out.println(" Command specific options:");
|
|
||||||
out.println(" TRUSTSTORE Path to truststore file");
|
|
||||||
out.println(" --trustpass PASSWORD Truststore password to unlock truststore (prompted for if set to '-')");
|
|
||||||
out.println(" -d, --delete Remove truststore configuration");
|
|
||||||
out.println();
|
|
||||||
out.println();
|
|
||||||
out.println("Examples:");
|
|
||||||
out.println();
|
|
||||||
out.println("Specify a truststore - you will be prompted for truststore password every time it is used:");
|
|
||||||
out.println(" " + PROMPT + " " + CMD + " config truststore " + OS_ARCH.path("~/.keycloak/truststore.jks"));
|
|
||||||
out.println();
|
|
||||||
out.println("Specify a truststore, and password - truststore will automatically be used without prompting for password:");
|
|
||||||
out.println(" " + PROMPT + " " + CMD + " config truststore --trustpass " + OS_ARCH.envVar("PASSWORD") + " " + OS_ARCH.path("~/.keycloak/truststore.jks"));
|
|
||||||
out.println();
|
|
||||||
out.println("Remove truststore configuration:");
|
|
||||||
out.println(" " + PROMPT + " " + CMD + " config truststore --delete");
|
|
||||||
out.println();
|
|
||||||
out.println();
|
|
||||||
out.println("Use '" + CMD + " help' for general information and a list of commands");
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,16 +16,17 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.client.admin.cli.commands;
|
package org.keycloak.client.admin.cli.commands;
|
||||||
|
|
||||||
|
import org.keycloak.client.admin.cli.KcAdmMain;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
|
|
||||||
import picocli.CommandLine.Command;
|
import picocli.CommandLine.Command;
|
||||||
import picocli.CommandLine.Option;
|
import picocli.CommandLine.Option;
|
||||||
|
|
||||||
import static org.keycloak.client.admin.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_STRING;
|
import static org.keycloak.client.admin.cli.KcAdmMain.CMD;
|
||||||
import static org.keycloak.client.admin.cli.util.OsUtil.CMD;
|
import static org.keycloak.client.cli.util.OsUtil.OS_ARCH;
|
||||||
import static org.keycloak.client.admin.cli.util.OsUtil.OS_ARCH;
|
import static org.keycloak.client.cli.util.OsUtil.PROMPT;
|
||||||
import static org.keycloak.client.admin.cli.util.OsUtil.PROMPT;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
|
@ -91,7 +92,7 @@ public class CreateCmd extends AbstractRequestCmd {
|
||||||
out.println();
|
out.println();
|
||||||
out.println(" Global options:");
|
out.println(" Global options:");
|
||||||
out.println(" -x Print full stack trace when exiting with error");
|
out.println(" -x Print full stack trace when exiting with error");
|
||||||
out.println(" --config Path to the config file (" + DEFAULT_CONFIG_FILE_STRING + " by default)");
|
out.println(" --config Path to the config file (" + KcAdmMain.DEFAULT_CONFIG_FILE_STRING + " by default)");
|
||||||
out.println(" --no-config Don't use config file - no authentication info is loaded or saved");
|
out.println(" --no-config Don't use config file - no authentication info is loaded or saved");
|
||||||
out.println(" --token Token to use to invoke on Keycloak. Other credential may be ignored if this flag is set.");
|
out.println(" --token Token to use to invoke on Keycloak. Other credential may be ignored if this flag is set.");
|
||||||
out.println(" --truststore PATH Path to a truststore containing trusted certificates");
|
out.println(" --truststore PATH Path to a truststore containing trusted certificates");
|
||||||
|
|
|
@ -16,14 +16,15 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.client.admin.cli.commands;
|
package org.keycloak.client.admin.cli.commands;
|
||||||
|
|
||||||
|
import org.keycloak.client.admin.cli.KcAdmMain;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
|
|
||||||
import picocli.CommandLine.Command;
|
import picocli.CommandLine.Command;
|
||||||
|
|
||||||
import static org.keycloak.client.admin.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_STRING;
|
import static org.keycloak.client.admin.cli.KcAdmMain.CMD;
|
||||||
import static org.keycloak.client.admin.cli.util.OsUtil.CMD;
|
import static org.keycloak.client.cli.util.OsUtil.PROMPT;
|
||||||
import static org.keycloak.client.admin.cli.util.OsUtil.PROMPT;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -55,7 +56,7 @@ public class DeleteCmd extends CreateCmd {
|
||||||
out.println();
|
out.println();
|
||||||
out.println(" Global options:");
|
out.println(" Global options:");
|
||||||
out.println(" -x Print full stack trace when exiting with error");
|
out.println(" -x Print full stack trace when exiting with error");
|
||||||
out.println(" --config Path to the config file (" + DEFAULT_CONFIG_FILE_STRING + " by default)");
|
out.println(" --config Path to the config file (" + KcAdmMain.DEFAULT_CONFIG_FILE_STRING + " by default)");
|
||||||
out.println(" --no-config Don't use config file - no authentication info is loaded or saved");
|
out.println(" --no-config Don't use config file - no authentication info is loaded or saved");
|
||||||
out.println(" --token Token to use to invoke on Keycloak. Other credential may be ignored if this flag is set.");
|
out.println(" --token Token to use to invoke on Keycloak. Other credential may be ignored if this flag is set.");
|
||||||
out.println(" --truststore PATH Path to a truststore containing trusted certificates");
|
out.println(" --truststore PATH Path to a truststore containing trusted certificates");
|
||||||
|
|
|
@ -16,15 +16,16 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.client.admin.cli.commands;
|
package org.keycloak.client.admin.cli.commands;
|
||||||
|
|
||||||
|
import org.keycloak.client.admin.cli.KcAdmMain;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
|
|
||||||
import picocli.CommandLine.Command;
|
import picocli.CommandLine.Command;
|
||||||
import picocli.CommandLine.Option;
|
import picocli.CommandLine.Option;
|
||||||
|
|
||||||
import static org.keycloak.client.admin.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_STRING;
|
import static org.keycloak.client.admin.cli.KcAdmMain.CMD;
|
||||||
import static org.keycloak.client.admin.cli.util.OsUtil.CMD;
|
import static org.keycloak.client.cli.util.OsUtil.PROMPT;
|
||||||
import static org.keycloak.client.admin.cli.util.OsUtil.PROMPT;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
|
@ -91,7 +92,7 @@ public class GetCmd extends AbstractRequestCmd {
|
||||||
out.println();
|
out.println();
|
||||||
out.println(" Global options:");
|
out.println(" Global options:");
|
||||||
out.println(" -x Print full stack trace when exiting with error");
|
out.println(" -x Print full stack trace when exiting with error");
|
||||||
out.println(" --config Path to the config file (" + DEFAULT_CONFIG_FILE_STRING + " by default)");
|
out.println(" --config Path to the config file (" + KcAdmMain.DEFAULT_CONFIG_FILE_STRING + " by default)");
|
||||||
out.println(" --no-config Don't use config file - no authentication info is loaded or saved");
|
out.println(" --no-config Don't use config file - no authentication info is loaded or saved");
|
||||||
out.println(" --token Token to use to invoke on Keycloak. Other credential may be ignored if this flag is set.");
|
out.println(" --token Token to use to invoke on Keycloak. Other credential may be ignored if this flag is set.");
|
||||||
out.println(" --truststore PATH Path to a truststore containing trusted certificates");
|
out.println(" --truststore PATH Path to a truststore containing trusted certificates");
|
||||||
|
|
|
@ -16,11 +16,12 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.client.admin.cli.commands;
|
package org.keycloak.client.admin.cli.commands;
|
||||||
|
|
||||||
import org.keycloak.client.admin.cli.config.ConfigData;
|
import org.keycloak.client.admin.cli.KcAdmMain;
|
||||||
import org.keycloak.client.admin.cli.operations.ClientOperations;
|
import org.keycloak.client.admin.cli.operations.ClientOperations;
|
||||||
import org.keycloak.client.admin.cli.operations.GroupOperations;
|
import org.keycloak.client.admin.cli.operations.GroupOperations;
|
||||||
import org.keycloak.client.admin.cli.operations.RoleOperations;
|
import org.keycloak.client.admin.cli.operations.RoleOperations;
|
||||||
import org.keycloak.client.admin.cli.operations.UserOperations;
|
import org.keycloak.client.admin.cli.operations.UserOperations;
|
||||||
|
import org.keycloak.client.cli.config.ConfigData;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
|
@ -28,13 +29,11 @@ import java.io.StringWriter;
|
||||||
import picocli.CommandLine.Command;
|
import picocli.CommandLine.Command;
|
||||||
import picocli.CommandLine.Option;
|
import picocli.CommandLine.Option;
|
||||||
|
|
||||||
import static org.keycloak.client.admin.cli.util.AuthUtil.ensureToken;
|
import static org.keycloak.client.admin.cli.KcAdmMain.CMD;
|
||||||
import static org.keycloak.client.admin.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_STRING;
|
import static org.keycloak.client.cli.util.ConfigUtil.credentialsAvailable;
|
||||||
import static org.keycloak.client.admin.cli.util.ConfigUtil.credentialsAvailable;
|
import static org.keycloak.client.cli.util.ConfigUtil.loadConfig;
|
||||||
import static org.keycloak.client.admin.cli.util.ConfigUtil.loadConfig;
|
import static org.keycloak.client.cli.util.HttpUtil.composeResourceUrl;
|
||||||
import static org.keycloak.client.admin.cli.util.HttpUtil.composeResourceUrl;
|
import static org.keycloak.client.cli.util.OsUtil.PROMPT;
|
||||||
import static org.keycloak.client.admin.cli.util.OsUtil.CMD;
|
|
||||||
import static org.keycloak.client.admin.cli.util.OsUtil.PROMPT;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
|
@ -325,7 +324,7 @@ public class GetRolesCmd extends GetCmd {
|
||||||
out.println();
|
out.println();
|
||||||
out.println(" Global options:");
|
out.println(" Global options:");
|
||||||
out.println(" -x Print full stack trace when exiting with error");
|
out.println(" -x Print full stack trace when exiting with error");
|
||||||
out.println(" --config Path to the config file (" + DEFAULT_CONFIG_FILE_STRING + " by default)");
|
out.println(" --config Path to the config file (" + KcAdmMain.DEFAULT_CONFIG_FILE_STRING + " by default)");
|
||||||
out.println(" --no-config Don't use config file - no authentication info is loaded or saved");
|
out.println(" --no-config Don't use config file - no authentication info is loaded or saved");
|
||||||
out.println(" --token Token to use to invoke on Keycloak. Other credential may be ignored if this flag is set.");
|
out.println(" --token Token to use to invoke on Keycloak. Other credential may be ignored if this flag is set.");
|
||||||
out.println(" --truststore PATH Path to a truststore containing trusted certificates");
|
out.println(" --truststore PATH Path to a truststore containing trusted certificates");
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.keycloak.client.admin.cli.commands;
|
||||||
|
|
||||||
|
import org.keycloak.client.cli.util.FilterUtil;
|
||||||
|
import org.keycloak.client.cli.util.ReturnFields;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
|
import static org.keycloak.client.cli.util.HttpUtil.normalize;
|
||||||
|
|
||||||
|
public interface GlobalOptionsCmdHelper {
|
||||||
|
|
||||||
|
default String composeAdminRoot(String server) {
|
||||||
|
return normalize(server) + "admin";
|
||||||
|
}
|
||||||
|
|
||||||
|
default String extractTypeNameFromUri(String resourceUrl) {
|
||||||
|
String type = extractLastComponentOfUri(resourceUrl);
|
||||||
|
if (type.endsWith("s")) {
|
||||||
|
type = type.substring(0, type.length()-1);
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
default String extractLastComponentOfUri(String resourceUrl) {
|
||||||
|
int endPos = resourceUrl.endsWith("/") ? resourceUrl.length()-2 : resourceUrl.length()-1;
|
||||||
|
int pos = resourceUrl.lastIndexOf("/", endPos);
|
||||||
|
pos = pos == -1 ? 0 : pos;
|
||||||
|
return resourceUrl.substring(pos+1, endPos+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
default JsonNode applyFieldFilter(ObjectMapper mapper, JsonNode rootNode, ReturnFields returnFields) {
|
||||||
|
// construct new JsonNode that satisfies filtering specified by returnFields
|
||||||
|
try {
|
||||||
|
return FilterUtil.copyFilteredObject(rootNode, returnFields);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException("Failed to apply fields filter", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -21,7 +21,7 @@ import java.util.List;
|
||||||
import picocli.CommandLine.Command;
|
import picocli.CommandLine.Command;
|
||||||
import picocli.CommandLine.Parameters;
|
import picocli.CommandLine.Parameters;
|
||||||
|
|
||||||
import static org.keycloak.client.admin.cli.util.IoUtil.printOut;
|
import static org.keycloak.client.cli.util.IoUtil.printOut;
|
||||||
|
|
||||||
@Command(name = "help", description = "This Help")
|
@Command(name = "help", description = "This Help")
|
||||||
public class HelpCmd implements Runnable {
|
public class HelpCmd implements Runnable {
|
||||||
|
@ -39,11 +39,11 @@ public class HelpCmd implements Runnable {
|
||||||
if (args.size() > 1) {
|
if (args.size() > 1) {
|
||||||
switch (args.get(1)) {
|
switch (args.get(1)) {
|
||||||
case "credentials": {
|
case "credentials": {
|
||||||
printOut(ConfigCredentialsCmd.usage());
|
printOut(new ConfigCredentialsCmd().help());
|
||||||
break outer;
|
break outer;
|
||||||
}
|
}
|
||||||
case "truststore": {
|
case "truststore": {
|
||||||
printOut(ConfigTruststoreCmd.usage());
|
printOut(new ConfigTruststoreCmd().help());
|
||||||
break outer;
|
break outer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,14 +16,16 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.client.admin.cli.commands;
|
package org.keycloak.client.admin.cli.commands;
|
||||||
|
|
||||||
|
import org.keycloak.client.admin.cli.KcAdmMain;
|
||||||
|
import org.keycloak.client.cli.common.BaseGlobalOptionsCmd;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
|
|
||||||
import picocli.CommandLine.Command;
|
import picocli.CommandLine.Command;
|
||||||
|
|
||||||
import static org.keycloak.client.admin.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_STRING;
|
import static org.keycloak.client.admin.cli.KcAdmMain.CMD;
|
||||||
import static org.keycloak.client.admin.cli.util.OsUtil.CMD;
|
import static org.keycloak.client.cli.util.OsUtil.PROMPT;
|
||||||
import static org.keycloak.client.admin.cli.util.OsUtil.PROMPT;
|
|
||||||
|
|
||||||
@Command(name = "kcadm",
|
@Command(name = "kcadm",
|
||||||
header = {
|
header = {
|
||||||
|
@ -47,13 +49,18 @@ subcommands = {
|
||||||
GetRolesCmd.class,
|
GetRolesCmd.class,
|
||||||
SetPasswordCmd.class
|
SetPasswordCmd.class
|
||||||
})
|
})
|
||||||
public class KcAdmCmd extends AbstractGlobalOptionsCmd {
|
public class KcAdmCmd extends BaseGlobalOptionsCmd {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean nothingToDo() {
|
protected boolean nothingToDo() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String help() {
|
||||||
|
return usage();
|
||||||
|
}
|
||||||
|
|
||||||
public static String usage() {
|
public static String usage() {
|
||||||
StringWriter sb = new StringWriter();
|
StringWriter sb = new StringWriter();
|
||||||
PrintWriter out = new PrintWriter(sb);
|
PrintWriter out = new PrintWriter(sb);
|
||||||
|
@ -76,7 +83,7 @@ public class KcAdmCmd extends AbstractGlobalOptionsCmd {
|
||||||
out.println("Global options:");
|
out.println("Global options:");
|
||||||
out.println(" -x Print full stack trace when exiting with error");
|
out.println(" -x Print full stack trace when exiting with error");
|
||||||
out.println(" --help Print help for specific command");
|
out.println(" --help Print help for specific command");
|
||||||
out.println(" --config Path to the config file (" + DEFAULT_CONFIG_FILE_STRING + " by default)");
|
out.println(" --config Path to the config file (" + KcAdmMain.DEFAULT_CONFIG_FILE_STRING + " by default)");
|
||||||
out.println();
|
out.println();
|
||||||
out.println("Commands: ");
|
out.println("Commands: ");
|
||||||
out.println(" config Set up credentials, and other configuration settings using the config file");
|
out.println(" config Set up credentials, and other configuration settings using the config file");
|
||||||
|
|
|
@ -21,9 +21,10 @@ import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import picocli.CommandLine.Command;
|
import picocli.CommandLine.Command;
|
||||||
import picocli.CommandLine.Option;
|
import picocli.CommandLine.Option;
|
||||||
|
|
||||||
import org.keycloak.client.admin.cli.common.AttributeOperation;
|
import org.keycloak.client.admin.cli.CmdStdinContext;
|
||||||
import org.keycloak.client.admin.cli.common.CmdStdinContext;
|
import org.keycloak.client.cli.common.AttributeOperation;
|
||||||
import org.keycloak.client.admin.cli.util.AccessibleBufferOutputStream;
|
import org.keycloak.client.cli.common.BaseGlobalOptionsCmd;
|
||||||
|
import org.keycloak.client.cli.util.AccessibleBufferOutputStream;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
|
@ -35,22 +36,20 @@ import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static org.keycloak.client.admin.cli.common.AttributeOperation.Type.SET;
|
import static org.keycloak.client.cli.common.AttributeOperation.Type.SET;
|
||||||
import static org.keycloak.client.admin.cli.util.IoUtil.copyStream;
|
import static org.keycloak.client.cli.util.IoUtil.copyStream;
|
||||||
import static org.keycloak.client.admin.cli.util.IoUtil.printErr;
|
import static org.keycloak.client.cli.util.IoUtil.printErr;
|
||||||
import static org.keycloak.client.admin.cli.util.OsUtil.CMD;
|
import static org.keycloak.client.cli.util.OsUtil.OS_ARCH;
|
||||||
import static org.keycloak.client.admin.cli.util.OsUtil.OS_ARCH;
|
import static org.keycloak.client.cli.util.OsUtil.PROMPT;
|
||||||
import static org.keycloak.client.admin.cli.util.OsUtil.PROMPT;
|
import static org.keycloak.client.cli.util.OutputUtil.MAPPER;
|
||||||
import static org.keycloak.client.admin.cli.util.OutputUtil.MAPPER;
|
import static org.keycloak.client.cli.util.ParseUtil.parseKeyVal;
|
||||||
import static org.keycloak.client.admin.cli.util.ParseUtil.mergeAttributes;
|
import static org.keycloak.client.admin.cli.KcAdmMain.CMD;
|
||||||
import static org.keycloak.client.admin.cli.util.ParseUtil.parseFileOrStdin;
|
|
||||||
import static org.keycloak.client.admin.cli.util.ParseUtil.parseKeyVal;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
*/
|
*/
|
||||||
@Command(name = "new-object", description = "Command to create new JSON objects locally")
|
@Command(name = "new-object", description = "Command to create new JSON objects locally")
|
||||||
public class NewObjectCmd extends AbstractGlobalOptionsCmd {
|
public class NewObjectCmd extends BaseGlobalOptionsCmd implements GlobalOptionsCmdHelper {
|
||||||
|
|
||||||
@Option(names = {"-f", "--file"}, description = "Read object from file or standard input if FILENAME is set to '-'")
|
@Option(names = {"-f", "--file"}, description = "Read object from file or standard input if FILENAME is set to '-'")
|
||||||
String file;
|
String file;
|
||||||
|
@ -73,11 +72,11 @@ public class NewObjectCmd extends AbstractGlobalOptionsCmd {
|
||||||
CmdStdinContext<JsonNode> ctx = new CmdStdinContext<>();
|
CmdStdinContext<JsonNode> ctx = new CmdStdinContext<>();
|
||||||
|
|
||||||
if (file != null) {
|
if (file != null) {
|
||||||
ctx = parseFileOrStdin(file);
|
ctx = CmdStdinContext.parseFileOrStdin(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attrs.size() > 0) {
|
if (attrs.size() > 0) {
|
||||||
ctx = mergeAttributes(ctx, MAPPER.createObjectNode(), attrs);
|
ctx = CmdStdinContext.mergeAttributes(ctx, MAPPER.createObjectNode(), attrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (body == null && ctx.getContent() != null) {
|
if (body == null && ctx.getContent() != null) {
|
||||||
|
|
|
@ -16,12 +16,13 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.client.admin.cli.commands;
|
package org.keycloak.client.admin.cli.commands;
|
||||||
|
|
||||||
import org.keycloak.client.admin.cli.config.ConfigData;
|
import org.keycloak.client.admin.cli.KcAdmMain;
|
||||||
import org.keycloak.client.admin.cli.operations.ClientOperations;
|
import org.keycloak.client.admin.cli.operations.ClientOperations;
|
||||||
import org.keycloak.client.admin.cli.operations.GroupOperations;
|
import org.keycloak.client.admin.cli.operations.GroupOperations;
|
||||||
import org.keycloak.client.admin.cli.operations.LocalSearch;
|
import org.keycloak.client.admin.cli.operations.LocalSearch;
|
||||||
import org.keycloak.client.admin.cli.operations.RoleOperations;
|
import org.keycloak.client.admin.cli.operations.RoleOperations;
|
||||||
import org.keycloak.client.admin.cli.operations.UserOperations;
|
import org.keycloak.client.admin.cli.operations.UserOperations;
|
||||||
|
import org.keycloak.client.cli.config.ConfigData;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
|
@ -35,12 +36,10 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
import picocli.CommandLine.Command;
|
import picocli.CommandLine.Command;
|
||||||
import picocli.CommandLine.Option;
|
import picocli.CommandLine.Option;
|
||||||
|
|
||||||
import static org.keycloak.client.admin.cli.util.AuthUtil.ensureToken;
|
import static org.keycloak.client.admin.cli.KcAdmMain.CMD;
|
||||||
import static org.keycloak.client.admin.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_STRING;
|
import static org.keycloak.client.cli.util.ConfigUtil.credentialsAvailable;
|
||||||
import static org.keycloak.client.admin.cli.util.ConfigUtil.credentialsAvailable;
|
import static org.keycloak.client.cli.util.ConfigUtil.loadConfig;
|
||||||
import static org.keycloak.client.admin.cli.util.ConfigUtil.loadConfig;
|
import static org.keycloak.client.cli.util.OsUtil.PROMPT;
|
||||||
import static org.keycloak.client.admin.cli.util.OsUtil.CMD;
|
|
||||||
import static org.keycloak.client.admin.cli.util.OsUtil.PROMPT;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
|
@ -297,7 +296,7 @@ public class RemoveRolesCmd extends AbstractAuthOptionsCmd {
|
||||||
out.println();
|
out.println();
|
||||||
out.println(" Global options:");
|
out.println(" Global options:");
|
||||||
out.println(" -x Print full stack trace when exiting with error");
|
out.println(" -x Print full stack trace when exiting with error");
|
||||||
out.println(" --config Path to the config file (" + DEFAULT_CONFIG_FILE_STRING + " by default)");
|
out.println(" --config Path to the config file (" + KcAdmMain.DEFAULT_CONFIG_FILE_STRING + " by default)");
|
||||||
out.println(" --no-config Don't use config file - no authentication info is loaded or saved");
|
out.println(" --no-config Don't use config file - no authentication info is loaded or saved");
|
||||||
out.println(" --token Token to use to invoke on Keycloak. Other credential may be ignored if this flag is set.");
|
out.println(" --token Token to use to invoke on Keycloak. Other credential may be ignored if this flag is set.");
|
||||||
out.println(" --truststore PATH Path to a truststore containing trusted certificates");
|
out.println(" --truststore PATH Path to a truststore containing trusted certificates");
|
||||||
|
|
|
@ -16,7 +16,8 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.client.admin.cli.commands;
|
package org.keycloak.client.admin.cli.commands;
|
||||||
|
|
||||||
import org.keycloak.client.admin.cli.config.ConfigData;
|
import org.keycloak.client.admin.cli.KcAdmMain;
|
||||||
|
import org.keycloak.client.cli.config.ConfigData;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
|
@ -26,13 +27,11 @@ import picocli.CommandLine.Option;
|
||||||
|
|
||||||
import static org.keycloak.client.admin.cli.operations.UserOperations.getIdFromUsername;
|
import static org.keycloak.client.admin.cli.operations.UserOperations.getIdFromUsername;
|
||||||
import static org.keycloak.client.admin.cli.operations.UserOperations.resetUserPassword;
|
import static org.keycloak.client.admin.cli.operations.UserOperations.resetUserPassword;
|
||||||
import static org.keycloak.client.admin.cli.util.AuthUtil.ensureToken;
|
import static org.keycloak.client.cli.util.ConfigUtil.credentialsAvailable;
|
||||||
import static org.keycloak.client.admin.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_STRING;
|
import static org.keycloak.client.cli.util.ConfigUtil.loadConfig;
|
||||||
import static org.keycloak.client.admin.cli.util.ConfigUtil.credentialsAvailable;
|
import static org.keycloak.client.cli.util.IoUtil.readSecret;
|
||||||
import static org.keycloak.client.admin.cli.util.ConfigUtil.loadConfig;
|
import static org.keycloak.client.cli.util.OsUtil.PROMPT;
|
||||||
import static org.keycloak.client.admin.cli.util.IoUtil.readSecret;
|
import static org.keycloak.client.admin.cli.KcAdmMain.CMD;
|
||||||
import static org.keycloak.client.admin.cli.util.OsUtil.CMD;
|
|
||||||
import static org.keycloak.client.admin.cli.util.OsUtil.PROMPT;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
|
@ -117,7 +116,7 @@ public class SetPasswordCmd extends AbstractAuthOptionsCmd {
|
||||||
out.println();
|
out.println();
|
||||||
out.println(" Global options:");
|
out.println(" Global options:");
|
||||||
out.println(" -x Print full stack trace when exiting with error");
|
out.println(" -x Print full stack trace when exiting with error");
|
||||||
out.println(" --config Path to the config file (" + DEFAULT_CONFIG_FILE_STRING + " by default)");
|
out.println(" --config Path to the config file (" + KcAdmMain.DEFAULT_CONFIG_FILE_STRING + " by default)");
|
||||||
out.println(" --no-config Don't use config file - no authentication info is loaded or saved");
|
out.println(" --no-config Don't use config file - no authentication info is loaded or saved");
|
||||||
out.println(" --token Token to use to invoke on Keycloak. Other credential may be ignored if this flag is set.");
|
out.println(" --token Token to use to invoke on Keycloak. Other credential may be ignored if this flag is set.");
|
||||||
out.println(" --truststore PATH Path to a truststore containing trusted certificates");
|
out.println(" --truststore PATH Path to a truststore containing trusted certificates");
|
||||||
|
|
|
@ -17,16 +17,17 @@
|
||||||
|
|
||||||
package org.keycloak.client.admin.cli.commands;
|
package org.keycloak.client.admin.cli.commands;
|
||||||
|
|
||||||
|
import org.keycloak.client.admin.cli.KcAdmMain;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
|
|
||||||
import picocli.CommandLine.Command;
|
import picocli.CommandLine.Command;
|
||||||
import picocli.CommandLine.Option;
|
import picocli.CommandLine.Option;
|
||||||
|
|
||||||
import static org.keycloak.client.admin.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_STRING;
|
import static org.keycloak.client.admin.cli.KcAdmMain.CMD;
|
||||||
import static org.keycloak.client.admin.cli.util.OsUtil.CMD;
|
import static org.keycloak.client.cli.util.OsUtil.OS_ARCH;
|
||||||
import static org.keycloak.client.admin.cli.util.OsUtil.OS_ARCH;
|
import static org.keycloak.client.cli.util.OsUtil.PROMPT;
|
||||||
import static org.keycloak.client.admin.cli.util.OsUtil.PROMPT;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
|
@ -97,7 +98,7 @@ public class UpdateCmd extends AbstractRequestCmd {
|
||||||
out.println();
|
out.println();
|
||||||
out.println(" Global options:");
|
out.println(" Global options:");
|
||||||
out.println(" -x Print full stack trace when exiting with error");
|
out.println(" -x Print full stack trace when exiting with error");
|
||||||
out.println(" --config Path to the config file (" + DEFAULT_CONFIG_FILE_STRING + " by default)");
|
out.println(" --config Path to the config file (" + KcAdmMain.DEFAULT_CONFIG_FILE_STRING + " by default)");
|
||||||
out.println(" --no-config Don't use config file - no authentication info is loaded or saved");
|
out.println(" --no-config Don't use config file - no authentication info is loaded or saved");
|
||||||
out.println(" --token Token to use to invoke on Keycloak. Other credential may be ignored if this flag is set.");
|
out.println(" --token Token to use to invoke on Keycloak. Other credential may be ignored if this flag is set.");
|
||||||
out.println(" --truststore PATH Path to a truststore containing trusted certificates");
|
out.println(" --truststore PATH Path to a truststore containing trusted certificates");
|
||||||
|
|
|
@ -1,184 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
|
||||||
* and other contributors as indicated by the @author tags.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package org.keycloak.client.admin.cli.config;
|
|
||||||
|
|
||||||
import org.keycloak.util.JsonSerialization;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
|
||||||
*/
|
|
||||||
public class RealmConfigData {
|
|
||||||
|
|
||||||
private String serverUrl;
|
|
||||||
|
|
||||||
private String realm;
|
|
||||||
|
|
||||||
private String clientId;
|
|
||||||
|
|
||||||
private String token;
|
|
||||||
|
|
||||||
private String refreshToken;
|
|
||||||
|
|
||||||
private String signingToken;
|
|
||||||
|
|
||||||
private String secret;
|
|
||||||
|
|
||||||
private String grantTypeForAuthentication;
|
|
||||||
|
|
||||||
private Long expiresAt;
|
|
||||||
|
|
||||||
private Long refreshExpiresAt;
|
|
||||||
|
|
||||||
private Long sigExpiresAt;
|
|
||||||
|
|
||||||
|
|
||||||
public String serverUrl() {
|
|
||||||
return serverUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void serverUrl(String serverUrl) {
|
|
||||||
this.serverUrl = serverUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String realm() {
|
|
||||||
return realm;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void realm(String realm) {
|
|
||||||
this.realm = realm;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getClientId() {
|
|
||||||
return clientId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setClientId(String clientId) {
|
|
||||||
this.clientId = clientId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getToken() {
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setToken(String token) {
|
|
||||||
this.token = token;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getRefreshToken() {
|
|
||||||
return refreshToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRefreshToken(String refreshToken) {
|
|
||||||
this.refreshToken = refreshToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getSigningToken() {
|
|
||||||
return signingToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSigningToken(String signingToken) {
|
|
||||||
this.signingToken = signingToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getSecret() {
|
|
||||||
return secret;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSecret(String secret) {
|
|
||||||
this.secret = secret;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getGrantTypeForAuthentication() {
|
|
||||||
return grantTypeForAuthentication;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setGrantTypeForAuthentication(String grantTypeForAuthentication) {
|
|
||||||
this.grantTypeForAuthentication = grantTypeForAuthentication;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Long getExpiresAt() {
|
|
||||||
return expiresAt;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setExpiresAt(Long expiresAt) {
|
|
||||||
this.expiresAt = expiresAt;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Long getRefreshExpiresAt() {
|
|
||||||
return refreshExpiresAt;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRefreshExpiresAt(Long refreshExpiresAt) {
|
|
||||||
this.refreshExpiresAt = refreshExpiresAt;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Long getSigExpiresAt() {
|
|
||||||
return sigExpiresAt;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSigExpiresAt(Long sigExpiresAt) {
|
|
||||||
this.sigExpiresAt = sigExpiresAt;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void merge(RealmConfigData source) {
|
|
||||||
serverUrl = source.serverUrl;
|
|
||||||
realm = source.realm;
|
|
||||||
clientId = source.clientId;
|
|
||||||
token = source.token;
|
|
||||||
refreshToken = source.refreshToken;
|
|
||||||
signingToken = source.signingToken;
|
|
||||||
secret = source.secret;
|
|
||||||
grantTypeForAuthentication = source.grantTypeForAuthentication;
|
|
||||||
expiresAt = source.expiresAt;
|
|
||||||
refreshExpiresAt = source.refreshExpiresAt;
|
|
||||||
sigExpiresAt = source.sigExpiresAt;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void mergeRefreshTokens(RealmConfigData source) {
|
|
||||||
token = source.token;
|
|
||||||
refreshToken = source.refreshToken;
|
|
||||||
expiresAt = source.expiresAt;
|
|
||||||
refreshExpiresAt = source.refreshExpiresAt;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
try {
|
|
||||||
return JsonSerialization.writeValueAsPrettyString(this);
|
|
||||||
} catch (IOException e) {
|
|
||||||
return super.toString() + " - Error: " + e.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public RealmConfigData deepcopy() {
|
|
||||||
RealmConfigData data = new RealmConfigData();
|
|
||||||
data.serverUrl = serverUrl;
|
|
||||||
data.realm = realm;
|
|
||||||
data.clientId = clientId;
|
|
||||||
data.token = token;
|
|
||||||
data.refreshToken = refreshToken;
|
|
||||||
data.signingToken = signingToken;
|
|
||||||
data.secret = secret;
|
|
||||||
data.grantTypeForAuthentication = grantTypeForAuthentication;
|
|
||||||
data.expiresAt = expiresAt;
|
|
||||||
data.refreshExpiresAt = refreshExpiresAt;
|
|
||||||
data.sigExpiresAt = sigExpiresAt;
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -16,14 +16,12 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.client.admin.cli.operations;
|
package org.keycloak.client.admin.cli.operations;
|
||||||
|
|
||||||
import static org.keycloak.client.admin.cli.util.HttpUtil.getIdForType;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
*/
|
*/
|
||||||
public class ClientOperations {
|
public class ClientOperations {
|
||||||
|
|
||||||
public static String getIdFromClientId(String rootUrl, String realm, String auth, String clientId) {
|
public static String getIdFromClientId(String rootUrl, String realm, String auth, String clientId) {
|
||||||
return getIdForType(rootUrl, realm, auth, "clients", "clientId", clientId, "clientId");
|
return OperationUtils.getIdForType(rootUrl, realm, auth, "clients", "clientId", clientId, "clientId");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,10 +18,9 @@ package org.keycloak.client.admin.cli.operations;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.keycloak.client.admin.cli.util.HttpUtil.composeResourceUrl;
|
import static org.keycloak.client.cli.util.HttpUtil.composeResourceUrl;
|
||||||
import static org.keycloak.client.admin.cli.util.HttpUtil.doDeleteJSON;
|
import static org.keycloak.client.cli.util.HttpUtil.doDeleteJSON;
|
||||||
import static org.keycloak.client.admin.cli.util.HttpUtil.doPostJSON;
|
import static org.keycloak.client.cli.util.HttpUtil.doPostJSON;
|
||||||
import static org.keycloak.client.admin.cli.util.HttpUtil.getIdForType;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
|
@ -29,11 +28,11 @@ import static org.keycloak.client.admin.cli.util.HttpUtil.getIdForType;
|
||||||
public class GroupOperations {
|
public class GroupOperations {
|
||||||
|
|
||||||
public static String getIdFromName(String rootUrl, String realm, String auth, String groupname) {
|
public static String getIdFromName(String rootUrl, String realm, String auth, String groupname) {
|
||||||
return getIdForType(rootUrl, realm, auth, "groups", "search", groupname, "name", () -> new String[] { "exact", "true" });
|
return OperationUtils.getIdForType(rootUrl, realm, auth, "groups", "search", groupname, "name", () -> new String[] { "exact", "true" });
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getIdFromPath(String rootUrl, String realm, String auth, String path) {
|
public static String getIdFromPath(String rootUrl, String realm, String auth, String path) {
|
||||||
return getIdForType(rootUrl, realm, auth, "groups", "path", path, "path");
|
return OperationUtils.getIdForType(rootUrl, realm, auth, "groups", "path", path, "path");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void addRealmRoles(String rootUrl, String realm, String auth, String groupid, List<?> roles) {
|
public static void addRealmRoles(String rootUrl, String realm, String auth, String groupid, List<?> roles) {
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.keycloak.client.admin.cli.operations;
|
||||||
|
|
||||||
|
import org.keycloak.client.cli.util.HttpUtil;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
|
||||||
|
import static org.keycloak.common.util.ObjectUtil.capitalize;
|
||||||
|
|
||||||
|
public class OperationUtils {
|
||||||
|
|
||||||
|
private static final String[] DEFAULT_QUERY_PARAMS = { "first", "0", "max", "2" };
|
||||||
|
|
||||||
|
public static String getIdForType(String rootUrl, String realm, String auth, String resourceEndpoint, String attrName, String attrValue, String inputAttrName) {
|
||||||
|
|
||||||
|
return getAttrForType(rootUrl, realm, auth, resourceEndpoint, attrName, attrValue, inputAttrName, "id", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getIdForType(String rootUrl, String realm, String auth, String resourceEndpoint, String attrName, String attrValue, String inputAttrName, Supplier<String[]> endpointParams) {
|
||||||
|
return getAttrForType(rootUrl, realm, auth, resourceEndpoint, attrName, attrValue, inputAttrName, "id", endpointParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getAttrForType(String rootUrl, String realm, String auth, String resourceEndpoint, String attrName, String attrValue, String inputAttrName, String returnAttrName) {
|
||||||
|
return getAttrForType(rootUrl, realm, auth, resourceEndpoint, attrName, attrValue, inputAttrName, returnAttrName, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getAttrForType(String rootUrl, String realm, String auth, String resourceEndpoint, String attrName, String attrValue, String inputAttrName, String returnAttrName, Supplier<String[]> endpointParams) {
|
||||||
|
String resourceUrl = HttpUtil.composeResourceUrl(rootUrl, realm, resourceEndpoint);
|
||||||
|
String[] defaultParams;
|
||||||
|
|
||||||
|
if (endpointParams == null) {
|
||||||
|
defaultParams = DEFAULT_QUERY_PARAMS;
|
||||||
|
} else {
|
||||||
|
defaultParams = endpointParams.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
resourceUrl = HttpUtil.addQueryParamsToUri(resourceUrl, attrName, attrValue);
|
||||||
|
resourceUrl = HttpUtil.addQueryParamsToUri(resourceUrl, defaultParams);
|
||||||
|
|
||||||
|
List<ObjectNode> results = HttpUtil.doGetJSON(RoleOperations.LIST_OF_NODES.class, resourceUrl, auth);
|
||||||
|
|
||||||
|
ObjectNode match;
|
||||||
|
try {
|
||||||
|
match = new LocalSearch(results).exactMatchOne(attrValue, inputAttrName);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Multiple " + resourceEndpoint + " found for " + inputAttrName + ": " + attrValue, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
String typeName = HttpUtil.singularize(resourceEndpoint);
|
||||||
|
if (match == null) {
|
||||||
|
if (results.size() > 1) {
|
||||||
|
throw new RuntimeException("Some matches, but not an exact match, found for " + capitalize(typeName) + " with " + inputAttrName + ": " + attrValue + ". Try using a more unique search, such as an id.");
|
||||||
|
}
|
||||||
|
throw new RuntimeException(capitalize(typeName) + " not found for " + inputAttrName + ": " + attrValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonNode attr = match.get(returnAttrName);
|
||||||
|
if (attr == null) {
|
||||||
|
throw new RuntimeException("Returned " + typeName + " info has no '" + returnAttrName + "' attribute");
|
||||||
|
}
|
||||||
|
return attr.asText();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -17,18 +17,17 @@
|
||||||
package org.keycloak.client.admin.cli.operations;
|
package org.keycloak.client.admin.cli.operations;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
|
||||||
|
import static org.keycloak.client.cli.util.HttpUtil.composeResourceUrl;
|
||||||
|
import static org.keycloak.client.cli.util.HttpUtil.doDeleteJSON;
|
||||||
|
import static org.keycloak.client.cli.util.HttpUtil.doGetJSON;
|
||||||
|
import static org.keycloak.client.cli.util.HttpUtil.doPostJSON;
|
||||||
|
|
||||||
import org.keycloak.representations.idm.RoleRepresentation;
|
import org.keycloak.representations.idm.RoleRepresentation;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.keycloak.client.admin.cli.util.HttpUtil.composeResourceUrl;
|
|
||||||
import static org.keycloak.client.admin.cli.util.HttpUtil.doDeleteJSON;
|
|
||||||
import static org.keycloak.client.admin.cli.util.HttpUtil.doGetJSON;
|
|
||||||
import static org.keycloak.client.admin.cli.util.HttpUtil.doPostJSON;
|
|
||||||
import static org.keycloak.client.admin.cli.util.HttpUtil.getAttrForType;
|
|
||||||
import static org.keycloak.client.admin.cli.util.HttpUtil.getIdForType;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
*/
|
*/
|
||||||
|
@ -38,7 +37,7 @@ public class RoleOperations {
|
||||||
public static class LIST_OF_NODES extends ArrayList<ObjectNode>{};
|
public static class LIST_OF_NODES extends ArrayList<ObjectNode>{};
|
||||||
|
|
||||||
public static String getIdFromRoleName(String adminRoot, String realm, String auth, String rname) {
|
public static String getIdFromRoleName(String adminRoot, String realm, String auth, String rname) {
|
||||||
return getIdForType(adminRoot, realm, auth, "roles", "search", rname, "name");
|
return OperationUtils.getIdForType(adminRoot, realm, auth, "roles", "search", rname, "name");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void addRealmRoles(String rootUrl, String realm, String auth, String roleid, List<?> roles) {
|
public static void addRealmRoles(String rootUrl, String realm, String auth, String roleid, List<?> roles) {
|
||||||
|
@ -60,11 +59,11 @@ public class RoleOperations {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getRoleNameFromId(String adminRoot, String realm, String auth, String rid) {
|
public static String getRoleNameFromId(String adminRoot, String realm, String auth, String rid) {
|
||||||
return getAttrForType(adminRoot, realm, auth, "roles", "id", rid, "id","name");
|
return OperationUtils.getAttrForType(adminRoot, realm, auth, "roles", "id", rid, "id","name");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getClientRoleNameFromId(String adminRoot, String realm, String auth, String cid, String rid) {
|
public static String getClientRoleNameFromId(String adminRoot, String realm, String auth, String cid, String rid) {
|
||||||
return getAttrForType(adminRoot, realm, auth, "clients/" + cid + "/roles", "id", rid, "id", "name");
|
return OperationUtils.getAttrForType(adminRoot, realm, auth, "clients/" + cid + "/roles", "id", rid, "id", "name");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<RoleRepresentation> getRealmRoles(String rootUrl, String realm, String auth) {
|
public static List<RoleRepresentation> getRealmRoles(String rootUrl, String realm, String auth) {
|
||||||
|
|
|
@ -16,10 +16,10 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.client.admin.cli.operations;
|
package org.keycloak.client.admin.cli.operations;
|
||||||
|
|
||||||
import org.keycloak.client.admin.cli.util.Headers;
|
import org.keycloak.client.cli.util.Headers;
|
||||||
import org.keycloak.client.admin.cli.util.HeadersBody;
|
import org.keycloak.client.cli.util.HeadersBody;
|
||||||
import org.keycloak.client.admin.cli.util.HeadersBodyStatus;
|
import org.keycloak.client.cli.util.HeadersBodyStatus;
|
||||||
import org.keycloak.client.admin.cli.util.HttpUtil;
|
import org.keycloak.client.cli.util.HttpUtil;
|
||||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||||
import org.keycloak.util.JsonSerialization;
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
|
||||||
|
@ -27,10 +27,9 @@ import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.keycloak.client.admin.cli.util.HttpUtil.composeResourceUrl;
|
import static org.keycloak.client.cli.util.HttpUtil.composeResourceUrl;
|
||||||
import static org.keycloak.client.admin.cli.util.HttpUtil.doDeleteJSON;
|
import static org.keycloak.client.cli.util.HttpUtil.doDeleteJSON;
|
||||||
import static org.keycloak.client.admin.cli.util.HttpUtil.doPostJSON;
|
import static org.keycloak.client.cli.util.HttpUtil.doPostJSON;
|
||||||
import static org.keycloak.client.admin.cli.util.HttpUtil.getIdForType;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
|
@ -91,7 +90,7 @@ public class UserOperations {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getIdFromUsername(String rootUrl, String realm, String auth, String username) {
|
public static String getIdFromUsername(String rootUrl, String realm, String auth, String username) {
|
||||||
return getIdForType(rootUrl, realm, auth, "users", "username", username, "username",
|
return OperationUtils.getIdForType(rootUrl, realm, auth, "users", "username", username, "username",
|
||||||
() -> new String[] {"exact", "true"});
|
() -> new String[] {"exact", "true"});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.keycloak.client.admin.cli.common;
|
package org.keycloak.client.cli.common;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
|
@ -14,7 +14,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.keycloak.client.admin.cli.common;
|
package org.keycloak.client.cli.common;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
|
@ -0,0 +1,276 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.keycloak.client.cli.common;
|
||||||
|
|
||||||
|
import org.keycloak.OAuth2Constants;
|
||||||
|
import org.keycloak.client.cli.config.ConfigData;
|
||||||
|
import org.keycloak.client.cli.config.ConfigHandler;
|
||||||
|
import org.keycloak.client.cli.config.FileConfigHandler;
|
||||||
|
import org.keycloak.client.cli.config.InMemoryConfigHandler;
|
||||||
|
import org.keycloak.client.cli.config.RealmConfigData;
|
||||||
|
import org.keycloak.client.cli.util.AuthUtil;
|
||||||
|
import org.keycloak.client.cli.util.ConfigUtil;
|
||||||
|
import org.keycloak.client.cli.util.HttpUtil;
|
||||||
|
import org.keycloak.client.cli.util.IoUtil;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import picocli.CommandLine.Option;
|
||||||
|
|
||||||
|
import static org.keycloak.client.cli.config.FileConfigHandler.setConfigFile;
|
||||||
|
import static org.keycloak.client.cli.util.ConfigUtil.DEFAULT_CLIENT;
|
||||||
|
import static org.keycloak.client.cli.util.ConfigUtil.checkServerInfo;
|
||||||
|
import static org.keycloak.client.cli.util.ConfigUtil.loadConfig;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
|
*/
|
||||||
|
public abstract class BaseAuthOptionsCmd extends BaseGlobalOptionsCmd {
|
||||||
|
|
||||||
|
public static final String DEFAULT_CONFIG_PATH_STRING_KEY = "default.config.path.string";
|
||||||
|
|
||||||
|
@Option(names = "--config", description = "Path to the config file (${sys:"+DEFAULT_CONFIG_PATH_STRING_KEY+"} by default)")
|
||||||
|
protected String config;
|
||||||
|
|
||||||
|
@Option(names = "--no-config", description = "Don't use config file - no authentication info is loaded or saved")
|
||||||
|
protected boolean noconfig;
|
||||||
|
|
||||||
|
@Option(names = "--server", description = "Server endpoint url (e.g. 'http://localhost:8080')")
|
||||||
|
protected String server;
|
||||||
|
|
||||||
|
@Option(names = "--realm", description = "Realm name to authenticate against")
|
||||||
|
protected String realm;
|
||||||
|
|
||||||
|
@Option(names = "--client", description = "Realm name to authenticate against")
|
||||||
|
protected String clientId;
|
||||||
|
|
||||||
|
@Option(names = "--user", description = "Username to login with")
|
||||||
|
protected String user;
|
||||||
|
|
||||||
|
@Option(names = "--password", description = "Password to login with (prompted for if not specified and --user is used)")
|
||||||
|
protected String password;
|
||||||
|
|
||||||
|
@Option(names = "--secret", description = "Secret to authenticate the client (prompted for if no --user or --keystore is specified)")
|
||||||
|
protected String secret;
|
||||||
|
|
||||||
|
@Option(names = "--keystore", description = "Path to a keystore containing private key")
|
||||||
|
protected String keystore;
|
||||||
|
|
||||||
|
@Option(names = "--storepass", description = "Keystore password (prompted for if not specified and --keystore is used)")
|
||||||
|
protected String storePass;
|
||||||
|
|
||||||
|
@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(names = "--alias", description = "Alias of the key inside a keystore (defaults to the value of ClientId)")
|
||||||
|
protected String alias;
|
||||||
|
|
||||||
|
@Option(names = "--truststore", description = "Path to a truststore")
|
||||||
|
protected String trustStore;
|
||||||
|
|
||||||
|
@Option(names = "--trustpass", description = "Truststore password (prompted for if not specified and --truststore is used)")
|
||||||
|
protected String trustPass;
|
||||||
|
|
||||||
|
@Option(names = "--insecure", description = "Turns off TLS validation")
|
||||||
|
protected boolean insecure;
|
||||||
|
|
||||||
|
// subclasses unfortunately use different options for this, so they must be declared elsewhere
|
||||||
|
protected String externalToken;
|
||||||
|
|
||||||
|
protected CommandState commandState;
|
||||||
|
|
||||||
|
public BaseAuthOptionsCmd(CommandState state) {
|
||||||
|
this.commandState = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getCommand() {
|
||||||
|
return commandState.getCommand();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getDefaultConfigFilePath() {
|
||||||
|
return commandState.getDefaultConfigFilePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void initFromParent(BaseAuthOptionsCmd parent) {
|
||||||
|
noconfig = parent.noconfig;
|
||||||
|
config = parent.config;
|
||||||
|
server = parent.server;
|
||||||
|
realm = parent.realm;
|
||||||
|
clientId = parent.clientId;
|
||||||
|
user = parent.user;
|
||||||
|
password = parent.password;
|
||||||
|
secret = parent.secret;
|
||||||
|
keystore = parent.keystore;
|
||||||
|
storePass = parent.storePass;
|
||||||
|
keyPass = parent.keyPass;
|
||||||
|
alias = parent.alias;
|
||||||
|
trustStore = parent.trustStore;
|
||||||
|
trustPass = parent.trustPass;
|
||||||
|
externalToken = parent.externalToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void applyDefaultOptionValues() {
|
||||||
|
if (clientId == null) {
|
||||||
|
clientId = DEFAULT_CLIENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean nothingToDo() {
|
||||||
|
return externalToken == null && server == null && realm == null && clientId == null && secret == null &&
|
||||||
|
user == null && password == null &&
|
||||||
|
keystore == null && storePass == null && keyPass == null && alias == null &&
|
||||||
|
trustStore == null && trustPass == null && config == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void processOptions() {
|
||||||
|
if (config != null && noconfig) {
|
||||||
|
throw new IllegalArgumentException("Options --config and --no-config are mutually exclusive");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!noconfig) {
|
||||||
|
setConfigFile(config != null ? config : getDefaultConfigFilePath());
|
||||||
|
ConfigUtil.setHandler(new FileConfigHandler());
|
||||||
|
} else {
|
||||||
|
InMemoryConfigHandler handler = new InMemoryConfigHandler();
|
||||||
|
ConfigData data = new ConfigData();
|
||||||
|
initConfigData(data);
|
||||||
|
handler.setConfigData(data);
|
||||||
|
ConfigUtil.setHandler(handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setupTruststore(ConfigData configData) {
|
||||||
|
|
||||||
|
if (!configData.getServerUrl().startsWith("https:")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String truststore = trustStore;
|
||||||
|
if (truststore == null) {
|
||||||
|
truststore = configData.getTruststore();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (truststore != null) {
|
||||||
|
String pass = trustPass;
|
||||||
|
if (pass == null) {
|
||||||
|
pass = configData.getTrustpass();
|
||||||
|
}
|
||||||
|
if (pass == null) {
|
||||||
|
pass = IoUtil.readSecret("Enter truststore password: ");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
HttpUtil.setTruststore(new File(truststore), pass);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Failed to load truststore: " + truststore, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (insecure) {
|
||||||
|
HttpUtil.setSkipCertificateValidation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ConfigData ensureAuthInfo(ConfigData config) {
|
||||||
|
|
||||||
|
if (requiresLogin()) {
|
||||||
|
// make sure current handler is in-memory handler
|
||||||
|
// restore it at the end
|
||||||
|
ConfigHandler old = ConfigUtil.getHandler();
|
||||||
|
try {
|
||||||
|
// make sure all defaults are initialized after this point
|
||||||
|
applyDefaultOptionValues();
|
||||||
|
|
||||||
|
initConfigData(config);
|
||||||
|
ConfigUtil.setupInMemoryHandler(config);
|
||||||
|
|
||||||
|
BaseConfigCredentialsCmd login = new BaseConfigCredentialsCmd(commandState);
|
||||||
|
login.initFromParent(this);
|
||||||
|
login.init(config);
|
||||||
|
login.process();
|
||||||
|
|
||||||
|
// this must be executed before finally block which restores config handler
|
||||||
|
return loadConfig();
|
||||||
|
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} finally {
|
||||||
|
ConfigUtil.setHandler(old);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
checkServerInfo(config, getCommand());
|
||||||
|
|
||||||
|
// make sure all defaults are initialized after this point
|
||||||
|
applyDefaultOptionValues();
|
||||||
|
return loadConfig();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean requiresLogin() {
|
||||||
|
return externalToken == null && (user != null || password != null || secret != null || keystore != null
|
||||||
|
|| keyPass != null || storePass != null || alias != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ConfigData copyWithServerInfo(ConfigData config) {
|
||||||
|
|
||||||
|
ConfigData result = config.deepcopy();
|
||||||
|
|
||||||
|
if (server != null) {
|
||||||
|
result.setServerUrl(server);
|
||||||
|
}
|
||||||
|
if (realm != null) {
|
||||||
|
result.setRealm(realm);
|
||||||
|
}
|
||||||
|
if (externalToken != null) {
|
||||||
|
result.setExternalToken(externalToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
checkServerInfo(result, getCommand());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initConfigData(ConfigData data) {
|
||||||
|
if (server != null)
|
||||||
|
data.setServerUrl(server);
|
||||||
|
if (realm != null)
|
||||||
|
data.setRealm(realm);
|
||||||
|
if (trustStore != null)
|
||||||
|
data.setTruststore(trustStore);
|
||||||
|
if (externalToken != null) {
|
||||||
|
data.setExternalToken(externalToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
RealmConfigData rdata = data.sessionRealmConfigData();
|
||||||
|
if (clientId != null)
|
||||||
|
rdata.setClientId(clientId);
|
||||||
|
if (secret != null)
|
||||||
|
rdata.setSecret(secret);
|
||||||
|
String grantTypeForAuthentication = user == null ? OAuth2Constants.CLIENT_CREDENTIALS : OAuth2Constants.PASSWORD;
|
||||||
|
rdata.setGrantTypeForAuthentication(grantTypeForAuthentication);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String ensureToken(ConfigData config) {
|
||||||
|
return AuthUtil.ensureToken(config, getCommand());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,248 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.keycloak.client.cli.common;
|
||||||
|
|
||||||
|
import org.keycloak.OAuth2Constants;
|
||||||
|
import org.keycloak.client.cli.config.ConfigData;
|
||||||
|
import org.keycloak.client.cli.config.RealmConfigData;
|
||||||
|
import org.keycloak.client.cli.util.AuthUtil;
|
||||||
|
import org.keycloak.representations.AccessTokenResponse;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
import static org.keycloak.client.cli.util.AuthUtil.getAuthTokens;
|
||||||
|
import static org.keycloak.client.cli.util.AuthUtil.getAuthTokensByJWT;
|
||||||
|
import static org.keycloak.client.cli.util.AuthUtil.getAuthTokensBySecret;
|
||||||
|
import static org.keycloak.client.cli.util.ConfigUtil.getHandler;
|
||||||
|
import static org.keycloak.client.cli.util.ConfigUtil.loadConfig;
|
||||||
|
import static org.keycloak.client.cli.util.ConfigUtil.saveTokens;
|
||||||
|
import static org.keycloak.client.cli.util.IoUtil.printErr;
|
||||||
|
import static org.keycloak.client.cli.util.IoUtil.readSecret;
|
||||||
|
import static org.keycloak.client.cli.util.OsUtil.OS_ARCH;
|
||||||
|
import static org.keycloak.client.cli.util.OsUtil.PROMPT;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
|
*/
|
||||||
|
public class BaseConfigCredentialsCmd extends BaseAuthOptionsCmd {
|
||||||
|
|
||||||
|
private int sigLifetime = 600;
|
||||||
|
|
||||||
|
public BaseConfigCredentialsCmd(CommandState commandState) {
|
||||||
|
super(commandState);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init(ConfigData configData) {
|
||||||
|
if (server == null) {
|
||||||
|
server = configData.getServerUrl();
|
||||||
|
}
|
||||||
|
if (realm == null) {
|
||||||
|
realm = configData.getRealm();
|
||||||
|
}
|
||||||
|
if (trustStore == null) {
|
||||||
|
trustStore = configData.getTruststore();
|
||||||
|
}
|
||||||
|
|
||||||
|
RealmConfigData rdata = configData.getRealmConfigData(server, realm);
|
||||||
|
if (rdata == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clientId == null) {
|
||||||
|
clientId = rdata.getClientId();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String[] getUnsupportedOptions() {
|
||||||
|
return new String[] {"--no-config", booleanOptionForCheck(noconfig)};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void process() {
|
||||||
|
// check server
|
||||||
|
if (server == null) {
|
||||||
|
throw new IllegalArgumentException("Required option not specified: --server");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
new URL(server);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Invalid server endpoint url: " + server, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (realm == null)
|
||||||
|
throw new IllegalArgumentException("Required option not specified: --realm");
|
||||||
|
|
||||||
|
String signedRequestToken = null;
|
||||||
|
boolean clientSet = clientId != null;
|
||||||
|
|
||||||
|
applyDefaultOptionValues();
|
||||||
|
String grantTypeForAuthentication = null;
|
||||||
|
|
||||||
|
if (user != null) {
|
||||||
|
grantTypeForAuthentication = OAuth2Constants.PASSWORD;
|
||||||
|
printErr("Logging into " + server + " as user " + user + " of realm " + realm);
|
||||||
|
|
||||||
|
// if user was set there needs to be a password so we can authenticate
|
||||||
|
if (password == null) {
|
||||||
|
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: ");
|
||||||
|
}
|
||||||
|
} 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: ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keystore != null) {
|
||||||
|
if (secret != null) {
|
||||||
|
throw new IllegalArgumentException("Can't use both --keystore and --secret");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!new File(keystore).isFile()) {
|
||||||
|
throw new RuntimeException("No such keystore file: " + keystore);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (storePass == null) {
|
||||||
|
storePass = readSecret("Enter keystore password: ");
|
||||||
|
keyPass = readSecret("Enter key password: ");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyPass == null) {
|
||||||
|
keyPass = storePass;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (alias == null) {
|
||||||
|
alias = clientId;
|
||||||
|
}
|
||||||
|
|
||||||
|
String realmInfoUrl = server + "/realms/" + realm;
|
||||||
|
|
||||||
|
signedRequestToken = AuthUtil.getSignedRequestToken(keystore, storePass, keyPass,
|
||||||
|
alias, sigLifetime, clientId, realmInfoUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if only server and realm are set, just save config and be done
|
||||||
|
if (user == null && secret == null && keystore == null) {
|
||||||
|
getHandler().saveMergeConfig(config -> {
|
||||||
|
config.setServerUrl(server);
|
||||||
|
config.setRealm(realm);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setupTruststore(copyWithServerInfo(loadConfig()));
|
||||||
|
|
||||||
|
// now use the token endpoint to retrieve access token, and refresh token
|
||||||
|
AccessTokenResponse tokens = signedRequestToken != null ?
|
||||||
|
getAuthTokensByJWT(server, realm, user, password, clientId, signedRequestToken) :
|
||||||
|
secret != null ?
|
||||||
|
getAuthTokensBySecret(server, realm, user, password, clientId, secret) :
|
||||||
|
getAuthTokens(server, realm, user, password, clientId);
|
||||||
|
|
||||||
|
Long sigExpiresAt = signedRequestToken == null ? null : System.currentTimeMillis() + sigLifetime * 1000;
|
||||||
|
|
||||||
|
// save tokens to config file
|
||||||
|
saveTokens(tokens, server, realm, clientId, signedRequestToken, sigExpiresAt, secret, grantTypeForAuthentication);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String help() {
|
||||||
|
StringWriter sb = new StringWriter();
|
||||||
|
PrintWriter out = new PrintWriter(sb);
|
||||||
|
out.println("Usage: " + getCommand() + " config credentials --server SERVER_URL --realm REALM [ARGUMENTS]");
|
||||||
|
out.println(" " + getCommand() + " config credentials --server SERVER_URL --realm REALM --user USER [--password PASSWORD] [ARGUMENTS]");
|
||||||
|
out.println(" " + getCommand() + " config credentials --server SERVER_URL --realm REALM --client CLIENT_ID [--secret SECRET] [ARGUMENTS]");
|
||||||
|
out.println(" " + getCommand() + " config credentials --server SERVER_URL --realm REALM --client CLIENT_ID [--keystore KEYSTORE] [ARGUMENTS]");
|
||||||
|
out.println();
|
||||||
|
out.println("Command to establish an authenticated client session with the server. There are many authentication");
|
||||||
|
out.println("options available, and it depends on server side client authentication configuration how client can or should authenticate.");
|
||||||
|
out.println("The information always required includes --server, and --realm. Then, --user and / or --client need to be used to authenticate.");
|
||||||
|
out.println("If --client is not provided it defaults to 'admin-cli'. The authentication options / requirements depend on how this client is configured.");
|
||||||
|
out.println();
|
||||||
|
out.println("If confidential client authentication is also configured, you may have to specify a client id, and client credentials in addition to");
|
||||||
|
out.println("user credentials. Client credentials are either a client secret, or a keystore information to use Signed JWT mechanism.");
|
||||||
|
out.println("If only client credentials are provided, and no user credentials, then the service account is used for login.");
|
||||||
|
out.println();
|
||||||
|
out.println("Arguments:");
|
||||||
|
out.println();
|
||||||
|
out.println(" Global options:");
|
||||||
|
out.println(" -x Print full stack trace when exiting with error");
|
||||||
|
out.println(" --config Path to a config file (" + getDefaultConfigFilePath() + " by default)");
|
||||||
|
out.println(" --truststore PATH Path to a truststore containing trusted certificates");
|
||||||
|
out.println(" --trustpass PASSWORD Truststore password (prompted for if not specified and --truststore is used)");
|
||||||
|
out.println();
|
||||||
|
out.println(" Command specific options:");
|
||||||
|
out.println(" --server SERVER_URL Server endpoint url (e.g. 'http://localhost:8080')");
|
||||||
|
out.println(" --realm REALM Realm name to use");
|
||||||
|
out.println(" --user USER Username to login with");
|
||||||
|
out.println(" --password PASSWORD Password to login with (prompted for if not specified and --user is used)");
|
||||||
|
out.println(" --client CLIENT_ID ClientId used by this client tool ('admin-cli' by default)");
|
||||||
|
out.println(" --secret SECRET Secret to authenticate the client (prompted for if --client is specified, and no --keystore is specified)");
|
||||||
|
out.println(" --keystore PATH Path to a keystore containing private key");
|
||||||
|
out.println(" --storepass PASSWORD Keystore password (prompted for if not specified and --keystore is used)");
|
||||||
|
out.println(" --keypass PASSWORD Key password (prompted for if not specified and --keystore is used without --storepass,");
|
||||||
|
out.println(" otherwise defaults to keystore password)");
|
||||||
|
out.println(" --alias ALIAS Alias of the key inside a keystore (defaults to the value of ClientId)");
|
||||||
|
out.println();
|
||||||
|
out.println();
|
||||||
|
out.println("Examples:");
|
||||||
|
out.println();
|
||||||
|
out.println("Login as 'admin' user of 'master' realm to a local Keycloak server running on default port.");
|
||||||
|
out.println("You will be prompted for a password:");
|
||||||
|
out.println(" " + PROMPT + " " + getCommand() + " config credentials --server http://localhost:8080 --realm master --user admin");
|
||||||
|
out.println();
|
||||||
|
out.println("Login to Keycloak server at non-default endpoint passing the password via standard input:");
|
||||||
|
if (OS_ARCH.isWindows()) {
|
||||||
|
out.println(" " + PROMPT + " echo mypassword | " + getCommand() + " config credentials --server http://localhost:9080 --realm master --user admin");
|
||||||
|
} else {
|
||||||
|
out.println(" " + PROMPT + " " + getCommand() + " config credentials --server http://localhost:9080 --realm master --user admin << EOF");
|
||||||
|
out.println(" mypassword");
|
||||||
|
out.println(" EOF");
|
||||||
|
}
|
||||||
|
out.println();
|
||||||
|
out.println("Login specifying a password through command line:");
|
||||||
|
out.println(" " + PROMPT + " " + getCommand() + " config credentials --server http://localhost:9080 --realm master --user admin --password " + OS_ARCH.envVar("PASSWORD"));
|
||||||
|
out.println();
|
||||||
|
out.println("Login using a client service account of a custom client. You will be prompted for a client secret:");
|
||||||
|
out.println(" " + PROMPT + " " + getCommand() + " config credentials --server http://localhost:9080 --realm master --client reg-cli");
|
||||||
|
out.println();
|
||||||
|
out.println("Login using a client service account of a custom client, authenticating with signed JWT.");
|
||||||
|
out.println("You will be prompted for a keystore password, and a key password:");
|
||||||
|
out.println(" " + PROMPT + " " + getCommand() + " config credentials --server http://localhost:9080 --realm master --client reg-cli --keystore " + OS_ARCH.path("~/.keycloak/keystore.jks"));
|
||||||
|
out.println();
|
||||||
|
out.println("Login as 'user' while also authenticating a custom client with signed JWT.");
|
||||||
|
out.println("You will be prompted for a user password, a keystore password, and a key password:");
|
||||||
|
out.println(" " + PROMPT + " " + getCommand() + " config credentials --server http://localhost:9080 --realm master --user user --client reg-cli --keystore " + OS_ARCH.path("~/.keycloak/keystore.jks"));
|
||||||
|
out.println();
|
||||||
|
out.println();
|
||||||
|
out.println("Use '" + getCommand() + " help' for general information and a list of commands");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,137 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.keycloak.client.cli.common;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
|
||||||
|
import picocli.CommandLine.Option;
|
||||||
|
import picocli.CommandLine.Parameters;
|
||||||
|
|
||||||
|
import static org.keycloak.client.cli.util.ConfigUtil.saveMergeConfig;
|
||||||
|
import static org.keycloak.client.cli.util.IoUtil.readSecret;
|
||||||
|
import static org.keycloak.client.cli.util.OsUtil.OS_ARCH;
|
||||||
|
import static org.keycloak.client.cli.util.OsUtil.PROMPT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
|
*/
|
||||||
|
public class BaseConfigTruststoreCmd extends BaseAuthOptionsCmd {
|
||||||
|
|
||||||
|
@Parameters(arity = "0..1")
|
||||||
|
private String store;
|
||||||
|
|
||||||
|
@Option(names = {"-d", "--delete"}, description = "Remove truststore configuration")
|
||||||
|
private boolean delete;
|
||||||
|
|
||||||
|
public BaseConfigTruststoreCmd(CommandState commandState) {
|
||||||
|
super(commandState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean nothingToDo() {
|
||||||
|
return super.nothingToDo() && store == null && !delete;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String[] getUnsupportedOptions() {
|
||||||
|
return new String[] {"--server", server,
|
||||||
|
"--realm", realm,
|
||||||
|
"--client", clientId,
|
||||||
|
"--user", user,
|
||||||
|
"--password", password,
|
||||||
|
"--secret", secret,
|
||||||
|
"--truststore", trustStore,
|
||||||
|
"--keystore", keystore,
|
||||||
|
"--keypass", keyPass,
|
||||||
|
"--alias", alias,
|
||||||
|
"--no-config", booleanOptionForCheck(noconfig)};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void process() {
|
||||||
|
String pass;
|
||||||
|
|
||||||
|
if (!delete) {
|
||||||
|
|
||||||
|
if (store == null) {
|
||||||
|
throw new IllegalArgumentException("No truststore specified");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!new File(store).isFile()) {
|
||||||
|
throw new RuntimeException("Truststore file not found: " + store);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("-".equals(trustPass)) {
|
||||||
|
trustPass = readSecret("Enter truststore password: ");
|
||||||
|
}
|
||||||
|
|
||||||
|
pass = trustPass;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (store != null) {
|
||||||
|
throw new IllegalArgumentException("Option --delete is mutually exclusive with specifying a TRUSTSTORE");
|
||||||
|
}
|
||||||
|
if (trustPass != null) {
|
||||||
|
throw new IllegalArgumentException("Options --trustpass and --delete are mutually exclusive");
|
||||||
|
}
|
||||||
|
pass = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
saveMergeConfig(config -> {
|
||||||
|
config.setTruststore(store);
|
||||||
|
config.setTrustpass(pass);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String help() {
|
||||||
|
StringWriter sb = new StringWriter();
|
||||||
|
PrintWriter out = new PrintWriter(sb);
|
||||||
|
out.println("Usage: " + getCommand() + " config truststore [TRUSTSTORE | --delete] [--trustpass PASSWORD] [ARGUMENTS]");
|
||||||
|
out.println();
|
||||||
|
out.println("Command to configure a global truststore to use when using https to connect to Keycloak server.");
|
||||||
|
out.println();
|
||||||
|
out.println("Arguments:");
|
||||||
|
out.println();
|
||||||
|
out.println(" Global options:");
|
||||||
|
out.println(" -x Print full stack trace when exiting with error");
|
||||||
|
out.println(" --config Path to the config file (" + getDefaultConfigFilePath() + " by default)");
|
||||||
|
out.println();
|
||||||
|
out.println(" Command specific options:");
|
||||||
|
out.println(" TRUSTSTORE Path to truststore file");
|
||||||
|
out.println(" --trustpass PASSWORD Truststore password to unlock truststore (prompted for if set to '-')");
|
||||||
|
out.println(" -d, --delete Remove truststore configuration");
|
||||||
|
out.println();
|
||||||
|
out.println();
|
||||||
|
out.println("Examples:");
|
||||||
|
out.println();
|
||||||
|
out.println("Specify a truststore - you will be prompted for truststore password every time it is used:");
|
||||||
|
out.println(" " + PROMPT + " " + getCommand() + " config truststore " + OS_ARCH.path("~/.keycloak/truststore.jks"));
|
||||||
|
out.println();
|
||||||
|
out.println("Specify a truststore, and password - truststore will automatically be used without prompting for password:");
|
||||||
|
out.println(" " + PROMPT + " " + getCommand() + " config truststore --trustpass " + OS_ARCH.envVar("PASSWORD") + " " + OS_ARCH.path("~/.keycloak/truststore.jks"));
|
||||||
|
out.println();
|
||||||
|
out.println("Remove truststore configuration:");
|
||||||
|
out.println(" " + PROMPT + " " + getCommand() + " config truststore --delete");
|
||||||
|
out.println();
|
||||||
|
out.println();
|
||||||
|
out.println("Use '" + getCommand() + " help' for general information and a list of commands");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,16 +1,27 @@
|
||||||
package org.keycloak.client.registration.cli.commands;
|
/*
|
||||||
|
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||||
import org.keycloak.client.registration.cli.Globals;
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.keycloak.client.cli.common;
|
||||||
|
|
||||||
import picocli.CommandLine;
|
import picocli.CommandLine;
|
||||||
import picocli.CommandLine.Option;
|
import picocli.CommandLine.Option;
|
||||||
|
|
||||||
import static org.keycloak.client.registration.cli.util.IoUtil.printOut;
|
import static org.keycloak.client.cli.util.IoUtil.printOut;
|
||||||
|
|
||||||
/**
|
public abstract class BaseGlobalOptionsCmd implements Runnable {
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
|
||||||
*/
|
|
||||||
public abstract class AbstractGlobalOptionsCmd implements Runnable {
|
|
||||||
|
|
||||||
@Option(names = "--help",
|
@Option(names = "--help",
|
||||||
description = "Print command specific help")
|
description = "Print command specific help")
|
||||||
|
@ -38,9 +49,7 @@ public abstract class AbstractGlobalOptionsCmd implements Runnable {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String help() {
|
protected abstract String help();
|
||||||
return KcRegCmd.usage();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
* Copyright 2024 Red Hat, Inc. and/or its affiliates
|
||||||
* and other contributors as indicated by the @author tags.
|
* and other contributors as indicated by the @author tags.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@ -14,15 +14,13 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.keycloak.client.admin.cli;
|
|
||||||
|
|
||||||
/**
|
package org.keycloak.client.cli.common;
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
|
||||||
*/
|
|
||||||
public class Globals {
|
|
||||||
|
|
||||||
public static boolean dumpTrace = false;
|
public interface CommandState {
|
||||||
|
|
||||||
public static boolean help = false;
|
String getCommand();
|
||||||
|
|
||||||
|
String getDefaultConfigFilePath();
|
||||||
|
|
||||||
}
|
}
|
|
@ -15,7 +15,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.keycloak.client.admin.cli;
|
package org.keycloak.client.cli.common;
|
||||||
|
|
||||||
import picocli.CommandLine;
|
import picocli.CommandLine;
|
||||||
import picocli.CommandLine.ParseResult;
|
import picocli.CommandLine.ParseResult;
|
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.keycloak.client.cli.common;
|
||||||
|
|
||||||
|
import org.keycloak.client.cli.util.ClassLoaderUtil;
|
||||||
|
import org.keycloak.common.crypto.CryptoIntegration;
|
||||||
|
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
|
||||||
|
import picocli.CommandLine;
|
||||||
|
import picocli.CommandLine.Model.CommandSpec;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
|
*/
|
||||||
|
public class Globals {
|
||||||
|
|
||||||
|
public static boolean dumpTrace = false;
|
||||||
|
|
||||||
|
public static boolean help = false;
|
||||||
|
|
||||||
|
public static void main(String [] args, BaseGlobalOptionsCmd rootCommand, String command, String defaultConfigFile) {
|
||||||
|
String libDir = System.getProperty("kc.lib.dir");
|
||||||
|
if (libDir == null) {
|
||||||
|
throw new RuntimeException("System property kc.lib.dir needs to be set");
|
||||||
|
}
|
||||||
|
ClassLoader cl = ClassLoaderUtil.resolveClassLoader(libDir);
|
||||||
|
Thread.currentThread().setContextClassLoader(cl);
|
||||||
|
|
||||||
|
CryptoIntegration.init(cl);
|
||||||
|
|
||||||
|
System.setProperty(BaseAuthOptionsCmd.DEFAULT_CONFIG_PATH_STRING_KEY, defaultConfigFile);
|
||||||
|
CommandLine cli = createCommandLine(rootCommand, command);
|
||||||
|
int exitCode = cli.execute(args);
|
||||||
|
System.exit(exitCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CommandLine createCommandLine(BaseGlobalOptionsCmd rootCommand, String command) {
|
||||||
|
CommandSpec spec = CommandSpec.forAnnotatedObject(rootCommand).name(command);
|
||||||
|
|
||||||
|
CommandLine cmd = new CommandLine(spec);
|
||||||
|
|
||||||
|
cmd.setExecutionExceptionHandler(new ExecutionExceptionHandler());
|
||||||
|
cmd.setParameterExceptionHandler(new ShortErrorMessageHandler());
|
||||||
|
cmd.setErr(new PrintWriter(System.err, true));
|
||||||
|
|
||||||
|
return cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -15,7 +15,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.keycloak.client.admin.cli;
|
package org.keycloak.client.cli.common;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.keycloak.client.admin.cli.config;
|
package org.keycloak.client.cli.config;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
import org.keycloak.util.JsonSerialization;
|
import org.keycloak.util.JsonSerialization;
|
|
@ -14,7 +14,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.keycloak.client.admin.cli.config;
|
package org.keycloak.client.cli.config;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
|
@ -14,7 +14,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.keycloak.client.admin.cli.config;
|
package org.keycloak.client.cli.config;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
|
@ -14,9 +14,9 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.keycloak.client.admin.cli.config;
|
package org.keycloak.client.cli.config;
|
||||||
|
|
||||||
import org.keycloak.client.admin.cli.util.IoUtil;
|
import org.keycloak.client.cli.util.IoUtil;
|
||||||
import org.keycloak.util.JsonSerialization;
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
|
@ -30,7 +30,7 @@ import java.nio.channels.OverlappingFileLockException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
import static org.keycloak.client.admin.cli.util.IoUtil.printErr;
|
import static org.keycloak.client.cli.util.IoUtil.printErr;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
|
@ -14,7 +14,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.keycloak.client.admin.cli.config;
|
package org.keycloak.client.cli.config;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
|
@ -14,8 +14,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
package org.keycloak.client.cli.config;
|
||||||
package org.keycloak.client.registration.cli.config;
|
|
||||||
|
|
||||||
import org.keycloak.util.JsonSerialization;
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
|
||||||
|
@ -23,6 +22,8 @@ import java.io.IOException;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
*/
|
*/
|
||||||
|
@ -50,11 +51,12 @@ public class RealmConfigData {
|
||||||
|
|
||||||
private Long sigExpiresAt;
|
private Long sigExpiresAt;
|
||||||
|
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
private String initialToken;
|
private String initialToken;
|
||||||
|
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||||
private Map<String, String> clients = new LinkedHashMap<String, String>();
|
private Map<String, String> clients = new LinkedHashMap<String, String>();
|
||||||
|
|
||||||
|
|
||||||
public String serverUrl() {
|
public String serverUrl() {
|
||||||
return serverUrl;
|
return serverUrl;
|
||||||
}
|
}
|
||||||
|
@ -111,6 +113,14 @@ public class RealmConfigData {
|
||||||
this.secret = secret;
|
this.secret = secret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getGrantTypeForAuthentication() {
|
||||||
|
return grantTypeForAuthentication;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGrantTypeForAuthentication(String grantTypeForAuthentication) {
|
||||||
|
this.grantTypeForAuthentication = grantTypeForAuthentication;
|
||||||
|
}
|
||||||
|
|
||||||
public Long getExpiresAt() {
|
public Long getExpiresAt() {
|
||||||
return expiresAt;
|
return expiresAt;
|
||||||
}
|
}
|
||||||
|
@ -127,14 +137,6 @@ public class RealmConfigData {
|
||||||
this.refreshExpiresAt = refreshExpiresAt;
|
this.refreshExpiresAt = refreshExpiresAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getGrantTypeForAuthentication() {
|
|
||||||
return grantTypeForAuthentication;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setGrantTypeForAuthentication(String grantTypeForAuthentication) {
|
|
||||||
this.grantTypeForAuthentication = grantTypeForAuthentication;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Long getSigExpiresAt() {
|
public Long getSigExpiresAt() {
|
||||||
return sigExpiresAt;
|
return sigExpiresAt;
|
||||||
}
|
}
|
||||||
|
@ -194,13 +196,6 @@ public class RealmConfigData {
|
||||||
refreshToken = source.refreshToken;
|
refreshToken = source.refreshToken;
|
||||||
expiresAt = source.expiresAt;
|
expiresAt = source.expiresAt;
|
||||||
refreshExpiresAt = source.refreshExpiresAt;
|
refreshExpiresAt = source.refreshExpiresAt;
|
||||||
|
|
||||||
mergeClients(source);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void mergeRegistrationTokens(RealmConfigData source) {
|
|
||||||
initialToken = source.initialToken;
|
|
||||||
mergeClients(source);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
|
@ -14,7 +14,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.keycloak.client.admin.cli.util;
|
package org.keycloak.client.cli.util;
|
||||||
|
|
||||||
import java.io.FilterOutputStream;
|
import java.io.FilterOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
|
@ -14,7 +14,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.keycloak.client.admin.cli.util;
|
package org.keycloak.client.cli.util;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
|
@ -14,10 +14,10 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.keycloak.client.admin.cli.util;
|
package org.keycloak.client.cli.util;
|
||||||
|
|
||||||
import org.keycloak.client.admin.cli.config.ConfigData;
|
import org.keycloak.client.cli.config.ConfigData;
|
||||||
import org.keycloak.client.admin.cli.config.RealmConfigData;
|
import org.keycloak.client.cli.config.RealmConfigData;
|
||||||
import org.keycloak.common.util.KeystoreUtil;
|
import org.keycloak.common.util.KeystoreUtil;
|
||||||
import org.keycloak.common.util.Time;
|
import org.keycloak.common.util.Time;
|
||||||
import org.keycloak.jose.jws.JWSBuilder;
|
import org.keycloak.jose.jws.JWSBuilder;
|
||||||
|
@ -33,24 +33,24 @@ import java.security.KeyPair;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import static java.lang.System.currentTimeMillis;
|
import static java.lang.System.currentTimeMillis;
|
||||||
import static org.keycloak.client.admin.cli.util.ConfigUtil.checkAuthInfo;
|
import static org.keycloak.client.cli.util.ConfigUtil.checkServerInfo;
|
||||||
import static org.keycloak.client.admin.cli.util.ConfigUtil.saveMergeConfig;
|
import static org.keycloak.client.cli.util.ConfigUtil.saveMergeConfig;
|
||||||
import static org.keycloak.client.admin.cli.util.HttpUtil.APPLICATION_FORM_URL_ENCODED;
|
import static org.keycloak.client.cli.util.HttpUtil.APPLICATION_FORM_URL_ENCODED;
|
||||||
import static org.keycloak.client.admin.cli.util.HttpUtil.APPLICATION_JSON;
|
import static org.keycloak.client.cli.util.HttpUtil.APPLICATION_JSON;
|
||||||
import static org.keycloak.client.admin.cli.util.HttpUtil.doPost;
|
import static org.keycloak.client.cli.util.HttpUtil.doPost;
|
||||||
import static org.keycloak.client.admin.cli.util.HttpUtil.urlencode;
|
import static org.keycloak.client.cli.util.HttpUtil.urlencode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
*/
|
*/
|
||||||
public class AuthUtil {
|
public class AuthUtil {
|
||||||
|
|
||||||
public static String ensureToken(ConfigData config) {
|
public static String ensureToken(ConfigData config, String cmd) {
|
||||||
if (config.getExternalToken() != null) {
|
if (config.getExternalToken() != null) {
|
||||||
return config.getExternalToken();
|
return config.getExternalToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
checkAuthInfo(config);
|
checkServerInfo(config, cmd);
|
||||||
|
|
||||||
RealmConfigData realmConfig = config.sessionRealmConfigData();
|
RealmConfigData realmConfig = config.sessionRealmConfigData();
|
||||||
|
|
||||||
|
@ -63,11 +63,11 @@ public class AuthUtil {
|
||||||
// check refresh_token against expiry time
|
// check refresh_token against expiry time
|
||||||
// if it's less than 5s to expiry, fail with credentials expired
|
// if it's less than 5s to expiry, fail with credentials expired
|
||||||
if (realmConfig.getRefreshExpiresAt() != null && realmConfig.getRefreshExpiresAt() - now < 5000) {
|
if (realmConfig.getRefreshExpiresAt() != null && realmConfig.getRefreshExpiresAt() - now < 5000) {
|
||||||
throw new RuntimeException("Session has expired. Login again with '" + OsUtil.CMD + " config credentials'");
|
throw new RuntimeException("Session has expired. Login again with '" + cmd + " config credentials'");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (realmConfig.getSigExpiresAt() != null && realmConfig.getSigExpiresAt() - now < 5000) {
|
if (realmConfig.getSigExpiresAt() != null && realmConfig.getSigExpiresAt() - now < 5000) {
|
||||||
throw new RuntimeException("Session has expired. Login again with '" + OsUtil.CMD + " config credentials'");
|
throw new RuntimeException("Session has expired. Login again with '" + cmd + " config credentials'");
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
|
@ -17,7 +17,7 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.keycloak.client.admin.cli.util;
|
package org.keycloak.client.cli.util;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
|
@ -14,14 +14,14 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.keycloak.client.admin.cli.util;
|
package org.keycloak.client.cli.util;
|
||||||
|
|
||||||
import org.keycloak.OAuth2Constants;
|
import org.keycloak.OAuth2Constants;
|
||||||
import org.keycloak.client.admin.cli.config.ConfigData;
|
import org.keycloak.client.cli.config.ConfigData;
|
||||||
import org.keycloak.client.admin.cli.config.ConfigHandler;
|
import org.keycloak.client.cli.config.ConfigHandler;
|
||||||
import org.keycloak.client.admin.cli.config.ConfigUpdateOperation;
|
import org.keycloak.client.cli.config.ConfigUpdateOperation;
|
||||||
import org.keycloak.client.admin.cli.config.InMemoryConfigHandler;
|
import org.keycloak.client.cli.config.InMemoryConfigHandler;
|
||||||
import org.keycloak.client.admin.cli.config.RealmConfigData;
|
import org.keycloak.client.cli.config.RealmConfigData;
|
||||||
import org.keycloak.representations.AccessTokenResponse;
|
import org.keycloak.representations.AccessTokenResponse;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,10 +31,6 @@ public class ConfigUtil {
|
||||||
|
|
||||||
public static final String DEFAULT_CLIENT = "admin-cli";
|
public static final String DEFAULT_CLIENT = "admin-cli";
|
||||||
|
|
||||||
public static final String DEFAULT_CONFIG_FILE_STRING = OsUtil.OS_ARCH.isWindows() ? "%HOMEDRIVE%%HOMEPATH%\\.keycloak\\kcadm.config" : "~/.keycloak/kcadm.config";
|
|
||||||
|
|
||||||
public static final String DEFAULT_CONFIG_FILE_PATH = System.getProperty("user.home") + "/.keycloak/kcadm.config";
|
|
||||||
|
|
||||||
private static ConfigHandler handler;
|
private static ConfigHandler handler;
|
||||||
|
|
||||||
public static ConfigHandler getHandler() {
|
public static ConfigHandler getHandler() {
|
||||||
|
@ -45,6 +41,15 @@ public class ConfigUtil {
|
||||||
ConfigUtil.handler = handler;
|
ConfigUtil.handler = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getRegistrationToken(RealmConfigData data, String clientId) {
|
||||||
|
String token = data.getClients().get(clientId);
|
||||||
|
return token == null || token.length() == 0 ? null : token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setRegistrationToken(RealmConfigData data, String clientId, String token) {
|
||||||
|
data.getClients().put(clientId, token == null ? "" : token);
|
||||||
|
}
|
||||||
|
|
||||||
public static void saveTokens(AccessTokenResponse tokens, String endpoint, String realm, String clientId, String signKey, Long sigExpiresAt, String secret,
|
public static void saveTokens(AccessTokenResponse tokens, String endpoint, String realm, String clientId, String signKey, Long sigExpiresAt, String secret,
|
||||||
String grantTypeForAuthentication) {
|
String grantTypeForAuthentication) {
|
||||||
handler.saveMergeConfig(config -> {
|
handler.saveMergeConfig(config -> {
|
||||||
|
@ -67,19 +72,15 @@ public class ConfigUtil {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void checkServerInfo(ConfigData config) {
|
public static void checkServerInfo(ConfigData config, String cmd) {
|
||||||
if (config.getServerUrl() == null) {
|
if (config.getServerUrl() == null) {
|
||||||
throw new RuntimeException("No server specified. Use --server, or '" + OsUtil.CMD + " config credentials'.");
|
throw new RuntimeException("No server specified. Use --server, or '" + cmd + " config credentials'.");
|
||||||
}
|
}
|
||||||
if (config.getRealm() == null && config.getExternalToken() == null) {
|
if (config.getRealm() == null && config.getExternalToken() == null) {
|
||||||
throw new RuntimeException("No realm or token specified. Use --realm, --token, or '" + OsUtil.CMD + " config credentials'.");
|
throw new RuntimeException("No realm or token specified. Use --realm, --token, or '" + cmd + " config credentials'.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void checkAuthInfo(ConfigData config) {
|
|
||||||
checkServerInfo(config);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean credentialsAvailable(ConfigData config) {
|
public static boolean credentialsAvailable(ConfigData config) {
|
||||||
// Just supporting "client_credentials" grant type for the case when refresh token is missing
|
// Just supporting "client_credentials" grant type for the case when refresh token is missing
|
||||||
boolean credsAvailable = config.getServerUrl() != null && (config.getExternalToken() != null || (config.getRealm() != null
|
boolean credsAvailable = config.getServerUrl() != null && (config.getExternalToken() != null || (config.getRealm() != null
|
|
@ -14,18 +14,18 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.keycloak.client.admin.cli.util;
|
package org.keycloak.client.cli.util;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
|
||||||
|
import static org.keycloak.client.cli.util.OutputUtil.MAPPER;
|
||||||
|
import static org.keycloak.client.cli.util.OutputUtil.convertToJsonNode;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
|
||||||
import static org.keycloak.client.admin.cli.util.OutputUtil.MAPPER;
|
|
||||||
import static org.keycloak.client.admin.cli.util.OutputUtil.convertToJsonNode;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
*/
|
*/
|
|
@ -14,7 +14,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.keycloak.client.admin.cli.util;
|
package org.keycloak.client.cli.util;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
|
@ -14,7 +14,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.keycloak.client.admin.cli.util;
|
package org.keycloak.client.cli.util;
|
||||||
|
|
||||||
import org.apache.http.entity.ContentType;
|
import org.apache.http.entity.ContentType;
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.keycloak.client.admin.cli.util;
|
package org.keycloak.client.cli.util;
|
||||||
|
|
||||||
import org.apache.http.entity.ContentType;
|
import org.apache.http.entity.ContentType;
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ import java.io.ByteArrayOutputStream;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
import static org.keycloak.client.admin.cli.util.IoUtil.copyStream;
|
import static org.keycloak.client.cli.util.IoUtil.copyStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
|
@ -14,7 +14,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.keycloak.client.admin.cli.util;
|
package org.keycloak.client.cli.util;
|
||||||
|
|
||||||
import org.keycloak.util.JsonSerialization;
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.keycloak.client.admin.cli.httpcomponents;
|
package org.keycloak.client.cli.util;
|
||||||
|
|
||||||
import org.apache.http.annotation.Contract;
|
import org.apache.http.annotation.Contract;
|
||||||
import org.apache.http.annotation.ThreadingBehavior;
|
import org.apache.http.annotation.ThreadingBehavior;
|
|
@ -14,7 +14,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.keycloak.client.admin.cli.util;
|
package org.keycloak.client.cli.util;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
|
@ -14,10 +14,8 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.keycloak.client.admin.cli.util;
|
package org.keycloak.client.cli.util;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
|
||||||
import org.apache.http.HeaderIterator;
|
import org.apache.http.HeaderIterator;
|
||||||
import org.apache.http.HttpHeaders;
|
import org.apache.http.HttpHeaders;
|
||||||
import org.apache.http.HttpResponse;
|
import org.apache.http.HttpResponse;
|
||||||
|
@ -36,9 +34,6 @@ import org.apache.http.entity.StringEntity;
|
||||||
import org.apache.http.impl.client.HttpClientBuilder;
|
import org.apache.http.impl.client.HttpClientBuilder;
|
||||||
import org.apache.http.ssl.SSLContextBuilder;
|
import org.apache.http.ssl.SSLContextBuilder;
|
||||||
import org.apache.http.ssl.SSLContexts;
|
import org.apache.http.ssl.SSLContexts;
|
||||||
import org.keycloak.client.admin.cli.httpcomponents.HttpDelete;
|
|
||||||
import org.keycloak.client.admin.cli.operations.LocalSearch;
|
|
||||||
import org.keycloak.client.admin.cli.operations.RoleOperations;
|
|
||||||
import org.keycloak.util.JsonSerialization;
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
|
||||||
import javax.net.ssl.SSLContext;
|
import javax.net.ssl.SSLContext;
|
||||||
|
@ -53,12 +48,8 @@ import java.security.KeyStoreException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.cert.CertificateException;
|
import java.security.cert.CertificateException;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
import static org.keycloak.common.util.ObjectUtil.capitalize;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
|
@ -69,7 +60,6 @@ public class HttpUtil {
|
||||||
public static final String APPLICATION_JSON = "application/json";
|
public static final String APPLICATION_JSON = "application/json";
|
||||||
public static final String APPLICATION_FORM_URL_ENCODED = "application/x-www-form-urlencoded";
|
public static final String APPLICATION_FORM_URL_ENCODED = "application/x-www-form-urlencoded";
|
||||||
public static final String UTF_8 = "utf-8";
|
public static final String UTF_8 = "utf-8";
|
||||||
private static final String[] DEFAULT_QUERY_PARAMS = { "first", "0", "max", "2" };
|
|
||||||
|
|
||||||
private static HttpClient httpClient;
|
private static HttpClient httpClient;
|
||||||
private static SSLConnectionSocketFactory sslsf;
|
private static SSLConnectionSocketFactory sslsf;
|
||||||
|
@ -436,57 +426,6 @@ public class HttpUtil {
|
||||||
checkSuccess(resourceUrl, response);
|
checkSuccess(resourceUrl, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getIdForType(String rootUrl, String realm, String auth, String resourceEndpoint, String attrName, String attrValue, String inputAttrName) {
|
|
||||||
|
|
||||||
return getAttrForType(rootUrl, realm, auth, resourceEndpoint, attrName, attrValue, inputAttrName, "id", null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getIdForType(String rootUrl, String realm, String auth, String resourceEndpoint, String attrName, String attrValue, String inputAttrName, Supplier<String[]> endpointParams) {
|
|
||||||
return getAttrForType(rootUrl, realm, auth, resourceEndpoint, attrName, attrValue, inputAttrName, "id", endpointParams);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getAttrForType(String rootUrl, String realm, String auth, String resourceEndpoint, String attrName, String attrValue, String inputAttrName, String returnAttrName) {
|
|
||||||
return getAttrForType(rootUrl, realm, auth, resourceEndpoint, attrName, attrValue, inputAttrName, returnAttrName, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getAttrForType(String rootUrl, String realm, String auth, String resourceEndpoint, String attrName, String attrValue, String inputAttrName, String returnAttrName, Supplier<String[]> endpointParams) {
|
|
||||||
String resourceUrl = composeResourceUrl(rootUrl, realm, resourceEndpoint);
|
|
||||||
String[] defaultParams;
|
|
||||||
|
|
||||||
if (endpointParams == null) {
|
|
||||||
defaultParams = DEFAULT_QUERY_PARAMS;
|
|
||||||
} else {
|
|
||||||
defaultParams = endpointParams.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
resourceUrl = HttpUtil.addQueryParamsToUri(resourceUrl, attrName, attrValue);
|
|
||||||
resourceUrl = HttpUtil.addQueryParamsToUri(resourceUrl, defaultParams);
|
|
||||||
|
|
||||||
List<ObjectNode> results = doGetJSON(RoleOperations.LIST_OF_NODES.class, resourceUrl, auth);
|
|
||||||
|
|
||||||
ObjectNode match;
|
|
||||||
try {
|
|
||||||
match = new LocalSearch(results).exactMatchOne(attrValue, inputAttrName);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException("Multiple " + resourceEndpoint + " found for " + inputAttrName + ": " + attrValue, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
String typeName = singularize(resourceEndpoint);
|
|
||||||
if (match == null) {
|
|
||||||
if (results.size() > 1) {
|
|
||||||
throw new RuntimeException("Some matches, but not an exact match, found for " + capitalize(typeName) + " with " + inputAttrName + ": " + attrValue + ". Try using a more unique search, such as an id.");
|
|
||||||
}
|
|
||||||
throw new RuntimeException(capitalize(typeName) + " not found for " + inputAttrName + ": " + attrValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonNode attr = match.get(returnAttrName);
|
|
||||||
if (attr == null) {
|
|
||||||
throw new RuntimeException("Returned " + typeName + " info has no '" + returnAttrName + "' attribute");
|
|
||||||
}
|
|
||||||
return attr.asText();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static String singularize(String value) {
|
public static String singularize(String value) {
|
||||||
return value.substring(0, value.length()-1);
|
return value.substring(0, value.length()-1);
|
||||||
}
|
}
|
|
@ -14,7 +14,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.keycloak.client.admin.cli.util;
|
package org.keycloak.client.cli.util;
|
||||||
|
|
||||||
import java.io.Console;
|
import java.io.Console;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
|
@ -14,7 +14,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.keycloak.client.admin.cli.util;
|
package org.keycloak.client.cli.util;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:marko.strukelj@gmail.com">Marko Strukelj</a>
|
* @author <a href="mailto:marko.strukelj@gmail.com">Marko Strukelj</a>
|
|
@ -14,7 +14,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.keycloak.client.admin.cli.util;
|
package org.keycloak.client.cli.util;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
|
@ -22,8 +22,6 @@ package org.keycloak.client.admin.cli.util;
|
||||||
public class OsUtil {
|
public class OsUtil {
|
||||||
|
|
||||||
public static final OsArch OS_ARCH = determineOSAndArch();
|
public static final OsArch OS_ARCH = determineOSAndArch();
|
||||||
// TODO: move CMD out of this class
|
|
||||||
public static final String CMD = OS_ARCH.isWindows() ? "kcadm.bat" : "kcadm.sh";
|
|
||||||
|
|
||||||
public static final String PROMPT = OS_ARCH.isWindows() ? "c:\\>" : "$";
|
public static final String PROMPT = OS_ARCH.isWindows() ? "c:\\>" : "$";
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.keycloak.client.admin.cli.util;
|
package org.keycloak.client.cli.util;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
|
@ -14,7 +14,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.keycloak.client.admin.cli.util;
|
package org.keycloak.client.cli.util;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
import com.fasterxml.jackson.databind.DeserializationFeature;
|
|
@ -14,31 +14,24 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.keycloak.client.admin.cli.common;
|
package org.keycloak.client.cli.util;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
*/
|
*/
|
||||||
public class CmdStdinContext<T> {
|
public class ParseUtil {
|
||||||
|
|
||||||
private T result;
|
public static String[] parseKeyVal(String keyval) {
|
||||||
private String content;
|
// we expect = as a separator
|
||||||
|
int pos = keyval.indexOf("=");
|
||||||
public CmdStdinContext() {}
|
if (pos <= 0) {
|
||||||
|
throw new IllegalArgumentException("Invalid key=value parameter: [" + keyval + "]");
|
||||||
public T getResult() {
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setResult(T result) {
|
String [] parsed = new String[2];
|
||||||
this.result = result;
|
parsed[0] = keyval.substring(0, pos);
|
||||||
}
|
parsed[1] = keyval.substring(pos+1);
|
||||||
|
|
||||||
public String getContent() {
|
return parsed;
|
||||||
return content;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setContent(String content) {
|
|
||||||
this.content = content;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -14,7 +14,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.keycloak.client.admin.cli.util;
|
package org.keycloak.client.cli.util;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
|
@ -15,7 +15,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.keycloak.client.admin.cli.util;
|
package org.keycloak.client.admin.cli;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
|
@ -14,22 +14,22 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.keycloak.client.admin.cli.util;
|
package org.keycloak.client.cli.util;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.client.admin.cli.common.AttributeOperation;
|
import org.keycloak.client.admin.cli.CmdStdinContext;
|
||||||
import org.keycloak.client.admin.cli.common.CmdStdinContext;
|
import org.keycloak.client.admin.cli.ReflectionUtil;
|
||||||
|
import org.keycloak.client.cli.common.AttributeOperation;
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.keycloak.client.admin.cli.common.AttributeOperation.Type.DELETE;
|
import static org.keycloak.client.cli.common.AttributeOperation.Type.DELETE;
|
||||||
import static org.keycloak.client.admin.cli.common.AttributeOperation.Type.SET;
|
import static org.keycloak.client.cli.common.AttributeOperation.Type.SET;
|
||||||
import static org.keycloak.client.admin.cli.util.OutputUtil.MAPPER;
|
import static org.keycloak.client.cli.util.OutputUtil.MAPPER;
|
||||||
import static org.keycloak.client.admin.cli.util.ParseUtil.mergeAttributes;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
|
@ -71,7 +71,7 @@ public class MergeAttributesTest {
|
||||||
CmdStdinContext<JsonNode> ctx = new CmdStdinContext<>();
|
CmdStdinContext<JsonNode> ctx = new CmdStdinContext<>();
|
||||||
ctx.setResult(localNode);
|
ctx.setResult(localNode);
|
||||||
|
|
||||||
ctx = mergeAttributes(ctx, MAPPER.createObjectNode(), attrs);
|
ctx = CmdStdinContext.mergeAttributes(ctx, MAPPER.createObjectNode(), attrs);
|
||||||
System.out.println(ctx);
|
System.out.println(ctx);
|
||||||
|
|
||||||
String remoteJSON = "{\n" +
|
String remoteJSON = "{\n" +
|
|
@ -15,9 +15,11 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.keycloak.client.admin.cli.util;
|
package org.keycloak.client.cli.util;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.keycloak.client.cli.util.OutputUtil;
|
||||||
|
import org.keycloak.client.cli.util.ReturnFields;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
|
@ -14,10 +14,11 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.keycloak.client.admin.cli.util;
|
package org.keycloak.client.cli.util;
|
||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.keycloak.client.cli.util.ReturnFields;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:marko.strukelj@gmail.com">Marko Strukelj</a>
|
* @author <a href="mailto:marko.strukelj@gmail.com">Marko Strukelj</a>
|
|
@ -15,13 +15,10 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.keycloak.client.registration.cli.util;
|
package org.keycloak.client.registration.cli;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonParseException;
|
import org.keycloak.client.cli.common.AttributeOperation;
|
||||||
import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException;
|
import org.keycloak.client.cli.util.AttributeException;
|
||||||
import org.keycloak.client.registration.cli.common.AttributeOperation;
|
|
||||||
import org.keycloak.client.registration.cli.common.CmdStdinContext;
|
|
||||||
import org.keycloak.client.registration.cli.common.EndpointType;
|
|
||||||
import org.keycloak.representations.idm.ClientRepresentation;
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
import org.keycloak.representations.oidc.OIDCClientRepresentation;
|
import org.keycloak.representations.oidc.OIDCClientRepresentation;
|
||||||
import org.keycloak.util.JsonSerialization;
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
@ -29,38 +26,65 @@ import org.keycloak.util.JsonSerialization;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static java.lang.System.arraycopy;
|
import com.fasterxml.jackson.core.JsonParseException;
|
||||||
import static org.keycloak.client.registration.cli.util.IoUtil.readFileOrStdin;
|
import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException;
|
||||||
import static org.keycloak.client.registration.cli.util.ReflectionUtil.setAttributes;
|
|
||||||
|
import static org.keycloak.client.cli.util.IoUtil.readFileOrStdin;
|
||||||
|
import static org.keycloak.client.registration.cli.ReflectionUtil.setAttributes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
*/
|
*/
|
||||||
public class ParseUtil {
|
public class CmdStdinContext {
|
||||||
|
|
||||||
|
private EndpointType regType;
|
||||||
|
private ClientRepresentation client;
|
||||||
|
private OIDCClientRepresentation oidcClient;
|
||||||
|
private String content;
|
||||||
public static final String CLIENT_OPTION_WARN = "You're using what looks like an OPTION as CLIENT: %s";
|
public static final String CLIENT_OPTION_WARN = "You're using what looks like an OPTION as CLIENT: %s";
|
||||||
public static final String TOKEN_OPTION_WARN = "You're using what looks like an OPTION as TOKEN: %s";
|
public static final String TOKEN_OPTION_WARN = "You're using what looks like an OPTION as TOKEN: %s";
|
||||||
|
|
||||||
public static String[] shift(String[] args) {
|
public CmdStdinContext() {}
|
||||||
if (args.length == 1)
|
|
||||||
return new String[0];
|
public EndpointType getEndpointType() {
|
||||||
String [] nu = new String [args.length-1];
|
return regType;
|
||||||
arraycopy(args, 1, nu, 0, args.length-1);
|
|
||||||
return nu;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String[] parseKeyVal(String keyval) {
|
public void setEndpointType(EndpointType regType) {
|
||||||
// we expect = as a separator
|
this.regType = regType;
|
||||||
int pos = keyval.indexOf("=");
|
|
||||||
if (pos <= 0) {
|
|
||||||
throw new RuntimeException("Invalid key=value parameter: [" + keyval + "]");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String [] parsed = new String[2];
|
public ClientRepresentation getClient() {
|
||||||
parsed[0] = keyval.substring(0, pos);
|
return client;
|
||||||
parsed[1] = keyval.substring(pos+1);
|
}
|
||||||
|
|
||||||
return parsed;
|
public void setClient(ClientRepresentation client) {
|
||||||
|
this.client = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OIDCClientRepresentation getOidcClient() {
|
||||||
|
return oidcClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOidcClient(OIDCClientRepresentation oidcClient) {
|
||||||
|
this.oidcClient = oidcClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getContent() {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContent(String content) {
|
||||||
|
this.content = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRegistrationAccessToken() {
|
||||||
|
if (client != null) {
|
||||||
|
return client.getRegistrationAccessToken();
|
||||||
|
} else if (oidcClient != null) {
|
||||||
|
return oidcClient.getRegistrationAccessToken();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CmdStdinContext parseFileOrStdin(String file, EndpointType type) {
|
public static CmdStdinContext parseFileOrStdin(String file, EndpointType type) {
|
|
@ -15,7 +15,9 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.keycloak.client.registration.cli.common;
|
package org.keycloak.client.registration.cli;
|
||||||
|
|
||||||
|
import org.keycloak.client.cli.util.HttpUtil;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
@ -60,4 +62,16 @@ public enum EndpointType {
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return preferredName;
|
return preferredName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getExpectedContentType(EndpointType type) {
|
||||||
|
switch (type) {
|
||||||
|
case DEFAULT:
|
||||||
|
case OIDC:
|
||||||
|
return HttpUtil.APPLICATION_JSON;
|
||||||
|
case SAML2:
|
||||||
|
return HttpUtil.APPLICATION_XML;
|
||||||
|
default:
|
||||||
|
throw new RuntimeException("Unsupported endpoint type: " + type);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,7 +1,5 @@
|
||||||
package org.keycloak.client.registration.cli;
|
package org.keycloak.client.registration.cli;
|
||||||
|
|
||||||
import org.keycloak.client.registration.cli.common.EndpointType;
|
|
||||||
|
|
||||||
import picocli.CommandLine.ITypeConverter;
|
import picocli.CommandLine.ITypeConverter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
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,46 +1,36 @@
|
||||||
package org.keycloak.client.registration.cli;
|
package org.keycloak.client.registration.cli;
|
||||||
|
|
||||||
import org.keycloak.client.admin.cli.ExecutionExceptionHandler;
|
import org.keycloak.client.cli.common.CommandState;
|
||||||
import org.keycloak.client.admin.cli.ShortErrorMessageHandler;
|
import org.keycloak.client.cli.common.Globals;
|
||||||
|
import org.keycloak.client.cli.util.OsUtil;
|
||||||
import org.keycloak.client.registration.cli.commands.KcRegCmd;
|
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.io.PrintWriter;
|
|
||||||
|
|
||||||
import picocli.CommandLine;
|
|
||||||
import picocli.CommandLine.Model.CommandSpec;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
*/
|
*/
|
||||||
public class KcRegMain {
|
public class KcRegMain {
|
||||||
|
|
||||||
|
public static final String DEFAULT_CONFIG_FILE_PATH = System.getProperty("user.home") + "/.keycloak/kcreg.config";
|
||||||
|
|
||||||
|
public static final String DEFAULT_CONFIG_FILE_STRING = OsUtil.OS_ARCH.isWindows() ? "%HOMEDRIVE%%HOMEPATH%\\.keycloak\\kcreg.config" : "~/.keycloak/kcreg.config";
|
||||||
|
|
||||||
|
public static final String CMD = OsUtil.OS_ARCH.isWindows() ? "kcreg.bat" : "kcreg.sh";
|
||||||
|
|
||||||
|
public static final CommandState COMMAND_STATE = new CommandState() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCommand() {
|
||||||
|
return CMD;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDefaultConfigFilePath() {
|
||||||
|
return DEFAULT_CONFIG_FILE_PATH;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
public static void main(String [] args) {
|
public static void main(String [] args) {
|
||||||
String libDir = System.getProperty("kc.lib.dir");
|
Globals.main(args, new KcRegCmd(), CMD, DEFAULT_CONFIG_FILE_STRING);
|
||||||
if (libDir == null) {
|
|
||||||
throw new RuntimeException("System property kc.lib.dir needs to be set");
|
|
||||||
}
|
|
||||||
ClassLoader cl = ClassLoaderUtil.resolveClassLoader(libDir);
|
|
||||||
Thread.currentThread().setContextClassLoader(cl);
|
|
||||||
|
|
||||||
CryptoIntegration.init(cl);
|
|
||||||
|
|
||||||
CommandLine cli = createCommandLine();
|
|
||||||
int exitCode = cli.execute(args);
|
|
||||||
System.exit(exitCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CommandLine createCommandLine() {
|
|
||||||
CommandSpec spec = CommandSpec.forAnnotatedObject(new KcRegCmd()).name(OsUtil.CMD);
|
|
||||||
|
|
||||||
CommandLine cmd = new CommandLine(spec);
|
|
||||||
|
|
||||||
cmd.setExecutionExceptionHandler(new ExecutionExceptionHandler());
|
|
||||||
cmd.setParameterExceptionHandler(new ShortErrorMessageHandler());
|
|
||||||
cmd.setErr(new PrintWriter(System.err, true));
|
|
||||||
|
|
||||||
return cmd;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,11 +15,13 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.keycloak.client.registration.cli.util;
|
package org.keycloak.client.registration.cli;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonParseException;
|
import com.fasterxml.jackson.core.JsonParseException;
|
||||||
import org.keycloak.client.registration.cli.common.AttributeKey;
|
|
||||||
import org.keycloak.client.registration.cli.common.AttributeOperation;
|
import org.keycloak.client.cli.common.AttributeKey;
|
||||||
|
import org.keycloak.client.cli.common.AttributeOperation;
|
||||||
|
import org.keycloak.client.cli.util.AttributeException;
|
||||||
import org.keycloak.util.JsonSerialization;
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
|
@ -1,255 +1,22 @@
|
||||||
package org.keycloak.client.registration.cli.commands;
|
package org.keycloak.client.registration.cli.commands;
|
||||||
|
|
||||||
import org.keycloak.OAuth2Constants;
|
import org.keycloak.client.cli.common.BaseAuthOptionsCmd;
|
||||||
import org.keycloak.client.registration.cli.config.ConfigData;
|
import org.keycloak.client.registration.cli.KcRegMain;
|
||||||
import org.keycloak.client.registration.cli.config.ConfigHandler;
|
|
||||||
import org.keycloak.client.registration.cli.config.FileConfigHandler;
|
|
||||||
import org.keycloak.client.registration.cli.config.InMemoryConfigHandler;
|
|
||||||
import org.keycloak.client.registration.cli.config.RealmConfigData;
|
|
||||||
import org.keycloak.client.registration.cli.util.ConfigUtil;
|
|
||||||
import org.keycloak.client.registration.cli.util.HttpUtil;
|
|
||||||
import org.keycloak.client.registration.cli.util.IoUtil;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
import picocli.CommandLine.Option;
|
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;
|
|
||||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.loadConfig;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractAuthOptionsCmd extends AbstractGlobalOptionsCmd {
|
public abstract class AbstractAuthOptionsCmd extends BaseAuthOptionsCmd {
|
||||||
|
|
||||||
static final String DEFAULT_CLIENT = "admin-cli";
|
|
||||||
|
|
||||||
@Option(names = "--config", description = "Path to the config file (~/.keycloak/kcreg.config by default)")
|
|
||||||
protected String config;
|
|
||||||
|
|
||||||
@Option(names = "--no-config", description = "No configuration file should be used, no authentication info is loaded or saved")
|
|
||||||
protected boolean noconfig;
|
|
||||||
|
|
||||||
@Option(names = "--server", description = "Server endpoint url (e.g. 'http://localhost:8080')")
|
|
||||||
protected String server;
|
|
||||||
|
|
||||||
@Option(names = "--realm", description = "Realm name to authenticate against")
|
|
||||||
protected String realm;
|
|
||||||
|
|
||||||
@Option(names = "--client", description = "Realm name to authenticate against")
|
|
||||||
protected String clientId;
|
|
||||||
|
|
||||||
@Option(names = "--user", description = "Username to login with")
|
|
||||||
protected String user;
|
|
||||||
|
|
||||||
@Option(names = "--password", description = "Password to login with (prompted for if not specified and --user is used)")
|
|
||||||
protected String password;
|
|
||||||
|
|
||||||
@Option(names = "--secret", description = "Secret to authenticate the client (prompted for if no --user or --keystore is specified)")
|
|
||||||
protected String secret;
|
|
||||||
|
|
||||||
@Option(names = "--keystore", description = "Path to a keystore containing private key")
|
|
||||||
protected String keystore;
|
|
||||||
|
|
||||||
@Option(names = "--storepass", description = "Keystore password (prompted for if not specified and --keystore is used)")
|
|
||||||
protected String storePass;
|
|
||||||
|
|
||||||
@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(names = "--alias", description = "Alias of the key inside a keystore (defaults to the value of ClientId)")
|
|
||||||
protected String alias;
|
|
||||||
|
|
||||||
@Option(names = "--truststore", description = "Path to a truststore")
|
|
||||||
protected String trustStore;
|
|
||||||
|
|
||||||
@Option(names = "--trustpass", description = "Truststore password (prompted for if not specified and --truststore is used)")
|
|
||||||
protected String trustPass;
|
|
||||||
|
|
||||||
@Option(names = "--insecure", description = "Turns off TLS validation")
|
|
||||||
protected boolean insecure;
|
|
||||||
|
|
||||||
@Option(names = {"-t", "--token"}, description = "Initial / Registration access token to use)")
|
@Option(names = {"-t", "--token"}, description = "Initial / Registration access token to use)")
|
||||||
protected String token;
|
public void setToken(String token) {
|
||||||
|
this.externalToken = token;
|
||||||
protected void initFromParent(AbstractAuthOptionsCmd parent) {
|
|
||||||
noconfig = parent.noconfig;
|
|
||||||
config = parent.config;
|
|
||||||
server = parent.server;
|
|
||||||
realm = parent.realm;
|
|
||||||
clientId = parent.clientId;
|
|
||||||
user = parent.user;
|
|
||||||
password = parent.password;
|
|
||||||
secret = parent.secret;
|
|
||||||
keystore = parent.keystore;
|
|
||||||
storePass = parent.storePass;
|
|
||||||
keyPass = parent.keyPass;
|
|
||||||
alias = parent.alias;
|
|
||||||
trustStore = parent.trustStore;
|
|
||||||
trustPass = parent.trustPass;
|
|
||||||
token = parent.token;
|
|
||||||
insecure = parent.insecure;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void applyDefaultOptionValues() {
|
public AbstractAuthOptionsCmd() {
|
||||||
if (clientId == null) {
|
super(KcRegMain.COMMAND_STATE);
|
||||||
clientId = DEFAULT_CLIENT;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean noOptions() {
|
|
||||||
return server == null && realm == null && clientId == null && secret == null &&
|
|
||||||
user == null && password == null &&
|
|
||||||
keystore == null && storePass == null && keyPass == null && alias == null &&
|
|
||||||
trustStore == null && trustPass == null &&
|
|
||||||
token == null && config == null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void processOptions() {
|
|
||||||
|
|
||||||
if (config != null && noconfig) {
|
|
||||||
throw new IllegalArgumentException("Options --config and --no-config are mutually exclusive");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!noconfig) {
|
|
||||||
setConfigFile(config != null ? config : ConfigUtil.DEFAULT_CONFIG_FILE_PATH);
|
|
||||||
ConfigUtil.setHandler(new FileConfigHandler());
|
|
||||||
} else {
|
|
||||||
InMemoryConfigHandler handler = new InMemoryConfigHandler();
|
|
||||||
ConfigData data = new ConfigData();
|
|
||||||
initConfigData(data);
|
|
||||||
handler.setConfigData(data);
|
|
||||||
ConfigUtil.setHandler(handler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void setupTruststore(ConfigData configData) {
|
|
||||||
|
|
||||||
if (!configData.getServerUrl().startsWith("https:")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String truststore = trustStore;
|
|
||||||
if (truststore == null) {
|
|
||||||
truststore = configData.getTruststore();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (truststore != null) {
|
|
||||||
String pass = trustPass;
|
|
||||||
if (pass == null) {
|
|
||||||
pass = configData.getTrustpass();
|
|
||||||
}
|
|
||||||
if (pass == null) {
|
|
||||||
pass = IoUtil.readSecret("Enter truststore password: ");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
HttpUtil.setTruststore(new File(truststore), pass);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException("Failed to load truststore: " + truststore, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (insecure) {
|
|
||||||
HttpUtil.setSkipCertificateValidation();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ConfigData ensureAuthInfo(ConfigData config) {
|
|
||||||
|
|
||||||
if (requiresLogin()) {
|
|
||||||
// make sure current handler is in-memory handler
|
|
||||||
// restore it at the end
|
|
||||||
ConfigHandler old = ConfigUtil.getHandler();
|
|
||||||
try {
|
|
||||||
// make sure all defaults are initialized after this point
|
|
||||||
applyDefaultOptionValues();
|
|
||||||
|
|
||||||
initConfigData(config);
|
|
||||||
ConfigUtil.setupInMemoryHandler(config);
|
|
||||||
|
|
||||||
ConfigCredentialsCmd login = new ConfigCredentialsCmd();
|
|
||||||
login.initFromParent(this);
|
|
||||||
login.init(config);
|
|
||||||
login.process();
|
|
||||||
|
|
||||||
// this must be executed before finally block which restores config handler
|
|
||||||
return loadConfig();
|
|
||||||
|
|
||||||
} catch (RuntimeException e) {
|
|
||||||
throw e;
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
} finally {
|
|
||||||
ConfigUtil.setHandler(old);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
checkAuthInfo(config);
|
|
||||||
|
|
||||||
// make sure all defaults are initialized after this point
|
|
||||||
applyDefaultOptionValues();
|
|
||||||
return loadConfig();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean requiresLogin() {
|
|
||||||
return user != null || password != null || secret != null || keystore != null
|
|
||||||
|| keyPass != null || storePass != null || alias != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ConfigData copyWithServerInfo(ConfigData config) {
|
|
||||||
|
|
||||||
ConfigData result = config.deepcopy();
|
|
||||||
|
|
||||||
if (server != null) {
|
|
||||||
result.setServerUrl(server);
|
|
||||||
}
|
|
||||||
if (realm != null) {
|
|
||||||
result.setRealm(realm);
|
|
||||||
}
|
|
||||||
|
|
||||||
checkServerInfo(result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initConfigData(ConfigData data) {
|
|
||||||
if (server != null)
|
|
||||||
data.setServerUrl(server);
|
|
||||||
if (realm != null)
|
|
||||||
data.setRealm(realm);
|
|
||||||
if (trustStore != null)
|
|
||||||
data.setTruststore(trustStore);
|
|
||||||
|
|
||||||
RealmConfigData rdata = data.sessionRealmConfigData();
|
|
||||||
if (clientId != null)
|
|
||||||
rdata.setClientId(clientId);
|
|
||||||
if (secret != null)
|
|
||||||
rdata.setSecret(secret);
|
|
||||||
String grantTypeForAuthentication = user == null ? OAuth2Constants.CLIENT_CREDENTIALS : OAuth2Constants.PASSWORD;
|
|
||||||
rdata.setGrantTypeForAuthentication(grantTypeForAuthentication);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
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,8 +1,9 @@
|
||||||
package org.keycloak.client.registration.cli.commands;
|
package org.keycloak.client.registration.cli.commands;
|
||||||
|
|
||||||
import org.keycloak.client.registration.cli.common.AttributeKey;
|
import org.keycloak.client.cli.common.AttributeKey;
|
||||||
import org.keycloak.client.registration.cli.common.EndpointType;
|
import org.keycloak.client.cli.common.BaseGlobalOptionsCmd;
|
||||||
import org.keycloak.client.registration.cli.util.ReflectionUtil;
|
import org.keycloak.client.registration.cli.EndpointType;
|
||||||
|
import org.keycloak.client.registration.cli.ReflectionUtil;
|
||||||
import org.keycloak.representations.idm.ClientRepresentation;
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
import org.keycloak.representations.oidc.OIDCClientRepresentation;
|
import org.keycloak.representations.oidc.OIDCClientRepresentation;
|
||||||
|
|
||||||
|
@ -20,18 +21,18 @@ import picocli.CommandLine.Command;
|
||||||
import picocli.CommandLine.Option;
|
import picocli.CommandLine.Option;
|
||||||
import picocli.CommandLine.Parameters;
|
import picocli.CommandLine.Parameters;
|
||||||
|
|
||||||
import static org.keycloak.client.registration.cli.util.OsUtil.CMD;
|
import static org.keycloak.client.cli.util.OsUtil.PROMPT;
|
||||||
import static org.keycloak.client.registration.cli.util.OsUtil.PROMPT;
|
import static org.keycloak.client.registration.cli.KcRegMain.CMD;
|
||||||
import static org.keycloak.client.registration.cli.util.ReflectionUtil.getAttributeListWithJSonTypes;
|
import static org.keycloak.client.registration.cli.ReflectionUtil.getAttributeListWithJSonTypes;
|
||||||
import static org.keycloak.client.registration.cli.util.ReflectionUtil.isBasicType;
|
import static org.keycloak.client.registration.cli.ReflectionUtil.isBasicType;
|
||||||
import static org.keycloak.client.registration.cli.util.ReflectionUtil.isListType;
|
import static org.keycloak.client.registration.cli.ReflectionUtil.isListType;
|
||||||
import static org.keycloak.client.registration.cli.util.ReflectionUtil.isMapType;
|
import static org.keycloak.client.registration.cli.ReflectionUtil.isMapType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
*/
|
*/
|
||||||
@Command(name = "attrs", description = "[ATTRIBUTE] [--endpoint TYPE]")
|
@Command(name = "attrs", description = "[ATTRIBUTE] [--endpoint TYPE]")
|
||||||
public class AttrsCmd extends AbstractGlobalOptionsCmd {
|
public class AttrsCmd extends BaseGlobalOptionsCmd {
|
||||||
|
|
||||||
CommandLine.Model.CommandSpec spec;
|
CommandLine.Model.CommandSpec spec;
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ import java.io.StringWriter;
|
||||||
|
|
||||||
import picocli.CommandLine.Command;
|
import picocli.CommandLine.Command;
|
||||||
|
|
||||||
import static org.keycloak.client.registration.cli.util.OsUtil.CMD;
|
import static org.keycloak.client.registration.cli.KcRegMain.CMD;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
|
|
|
@ -1,243 +1,18 @@
|
||||||
package org.keycloak.client.registration.cli.commands;
|
package org.keycloak.client.registration.cli.commands;
|
||||||
|
|
||||||
import org.keycloak.OAuth2Constants;
|
import org.keycloak.client.cli.common.BaseConfigCredentialsCmd;
|
||||||
import org.keycloak.client.registration.cli.config.ConfigData;
|
import org.keycloak.client.registration.cli.KcRegMain;
|
||||||
import org.keycloak.client.registration.cli.config.RealmConfigData;
|
|
||||||
import org.keycloak.client.registration.cli.util.AuthUtil;
|
|
||||||
import org.keycloak.representations.AccessTokenResponse;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.PrintWriter;
|
|
||||||
import java.io.StringWriter;
|
|
||||||
import java.net.URL;
|
|
||||||
|
|
||||||
import picocli.CommandLine.Command;
|
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;
|
|
||||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_STRING;
|
|
||||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.getHandler;
|
|
||||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.loadConfig;
|
|
||||||
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.OS_ARCH;
|
|
||||||
import static org.keycloak.client.registration.cli.util.OsUtil.PROMPT;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
*/
|
*/
|
||||||
@Command(name = "credentials", description = "--server SERVER_URL --realm REALM [ARGUMENTS]")
|
@Command(name = "credentials", description = "--server SERVER_URL --realm REALM [ARGUMENTS]")
|
||||||
public class ConfigCredentialsCmd extends AbstractAuthOptionsCmd {
|
public class ConfigCredentialsCmd extends BaseConfigCredentialsCmd {
|
||||||
|
|
||||||
private int sigLifetime = 600;
|
public ConfigCredentialsCmd() {
|
||||||
|
super(KcRegMain.COMMAND_STATE);
|
||||||
|
|
||||||
public void init(ConfigData configData) {
|
|
||||||
if (server == null) {
|
|
||||||
server = configData.getServerUrl();
|
|
||||||
}
|
|
||||||
if (realm == null) {
|
|
||||||
realm = configData.getRealm();
|
|
||||||
}
|
|
||||||
if (trustStore == null) {
|
|
||||||
trustStore = configData.getTruststore();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RealmConfigData rdata = configData.getRealmConfigData(server, realm);
|
|
||||||
if (rdata == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (clientId == null) {
|
|
||||||
clientId = rdata.getClientId();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String[] getUnsupportedOptions() {
|
|
||||||
return new String[] {"--no-config", booleanOptionForCheck(noconfig)};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean nothingToDo() {
|
|
||||||
return noOptions();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void process() {
|
|
||||||
// check server
|
|
||||||
if (server == null) {
|
|
||||||
throw new IllegalArgumentException("Required option not specified: --server");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
new URL(server);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException("Invalid server endpoint url: " + server, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (realm == null)
|
|
||||||
throw new IllegalArgumentException("Required option not specified: --realm");
|
|
||||||
|
|
||||||
String signedRequestToken = null;
|
|
||||||
boolean clientSet = clientId != null;
|
|
||||||
|
|
||||||
applyDefaultOptionValues();
|
|
||||||
String grantTypeForAuthentication = null;
|
|
||||||
|
|
||||||
if (user != null) {
|
|
||||||
grantTypeForAuthentication = OAuth2Constants.PASSWORD;
|
|
||||||
printErr("Logging into " + server + " as user " + user + " of realm " + realm);
|
|
||||||
|
|
||||||
// if user was set there needs to be a password so we can authenticate
|
|
||||||
if (password == null) {
|
|
||||||
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: ");
|
|
||||||
}
|
|
||||||
} 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: ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (keystore != null) {
|
|
||||||
if (secret != null) {
|
|
||||||
throw new IllegalArgumentException("Can't use both --keystore and --secret");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!new File(keystore).isFile()) {
|
|
||||||
throw new RuntimeException("No such keystore file: " + keystore);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (storePass == null) {
|
|
||||||
storePass = readSecret("Enter keystore password: ");
|
|
||||||
keyPass = readSecret("Enter key password: ");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (keyPass == null) {
|
|
||||||
keyPass = storePass;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (alias == null) {
|
|
||||||
alias = clientId;
|
|
||||||
}
|
|
||||||
|
|
||||||
String realmInfoUrl = server + "/realms/" + realm;
|
|
||||||
|
|
||||||
signedRequestToken = AuthUtil.getSignedRequestToken(keystore, storePass, keyPass,
|
|
||||||
alias, sigLifetime, clientId, realmInfoUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if only server and realm are set, just save config and be done
|
|
||||||
if (user == null && secret == null && keystore == null) {
|
|
||||||
getHandler().saveMergeConfig(config -> {
|
|
||||||
config.setServerUrl(server);
|
|
||||||
config.setRealm(realm);
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setupTruststore(copyWithServerInfo(loadConfig()));
|
|
||||||
|
|
||||||
// now use the token endpoint to retrieve access token, and refresh token
|
|
||||||
AccessTokenResponse tokens = signedRequestToken != null ?
|
|
||||||
getAuthTokensByJWT(server, realm, user, password, clientId, signedRequestToken) :
|
|
||||||
secret != null ?
|
|
||||||
getAuthTokensBySecret(server, realm, user, password, clientId, secret) :
|
|
||||||
getAuthTokens(server, realm, user, password, clientId);
|
|
||||||
|
|
||||||
Long sigExpiresAt = signedRequestToken == null ? null : System.currentTimeMillis() + sigLifetime * 1000;
|
|
||||||
|
|
||||||
// save tokens to config file
|
|
||||||
saveTokens(tokens, server, realm, clientId, signedRequestToken, sigExpiresAt, secret, grantTypeForAuthentication);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String help() {
|
|
||||||
return usage();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String usage() {
|
|
||||||
StringWriter sb = new StringWriter();
|
|
||||||
PrintWriter out = new PrintWriter(sb);
|
|
||||||
out.println("Usage: " + CMD + " config credentials --server SERVER_URL --realm REALM [ARGUMENTS]");
|
|
||||||
out.println(" " + CMD + " config credentials --server SERVER_URL --realm REALM --user USER [--password PASSWORD] [ARGUMENTS]");
|
|
||||||
out.println(" " + CMD + " config credentials --server SERVER_URL --realm REALM --client CLIENT_ID [--secret SECRET] [ARGUMENTS]");
|
|
||||||
out.println(" " + CMD + " config credentials --server SERVER_URL --realm REALM --client CLIENT_ID [--keystore KEYSTORE] [ARGUMENTS]");
|
|
||||||
out.println();
|
|
||||||
out.println("Command to establish an authenticated client session with the server. There are many authentication");
|
|
||||||
out.println("options available, and it depends on server side client authentication configuration how client can or should authenticate.");
|
|
||||||
out.println("The information always required includes --server, and --realm. That is enough to establish unauthenticated session.");
|
|
||||||
out.println("If --client is not provided it defaults to 'admin-cli'. The authantication options / requirements depend on how this client is configured.");
|
|
||||||
out.println();
|
|
||||||
out.println("If you have an account configured with the rights to manage clients then you can use username, and password to authenticate.");
|
|
||||||
out.println("If confidential client authentication is also configured, you may have to specify a client id, and client credentials in addition to");
|
|
||||||
out.println("user credentials. Client credentials are either a client secret, or a keystore information to use Signed JWT mechanism.");
|
|
||||||
out.println("If only client credentials are provided, and no user credentials, then the service account is used for login.");
|
|
||||||
out.println();
|
|
||||||
out.println("Arguments:");
|
|
||||||
out.println();
|
|
||||||
out.println(" Global options:");
|
|
||||||
out.println(" -x Print full stack trace when exiting with error");
|
|
||||||
out.println(" --config Path to a config file (" + DEFAULT_CONFIG_FILE_STRING + " by default)");
|
|
||||||
out.println(" --truststore PATH Path to a truststore containing trusted certificates");
|
|
||||||
out.println(" --trustpass PASSWORD Truststore password (prompted for if not specified and --truststore is used)");
|
|
||||||
out.println();
|
|
||||||
out.println(" Command specific options:");
|
|
||||||
out.println(" --server SERVER_URL Server endpoint url (e.g. 'http://localhost:8080')");
|
|
||||||
out.println(" --realm REALM Realm name to use");
|
|
||||||
out.println(" --user USER Username to login with");
|
|
||||||
out.println(" --password PASSWORD Password to login with (prompted for if not specified and --user is used)");
|
|
||||||
out.println(" --client CLIENT_ID ClientId used by this client tool ('admin-cli' by default)");
|
|
||||||
out.println(" --secret SECRET Secret to authenticate the client (prompted for if --client is specified, and no --keystore is specified)");
|
|
||||||
out.println(" --keystore PATH Path to a keystore containing private key");
|
|
||||||
out.println(" --storepass PASSWORD Keystore password (prompted for if not specified and --keystore is used)");
|
|
||||||
out.println(" --keypass PASSWORD Key password (prompted for if not specified and --keystore is used without --storepass,");
|
|
||||||
out.println(" otherwise defaults to keystore password)");
|
|
||||||
out.println(" --alias ALIAS Alias of the key inside a keystore (defaults to the value of ClientId)");
|
|
||||||
out.println();
|
|
||||||
out.println();
|
|
||||||
out.println("Examples:");
|
|
||||||
out.println();
|
|
||||||
out.println("Login as 'admin' user of 'master' realm to a local Keycloak server running on default port.");
|
|
||||||
out.println("You will be prompted for a password:");
|
|
||||||
out.println(" " + PROMPT + " " + CMD + " config credentials --server http://localhost:8080 --realm master --user admin");
|
|
||||||
out.println();
|
|
||||||
out.println("Login to Keycloak server at non-default endpoint passing the password via standard input:");
|
|
||||||
if (OS_ARCH.isWindows()) {
|
|
||||||
out.println(" " + PROMPT + " echo mypassword | " + CMD + " config credentials --server http://localhost:9080 --realm master --user admin");
|
|
||||||
} else {
|
|
||||||
out.println(" " + PROMPT + " " + CMD + " config credentials --server http://localhost:9080 --realm master --user admin << EOF");
|
|
||||||
out.println(" mypassword");
|
|
||||||
out.println(" EOF");
|
|
||||||
}
|
|
||||||
out.println();
|
|
||||||
out.println("Login specifying a password through command line:");
|
|
||||||
out.println(" " + PROMPT + " " + CMD + " config credentials --server http://localhost:9080 --realm master --user admin --password " + OS_ARCH.envVar("PASSWORD"));
|
|
||||||
out.println();
|
|
||||||
out.println("Login using a client service account of a custom client. You will be prompted for a client secret:");
|
|
||||||
out.println(" " + PROMPT + " " + CMD + " config credentials --server http://localhost:9080 --realm master --client reg-cli");
|
|
||||||
out.println();
|
|
||||||
out.println("Login using a client service account of a custom client, authenticating with signed JWT.");
|
|
||||||
out.println("You will be prompted for a keystore password, and a key password:");
|
|
||||||
out.println(" " + PROMPT + " " + CMD + " config credentials --server http://localhost:9080 --realm master --client reg-cli --keystore " + OS_ARCH.path("~/.keycloak/keystore.jks"));
|
|
||||||
out.println();
|
|
||||||
out.println("Login as 'user' while also authenticating a custom client with signed JWT.");
|
|
||||||
out.println("You will be prompted for a user password, a keystore password, and a key password:");
|
|
||||||
out.println(" " + PROMPT + " " + CMD + " config credentials --server http://localhost:9080 --realm master --user user --client reg-cli --keystore " + OS_ARCH.path("~/.keycloak/keystore.jks"));
|
|
||||||
out.println();
|
|
||||||
out.println();
|
|
||||||
out.println("Use '" + CMD + " help' for general information and a list of commands");
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
package org.keycloak.client.registration.cli.commands;
|
package org.keycloak.client.registration.cli.commands;
|
||||||
|
|
||||||
import org.keycloak.client.registration.cli.config.RealmConfigData;
|
import org.keycloak.client.cli.config.RealmConfigData;
|
||||||
import org.keycloak.client.registration.cli.util.IoUtil;
|
import org.keycloak.client.cli.util.IoUtil;
|
||||||
import org.keycloak.client.registration.cli.util.ParseUtil;
|
import org.keycloak.client.registration.cli.CmdStdinContext;
|
||||||
|
import org.keycloak.client.registration.cli.KcRegMain;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
|
@ -11,12 +12,11 @@ import picocli.CommandLine.Command;
|
||||||
import picocli.CommandLine.Option;
|
import picocli.CommandLine.Option;
|
||||||
import picocli.CommandLine.Parameters;
|
import picocli.CommandLine.Parameters;
|
||||||
|
|
||||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_STRING;
|
import static org.keycloak.client.cli.util.ConfigUtil.saveMergeConfig;
|
||||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.saveMergeConfig;
|
import static org.keycloak.client.cli.util.IoUtil.warnfOut;
|
||||||
import static org.keycloak.client.registration.cli.util.IoUtil.warnfOut;
|
import static org.keycloak.client.cli.util.OsUtil.OS_ARCH;
|
||||||
import static org.keycloak.client.registration.cli.util.OsUtil.CMD;
|
import static org.keycloak.client.cli.util.OsUtil.PROMPT;
|
||||||
import static org.keycloak.client.registration.cli.util.OsUtil.OS_ARCH;
|
import static org.keycloak.client.registration.cli.KcRegMain.CMD;
|
||||||
import static org.keycloak.client.registration.cli.util.OsUtil.PROMPT;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
|
@ -34,7 +34,7 @@ public class ConfigInitialTokenCmd extends AbstractAuthOptionsCmd {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean nothingToDo() {
|
protected boolean nothingToDo() {
|
||||||
return noOptions() && token == null && !delete && !keepDomain;
|
return super.nothingToDo() && token == null && !delete && !keepDomain;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -60,7 +60,7 @@ public class ConfigInitialTokenCmd extends AbstractAuthOptionsCmd {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (token != null && token.startsWith("-")) {
|
if (token != null && token.startsWith("-")) {
|
||||||
warnfOut(ParseUtil.TOKEN_OPTION_WARN, token);
|
warnfOut(CmdStdinContext.TOKEN_OPTION_WARN, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!delete && token == null) {
|
if (!delete && token == null) {
|
||||||
|
@ -106,7 +106,7 @@ public class ConfigInitialTokenCmd extends AbstractAuthOptionsCmd {
|
||||||
out.println();
|
out.println();
|
||||||
out.println(" Global options:");
|
out.println(" Global options:");
|
||||||
out.println(" -x Print full stack trace when exiting with error");
|
out.println(" -x Print full stack trace when exiting with error");
|
||||||
out.println(" --config Path to the config file (" + DEFAULT_CONFIG_FILE_STRING + " by default)");
|
out.println(" --config Path to the config file (" + KcRegMain.DEFAULT_CONFIG_FILE_STRING + " by default)");
|
||||||
out.println();
|
out.println();
|
||||||
out.println(" Command specific options:");
|
out.println(" Command specific options:");
|
||||||
out.println(" --server SERVER Server endpoint url (e.g. 'http://localhost:8080')");
|
out.println(" --server SERVER Server endpoint url (e.g. 'http://localhost:8080')");
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
package org.keycloak.client.registration.cli.commands;
|
package org.keycloak.client.registration.cli.commands;
|
||||||
|
|
||||||
import org.keycloak.client.registration.cli.config.RealmConfigData;
|
import org.keycloak.client.registration.cli.KcRegMain;
|
||||||
import org.keycloak.client.registration.cli.util.IoUtil;
|
import org.keycloak.client.cli.config.RealmConfigData;
|
||||||
|
import org.keycloak.client.cli.util.IoUtil;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
|
@ -10,11 +11,10 @@ import picocli.CommandLine.Command;
|
||||||
import picocli.CommandLine.Option;
|
import picocli.CommandLine.Option;
|
||||||
import picocli.CommandLine.Parameters;
|
import picocli.CommandLine.Parameters;
|
||||||
|
|
||||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_STRING;
|
import static org.keycloak.client.cli.util.ConfigUtil.saveMergeConfig;
|
||||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.saveMergeConfig;
|
import static org.keycloak.client.cli.util.OsUtil.OS_ARCH;
|
||||||
import static org.keycloak.client.registration.cli.util.OsUtil.CMD;
|
import static org.keycloak.client.cli.util.OsUtil.PROMPT;
|
||||||
import static org.keycloak.client.registration.cli.util.OsUtil.OS_ARCH;
|
import static org.keycloak.client.registration.cli.KcRegMain.CMD;
|
||||||
import static org.keycloak.client.registration.cli.util.OsUtil.PROMPT;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
|
@ -30,7 +30,7 @@ public class ConfigRegistrationTokenCmd extends AbstractAuthOptionsCmd {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean nothingToDo() {
|
protected boolean nothingToDo() {
|
||||||
return noOptions() && token == null && !delete;
|
return super.nothingToDo() && token == null && !delete;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -97,7 +97,7 @@ public class ConfigRegistrationTokenCmd extends AbstractAuthOptionsCmd {
|
||||||
out.println();
|
out.println();
|
||||||
out.println(" Global options:");
|
out.println(" Global options:");
|
||||||
out.println(" -x Print full stack trace when exiting with error");
|
out.println(" -x Print full stack trace when exiting with error");
|
||||||
out.println(" --config Path to the config file (" + DEFAULT_CONFIG_FILE_STRING + " by default)");
|
out.println(" --config Path to the config file (" + KcRegMain.DEFAULT_CONFIG_FILE_STRING + " by default)");
|
||||||
out.println();
|
out.println();
|
||||||
out.println(" Command specific options:");
|
out.println(" Command specific options:");
|
||||||
out.println(" --server SERVER Server endpoint url (e.g. 'http://localhost:8080')");
|
out.println(" --server SERVER Server endpoint url (e.g. 'http://localhost:8080')");
|
||||||
|
|
|
@ -16,131 +16,19 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.client.registration.cli.commands;
|
package org.keycloak.client.registration.cli.commands;
|
||||||
|
|
||||||
import java.io.File;
|
import org.keycloak.client.cli.common.BaseConfigTruststoreCmd;
|
||||||
import java.io.PrintWriter;
|
import org.keycloak.client.registration.cli.KcRegMain;
|
||||||
import java.io.StringWriter;
|
|
||||||
|
|
||||||
import picocli.CommandLine.Command;
|
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.OS_ARCH;
|
|
||||||
import static org.keycloak.client.registration.cli.util.OsUtil.PROMPT;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
*/
|
*/
|
||||||
@Command(name = "truststore", description = "PATH [ARGUMENTS]")
|
@Command(name = "truststore", description = "PATH [ARGUMENTS]")
|
||||||
public class ConfigTruststoreCmd extends AbstractAuthOptionsCmd {
|
public class ConfigTruststoreCmd extends BaseConfigTruststoreCmd {
|
||||||
|
|
||||||
@Option(names = {"-d", "--delete"}, description = "Indicates that initial access token should be removed")
|
public ConfigTruststoreCmd() {
|
||||||
private boolean delete;
|
super(KcRegMain.COMMAND_STATE);
|
||||||
|
|
||||||
@Parameters(arity = "0..1")
|
|
||||||
private String truststorePath;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean nothingToDo() {
|
|
||||||
return noOptions() && truststorePath == null && !delete;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String[] getUnsupportedOptions() {
|
|
||||||
return new String[] {"--server", server,
|
|
||||||
"--realm", realm,
|
|
||||||
"--client", clientId,
|
|
||||||
"--user", user,
|
|
||||||
"--password", password,
|
|
||||||
"--secret", secret,
|
|
||||||
"--truststore", trustStore,
|
|
||||||
"--keystore", keystore,
|
|
||||||
"--keypass", keyPass,
|
|
||||||
"--alias", alias,
|
|
||||||
"--no-config", booleanOptionForCheck(noconfig)};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void process() {
|
|
||||||
// now update the config
|
|
||||||
|
|
||||||
String store;
|
|
||||||
String pass;
|
|
||||||
|
|
||||||
if (!delete) {
|
|
||||||
|
|
||||||
if (truststorePath == null) {
|
|
||||||
throw new IllegalArgumentException("No truststore specified");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!new File(truststorePath).isFile()) {
|
|
||||||
throw new RuntimeException("Truststore file not found: " + truststorePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ("-".equals(trustPass)) {
|
|
||||||
trustPass = readSecret("Enter truststore password: ");
|
|
||||||
}
|
|
||||||
|
|
||||||
store = truststorePath;
|
|
||||||
pass = trustPass;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
if (truststorePath != null) {
|
|
||||||
throw new IllegalArgumentException("Option --delete is mutually exclusive with specifying a TRUSTSTORE");
|
|
||||||
}
|
|
||||||
if (trustPass != null) {
|
|
||||||
throw new IllegalArgumentException("Options --trustpass and --delete are mutually exclusive");
|
|
||||||
}
|
|
||||||
store = null;
|
|
||||||
pass = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
saveMergeConfig(config -> {
|
|
||||||
config.setTruststore(store);
|
|
||||||
config.setTrustpass(pass);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String help() {
|
|
||||||
return usage();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String usage() {
|
|
||||||
StringWriter sb = new StringWriter();
|
|
||||||
PrintWriter out = new PrintWriter(sb);
|
|
||||||
out.println("Usage: " + CMD + " config truststore [TRUSTSTORE | --delete] [--trustpass PASSWORD] [ARGUMENTS]");
|
|
||||||
out.println();
|
|
||||||
out.println("Command to configure a global truststore to use when using https to connect to Keycloak server.");
|
|
||||||
out.println();
|
|
||||||
out.println("Arguments:");
|
|
||||||
out.println();
|
|
||||||
out.println(" Global options:");
|
|
||||||
out.println(" -x Print full stack trace when exiting with error");
|
|
||||||
out.println(" --config Path to the config file (" + DEFAULT_CONFIG_FILE_STRING + " by default)");
|
|
||||||
out.println();
|
|
||||||
out.println(" Command specific options:");
|
|
||||||
out.println(" TRUSTSTORE Path to truststore file");
|
|
||||||
out.println(" --trustpass PASSWORD Truststore password to unlock truststore (prompted for if set to '-')");
|
|
||||||
out.println(" -d, --delete Remove truststore configuration");
|
|
||||||
out.println();
|
|
||||||
out.println();
|
|
||||||
out.println("Examples:");
|
|
||||||
out.println();
|
|
||||||
out.println("Specify a truststore - you will be prompted for truststore password every time it is used:");
|
|
||||||
out.println(" " + PROMPT + " " + CMD + " config truststore " + OS_ARCH.path("~/.keycloak/truststore.jks"));
|
|
||||||
out.println();
|
|
||||||
out.println("Specify a truststore, and password - truststore will automatically be used without prompting for password:");
|
|
||||||
out.println(" " + PROMPT + " " + CMD + " config truststore --storepass " + OS_ARCH.envVar("PASSWORD") + " " + OS_ARCH.path("~/.keycloak/truststore.jks"));
|
|
||||||
out.println();
|
|
||||||
out.println("Remove truststore configuration:");
|
|
||||||
out.println(" " + PROMPT + " " + CMD + " config truststore --delete");
|
|
||||||
out.println();
|
|
||||||
out.println();
|
|
||||||
out.println("Use '" + CMD + " help' for general information and a list of commands");
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,12 +17,13 @@
|
||||||
|
|
||||||
package org.keycloak.client.registration.cli.commands;
|
package org.keycloak.client.registration.cli.commands;
|
||||||
|
|
||||||
|
import org.keycloak.client.registration.cli.CmdStdinContext;
|
||||||
|
import org.keycloak.client.registration.cli.EndpointType;
|
||||||
import org.keycloak.client.registration.cli.EndpointTypeConverter;
|
import org.keycloak.client.registration.cli.EndpointTypeConverter;
|
||||||
import org.keycloak.client.registration.cli.common.AttributeOperation;
|
import org.keycloak.client.registration.cli.KcRegMain;
|
||||||
import org.keycloak.client.registration.cli.common.CmdStdinContext;
|
import org.keycloak.client.cli.common.AttributeOperation;
|
||||||
import org.keycloak.client.registration.cli.common.EndpointType;
|
import org.keycloak.client.cli.config.ConfigData;
|
||||||
import org.keycloak.client.registration.cli.config.ConfigData;
|
import org.keycloak.client.cli.util.HttpUtil;
|
||||||
import org.keycloak.client.registration.cli.util.HttpUtil;
|
|
||||||
import org.keycloak.representations.idm.ClientRepresentation;
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
import org.keycloak.representations.oidc.OIDCClientRepresentation;
|
import org.keycloak.representations.oidc.OIDCClientRepresentation;
|
||||||
import org.keycloak.util.JsonSerialization;
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
@ -39,28 +40,23 @@ import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException;
|
||||||
import picocli.CommandLine.Command;
|
import picocli.CommandLine.Command;
|
||||||
import picocli.CommandLine.Option;
|
import picocli.CommandLine.Option;
|
||||||
|
|
||||||
import static org.keycloak.client.registration.cli.common.AttributeOperation.Type.SET;
|
import static org.keycloak.client.cli.common.AttributeOperation.Type.SET;
|
||||||
import static org.keycloak.client.registration.cli.common.EndpointType.DEFAULT;
|
import static org.keycloak.client.cli.util.ConfigUtil.credentialsAvailable;
|
||||||
import static org.keycloak.client.registration.cli.common.EndpointType.OIDC;
|
import static org.keycloak.client.cli.util.ConfigUtil.loadConfig;
|
||||||
import static org.keycloak.client.registration.cli.common.EndpointType.SAML2;
|
import static org.keycloak.client.cli.util.ConfigUtil.saveMergeConfig;
|
||||||
import static org.keycloak.client.registration.cli.util.AuthUtil.ensureToken;
|
import static org.keycloak.client.cli.util.ConfigUtil.setRegistrationToken;
|
||||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_STRING;
|
import static org.keycloak.client.cli.util.HttpUtil.doPost;
|
||||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.credentialsAvailable;
|
import static org.keycloak.client.cli.util.IoUtil.printErr;
|
||||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.loadConfig;
|
import static org.keycloak.client.cli.util.IoUtil.printOut;
|
||||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.saveMergeConfig;
|
import static org.keycloak.client.cli.util.IoUtil.readFully;
|
||||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.setRegistrationToken;
|
import static org.keycloak.client.cli.util.IoUtil.readSecret;
|
||||||
import static org.keycloak.client.registration.cli.util.HttpUtil.doPost;
|
import static org.keycloak.client.cli.util.OsUtil.OS_ARCH;
|
||||||
import static org.keycloak.client.registration.cli.util.HttpUtil.getExpectedContentType;
|
import static org.keycloak.client.cli.util.OsUtil.PROMPT;
|
||||||
import static org.keycloak.client.registration.cli.util.IoUtil.printErr;
|
import static org.keycloak.client.cli.util.ParseUtil.parseKeyVal;
|
||||||
import static org.keycloak.client.registration.cli.util.IoUtil.printOut;
|
import static org.keycloak.client.registration.cli.EndpointType.DEFAULT;
|
||||||
import static org.keycloak.client.registration.cli.util.IoUtil.readFully;
|
import static org.keycloak.client.registration.cli.EndpointType.OIDC;
|
||||||
import static org.keycloak.client.registration.cli.util.IoUtil.readSecret;
|
import static org.keycloak.client.registration.cli.EndpointType.SAML2;
|
||||||
import static org.keycloak.client.registration.cli.util.OsUtil.CMD;
|
import static org.keycloak.client.registration.cli.KcRegMain.CMD;
|
||||||
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.ParseUtil.parseKeyVal;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
|
@ -109,13 +105,13 @@ public class CreateCmd extends AbstractAuthOptionsCmd {
|
||||||
}
|
}
|
||||||
|
|
||||||
// if --token is specified read it
|
// if --token is specified read it
|
||||||
if ("-".equals(token)) {
|
if ("-".equals(externalToken)) {
|
||||||
token = readSecret("Enter Initial Access Token: ");
|
externalToken = readSecret("Enter Initial Access Token: ");
|
||||||
}
|
}
|
||||||
|
|
||||||
CmdStdinContext ctx = new CmdStdinContext();
|
CmdStdinContext ctx = new CmdStdinContext();
|
||||||
if (file != null) {
|
if (file != null) {
|
||||||
ctx = parseFileOrStdin(file, regType);
|
ctx = CmdStdinContext.parseFileOrStdin(file, regType);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctx.getEndpointType() == null) {
|
if (ctx.getEndpointType() == null) {
|
||||||
|
@ -126,22 +122,22 @@ public class CreateCmd extends AbstractAuthOptionsCmd {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attrs.size() > 0) {
|
if (attrs.size() > 0) {
|
||||||
ctx = mergeAttributes(ctx, attrs);
|
ctx = CmdStdinContext.mergeAttributes(ctx, attrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
String contentType = getExpectedContentType(ctx.getEndpointType());
|
String contentType = EndpointType.getExpectedContentType(ctx.getEndpointType());
|
||||||
|
|
||||||
ConfigData config = loadConfig();
|
ConfigData config = loadConfig();
|
||||||
config = copyWithServerInfo(config);
|
config = copyWithServerInfo(config);
|
||||||
|
|
||||||
if (token == null) {
|
if (externalToken == null) {
|
||||||
// if initial token is not set, try use the one from configuration
|
// if initial token is not set, try use the one from configuration
|
||||||
token = config.sessionRealmConfigData().getInitialToken();
|
externalToken = config.sessionRealmConfigData().getInitialToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
setupTruststore(config);
|
setupTruststore(config);
|
||||||
|
|
||||||
String auth = token;
|
String auth = externalToken;
|
||||||
if (auth == null) {
|
if (auth == null) {
|
||||||
config = ensureAuthInfo(config);
|
config = ensureAuthInfo(config);
|
||||||
config = copyWithServerInfo(config);
|
config = copyWithServerInfo(config);
|
||||||
|
@ -199,7 +195,7 @@ public class CreateCmd extends AbstractAuthOptionsCmd {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean nothingToDo() {
|
protected boolean nothingToDo() {
|
||||||
return noOptions() && regType == null && file == null && rawSets.isEmpty();
|
return super.nothingToDo() && regType == null && file == null && rawSets.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -220,7 +216,7 @@ public class CreateCmd extends AbstractAuthOptionsCmd {
|
||||||
out.println();
|
out.println();
|
||||||
out.println(" Global options:");
|
out.println(" Global options:");
|
||||||
out.println(" -x Print full stack trace when exiting with error");
|
out.println(" -x Print full stack trace when exiting with error");
|
||||||
out.println(" --config Path to the config file (" + DEFAULT_CONFIG_FILE_STRING + " by default)");
|
out.println(" --config Path to the config file (" + KcRegMain.DEFAULT_CONFIG_FILE_STRING + " by default)");
|
||||||
out.println(" --no-config Don't use config file - no authentication info is loaded or saved");
|
out.println(" --no-config Don't use config file - no authentication info is loaded or saved");
|
||||||
out.println(" --truststore PATH Path to a truststore containing trusted certificates");
|
out.println(" --truststore PATH Path to a truststore containing trusted certificates");
|
||||||
out.println(" --trustpass PASSWORD Truststore password (prompted for if not specified and --truststore is used)");
|
out.println(" --trustpass PASSWORD Truststore password (prompted for if not specified and --truststore is used)");
|
||||||
|
|
|
@ -17,8 +17,9 @@
|
||||||
|
|
||||||
package org.keycloak.client.registration.cli.commands;
|
package org.keycloak.client.registration.cli.commands;
|
||||||
|
|
||||||
import org.keycloak.client.registration.cli.config.ConfigData;
|
import org.keycloak.client.registration.cli.KcRegMain;
|
||||||
import org.keycloak.client.registration.cli.util.ParseUtil;
|
import org.keycloak.client.cli.config.ConfigData;
|
||||||
|
import org.keycloak.client.registration.cli.CmdStdinContext;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
|
@ -26,17 +27,15 @@ import java.io.StringWriter;
|
||||||
import picocli.CommandLine.Command;
|
import picocli.CommandLine.Command;
|
||||||
import picocli.CommandLine.Parameters;
|
import picocli.CommandLine.Parameters;
|
||||||
|
|
||||||
import static org.keycloak.client.registration.cli.util.AuthUtil.ensureToken;
|
import static org.keycloak.client.cli.util.ConfigUtil.credentialsAvailable;
|
||||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_STRING;
|
import static org.keycloak.client.cli.util.ConfigUtil.getRegistrationToken;
|
||||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.credentialsAvailable;
|
import static org.keycloak.client.cli.util.ConfigUtil.loadConfig;
|
||||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.getRegistrationToken;
|
import static org.keycloak.client.cli.util.ConfigUtil.saveMergeConfig;
|
||||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.loadConfig;
|
import static org.keycloak.client.cli.util.HttpUtil.doDelete;
|
||||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.saveMergeConfig;
|
import static org.keycloak.client.cli.util.HttpUtil.urlencode;
|
||||||
import static org.keycloak.client.registration.cli.util.HttpUtil.doDelete;
|
import static org.keycloak.client.cli.util.IoUtil.warnfErr;
|
||||||
import static org.keycloak.client.registration.cli.util.HttpUtil.urlencode;
|
import static org.keycloak.client.cli.util.OsUtil.PROMPT;
|
||||||
import static org.keycloak.client.registration.cli.util.IoUtil.warnfErr;
|
import static org.keycloak.client.registration.cli.KcRegMain.CMD;
|
||||||
import static org.keycloak.client.registration.cli.util.OsUtil.CMD;
|
|
||||||
import static org.keycloak.client.registration.cli.util.OsUtil.PROMPT;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -55,7 +54,7 @@ public class DeleteCmd extends AbstractAuthOptionsCmd {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clientId.startsWith("-")) {
|
if (clientId.startsWith("-")) {
|
||||||
warnfErr(ParseUtil.CLIENT_OPTION_WARN, clientId);
|
warnfErr(CmdStdinContext.CLIENT_OPTION_WARN, clientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
String regType = "default";
|
String regType = "default";
|
||||||
|
@ -63,14 +62,14 @@ public class DeleteCmd extends AbstractAuthOptionsCmd {
|
||||||
ConfigData config = loadConfig();
|
ConfigData config = loadConfig();
|
||||||
config = copyWithServerInfo(config);
|
config = copyWithServerInfo(config);
|
||||||
|
|
||||||
if (token == null) {
|
if (externalToken == null) {
|
||||||
// if registration access token is not set via -t, try use the one from configuration
|
// if registration access token is not set via -t, try use the one from configuration
|
||||||
token = getRegistrationToken(config.sessionRealmConfigData(), clientId);
|
externalToken = getRegistrationToken(config.sessionRealmConfigData(), clientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
setupTruststore(config);
|
setupTruststore(config);
|
||||||
|
|
||||||
String auth = token;
|
String auth = externalToken;
|
||||||
if (auth == null) {
|
if (auth == null) {
|
||||||
config = ensureAuthInfo(config);
|
config = ensureAuthInfo(config);
|
||||||
config = copyWithServerInfo(config);
|
config = copyWithServerInfo(config);
|
||||||
|
@ -94,7 +93,7 @@ public class DeleteCmd extends AbstractAuthOptionsCmd {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean nothingToDo() {
|
protected boolean nothingToDo() {
|
||||||
return noOptions() && clientId == null;
|
return super.nothingToDo() && clientId == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -114,7 +113,7 @@ public class DeleteCmd extends AbstractAuthOptionsCmd {
|
||||||
out.println();
|
out.println();
|
||||||
out.println(" Global options:");
|
out.println(" Global options:");
|
||||||
out.println(" -x Print full stack trace when exiting with error");
|
out.println(" -x Print full stack trace when exiting with error");
|
||||||
out.println(" --config Path to the config file (" + DEFAULT_CONFIG_FILE_STRING + " by default)");
|
out.println(" --config Path to the config file (" + KcRegMain.DEFAULT_CONFIG_FILE_STRING + " by default)");
|
||||||
out.println(" --no-config Don't use config file - no authentication info is loaded or saved");
|
out.println(" --no-config Don't use config file - no authentication info is loaded or saved");
|
||||||
out.println(" --truststore PATH Path to a truststore containing trusted certificates");
|
out.println(" --truststore PATH Path to a truststore containing trusted certificates");
|
||||||
out.println(" --trustpass PASSWORD Truststore password (prompted for if not specified and --truststore is used)");
|
out.println(" --trustpass PASSWORD Truststore password (prompted for if not specified and --truststore is used)");
|
||||||
|
|
|
@ -17,9 +17,10 @@
|
||||||
|
|
||||||
package org.keycloak.client.registration.cli.commands;
|
package org.keycloak.client.registration.cli.commands;
|
||||||
|
|
||||||
import org.keycloak.client.registration.cli.config.ConfigData;
|
import org.keycloak.client.registration.cli.KcRegMain;
|
||||||
import org.keycloak.client.registration.cli.common.EndpointType;
|
import org.keycloak.client.cli.config.ConfigData;
|
||||||
import org.keycloak.client.registration.cli.util.ParseUtil;
|
import org.keycloak.client.registration.cli.CmdStdinContext;
|
||||||
|
import org.keycloak.client.registration.cli.EndpointType;
|
||||||
import org.keycloak.representations.adapters.config.AdapterConfig;
|
import org.keycloak.representations.adapters.config.AdapterConfig;
|
||||||
import org.keycloak.representations.idm.ClientRepresentation;
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
import org.keycloak.representations.oidc.OIDCClientRepresentation;
|
import org.keycloak.representations.oidc.OIDCClientRepresentation;
|
||||||
|
@ -34,21 +35,19 @@ import picocli.CommandLine.Command;
|
||||||
import picocli.CommandLine.Option;
|
import picocli.CommandLine.Option;
|
||||||
import picocli.CommandLine.Parameters;
|
import picocli.CommandLine.Parameters;
|
||||||
|
|
||||||
import static org.keycloak.client.registration.cli.util.AuthUtil.ensureToken;
|
import static org.keycloak.client.cli.util.ConfigUtil.credentialsAvailable;
|
||||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_STRING;
|
import static org.keycloak.client.cli.util.ConfigUtil.getRegistrationToken;
|
||||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.credentialsAvailable;
|
import static org.keycloak.client.cli.util.ConfigUtil.loadConfig;
|
||||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.getRegistrationToken;
|
import static org.keycloak.client.cli.util.ConfigUtil.saveMergeConfig;
|
||||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.loadConfig;
|
import static org.keycloak.client.cli.util.ConfigUtil.setRegistrationToken;
|
||||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.saveMergeConfig;
|
import static org.keycloak.client.cli.util.HttpUtil.APPLICATION_JSON;
|
||||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.setRegistrationToken;
|
import static org.keycloak.client.cli.util.HttpUtil.doGet;
|
||||||
import static org.keycloak.client.registration.cli.util.HttpUtil.APPLICATION_JSON;
|
import static org.keycloak.client.cli.util.HttpUtil.urlencode;
|
||||||
import static org.keycloak.client.registration.cli.util.HttpUtil.doGet;
|
import static org.keycloak.client.cli.util.IoUtil.printOut;
|
||||||
import static org.keycloak.client.registration.cli.util.HttpUtil.urlencode;
|
import static org.keycloak.client.cli.util.IoUtil.readFully;
|
||||||
import static org.keycloak.client.registration.cli.util.IoUtil.warnfErr;
|
import static org.keycloak.client.cli.util.IoUtil.warnfErr;
|
||||||
import static org.keycloak.client.registration.cli.util.IoUtil.printOut;
|
import static org.keycloak.client.cli.util.OsUtil.PROMPT;
|
||||||
import static org.keycloak.client.registration.cli.util.IoUtil.readFully;
|
import static org.keycloak.client.registration.cli.KcRegMain.CMD;
|
||||||
import static org.keycloak.client.registration.cli.util.OsUtil.CMD;
|
|
||||||
import static org.keycloak.client.registration.cli.util.OsUtil.PROMPT;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
|
@ -75,20 +74,20 @@ public class GetCmd extends AbstractAuthOptionsCmd {
|
||||||
|
|
||||||
|
|
||||||
if (clientId.startsWith("-")) {
|
if (clientId.startsWith("-")) {
|
||||||
warnfErr(ParseUtil.CLIENT_OPTION_WARN, clientId);
|
warnfErr(CmdStdinContext.CLIENT_OPTION_WARN, clientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigData config = loadConfig();
|
ConfigData config = loadConfig();
|
||||||
config = copyWithServerInfo(config);
|
config = copyWithServerInfo(config);
|
||||||
|
|
||||||
if (token == null) {
|
if (externalToken == null) {
|
||||||
// if registration access token is not set via -t, try use the one from configuration
|
// if registration access token is not set via -t, try use the one from configuration
|
||||||
token = getRegistrationToken(config.sessionRealmConfigData(), clientId);
|
externalToken = getRegistrationToken(config.sessionRealmConfigData(), clientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
setupTruststore(config);
|
setupTruststore(config);
|
||||||
|
|
||||||
String auth = token;
|
String auth = externalToken;
|
||||||
if (auth == null) {
|
if (auth == null) {
|
||||||
config = ensureAuthInfo(config);
|
config = ensureAuthInfo(config);
|
||||||
config = copyWithServerInfo(config);
|
config = copyWithServerInfo(config);
|
||||||
|
@ -156,7 +155,7 @@ public class GetCmd extends AbstractAuthOptionsCmd {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean nothingToDo() {
|
protected boolean nothingToDo() {
|
||||||
return noOptions() && endpoint == null && clientId == null;
|
return super.nothingToDo() && endpoint == null && clientId == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -176,7 +175,7 @@ public class GetCmd extends AbstractAuthOptionsCmd {
|
||||||
out.println();
|
out.println();
|
||||||
out.println(" Global options:");
|
out.println(" Global options:");
|
||||||
out.println(" -x Print full stack trace when exiting with error");
|
out.println(" -x Print full stack trace when exiting with error");
|
||||||
out.println(" --config Path to the config file (" + DEFAULT_CONFIG_FILE_STRING + " by default)");
|
out.println(" --config Path to the config file (" + KcRegMain.DEFAULT_CONFIG_FILE_STRING + " by default)");
|
||||||
out.println(" --no-config Don't use config file - no authentication info is loaded or saved");
|
out.println(" --no-config Don't use config file - no authentication info is loaded or saved");
|
||||||
out.println(" --truststore PATH Path to a truststore containing trusted certificates");
|
out.println(" --truststore PATH Path to a truststore containing trusted certificates");
|
||||||
out.println(" --trustpass PASSWORD Truststore password (prompted for if not specified and --truststore is used)");
|
out.println(" --trustpass PASSWORD Truststore password (prompted for if not specified and --truststore is used)");
|
||||||
|
|
|
@ -5,7 +5,7 @@ import java.util.List;
|
||||||
import picocli.CommandLine.Command;
|
import picocli.CommandLine.Command;
|
||||||
import picocli.CommandLine.Parameters;
|
import picocli.CommandLine.Parameters;
|
||||||
|
|
||||||
import static org.keycloak.client.registration.cli.util.IoUtil.printOut;
|
import static org.keycloak.client.cli.util.IoUtil.printOut;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
|
@ -27,7 +27,7 @@ public class HelpCmd implements Runnable {
|
||||||
if (args.size() > 1) {
|
if (args.size() > 1) {
|
||||||
switch (args.get(1)) {
|
switch (args.get(1)) {
|
||||||
case "credentials": {
|
case "credentials": {
|
||||||
printOut(ConfigCredentialsCmd.usage());
|
printOut(new ConfigCredentialsCmd().help());
|
||||||
break outer;
|
break outer;
|
||||||
}
|
}
|
||||||
case "initial-token": {
|
case "initial-token": {
|
||||||
|
@ -39,7 +39,7 @@ public class HelpCmd implements Runnable {
|
||||||
break outer;
|
break outer;
|
||||||
}
|
}
|
||||||
case "truststore": {
|
case "truststore": {
|
||||||
printOut(ConfigTruststoreCmd.usage());
|
printOut(new ConfigTruststoreCmd().help());
|
||||||
break outer;
|
break outer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,15 +16,16 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.client.registration.cli.commands;
|
package org.keycloak.client.registration.cli.commands;
|
||||||
|
|
||||||
|
import org.keycloak.client.cli.common.BaseGlobalOptionsCmd;
|
||||||
|
import org.keycloak.client.registration.cli.KcRegMain;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
|
|
||||||
import picocli.CommandLine.Command;
|
import picocli.CommandLine.Command;
|
||||||
|
|
||||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_STRING;
|
import static org.keycloak.client.cli.util.OsUtil.PROMPT;
|
||||||
import static org.keycloak.client.registration.cli.util.IoUtil.printOut;
|
import static org.keycloak.client.registration.cli.KcRegMain.CMD;
|
||||||
import static org.keycloak.client.registration.cli.util.OsUtil.CMD;
|
|
||||||
import static org.keycloak.client.registration.cli.util.OsUtil.PROMPT;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
|
@ -49,13 +50,18 @@ subcommands = {
|
||||||
AttrsCmd.class,
|
AttrsCmd.class,
|
||||||
UpdateTokenCmd.class
|
UpdateTokenCmd.class
|
||||||
})
|
})
|
||||||
public class KcRegCmd extends AbstractGlobalOptionsCmd {
|
public class KcRegCmd extends BaseGlobalOptionsCmd {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean nothingToDo() {
|
protected boolean nothingToDo() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String help() {
|
||||||
|
return usage();
|
||||||
|
}
|
||||||
|
|
||||||
public static String usage() {
|
public static String usage() {
|
||||||
StringWriter sb = new StringWriter();
|
StringWriter sb = new StringWriter();
|
||||||
PrintWriter out = new PrintWriter(sb);
|
PrintWriter out = new PrintWriter(sb);
|
||||||
|
@ -93,7 +99,7 @@ public class KcRegCmd extends AbstractGlobalOptionsCmd {
|
||||||
out.println("Global options:");
|
out.println("Global options:");
|
||||||
out.println(" -x Print full stack trace when exiting with error");
|
out.println(" -x Print full stack trace when exiting with error");
|
||||||
out.println(" --help Print help for specific command");
|
out.println(" --help Print help for specific command");
|
||||||
out.println(" --config Path to the config file (" + DEFAULT_CONFIG_FILE_STRING + " by default)");
|
out.println(" --config Path to the config file (" + KcRegMain.DEFAULT_CONFIG_FILE_STRING + " by default)");
|
||||||
out.println(" --no-config Don't use config file - no authentication info is loaded or saved");
|
out.println(" --no-config Don't use config file - no authentication info is loaded or saved");
|
||||||
out.println();
|
out.println();
|
||||||
out.println("Commands: ");
|
out.println("Commands: ");
|
||||||
|
|
|
@ -24,13 +24,13 @@ import picocli.CommandLine.Command;
|
||||||
import picocli.CommandLine.Option;
|
import picocli.CommandLine.Option;
|
||||||
import picocli.CommandLine.Parameters;
|
import picocli.CommandLine.Parameters;
|
||||||
|
|
||||||
|
import org.keycloak.client.registration.cli.CmdStdinContext;
|
||||||
|
import org.keycloak.client.registration.cli.EndpointType;
|
||||||
import org.keycloak.client.registration.cli.EndpointTypeConverter;
|
import org.keycloak.client.registration.cli.EndpointTypeConverter;
|
||||||
import org.keycloak.client.registration.cli.common.AttributeOperation;
|
import org.keycloak.client.registration.cli.ReflectionUtil;
|
||||||
import org.keycloak.client.registration.cli.config.ConfigData;
|
import org.keycloak.client.registration.cli.KcRegMain;
|
||||||
import org.keycloak.client.registration.cli.common.CmdStdinContext;
|
import org.keycloak.client.cli.common.AttributeOperation;
|
||||||
import org.keycloak.client.registration.cli.common.EndpointType;
|
import org.keycloak.client.cli.config.ConfigData;
|
||||||
import org.keycloak.client.registration.cli.util.ParseUtil;
|
|
||||||
import org.keycloak.client.registration.cli.util.ReflectionUtil;
|
|
||||||
import org.keycloak.representations.idm.ClientRepresentation;
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
import org.keycloak.representations.oidc.OIDCClientRepresentation;
|
import org.keycloak.representations.oidc.OIDCClientRepresentation;
|
||||||
import org.keycloak.util.JsonSerialization;
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
@ -43,29 +43,25 @@ import java.util.ArrayList;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.keycloak.client.registration.cli.common.AttributeOperation.Type.DELETE;
|
import static org.keycloak.client.cli.common.AttributeOperation.Type.DELETE;
|
||||||
import static org.keycloak.client.registration.cli.common.AttributeOperation.Type.SET;
|
import static org.keycloak.client.cli.common.AttributeOperation.Type.SET;
|
||||||
import static org.keycloak.client.registration.cli.util.AuthUtil.ensureToken;
|
import static org.keycloak.client.cli.util.ConfigUtil.credentialsAvailable;
|
||||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_STRING;
|
import static org.keycloak.client.cli.util.ConfigUtil.getRegistrationToken;
|
||||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.credentialsAvailable;
|
import static org.keycloak.client.cli.util.ConfigUtil.loadConfig;
|
||||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.getRegistrationToken;
|
import static org.keycloak.client.cli.util.ConfigUtil.saveMergeConfig;
|
||||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.loadConfig;
|
import static org.keycloak.client.cli.util.ConfigUtil.setRegistrationToken;
|
||||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.saveMergeConfig;
|
import static org.keycloak.client.cli.util.HttpUtil.APPLICATION_JSON;
|
||||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.setRegistrationToken;
|
import static org.keycloak.client.cli.util.HttpUtil.doGet;
|
||||||
import static org.keycloak.client.registration.cli.common.EndpointType.DEFAULT;
|
import static org.keycloak.client.cli.util.HttpUtil.doPut;
|
||||||
import static org.keycloak.client.registration.cli.common.EndpointType.OIDC;
|
import static org.keycloak.client.cli.util.HttpUtil.urlencode;
|
||||||
import static org.keycloak.client.registration.cli.util.HttpUtil.doGet;
|
import static org.keycloak.client.cli.util.IoUtil.printOut;
|
||||||
import static org.keycloak.client.registration.cli.util.HttpUtil.doPut;
|
import static org.keycloak.client.cli.util.IoUtil.readFully;
|
||||||
import static org.keycloak.client.registration.cli.util.HttpUtil.urlencode;
|
import static org.keycloak.client.cli.util.IoUtil.warnfErr;
|
||||||
import static org.keycloak.client.registration.cli.util.IoUtil.printOut;
|
import static org.keycloak.client.cli.util.OsUtil.PROMPT;
|
||||||
import static org.keycloak.client.registration.cli.util.IoUtil.warnfErr;
|
import static org.keycloak.client.cli.util.ParseUtil.parseKeyVal;
|
||||||
import static org.keycloak.client.registration.cli.util.IoUtil.readFully;
|
import static org.keycloak.client.registration.cli.EndpointType.DEFAULT;
|
||||||
import static org.keycloak.client.registration.cli.util.HttpUtil.APPLICATION_JSON;
|
import static org.keycloak.client.registration.cli.EndpointType.OIDC;
|
||||||
import static org.keycloak.client.registration.cli.util.OsUtil.CMD;
|
import static org.keycloak.client.registration.cli.KcRegMain.CMD;
|
||||||
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.ParseUtil.parseKeyVal;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
|
@ -123,7 +119,7 @@ public class UpdateCmd extends AbstractAuthOptionsCmd {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clientId.startsWith("-")) {
|
if (clientId.startsWith("-")) {
|
||||||
warnfErr(ParseUtil.CLIENT_OPTION_WARN, clientId);
|
warnfErr(CmdStdinContext.CLIENT_OPTION_WARN, clientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file == null && attrs.size() == 0) {
|
if (file == null && attrs.size() == 0) {
|
||||||
|
@ -165,7 +161,7 @@ public class UpdateCmd extends AbstractAuthOptionsCmd {
|
||||||
|
|
||||||
CmdStdinContext ctx = new CmdStdinContext();
|
CmdStdinContext ctx = new CmdStdinContext();
|
||||||
if (file != null) {
|
if (file != null) {
|
||||||
ctx = parseFileOrStdin(file, regType);
|
ctx = CmdStdinContext.parseFileOrStdin(file, regType);
|
||||||
regType = ctx.getEndpointType();
|
regType = ctx.getEndpointType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,7 +181,7 @@ public class UpdateCmd extends AbstractAuthOptionsCmd {
|
||||||
final String server = config.getServerUrl();
|
final String server = config.getServerUrl();
|
||||||
final String realm = config.getRealm();
|
final String realm = config.getRealm();
|
||||||
|
|
||||||
if (token == null) {
|
if (externalToken == null) {
|
||||||
// if registration access token is not set via --token, see if it's in the body of any input file
|
// 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
|
// but first see if it's overridden by --set, or maybe deliberately muted via -d registrationAccessToken
|
||||||
boolean processed = false;
|
boolean processed = false;
|
||||||
|
@ -193,25 +189,25 @@ public class UpdateCmd extends AbstractAuthOptionsCmd {
|
||||||
if ("registrationAccessToken".equals(op.getKey().toString())) {
|
if ("registrationAccessToken".equals(op.getKey().toString())) {
|
||||||
processed = true;
|
processed = true;
|
||||||
if (op.getType() == AttributeOperation.Type.SET) {
|
if (op.getType() == AttributeOperation.Type.SET) {
|
||||||
token = op.getValue();
|
externalToken = op.getValue();
|
||||||
}
|
}
|
||||||
// otherwise it's delete - meaning it should stay null
|
// otherwise it's delete - meaning it should stay null
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!processed) {
|
if (!processed) {
|
||||||
token = ctx.getRegistrationAccessToken();
|
externalToken = ctx.getRegistrationAccessToken();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (token == null) {
|
if (externalToken == null) {
|
||||||
// if registration access token is not set, try use the one from configuration
|
// if registration access token is not set, try use the one from configuration
|
||||||
token = getRegistrationToken(config.sessionRealmConfigData(), clientId);
|
externalToken = getRegistrationToken(config.sessionRealmConfigData(), clientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
setupTruststore(config);
|
setupTruststore(config);
|
||||||
|
|
||||||
String auth = token;
|
String auth = externalToken;
|
||||||
if (auth == null) {
|
if (auth == null) {
|
||||||
config = ensureAuthInfo(config);
|
config = ensureAuthInfo(config);
|
||||||
config = copyWithServerInfo(config);
|
config = copyWithServerInfo(config);
|
||||||
|
@ -236,10 +232,10 @@ public class UpdateCmd extends AbstractAuthOptionsCmd {
|
||||||
|
|
||||||
if (regType == DEFAULT) {
|
if (regType == DEFAULT) {
|
||||||
ctxremote.setClient(JsonSerialization.readValue(json, ClientRepresentation.class));
|
ctxremote.setClient(JsonSerialization.readValue(json, ClientRepresentation.class));
|
||||||
token = ctxremote.getClient().getRegistrationAccessToken();
|
externalToken = ctxremote.getClient().getRegistrationAccessToken();
|
||||||
} else if (regType == OIDC) {
|
} else if (regType == OIDC) {
|
||||||
ctxremote.setOidcClient(JsonSerialization.readValue(json, OIDCClientRepresentation.class));
|
ctxremote.setOidcClient(JsonSerialization.readValue(json, OIDCClientRepresentation.class));
|
||||||
token = ctxremote.getOidcClient().getRegistrationAccessToken();
|
externalToken = ctxremote.getOidcClient().getRegistrationAccessToken();
|
||||||
}
|
}
|
||||||
} catch (JsonParseException e) {
|
} catch (JsonParseException e) {
|
||||||
throw new RuntimeException("Not a valid JSON document. " + e.getMessage(), e);
|
throw new RuntimeException("Not a valid JSON document. " + e.getMessage(), e);
|
||||||
|
@ -249,11 +245,11 @@ public class UpdateCmd extends AbstractAuthOptionsCmd {
|
||||||
|
|
||||||
// we have to use registration access token retrieved from previous operation
|
// we have to use registration access token retrieved from previous operation
|
||||||
// that ensures optimistic locking semantics
|
// that ensures optimistic locking semantics
|
||||||
if (token != null) {
|
if (externalToken != null) {
|
||||||
// we use auth with doPost later
|
// we use auth with doPost later
|
||||||
auth = "Bearer " + token;
|
auth = "Bearer " + externalToken;
|
||||||
|
|
||||||
String newToken = token;
|
String newToken = externalToken;
|
||||||
String clientToUpdate = clientId;
|
String clientToUpdate = clientId;
|
||||||
saveMergeConfig(cfg -> {
|
saveMergeConfig(cfg -> {
|
||||||
setRegistrationToken(cfg.ensureRealmConfigData(server, realm), clientToUpdate, newToken);
|
setRegistrationToken(cfg.ensureRealmConfigData(server, realm), clientToUpdate, newToken);
|
||||||
|
@ -270,7 +266,7 @@ public class UpdateCmd extends AbstractAuthOptionsCmd {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attrs.size() > 0) {
|
if (attrs.size() > 0) {
|
||||||
ctx = mergeAttributes(ctx, attrs);
|
ctx = CmdStdinContext.mergeAttributes(ctx, attrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// now update
|
// now update
|
||||||
|
@ -280,14 +276,14 @@ public class UpdateCmd extends AbstractAuthOptionsCmd {
|
||||||
if (regType == DEFAULT) {
|
if (regType == DEFAULT) {
|
||||||
ClientRepresentation clirep = JsonSerialization.readValue(response, ClientRepresentation.class);
|
ClientRepresentation clirep = JsonSerialization.readValue(response, ClientRepresentation.class);
|
||||||
outputResult(clirep);
|
outputResult(clirep);
|
||||||
token = clirep.getRegistrationAccessToken();
|
externalToken = clirep.getRegistrationAccessToken();
|
||||||
} else if (regType == OIDC) {
|
} else if (regType == OIDC) {
|
||||||
OIDCClientRepresentation clirep = JsonSerialization.readValue(response, OIDCClientRepresentation.class);
|
OIDCClientRepresentation clirep = JsonSerialization.readValue(response, OIDCClientRepresentation.class);
|
||||||
outputResult(clirep);
|
outputResult(clirep);
|
||||||
token = clirep.getRegistrationAccessToken();
|
externalToken = clirep.getRegistrationAccessToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
String newToken = token;
|
String newToken = externalToken;
|
||||||
String clientToUpdate = clientId;
|
String clientToUpdate = clientId;
|
||||||
saveMergeConfig(cfg -> {
|
saveMergeConfig(cfg -> {
|
||||||
setRegistrationToken(cfg.ensureRealmConfigData(server, realm), clientToUpdate, newToken);
|
setRegistrationToken(cfg.ensureRealmConfigData(server, realm), clientToUpdate, newToken);
|
||||||
|
@ -310,7 +306,7 @@ public class UpdateCmd extends AbstractAuthOptionsCmd {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean nothingToDo() {
|
protected boolean nothingToDo() {
|
||||||
return noOptions() && regType == null && file == null && rawAttributeOperations.isEmpty() && clientId == null;
|
return super.nothingToDo() && regType == null && file == null && rawAttributeOperations.isEmpty() && clientId == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -332,7 +328,7 @@ public class UpdateCmd extends AbstractAuthOptionsCmd {
|
||||||
out.println();
|
out.println();
|
||||||
out.println(" Global options:");
|
out.println(" Global options:");
|
||||||
out.println(" -x Print full stack trace when exiting with error");
|
out.println(" -x Print full stack trace when exiting with error");
|
||||||
out.println(" --config Path to the config file (" + DEFAULT_CONFIG_FILE_STRING + " by default)");
|
out.println(" --config Path to the config file (" + KcRegMain.DEFAULT_CONFIG_FILE_STRING + " by default)");
|
||||||
out.println(" --no-config Don't use config file - no authentication info is loaded or saved");
|
out.println(" --no-config Don't use config file - no authentication info is loaded or saved");
|
||||||
out.println(" --truststore PATH Path to a truststore containing trusted certificates");
|
out.println(" --truststore PATH Path to a truststore containing trusted certificates");
|
||||||
out.println(" --trustpass PASSWORD Truststore password (prompted for if not specified and --truststore is used)");
|
out.println(" --trustpass PASSWORD Truststore password (prompted for if not specified and --truststore is used)");
|
||||||
|
|
|
@ -22,8 +22,9 @@ import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
import picocli.CommandLine.Command;
|
import picocli.CommandLine.Command;
|
||||||
import picocli.CommandLine.Parameters;
|
import picocli.CommandLine.Parameters;
|
||||||
|
|
||||||
import org.keycloak.client.registration.cli.config.ConfigData;
|
import org.keycloak.client.registration.cli.KcRegMain;
|
||||||
import org.keycloak.client.registration.cli.util.ParseUtil;
|
import org.keycloak.client.cli.config.ConfigData;
|
||||||
|
import org.keycloak.client.registration.cli.CmdStdinContext;
|
||||||
import org.keycloak.representations.idm.ClientRepresentation;
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
import org.keycloak.util.JsonSerialization;
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
|
||||||
|
@ -33,18 +34,16 @@ import java.io.PrintWriter;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.keycloak.client.registration.cli.util.AuthUtil.ensureToken;
|
import static org.keycloak.client.cli.util.ConfigUtil.loadConfig;
|
||||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_STRING;
|
import static org.keycloak.client.cli.util.ConfigUtil.saveMergeConfig;
|
||||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.loadConfig;
|
import static org.keycloak.client.cli.util.ConfigUtil.setRegistrationToken;
|
||||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.saveMergeConfig;
|
import static org.keycloak.client.cli.util.HttpUtil.APPLICATION_JSON;
|
||||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.setRegistrationToken;
|
import static org.keycloak.client.cli.util.HttpUtil.doGet;
|
||||||
import static org.keycloak.client.registration.cli.util.HttpUtil.APPLICATION_JSON;
|
import static org.keycloak.client.cli.util.HttpUtil.doPost;
|
||||||
import static org.keycloak.client.registration.cli.util.HttpUtil.doGet;
|
import static org.keycloak.client.cli.util.IoUtil.printOut;
|
||||||
import static org.keycloak.client.registration.cli.util.HttpUtil.doPost;
|
import static org.keycloak.client.cli.util.IoUtil.warnfOut;
|
||||||
import static org.keycloak.client.registration.cli.util.IoUtil.printOut;
|
import static org.keycloak.client.cli.util.OsUtil.PROMPT;
|
||||||
import static org.keycloak.client.registration.cli.util.IoUtil.warnfOut;
|
import static org.keycloak.client.registration.cli.KcRegMain.CMD;
|
||||||
import static org.keycloak.client.registration.cli.util.OsUtil.CMD;
|
|
||||||
import static org.keycloak.client.registration.cli.util.OsUtil.PROMPT;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||||
|
@ -62,7 +61,7 @@ public class UpdateTokenCmd extends AbstractAuthOptionsCmd {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clientId.startsWith("-")) {
|
if (clientId.startsWith("-")) {
|
||||||
warnfOut(ParseUtil.CLIENT_OPTION_WARN, clientId);
|
warnfOut(CmdStdinContext.CLIENT_OPTION_WARN, clientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigData config = loadConfig();
|
ConfigData config = loadConfig();
|
||||||
|
@ -116,7 +115,7 @@ public class UpdateTokenCmd extends AbstractAuthOptionsCmd {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean nothingToDo() {
|
protected boolean nothingToDo() {
|
||||||
return noOptions() && clientId == null;
|
return super.nothingToDo() && clientId == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -136,7 +135,7 @@ public class UpdateTokenCmd extends AbstractAuthOptionsCmd {
|
||||||
out.println();
|
out.println();
|
||||||
out.println(" Global options:");
|
out.println(" Global options:");
|
||||||
out.println(" -x Print full stack trace when exiting with error");
|
out.println(" -x Print full stack trace when exiting with error");
|
||||||
out.println(" --config Path to the config file (" + DEFAULT_CONFIG_FILE_STRING + " by default)");
|
out.println(" --config Path to the config file (" + KcRegMain.DEFAULT_CONFIG_FILE_STRING + " by default)");
|
||||||
out.println(" --no-config Don't use config file - no authentication info is loaded or saved");
|
out.println(" --no-config Don't use config file - no authentication info is loaded or saved");
|
||||||
out.println(" --truststore PATH Path to a truststore containing trusted certificates");
|
out.println(" --truststore PATH Path to a truststore containing trusted certificates");
|
||||||
out.println(" --trustpass PASSWORD Truststore password (prompted for if not specified and --truststore is used)");
|
out.println(" --trustpass PASSWORD Truststore password (prompted for if not specified and --truststore is used)");
|
||||||
|
|
|
@ -1,154 +0,0 @@
|
||||||
package org.keycloak.client.registration.cli.common;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
|
||||||
*/
|
|
||||||
public class AttributeKey {
|
|
||||||
|
|
||||||
private static final int START = 0;
|
|
||||||
private static final int QUOTED = 1;
|
|
||||||
private static final int UNQUOTED = 2;
|
|
||||||
private static final int END = 3;
|
|
||||||
|
|
||||||
private List<Component> components;
|
|
||||||
private boolean append;
|
|
||||||
|
|
||||||
public AttributeKey() {
|
|
||||||
components = Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public AttributeKey(String key) {
|
|
||||||
if (key.endsWith("+")) {
|
|
||||||
append = true;
|
|
||||||
key = key.substring(0, key.length() - 1);
|
|
||||||
}
|
|
||||||
components = parse(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
static List<Component> parse(String key) {
|
|
||||||
|
|
||||||
if (key == null || "".equals(key)) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Component> cs = new LinkedList<>();
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
int state = START;
|
|
||||||
|
|
||||||
char[] buf = key.toCharArray();
|
|
||||||
|
|
||||||
for (int pos = 0; pos < buf.length; pos++) {
|
|
||||||
char c = buf[pos];
|
|
||||||
|
|
||||||
if (state == START) {
|
|
||||||
if ('\"' == c) {
|
|
||||||
state = QUOTED;
|
|
||||||
} else if ('.' == c) {
|
|
||||||
throw new RuntimeException("Invalid attribute key: " + key + " (at position " + (pos + 1) + ")");
|
|
||||||
} else {
|
|
||||||
state = UNQUOTED;
|
|
||||||
sb.append(c);
|
|
||||||
}
|
|
||||||
} else if (state == QUOTED) {
|
|
||||||
if ('\"' == c) {
|
|
||||||
state = END;
|
|
||||||
} else {
|
|
||||||
sb.append(c);
|
|
||||||
}
|
|
||||||
} else if (state == UNQUOTED || state == END) {
|
|
||||||
if ('.' == c) {
|
|
||||||
state = START;
|
|
||||||
cs.add(new Component(sb.toString()));
|
|
||||||
sb.setLength(0);
|
|
||||||
} else if (state == END || '\"' == c) {
|
|
||||||
throw new RuntimeException("Invalid attribute key: " + key + " (at position " + (pos + 1) + ")");
|
|
||||||
} else {
|
|
||||||
sb.append(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean ok = false;
|
|
||||||
if (sb.length() > 0) {
|
|
||||||
if (state == UNQUOTED || state == END) {
|
|
||||||
cs.add(new Component(sb.toString()));
|
|
||||||
ok = true;
|
|
||||||
}
|
|
||||||
} else if (state == END) {
|
|
||||||
ok = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ok) {
|
|
||||||
throw new RuntimeException("Invalid attribute key: " + key + " (at position " + (buf.length) + ")");
|
|
||||||
}
|
|
||||||
|
|
||||||
return Collections.unmodifiableList(cs);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Component> getComponents() {
|
|
||||||
return components;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isAppend() {
|
|
||||||
return append;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
for (Component c: components) {
|
|
||||||
if (sb.length() > 0) {
|
|
||||||
sb.append(".");
|
|
||||||
}
|
|
||||||
sb.append(c.toString());
|
|
||||||
}
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static class Component {
|
|
||||||
|
|
||||||
private int index = -1;
|
|
||||||
private String name;
|
|
||||||
|
|
||||||
Component(String name) {
|
|
||||||
if (name.endsWith("]")) {
|
|
||||||
int pos = name.lastIndexOf("[", name.length() - 1);
|
|
||||||
if (pos == -1) {
|
|
||||||
throw new RuntimeException("Invalid attribute key: " + name + " (']' not allowed here)");
|
|
||||||
}
|
|
||||||
String idx = name.substring(pos + 1, name.length() - 1);
|
|
||||||
try {
|
|
||||||
index = Integer.parseInt(idx);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException("Invalid attribute key: " + name + " (Invalid array index: '[" + idx + "]')");
|
|
||||||
}
|
|
||||||
this.name = name.substring(0, pos);
|
|
||||||
} else {
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isArray() {
|
|
||||||
return index >= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getIndex() {
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return name + (index != -1 ? "[" + index + "]" : "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
package org.keycloak.client.registration.cli.common;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
|
||||||
*/
|
|
||||||
public class AttributeOperation {
|
|
||||||
|
|
||||||
private Type type;
|
|
||||||
private AttributeKey key;
|
|
||||||
private String value;
|
|
||||||
|
|
||||||
public AttributeOperation(Type type, String key) {
|
|
||||||
this(type, key, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public AttributeOperation(Type type, String key, String value) {
|
|
||||||
if (type == Type.DELETE && value != null) {
|
|
||||||
throw new IllegalArgumentException("When type is DELETE, value has to be null");
|
|
||||||
}
|
|
||||||
this.type = type;
|
|
||||||
this.key = new AttributeKey(key);
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Type getType() {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AttributeKey getKey() {
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public enum Type {
|
|
||||||
SET,
|
|
||||||
DELETE
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,75 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
|
||||||
* and other contributors as indicated by the @author tags.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.keycloak.client.registration.cli.common;
|
|
||||||
|
|
||||||
import org.keycloak.representations.idm.ClientRepresentation;
|
|
||||||
import org.keycloak.representations.oidc.OIDCClientRepresentation;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
|
||||||
*/
|
|
||||||
public class CmdStdinContext {
|
|
||||||
|
|
||||||
private EndpointType regType;
|
|
||||||
private ClientRepresentation client;
|
|
||||||
private OIDCClientRepresentation oidcClient;
|
|
||||||
private String content;
|
|
||||||
|
|
||||||
public CmdStdinContext() {}
|
|
||||||
|
|
||||||
public EndpointType getEndpointType() {
|
|
||||||
return regType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEndpointType(EndpointType regType) {
|
|
||||||
this.regType = regType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ClientRepresentation getClient() {
|
|
||||||
return client;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setClient(ClientRepresentation client) {
|
|
||||||
this.client = client;
|
|
||||||
}
|
|
||||||
|
|
||||||
public OIDCClientRepresentation getOidcClient() {
|
|
||||||
return oidcClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOidcClient(OIDCClientRepresentation oidcClient) {
|
|
||||||
this.oidcClient = oidcClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getContent() {
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setContent(String content) {
|
|
||||||
this.content = content;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getRegistrationAccessToken() {
|
|
||||||
if (client != null) {
|
|
||||||
return client.getRegistrationAccessToken();
|
|
||||||
} else if (oidcClient != null) {
|
|
||||||
return oidcClient.getRegistrationAccessToken();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,113 +0,0 @@
|
||||||
package org.keycloak.client.registration.cli.common;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An iterator wrapping command line
|
|
||||||
*
|
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
|
||||||
*/
|
|
||||||
public class ParsingContext {
|
|
||||||
|
|
||||||
private int offset;
|
|
||||||
private int pos = -1;
|
|
||||||
private String [] args;
|
|
||||||
|
|
||||||
public ParsingContext(String [] args) {
|
|
||||||
this(args, 0, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ParsingContext(String [] args, int offset) {
|
|
||||||
this(args, offset, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ParsingContext(String [] args, int offset, int pos) {
|
|
||||||
this.args = args.clone();
|
|
||||||
this.offset = offset;
|
|
||||||
this.pos = pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasNext() {
|
|
||||||
return pos < args.length-1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public boolean hasNext(int count) {
|
|
||||||
return pos < args.length - count;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasPrevious() {
|
|
||||||
return pos > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get next argument
|
|
||||||
*
|
|
||||||
* @return Next argument or null if beyond the end of arguments
|
|
||||||
*/
|
|
||||||
public String next() {
|
|
||||||
if (hasNext()) {
|
|
||||||
return args[++pos];
|
|
||||||
} else {
|
|
||||||
pos = args.length;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check that a next argument is available
|
|
||||||
*
|
|
||||||
* @return Next argument or RuntimeException if next argument is not available
|
|
||||||
*/
|
|
||||||
public String nextRequired() {
|
|
||||||
if (!hasNext()) {
|
|
||||||
throw new RuntimeException("Option " + current() + " requires a value");
|
|
||||||
}
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get next n-th argument
|
|
||||||
*
|
|
||||||
* @return Next n-th argument or null if beyond the end of arguments
|
|
||||||
*/
|
|
||||||
public String next(int n) {
|
|
||||||
if (hasNext(n)) {
|
|
||||||
pos += n;
|
|
||||||
return args[pos];
|
|
||||||
} else {
|
|
||||||
pos = args.length;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get previous argument
|
|
||||||
*
|
|
||||||
* @return Previous argument or null if previous call was at the beginning of the arguments (pos == 0)
|
|
||||||
*/
|
|
||||||
public String previous() {
|
|
||||||
if (hasPrevious()) {
|
|
||||||
return args[--pos];
|
|
||||||
} else {
|
|
||||||
pos = -1;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get current argument
|
|
||||||
*
|
|
||||||
* @return Current argument or null if current parsing position is beyond end, or before start
|
|
||||||
*/
|
|
||||||
public String current() {
|
|
||||||
if (pos < 0 || pos >= args.length) {
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
return args[pos];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String [] getArgs() {
|
|
||||||
return args;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,177 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
|
||||||
* and other contributors as indicated by the @author tags.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.keycloak.client.registration.cli.config;
|
|
||||||
|
|
||||||
import org.keycloak.util.JsonSerialization;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
|
||||||
*/
|
|
||||||
public class ConfigData {
|
|
||||||
|
|
||||||
private String serverUrl;
|
|
||||||
|
|
||||||
private String realm;
|
|
||||||
|
|
||||||
private String truststore;
|
|
||||||
|
|
||||||
private String trustpass;
|
|
||||||
|
|
||||||
private Map<String, Map<String, RealmConfigData>> endpoints = new HashMap<>();
|
|
||||||
|
|
||||||
|
|
||||||
public String getServerUrl() {
|
|
||||||
return serverUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setServerUrl(String serverUrl) {
|
|
||||||
this.serverUrl = serverUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getRealm() {
|
|
||||||
return realm;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRealm(String realm) {
|
|
||||||
this.realm = realm;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getTruststore() {
|
|
||||||
return truststore;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTruststore(String truststore) {
|
|
||||||
this.truststore = truststore;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getTrustpass() {
|
|
||||||
return trustpass;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTrustpass(String trustpass) {
|
|
||||||
this.trustpass = trustpass;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<String, Map<String, RealmConfigData>> getEndpoints() {
|
|
||||||
return endpoints;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEndpoints(Map<String, Map<String, RealmConfigData>> endpoints) {
|
|
||||||
for (Map.Entry<String, Map<String, RealmConfigData>> entry: endpoints.entrySet()) {
|
|
||||||
String endpoint = entry.getKey();
|
|
||||||
for (Map.Entry<String, RealmConfigData> sub: entry.getValue().entrySet()) {
|
|
||||||
RealmConfigData rdata = sub.getValue();
|
|
||||||
rdata.serverUrl(endpoint);
|
|
||||||
rdata.realm(sub.getKey());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.endpoints = endpoints;
|
|
||||||
}
|
|
||||||
|
|
||||||
public RealmConfigData sessionRealmConfigData() {
|
|
||||||
if (serverUrl == null)
|
|
||||||
throw new RuntimeException("Illegal state - no current endpoint in config data");
|
|
||||||
if (realm == null)
|
|
||||||
throw new RuntimeException("Illegal state - no current realm in config data");
|
|
||||||
return ensureRealmConfigData(serverUrl, realm);
|
|
||||||
}
|
|
||||||
|
|
||||||
public RealmConfigData getRealmConfigData(String endpoint, String realm) {
|
|
||||||
Map<String, RealmConfigData> realmData = endpoints.get(endpoint);
|
|
||||||
if (realmData == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return realmData.get(realm);
|
|
||||||
}
|
|
||||||
|
|
||||||
public RealmConfigData ensureRealmConfigData(String endpoint, String realm) {
|
|
||||||
RealmConfigData result = getRealmConfigData(endpoint, realm);
|
|
||||||
if (result == null) {
|
|
||||||
result = new RealmConfigData();
|
|
||||||
result.serverUrl(endpoint);
|
|
||||||
result.realm(realm);
|
|
||||||
setRealmConfigData(result);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void setRealmConfigData(RealmConfigData data) {
|
|
||||||
Map<String, RealmConfigData> realm = endpoints.get(data.serverUrl());
|
|
||||||
if (realm == null) {
|
|
||||||
realm = new HashMap<>();
|
|
||||||
endpoints.put(data.serverUrl(), realm);
|
|
||||||
}
|
|
||||||
realm.put(data.realm(), data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void merge(ConfigData source) {
|
|
||||||
serverUrl = source.serverUrl;
|
|
||||||
realm = source.realm;
|
|
||||||
truststore = source.truststore;
|
|
||||||
trustpass = source.trustpass;
|
|
||||||
|
|
||||||
RealmConfigData current = getRealmConfigData(serverUrl, realm);
|
|
||||||
RealmConfigData sourceRealm = source.getRealmConfigData(serverUrl, realm);
|
|
||||||
|
|
||||||
if (current == null) {
|
|
||||||
setRealmConfigData(sourceRealm);
|
|
||||||
} else {
|
|
||||||
current.merge(sourceRealm);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConfigData deepcopy() {
|
|
||||||
ConfigData data = new ConfigData();
|
|
||||||
data.serverUrl = serverUrl;
|
|
||||||
data.realm = realm;
|
|
||||||
data.truststore = truststore;
|
|
||||||
data.trustpass = trustpass;
|
|
||||||
data.endpoints = new HashMap<>();
|
|
||||||
|
|
||||||
for (Map.Entry<String, Map<String, RealmConfigData>> item: endpoints.entrySet()) {
|
|
||||||
|
|
||||||
Map<String, RealmConfigData> nuitems = new HashMap<>();
|
|
||||||
Map<String, RealmConfigData> curitems = item.getValue();
|
|
||||||
|
|
||||||
if (curitems != null) {
|
|
||||||
for (Map.Entry<String, RealmConfigData> ditem : curitems.entrySet()) {
|
|
||||||
RealmConfigData nudata = ditem.getValue();
|
|
||||||
if (nudata != null) {
|
|
||||||
nuitems.put(ditem.getKey(), nudata.deepcopy());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
data.endpoints.put(item.getKey(), nuitems);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
try {
|
|
||||||
return JsonSerialization.writeValueAsPrettyString(this);
|
|
||||||
} catch (IOException e) {
|
|
||||||
return super.toString() + " - Error: " + e.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
package org.keycloak.client.registration.cli.config;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
|
||||||
*/
|
|
||||||
public interface ConfigHandler {
|
|
||||||
|
|
||||||
void saveMergeConfig(ConfigUpdateOperation op);
|
|
||||||
|
|
||||||
ConfigData loadConfig();
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
package org.keycloak.client.registration.cli.config;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
|
||||||
*/
|
|
||||||
public interface ConfigUpdateOperation {
|
|
||||||
|
|
||||||
void update(ConfigData data);
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,119 +0,0 @@
|
||||||
package org.keycloak.client.registration.cli.config;
|
|
||||||
|
|
||||||
import org.keycloak.client.registration.cli.util.IoUtil;
|
|
||||||
import org.keycloak.util.JsonSerialization;
|
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.RandomAccessFile;
|
|
||||||
import java.nio.channels.FileChannel;
|
|
||||||
import java.nio.channels.FileLock;
|
|
||||||
import java.nio.channels.OverlappingFileLockException;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
|
|
||||||
import static org.keycloak.client.registration.cli.util.IoUtil.printErr;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
|
||||||
*/
|
|
||||||
public class FileConfigHandler implements ConfigHandler {
|
|
||||||
|
|
||||||
private static final long MAX_SIZE = 10 * 1024 * 1024;
|
|
||||||
private static String configFile;
|
|
||||||
|
|
||||||
public static void setConfigFile(String filename) {
|
|
||||||
configFile = filename;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getConfigFile() {
|
|
||||||
return configFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConfigData loadConfig() {
|
|
||||||
// for now just dumb impl ignoring file locks for read
|
|
||||||
File file = new File(configFile);
|
|
||||||
if (!file.isFile() || file.length() == 0) {
|
|
||||||
return new ConfigData();
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
try (FileInputStream is = new FileInputStream(configFile)) {
|
|
||||||
return JsonSerialization.readValue(is, ConfigData.class);
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException("Failed to load " + configFile, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void ensureFile() {
|
|
||||||
Path path = null;
|
|
||||||
try {
|
|
||||||
path = Paths.get(new File(configFile).getAbsolutePath());
|
|
||||||
IoUtil.ensureFile(path);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException("Failed to create config file: " + path, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void saveMergeConfig(ConfigUpdateOperation op) {
|
|
||||||
try {
|
|
||||||
ensureFile();
|
|
||||||
|
|
||||||
try (RandomAccessFile file = new RandomAccessFile(new File(configFile), "rw")) {
|
|
||||||
FileChannel fileChannel = file.getChannel();
|
|
||||||
|
|
||||||
FileLock fileLock = null;
|
|
||||||
|
|
||||||
// lock file for write
|
|
||||||
int tryCount = 0;
|
|
||||||
do try {
|
|
||||||
fileLock = fileChannel.tryLock();
|
|
||||||
break;
|
|
||||||
} catch (OverlappingFileLockException e) {
|
|
||||||
// sleep a little, and try again
|
|
||||||
try {
|
|
||||||
Thread.sleep(100);
|
|
||||||
continue;
|
|
||||||
} catch (InterruptedException e1) {
|
|
||||||
throw new RuntimeException("Interrupted");
|
|
||||||
}
|
|
||||||
} while (tryCount++ < 10);
|
|
||||||
|
|
||||||
if (fileLock != null) {
|
|
||||||
try {
|
|
||||||
// load config from file
|
|
||||||
ConfigData config = new ConfigData();
|
|
||||||
long size = file.length();
|
|
||||||
if (size > MAX_SIZE) {
|
|
||||||
printErr("Config file " + configFile + " is too big. It will be overwritten.");
|
|
||||||
file.setLength(0);
|
|
||||||
} else if (size > 0){
|
|
||||||
byte[] buf = new byte[(int) size];
|
|
||||||
file.readFully(buf);
|
|
||||||
config = JsonSerialization.readValue(new ByteArrayInputStream(buf), ConfigData.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
// update loaded config
|
|
||||||
op.update(config);
|
|
||||||
|
|
||||||
// save config to file
|
|
||||||
byte [] content = JsonSerialization.writeValueAsPrettyString(config).getBytes("utf-8");
|
|
||||||
file.seek(0);
|
|
||||||
file.write(content);
|
|
||||||
file.setLength(content.length);
|
|
||||||
|
|
||||||
} finally {
|
|
||||||
fileLock.release();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new RuntimeException("Failed to get lock on " + configFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException("Failed to save " + configFile, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
package org.keycloak.client.registration.cli.config;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
|
||||||
*/
|
|
||||||
public class InMemoryConfigHandler implements ConfigHandler {
|
|
||||||
|
|
||||||
private ConfigData cached;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void saveMergeConfig(ConfigUpdateOperation config) {
|
|
||||||
config.update(cached);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ConfigData loadConfig() {
|
|
||||||
return cached;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setConfigData(ConfigData data) {
|
|
||||||
this.cached = data;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
package org.keycloak.client.registration.cli.util;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
|
||||||
*/
|
|
||||||
public class AttributeException extends RuntimeException {
|
|
||||||
|
|
||||||
private final String attrName;
|
|
||||||
|
|
||||||
public AttributeException(String attrName, String message) {
|
|
||||||
super(message);
|
|
||||||
this.attrName = attrName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AttributeException(String attrName, String message, Throwable th) {
|
|
||||||
super(message, th);
|
|
||||||
this.attrName = attrName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getAttributeName() {
|
|
||||||
return attrName;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,214 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
|
||||||
* and other contributors as indicated by the @author tags.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.keycloak.client.registration.cli.util;
|
|
||||||
|
|
||||||
import org.keycloak.client.registration.cli.config.ConfigData;
|
|
||||||
import org.keycloak.client.registration.cli.config.RealmConfigData;
|
|
||||||
import org.keycloak.common.util.KeystoreUtil;
|
|
||||||
import org.keycloak.common.util.Time;
|
|
||||||
import org.keycloak.jose.jws.JWSBuilder;
|
|
||||||
import org.keycloak.representations.AccessTokenResponse;
|
|
||||||
import org.keycloak.representations.JsonWebToken;
|
|
||||||
import org.keycloak.util.BasicAuthHelper;
|
|
||||||
import org.keycloak.util.JsonSerialization;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import java.security.KeyPair;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import static java.lang.System.currentTimeMillis;
|
|
||||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.checkAuthInfo;
|
|
||||||
import static org.keycloak.client.registration.cli.util.ConfigUtil.saveMergeConfig;
|
|
||||||
import static org.keycloak.client.registration.cli.util.HttpUtil.APPLICATION_FORM_URL_ENCODED;
|
|
||||||
import static org.keycloak.client.registration.cli.util.HttpUtil.APPLICATION_JSON;
|
|
||||||
import static org.keycloak.client.registration.cli.util.HttpUtil.doPost;
|
|
||||||
import static org.keycloak.client.registration.cli.util.HttpUtil.urlencode;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
|
||||||
*/
|
|
||||||
public class AuthUtil {
|
|
||||||
|
|
||||||
public static String ensureToken(ConfigData config) {
|
|
||||||
|
|
||||||
checkAuthInfo(config);
|
|
||||||
|
|
||||||
RealmConfigData realmConfig = config.sessionRealmConfigData();
|
|
||||||
|
|
||||||
long now = currentTimeMillis();
|
|
||||||
|
|
||||||
// check expires of access_token against time
|
|
||||||
// if it's less than 5s to expiry, renew it
|
|
||||||
if (realmConfig.getExpiresAt() - now < 5000) {
|
|
||||||
|
|
||||||
// check refresh_token against expiry time
|
|
||||||
// if it's less than 5s to expiry, fail with credentials expired
|
|
||||||
if (realmConfig.getRefreshExpiresAt() != null && realmConfig.getRefreshExpiresAt() - now < 5000) {
|
|
||||||
throw new RuntimeException("Session has expired. Login again with '" + OsUtil.CMD + " config credentials'");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (realmConfig.getSigExpiresAt() != null && realmConfig.getSigExpiresAt() - now < 5000) {
|
|
||||||
throw new RuntimeException("Session has expired. Login again with '" + OsUtil.CMD + " config credentials'");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
String authorization = null;
|
|
||||||
|
|
||||||
StringBuilder body = new StringBuilder();
|
|
||||||
if (realmConfig.getRefreshToken() != null) {
|
|
||||||
body.append("grant_type=refresh_token")
|
|
||||||
.append("&refresh_token=").append(realmConfig.getRefreshToken());
|
|
||||||
} else {
|
|
||||||
body.append("grant_type=").append(realmConfig.getGrantTypeForAuthentication());
|
|
||||||
}
|
|
||||||
|
|
||||||
body.append("&client_id=").append(urlencode(realmConfig.getClientId()));
|
|
||||||
|
|
||||||
if (realmConfig.getSigningToken() != null) {
|
|
||||||
body.append("&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer")
|
|
||||||
.append("&client_assertion=").append(realmConfig.getSigningToken());
|
|
||||||
} else if (realmConfig.getSecret() != null) {
|
|
||||||
authorization = BasicAuthHelper.createHeader(realmConfig.getClientId(), realmConfig.getSecret());
|
|
||||||
}
|
|
||||||
|
|
||||||
InputStream result = doPost(realmConfig.serverUrl() + "/realms/" + realmConfig.realm() + "/protocol/openid-connect/token",
|
|
||||||
APPLICATION_FORM_URL_ENCODED, APPLICATION_JSON, body.toString(), authorization);
|
|
||||||
|
|
||||||
AccessTokenResponse token = JsonSerialization.readValue(result, AccessTokenResponse.class);
|
|
||||||
|
|
||||||
saveMergeConfig(cfg -> {
|
|
||||||
RealmConfigData realmData = cfg.sessionRealmConfigData();
|
|
||||||
realmData.setToken(token.getToken());
|
|
||||||
realmData.setRefreshToken(token.getRefreshToken());
|
|
||||||
realmData.setExpiresAt(currentTimeMillis() + token.getExpiresIn() * 1000);
|
|
||||||
if (token.getRefreshToken() != null) {
|
|
||||||
realmData.setRefreshExpiresAt(currentTimeMillis() + token.getRefreshExpiresIn() * 1000);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return token.getToken();
|
|
||||||
|
|
||||||
} catch (UnsupportedEncodingException e) {
|
|
||||||
throw new RuntimeException("Unexpected error", e);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException("Failed to read Refresh Token response", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return realmConfig.getToken();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static AccessTokenResponse getAuthTokens(String server, String realm, String user, String password, String clientId) {
|
|
||||||
StringBuilder body = new StringBuilder();
|
|
||||||
try {
|
|
||||||
body.append("grant_type=password")
|
|
||||||
.append("&username=").append(urlencode(user))
|
|
||||||
.append("&password=").append(urlencode(password))
|
|
||||||
.append("&client_id=").append(urlencode(clientId));
|
|
||||||
|
|
||||||
InputStream result = doPost(server + "/realms/" + realm + "/protocol/openid-connect/token",
|
|
||||||
APPLICATION_FORM_URL_ENCODED, APPLICATION_JSON, body.toString(), null);
|
|
||||||
return JsonSerialization.readValue(result, AccessTokenResponse.class);
|
|
||||||
|
|
||||||
} catch (UnsupportedEncodingException e) {
|
|
||||||
throw new RuntimeException("Unexpected error: ", e);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException("Error receiving response: ", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static AccessTokenResponse getAuthTokensByJWT(String server, String realm, String user, String password, String clientId, String signedRequestToken) {
|
|
||||||
StringBuilder body = new StringBuilder();
|
|
||||||
try {
|
|
||||||
body.append("client_id=").append(urlencode(clientId))
|
|
||||||
.append("&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer")
|
|
||||||
.append("&client_assertion=").append(signedRequestToken);
|
|
||||||
|
|
||||||
if (user != null) {
|
|
||||||
if (password == null) {
|
|
||||||
throw new RuntimeException("No password specified");
|
|
||||||
}
|
|
||||||
body.append("&grant_type=password")
|
|
||||||
.append("&username=").append(urlencode(user))
|
|
||||||
.append("&password=").append(urlencode(password));
|
|
||||||
} else {
|
|
||||||
body.append("&grant_type=client_credentials");
|
|
||||||
}
|
|
||||||
|
|
||||||
InputStream result = doPost(server + "/realms/" + realm + "/protocol/openid-connect/token",
|
|
||||||
APPLICATION_FORM_URL_ENCODED, APPLICATION_JSON, body.toString(), null);
|
|
||||||
return JsonSerialization.readValue(result, AccessTokenResponse.class);
|
|
||||||
|
|
||||||
} catch (UnsupportedEncodingException e) {
|
|
||||||
throw new RuntimeException("Unexpected error: ", e);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException("Error receiving response: ", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static AccessTokenResponse getAuthTokensBySecret(String server, String realm, String user, String password, String clientId, String secret) {
|
|
||||||
|
|
||||||
StringBuilder body = new StringBuilder();
|
|
||||||
try {
|
|
||||||
if (user != null) {
|
|
||||||
if (password == null) {
|
|
||||||
throw new RuntimeException("No password specified");
|
|
||||||
}
|
|
||||||
|
|
||||||
body.append("client_id=").append(urlencode(clientId))
|
|
||||||
.append("&grant_type=password")
|
|
||||||
.append("&username=").append(urlencode(user))
|
|
||||||
.append("&password=").append(urlencode(password));
|
|
||||||
} else {
|
|
||||||
body.append("grant_type=client_credentials");
|
|
||||||
}
|
|
||||||
|
|
||||||
InputStream result = doPost(server + "/realms/" + realm + "/protocol/openid-connect/token",
|
|
||||||
APPLICATION_FORM_URL_ENCODED, APPLICATION_JSON, body.toString(), BasicAuthHelper.createHeader(clientId, secret));
|
|
||||||
return JsonSerialization.readValue(result, AccessTokenResponse.class);
|
|
||||||
|
|
||||||
} catch (UnsupportedEncodingException e) {
|
|
||||||
throw new RuntimeException("Unexpected error: ", e);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException("Error receiving response: ", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getSignedRequestToken(String keystore, String storePass, String keyPass, String alias, int sigLifetime, String clientId, String realmInfoUrl) {
|
|
||||||
|
|
||||||
KeystoreUtil.KeystoreFormat keystoreType = Enum.valueOf(KeystoreUtil.KeystoreFormat.class, KeystoreUtil.getKeystoreType(null, keystore, KeystoreUtil.KeystoreFormat.JKS.toString()));
|
|
||||||
KeyPair keypair = KeystoreUtil.loadKeyPairFromKeystore(keystore, storePass, keyPass, alias, keystoreType);
|
|
||||||
|
|
||||||
JsonWebToken reqToken = new JsonWebToken();
|
|
||||||
reqToken.id(UUID.randomUUID().toString());
|
|
||||||
reqToken.issuer(clientId);
|
|
||||||
reqToken.subject(clientId);
|
|
||||||
reqToken.audience(realmInfoUrl);
|
|
||||||
|
|
||||||
int now = Time.currentTime();
|
|
||||||
reqToken.issuedAt(now);
|
|
||||||
reqToken.expiration(now + sigLifetime);
|
|
||||||
reqToken.notBefore(now);
|
|
||||||
|
|
||||||
String signedRequestToken = new JWSBuilder()
|
|
||||||
.jsonContent(reqToken)
|
|
||||||
.rsa256(keypair.getPrivate());
|
|
||||||
return signedRequestToken;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,60 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2022 Red Hat, Inc. and/or its affiliates
|
|
||||||
* and other contributors as indicated by the @author tags.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
*
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.keycloak.client.registration.cli.util;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.net.MalformedURLException;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.net.URLClassLoader;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
|
||||||
*/
|
|
||||||
public class ClassLoaderUtil {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Detect if BC FIPS jars are present in the given directory. Return classloader with appropriate JARS based on that
|
|
||||||
*/
|
|
||||||
public static ClassLoader resolveClassLoader(String libDir) {
|
|
||||||
File[] jarsInDir = new File(libDir).listFiles(file -> file.getName().endsWith(".jar"));
|
|
||||||
|
|
||||||
// Detect if BC FIPS jars are present in the "client/lib" directory
|
|
||||||
boolean bcFipsJarPresent = Stream.of(jarsInDir).anyMatch(file -> file.getName().startsWith("bc-fips"));
|
|
||||||
String[] validJarPrefixes = bcFipsJarPresent ? new String[] {"keycloak-crypto-fips1402", "bc-fips", "bctls-fips"} : new String[] {"keycloak-crypto-default", "bcprov-jdk18on"};
|
|
||||||
URL[] usedJars = Stream.of(jarsInDir)
|
|
||||||
.filter(file -> {
|
|
||||||
for (String prefix : validJarPrefixes) {
|
|
||||||
if (file.getName().startsWith(prefix + "-")) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
})
|
|
||||||
.map(file -> {
|
|
||||||
try {
|
|
||||||
return file.toURI().toURL();
|
|
||||||
} catch (MalformedURLException ex) {
|
|
||||||
throw new IllegalStateException("Error when converting file into URL. Please check the files in the directory " + jarsInDir, ex);
|
|
||||||
}
|
|
||||||
}).toArray(URL[]::new);
|
|
||||||
|
|
||||||
return new URLClassLoader(usedJars, ClassLoaderUtil.class.getClassLoader());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,122 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
|
||||||
* and other contributors as indicated by the @author tags.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.keycloak.client.registration.cli.util;
|
|
||||||
|
|
||||||
import org.keycloak.OAuth2Constants;
|
|
||||||
import org.keycloak.client.registration.cli.config.ConfigData;
|
|
||||||
import org.keycloak.client.registration.cli.config.ConfigHandler;
|
|
||||||
import org.keycloak.client.registration.cli.config.ConfigUpdateOperation;
|
|
||||||
import org.keycloak.client.registration.cli.config.InMemoryConfigHandler;
|
|
||||||
import org.keycloak.client.registration.cli.config.RealmConfigData;
|
|
||||||
import org.keycloak.representations.AccessTokenResponse;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
|
||||||
*/
|
|
||||||
public class ConfigUtil {
|
|
||||||
|
|
||||||
public static final String DEFAULT_CONFIG_FILE_STRING = OsUtil.OS_ARCH.isWindows() ? "%HOMEDRIVE%%HOMEPATH%\\.keycloak\\kcreg.config" : "~/.keycloak/kcreg.config";
|
|
||||||
|
|
||||||
public static final String DEFAULT_CONFIG_FILE_PATH = System.getProperty("user.home") + "/.keycloak/kcreg.config";
|
|
||||||
|
|
||||||
private static ConfigHandler handler;
|
|
||||||
|
|
||||||
public static ConfigHandler getHandler() {
|
|
||||||
return handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void setHandler(ConfigHandler handler) {
|
|
||||||
ConfigUtil.handler = handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getRegistrationToken(RealmConfigData data, String clientId) {
|
|
||||||
String token = data.getClients().get(clientId);
|
|
||||||
return token == null || token.length() == 0 ? null : token;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void setRegistrationToken(RealmConfigData data, String clientId, String token) {
|
|
||||||
data.getClients().put(clientId, token == null ? "" : token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void saveTokens(AccessTokenResponse tokens, String endpoint, String realm, String clientId, String signKey, Long sigExpiresAt, String secret,
|
|
||||||
String grantTypeForAuthentication) {
|
|
||||||
handler.saveMergeConfig(config -> {
|
|
||||||
config.setServerUrl(endpoint);
|
|
||||||
config.setRealm(realm);
|
|
||||||
|
|
||||||
RealmConfigData realmConfig = config.ensureRealmConfigData(endpoint, realm);
|
|
||||||
realmConfig.setToken(tokens.getToken());
|
|
||||||
realmConfig.setRefreshToken(tokens.getRefreshToken());
|
|
||||||
realmConfig.setSigningToken(signKey);
|
|
||||||
realmConfig.setSecret(secret);
|
|
||||||
realmConfig.setExpiresAt(System.currentTimeMillis() + tokens.getExpiresIn() * 1000);
|
|
||||||
if (realmConfig.getRefreshToken() != null) {
|
|
||||||
realmConfig.setRefreshExpiresAt(tokens.getRefreshExpiresIn() == 0 ?
|
|
||||||
Long.MAX_VALUE : System.currentTimeMillis() + tokens.getRefreshExpiresIn() * 1000);
|
|
||||||
}
|
|
||||||
realmConfig.setSigExpiresAt(sigExpiresAt);
|
|
||||||
realmConfig.setClientId(clientId);
|
|
||||||
realmConfig.setGrantTypeForAuthentication(grantTypeForAuthentication);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void checkServerInfo(ConfigData config) {
|
|
||||||
if (config.getServerUrl() == null || config.getRealm() == null) {
|
|
||||||
throw new RuntimeException("No server or realm specified. Use --server, --realm, or '" + OsUtil.CMD + " config credentials'.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void checkAuthInfo(ConfigData config) {
|
|
||||||
checkServerInfo(config);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean credentialsAvailable(ConfigData config) {
|
|
||||||
// Just supporting "client_credentials" grant type for the case when refresh token is missing
|
|
||||||
boolean credsAvailable = config.getServerUrl() != null && config.getRealm() != null
|
|
||||||
&& config.sessionRealmConfigData() != null &&
|
|
||||||
(config.sessionRealmConfigData().getRefreshToken() != null || (config.sessionRealmConfigData().getToken() != null && OAuth2Constants.CLIENT_CREDENTIALS.equals(config.sessionRealmConfigData().getGrantTypeForAuthentication())));
|
|
||||||
return credsAvailable;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ConfigData loadConfig() {
|
|
||||||
if (handler == null) {
|
|
||||||
throw new RuntimeException("No ConfigHandler set");
|
|
||||||
}
|
|
||||||
|
|
||||||
return handler.loadConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void saveMergeConfig(ConfigUpdateOperation op) {
|
|
||||||
if (handler == null) {
|
|
||||||
throw new RuntimeException("No ConfigHandler set");
|
|
||||||
}
|
|
||||||
|
|
||||||
handler.saveMergeConfig(op);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void setupInMemoryHandler(ConfigData config) {
|
|
||||||
InMemoryConfigHandler memhandler = null;
|
|
||||||
if (handler instanceof InMemoryConfigHandler) {
|
|
||||||
memhandler = (InMemoryConfigHandler) handler;
|
|
||||||
} else {
|
|
||||||
memhandler = new InMemoryConfigHandler();
|
|
||||||
handler = memhandler;
|
|
||||||
}
|
|
||||||
memhandler.setConfigData(config);
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue