From 0be34d64e71cd80f6a72382b63ec454adaac5dc4 Mon Sep 17 00:00:00 2001 From: Steve Hawkins Date: Fri, 29 Mar 2024 11:57:13 -0400 Subject: [PATCH] task: refactor overlap between cli clients also repackaging to more clearly delineate code roles closes: #28329 Signed-off-by: Steve Hawkins --- .../ParseUtil.java => CmdStdinContext.java} | 66 +++-- .../keycloak/client/admin/cli/KcAdmMain.java | 50 ++-- .../admin/cli/{util => }/ReflectionUtil.java | 11 +- .../cli/commands/AbstractAuthOptionsCmd.java | 233 +-------------- .../commands/AbstractGlobalOptionsCmd.java | 136 --------- .../cli/commands/AbstractRequestCmd.java | 57 ++-- .../admin/cli/commands/AddRolesCmd.java | 15 +- .../client/admin/cli/commands/ConfigCmd.java | 2 +- .../cli/commands/ConfigCredentialsCmd.java | 227 +------------- .../cli/commands/ConfigTruststoreCmd.java | 117 +------- .../client/admin/cli/commands/CreateCmd.java | 11 +- .../client/admin/cli/commands/DeleteCmd.java | 9 +- .../client/admin/cli/commands/GetCmd.java | 9 +- .../admin/cli/commands/GetRolesCmd.java | 17 +- .../cli/commands/GlobalOptionsCmdHelper.java | 59 ++++ .../client/admin/cli/commands/HelpCmd.java | 6 +- .../client/admin/cli/commands/KcAdmCmd.java | 17 +- .../admin/cli/commands/NewObjectCmd.java | 31 +- .../admin/cli/commands/RemoveRolesCmd.java | 15 +- .../admin/cli/commands/SetPasswordCmd.java | 17 +- .../client/admin/cli/commands/UpdateCmd.java | 11 +- .../admin/cli/config/RealmConfigData.java | 184 ------------ .../cli/operations/ClientOperations.java | 4 +- .../admin/cli/operations/GroupOperations.java | 11 +- .../admin/cli/operations/OperationUtils.java | 84 ++++++ .../admin/cli/operations/RoleOperations.java | 19 +- .../admin/cli/operations/UserOperations.java | 17 +- .../{admin => }/cli/common/AttributeKey.java | 2 +- .../cli/common/AttributeOperation.java | 2 +- .../client/cli/common/BaseAuthOptionsCmd.java | 276 ++++++++++++++++++ .../cli/common/BaseConfigCredentialsCmd.java | 248 ++++++++++++++++ .../cli/common/BaseConfigTruststoreCmd.java | 137 +++++++++ .../cli/common/BaseGlobalOptionsCmd.java} | 31 +- .../common/CommandState.java} | 14 +- .../common}/ExecutionExceptionHandler.java | 2 +- .../keycloak/client/cli/common/Globals.java | 65 +++++ .../common}/ShortErrorMessageHandler.java | 2 +- .../{admin => }/cli/config/ConfigData.java | 2 +- .../{admin => }/cli/config/ConfigHandler.java | 2 +- .../cli/config/ConfigUpdateOperation.java | 2 +- .../cli/config/FileConfigHandler.java | 6 +- .../cli/config/InMemoryConfigHandler.java | 2 +- .../client}/cli/config/RealmConfigData.java | 31 +- .../util/AccessibleBufferOutputStream.java | 2 +- .../cli/util/AttributeException.java | 2 +- .../client/{admin => }/cli/util/AuthUtil.java | 26 +- .../{admin => }/cli/util/ClassLoaderUtil.java | 2 +- .../{admin => }/cli/util/ConfigUtil.java | 35 +-- .../{admin => }/cli/util/FilterUtil.java | 8 +- .../client/{admin => }/cli/util/Header.java | 2 +- .../client/{admin => }/cli/util/Headers.java | 2 +- .../{admin => }/cli/util/HeadersBody.java | 4 +- .../cli/util/HeadersBodyStatus.java | 2 +- .../util}/HttpDelete.java | 2 +- .../cli/util/HttpResponseException.java | 2 +- .../client/{admin => }/cli/util/HttpUtil.java | 63 +--- .../client/{admin => }/cli/util/IoUtil.java | 2 +- .../client/{admin => }/cli/util/OsArch.java | 2 +- .../client/{admin => }/cli/util/OsUtil.java | 4 +- .../{admin => }/cli/util/OutputFormat.java | 2 +- .../{admin => }/cli/util/OutputUtil.java | 2 +- .../util/ParseUtil.java} | 31 +- .../{admin => }/cli/util/ReturnFields.java | 2 +- .../cli/{util => }/ReflectionUtilTest.java | 2 +- .../cli/util/MergeAttributesTest.java | 16 +- .../{admin => }/cli/util/OuputUtilTest.java | 4 +- .../cli/util/ReturnFieldsTest.java | 3 +- .../ParseUtil.java => CmdStdinContext.java} | 94 +++--- .../cli/{common => }/EndpointType.java | 16 +- .../cli/EndpointTypeConverter.java | 2 - .../client/registration/cli/Globals.java | 12 - .../client/registration/cli/KcRegMain.java | 52 ++-- .../cli/{util => }/ReflectionUtil.java | 8 +- .../cli/commands/AbstractAuthOptionsCmd.java | 247 +--------------- .../registration/cli/commands/AttrsCmd.java | 21 +- .../registration/cli/commands/ConfigCmd.java | 2 +- .../cli/commands/ConfigCredentialsCmd.java | 235 +-------------- .../cli/commands/ConfigInitialTokenCmd.java | 24 +- .../commands/ConfigRegistrationTokenCmd.java | 18 +- .../cli/commands/ConfigTruststoreCmd.java | 122 +------- .../registration/cli/commands/CreateCmd.java | 70 +++-- .../registration/cli/commands/DeleteCmd.java | 37 ++- .../registration/cli/commands/GetCmd.java | 47 ++- .../registration/cli/commands/HelpCmd.java | 6 +- .../registration/cli/commands/KcRegCmd.java | 20 +- .../registration/cli/commands/UpdateCmd.java | 92 +++--- .../cli/commands/UpdateTokenCmd.java | 33 +-- .../registration/cli/common/AttributeKey.java | 154 ---------- .../cli/common/AttributeOperation.java | 42 --- .../cli/common/CmdStdinContext.java | 75 ----- .../cli/common/ParsingContext.java | 113 ------- .../registration/cli/config/ConfigData.java | 177 ----------- .../cli/config/ConfigHandler.java | 12 - .../cli/config/ConfigUpdateOperation.java | 10 - .../cli/config/FileConfigHandler.java | 119 -------- .../cli/config/InMemoryConfigHandler.java | 24 -- .../cli/util/AttributeException.java | 23 -- .../registration/cli/util/AuthUtil.java | 214 -------------- .../cli/util/ClassLoaderUtil.java | 60 ---- .../registration/cli/util/ConfigUtil.java | 122 -------- .../cli/util/DebugBufferedInputStream.java | 78 ----- .../registration/cli/util/HttpUtil.java | 210 ------------- .../client/registration/cli/util/IoUtil.java | 198 ------------- .../client/registration/cli/util/OsArch.java | 60 ---- .../client/registration/cli/util/OsUtil.java | 52 ---- .../cli/{util => }/ReflectionUtilTest.java | 13 +- .../testsuite/cli/exec/AbstractExec.java | 2 +- .../admin/FineGrainAdminUnitTest.java | 2 +- .../cli/admin/AbstractAdmCliTest.java | 6 +- .../testsuite/cli/admin/KcAdmCreateTest.java | 2 +- .../testsuite/cli/admin/KcAdmSessionTest.java | 2 +- .../testsuite/cli/admin/KcAdmTest.java | 18 +- .../cli/admin/KcAdmTruststoreTest.java | 16 +- .../testsuite/cli/admin/KcAdmUpdateTest.java | 2 +- .../cli/registration/AbstractRegCliTest.java | 6 +- .../cli/registration/KcRegConfigTest.java | 6 +- .../cli/registration/KcRegCreateTest.java | 4 +- .../testsuite/cli/registration/KcRegTest.java | 10 +- .../cli/registration/KcRegTruststoreTest.java | 13 +- .../cli/registration/KcRegUpdateTest.java | 4 +- .../registration/KcRegUpdateTokenTest.java | 8 +- 121 files changed, 1569 insertions(+), 3927 deletions(-) rename integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/{util/ParseUtil.java => CmdStdinContext.java} (80%) rename integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/{util => }/ReflectionUtil.java (95%) delete mode 100644 integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/AbstractGlobalOptionsCmd.java create mode 100644 integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/GlobalOptionsCmdHelper.java delete mode 100644 integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/config/RealmConfigData.java create mode 100644 integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/operations/OperationUtils.java rename integration/client-cli/admin-cli/src/main/java/org/keycloak/client/{admin => }/cli/common/AttributeKey.java (99%) rename integration/client-cli/admin-cli/src/main/java/org/keycloak/client/{admin => }/cli/common/AttributeOperation.java (97%) create mode 100644 integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/common/BaseAuthOptionsCmd.java create mode 100644 integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/common/BaseConfigCredentialsCmd.java create mode 100644 integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/common/BaseConfigTruststoreCmd.java rename integration/client-cli/{client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/AbstractGlobalOptionsCmd.java => admin-cli/src/main/java/org/keycloak/client/cli/common/BaseGlobalOptionsCmd.java} (66%) rename integration/client-cli/admin-cli/src/main/java/org/keycloak/client/{admin/cli/Globals.java => cli/common/CommandState.java} (69%) rename integration/client-cli/admin-cli/src/main/java/org/keycloak/client/{admin/cli => cli/common}/ExecutionExceptionHandler.java (96%) create mode 100644 integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/common/Globals.java rename integration/client-cli/admin-cli/src/main/java/org/keycloak/client/{admin/cli => cli/common}/ShortErrorMessageHandler.java (98%) rename integration/client-cli/admin-cli/src/main/java/org/keycloak/client/{admin => }/cli/config/ConfigData.java (99%) rename integration/client-cli/admin-cli/src/main/java/org/keycloak/client/{admin => }/cli/config/ConfigHandler.java (94%) rename integration/client-cli/admin-cli/src/main/java/org/keycloak/client/{admin => }/cli/config/ConfigUpdateOperation.java (94%) rename integration/client-cli/admin-cli/src/main/java/org/keycloak/client/{admin => }/cli/config/FileConfigHandler.java (96%) rename integration/client-cli/admin-cli/src/main/java/org/keycloak/client/{admin => }/cli/config/InMemoryConfigHandler.java (96%) rename integration/client-cli/{client-registration-cli/src/main/java/org/keycloak/client/registration => admin-cli/src/main/java/org/keycloak/client}/cli/config/RealmConfigData.java (96%) rename integration/client-cli/admin-cli/src/main/java/org/keycloak/client/{admin => }/cli/util/AccessibleBufferOutputStream.java (97%) rename integration/client-cli/admin-cli/src/main/java/org/keycloak/client/{admin => }/cli/util/AttributeException.java (96%) rename integration/client-cli/admin-cli/src/main/java/org/keycloak/client/{admin => }/cli/util/AuthUtil.java (91%) rename integration/client-cli/admin-cli/src/main/java/org/keycloak/client/{admin => }/cli/util/ClassLoaderUtil.java (98%) rename integration/client-cli/admin-cli/src/main/java/org/keycloak/client/{admin => }/cli/util/ConfigUtil.java (82%) rename integration/client-cli/admin-cli/src/main/java/org/keycloak/client/{admin => }/cli/util/FilterUtil.java (90%) rename integration/client-cli/admin-cli/src/main/java/org/keycloak/client/{admin => }/cli/util/Header.java (95%) rename integration/client-cli/admin-cli/src/main/java/org/keycloak/client/{admin => }/cli/util/Headers.java (97%) rename integration/client-cli/admin-cli/src/main/java/org/keycloak/client/{admin => }/cli/util/HeadersBody.java (94%) rename integration/client-cli/admin-cli/src/main/java/org/keycloak/client/{admin => }/cli/util/HeadersBodyStatus.java (98%) rename integration/client-cli/admin-cli/src/main/java/org/keycloak/client/{admin/cli/httpcomponents => cli/util}/HttpDelete.java (95%) rename integration/client-cli/admin-cli/src/main/java/org/keycloak/client/{admin => }/cli/util/HttpResponseException.java (96%) rename integration/client-cli/admin-cli/src/main/java/org/keycloak/client/{admin => }/cli/util/HttpUtil.java (83%) rename integration/client-cli/admin-cli/src/main/java/org/keycloak/client/{admin => }/cli/util/IoUtil.java (99%) rename integration/client-cli/admin-cli/src/main/java/org/keycloak/client/{admin => }/cli/util/OsArch.java (97%) rename integration/client-cli/admin-cli/src/main/java/org/keycloak/client/{admin => }/cli/util/OsUtil.java (93%) rename integration/client-cli/admin-cli/src/main/java/org/keycloak/client/{admin => }/cli/util/OutputFormat.java (94%) rename integration/client-cli/admin-cli/src/main/java/org/keycloak/client/{admin => }/cli/util/OutputUtil.java (98%) rename integration/client-cli/admin-cli/src/main/java/org/keycloak/client/{admin/cli/common/CmdStdinContext.java => cli/util/ParseUtil.java} (61%) rename integration/client-cli/admin-cli/src/main/java/org/keycloak/client/{admin => }/cli/util/ReturnFields.java (99%) rename integration/client-cli/admin-cli/src/test/java/org/keycloak/client/admin/cli/{util => }/ReflectionUtilTest.java (97%) rename integration/client-cli/admin-cli/src/test/java/org/keycloak/client/{admin => }/cli/util/MergeAttributesTest.java (88%) rename integration/client-cli/admin-cli/src/test/java/org/keycloak/client/{admin => }/cli/util/OuputUtilTest.java (91%) rename integration/client-cli/admin-cli/src/test/java/org/keycloak/client/{admin => }/cli/util/ReturnFieldsTest.java (98%) rename integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/{util/ParseUtil.java => CmdStdinContext.java} (80%) rename integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/{common => }/EndpointType.java (81%) delete mode 100644 integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/Globals.java rename integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/{util => }/ReflectionUtil.java (99%) delete mode 100644 integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/common/AttributeKey.java delete mode 100644 integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/common/AttributeOperation.java delete mode 100644 integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/common/CmdStdinContext.java delete mode 100644 integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/common/ParsingContext.java delete mode 100644 integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/config/ConfigData.java delete mode 100644 integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/config/ConfigHandler.java delete mode 100644 integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/config/ConfigUpdateOperation.java delete mode 100644 integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/config/FileConfigHandler.java delete mode 100644 integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/config/InMemoryConfigHandler.java delete mode 100644 integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/AttributeException.java delete mode 100644 integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/AuthUtil.java delete mode 100644 integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/ClassLoaderUtil.java delete mode 100644 integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/ConfigUtil.java delete mode 100644 integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/DebugBufferedInputStream.java delete mode 100644 integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/HttpUtil.java delete mode 100644 integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/IoUtil.java delete mode 100644 integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/OsArch.java delete mode 100644 integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/OsUtil.java rename integration/client-cli/client-registration-cli/src/test/java/org/keycloak/client/registration/cli/{util => }/ReflectionUtilTest.java (96%) diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/ParseUtil.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/CmdStdinContext.java similarity index 80% rename from integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/ParseUtil.java rename to integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/CmdStdinContext.java index 7d7399ec01..525a436888 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/ParseUtil.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/CmdStdinContext.java @@ -14,49 +14,57 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.client.admin.cli.util; +package org.keycloak.client.admin.cli; -import com.fasterxml.jackson.core.JsonParseException; -import com.fasterxml.jackson.databind.JsonNode; -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.client.cli.common.AttributeOperation; +import org.keycloak.client.cli.util.AttributeException; import org.keycloak.util.JsonSerialization; import java.io.IOException; import java.util.List; -import static org.keycloak.client.admin.cli.util.IoUtil.readFileOrStdin; -import static org.keycloak.client.admin.cli.util.ReflectionUtil.setAttributes; +import com.fasterxml.jackson.core.JsonParseException; +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 Marko Strukelj */ -public class ParseUtil { +public class CmdStdinContext { - public static String[] parseKeyVal(String keyval) { - // we expect = as a separator - int pos = keyval.indexOf("="); - if (pos <= 0) { - throw new IllegalArgumentException("Invalid key=value parameter: [" + keyval + "]"); - } + private T result; + private String content; - String [] parsed = new String[2]; - parsed[0] = keyval.substring(0, pos); - parsed[1] = keyval.substring(pos+1); + public CmdStdinContext() {} - return parsed; + public T getResult() { + return result; + } + + public void setResult(T result) { + this.result = result; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; } public static CmdStdinContext parseFileOrStdin(String file) { - + String content = readFileOrStdin(file).trim(); JsonNode result = null; - + if (content.length() == 0) { throw new RuntimeException("Document provided by --file option is empty"); } - + try { result = JsonSerialization.readValue(content, JsonNode.class); } catch (JsonParseException e) { @@ -66,7 +74,7 @@ public class ParseUtil { } catch (Exception e) { throw new RuntimeException("Not a valid JSON document", e); } - + CmdStdinContext ctx = new CmdStdinContext<>(); ctx.setContent(content); ctx.setResult(result); @@ -74,33 +82,33 @@ public class ParseUtil { } public static CmdStdinContext mergeAttributes(CmdStdinContext ctx, ObjectNode newObject, List attrs) { - + JsonNode node = ctx.getResult(); if (node != null && !node.isObject()) { throw new RuntimeException("Not a JSON object: " + node); } ObjectNode result = (ObjectNode) node; try { - + if (result == null) { result = newObject; } - + if (result == null) { throw new RuntimeException("Failed to set attribute(s) - no target object"); } - + try { setAttributes(result, attrs); } catch (AttributeException e) { throw new RuntimeException("Failed to set attribute '" + e.getAttributeName() + "' on document type '" + result.getClass().getName() + "'", e); } ctx.setContent(JsonSerialization.writeValueAsString(result)); - + } catch (IOException e) { throw new RuntimeException("Failed to merge attributes with configuration from file", e); } - + ctx.setResult(result); return ctx; } diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/KcAdmMain.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/KcAdmMain.java index cc913235e0..50e35ee2b8 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/KcAdmMain.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/KcAdmMain.java @@ -17,45 +17,37 @@ package org.keycloak.client.admin.cli; import org.keycloak.client.admin.cli.commands.KcAdmCmd; -import org.keycloak.client.admin.cli.util.ClassLoaderUtil; -import org.keycloak.client.admin.cli.util.OsUtil; -import org.keycloak.common.crypto.CryptoIntegration; - -import java.io.PrintWriter; - -import picocli.CommandLine; -import picocli.CommandLine.Model.CommandSpec; +import org.keycloak.client.cli.common.CommandState; +import org.keycloak.client.cli.common.Globals; +import org.keycloak.client.cli.util.OsUtil; /** * @author Marko Strukelj */ public class KcAdmMain { - public static void main(String [] args) { - String libDir = System.getProperty("kc.lib.dir"); - if (libDir == null) { - throw new RuntimeException("System property kc.lib.dir needs to be set"); + 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; } - ClassLoader cl = ClassLoaderUtil.resolveClassLoader(libDir); - Thread.currentThread().setContextClassLoader(cl); - CryptoIntegration.init(cl); + @Override + public String getDefaultConfigFilePath() { + return DEFAULT_CONFIG_FILE_PATH; + } - 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; + public static void main(String [] args) { + Globals.main(args, new KcAdmCmd(), CMD, DEFAULT_CONFIG_FILE_STRING); } } diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/ReflectionUtil.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/ReflectionUtil.java similarity index 95% rename from integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/ReflectionUtil.java rename to integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/ReflectionUtil.java index 4a0774502a..3a7cede5e8 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/ReflectionUtil.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/ReflectionUtil.java @@ -14,22 +14,23 @@ * See the License for the specific language governing permissions and * 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.node.ArrayNode; import com.fasterxml.jackson.databind.node.NullNode; import com.fasterxml.jackson.databind.node.ObjectNode; 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.List; import java.util.Map; -import static org.keycloak.client.admin.cli.common.AttributeOperation.Type.SET; -import static org.keycloak.client.admin.cli.util.OutputUtil.MAPPER; +import static org.keycloak.client.cli.common.AttributeOperation.Type.SET; +import static org.keycloak.client.cli.util.OutputUtil.MAPPER; /** * @author Marko Strukelj diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/AbstractAuthOptionsCmd.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/AbstractAuthOptionsCmd.java index c17bb19e91..88234618d2 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/AbstractAuthOptionsCmd.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/AbstractAuthOptionsCmd.java @@ -16,252 +16,35 @@ */ package org.keycloak.client.admin.cli.commands; -import org.keycloak.OAuth2Constants; -import org.keycloak.client.admin.cli.config.ConfigData; -import org.keycloak.client.admin.cli.config.ConfigHandler; -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 org.keycloak.client.admin.cli.KcAdmMain; +import org.keycloak.client.cli.common.BaseAuthOptionsCmd; +import org.keycloak.client.cli.config.ConfigData; 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 Marko Strukelj */ -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") 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") 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") - String externalToken; - - 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; + public void setToken(String token) { + this.externalToken = token; } - protected void applyDefaultOptionValues() { - if (clientId == null) { - clientId = DEFAULT_CLIENT; - } + public AbstractAuthOptionsCmd() { + super(KcAdmMain.COMMAND_STATE); } - @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) { 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); - } - } diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/AbstractGlobalOptionsCmd.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/AbstractGlobalOptionsCmd.java deleted file mode 100644 index 7190595021..0000000000 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/AbstractGlobalOptionsCmd.java +++ /dev/null @@ -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; - } - -} diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/AbstractRequestCmd.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/AbstractRequestCmd.java index 882a37b6ba..69e4cf9be0 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/AbstractRequestCmd.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/AbstractRequestCmd.java @@ -17,18 +17,18 @@ package org.keycloak.client.admin.cli.commands; import org.apache.http.entity.ContentType; -import org.keycloak.client.admin.cli.common.AttributeOperation; -import org.keycloak.client.admin.cli.common.CmdStdinContext; -import org.keycloak.client.admin.cli.config.ConfigData; -import org.keycloak.client.admin.cli.util.AccessibleBufferOutputStream; -import org.keycloak.client.admin.cli.util.Header; -import org.keycloak.client.admin.cli.util.Headers; -import org.keycloak.client.admin.cli.util.HeadersBody; -import org.keycloak.client.admin.cli.util.HeadersBodyStatus; -import org.keycloak.client.admin.cli.util.HttpUtil; -import org.keycloak.client.admin.cli.util.OutputFormat; -import org.keycloak.client.admin.cli.util.ReflectionUtil; -import org.keycloak.client.admin.cli.util.ReturnFields; +import org.keycloak.client.admin.cli.CmdStdinContext; +import org.keycloak.client.admin.cli.ReflectionUtil; +import org.keycloak.client.cli.common.AttributeOperation; +import org.keycloak.client.cli.config.ConfigData; +import org.keycloak.client.cli.util.AccessibleBufferOutputStream; +import org.keycloak.client.cli.util.Header; +import org.keycloak.client.cli.util.Headers; +import org.keycloak.client.cli.util.HeadersBody; +import org.keycloak.client.cli.util.HeadersBodyStatus; +import org.keycloak.client.cli.util.HttpUtil; +import org.keycloak.client.cli.util.OutputFormat; +import org.keycloak.client.cli.util.ReturnFields; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; @@ -52,22 +52,19 @@ import picocli.CommandLine.ArgGroup; import picocli.CommandLine.Option; import picocli.CommandLine.Parameters; -import static org.keycloak.client.admin.cli.common.AttributeOperation.Type.DELETE; -import static org.keycloak.client.admin.cli.common.AttributeOperation.Type.SET; -import static org.keycloak.client.admin.cli.util.AuthUtil.ensureToken; -import static org.keycloak.client.admin.cli.util.ConfigUtil.credentialsAvailable; -import static org.keycloak.client.admin.cli.util.ConfigUtil.loadConfig; -import static org.keycloak.client.admin.cli.util.HttpUtil.checkSuccess; -import static org.keycloak.client.admin.cli.util.HttpUtil.composeResourceUrl; -import static org.keycloak.client.admin.cli.util.HttpUtil.doGet; -import static org.keycloak.client.admin.cli.util.IoUtil.copyStream; -import static org.keycloak.client.admin.cli.util.IoUtil.printErr; -import static org.keycloak.client.admin.cli.util.IoUtil.printOut; -import static org.keycloak.client.admin.cli.util.OutputUtil.MAPPER; -import static org.keycloak.client.admin.cli.util.OutputUtil.printAsCsv; -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; +import static org.keycloak.client.cli.common.AttributeOperation.Type.DELETE; +import static org.keycloak.client.cli.common.AttributeOperation.Type.SET; +import static org.keycloak.client.cli.util.ConfigUtil.credentialsAvailable; +import static org.keycloak.client.cli.util.ConfigUtil.loadConfig; +import static org.keycloak.client.cli.util.HttpUtil.checkSuccess; +import static org.keycloak.client.cli.util.HttpUtil.composeResourceUrl; +import static org.keycloak.client.cli.util.HttpUtil.doGet; +import static org.keycloak.client.cli.util.IoUtil.copyStream; +import static org.keycloak.client.cli.util.IoUtil.printErr; +import static org.keycloak.client.cli.util.IoUtil.printOut; +import static org.keycloak.client.cli.util.OutputUtil.MAPPER; +import static org.keycloak.client.cli.util.OutputUtil.printAsCsv; +import static org.keycloak.client.cli.util.ParseUtil.parseKeyVal; /** * @author Marko Strukelj @@ -208,7 +205,7 @@ public abstract class AbstractRequestCmd extends AbstractAuthOptionsCmd { } } } else { - ctx = parseFileOrStdin(file); + ctx = CmdStdinContext.parseFileOrStdin(file); } } else if (body != null) { 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"); } - ctx = mergeAttributes(ctx, MAPPER.createObjectNode(), attrs); + ctx = CmdStdinContext.mergeAttributes(ctx, MAPPER.createObjectNode(), attrs); } if (content == null && ctx.getContent() != null) { diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/AddRolesCmd.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/AddRolesCmd.java index a4ef642df3..9f4c8e3d95 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/AddRolesCmd.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/AddRolesCmd.java @@ -21,12 +21,13 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import picocli.CommandLine.Command; 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.GroupOperations; import org.keycloak.client.admin.cli.operations.RoleOperations; import org.keycloak.client.admin.cli.operations.LocalSearch; import org.keycloak.client.admin.cli.operations.UserOperations; +import org.keycloak.client.cli.config.ConfigData; import java.io.PrintWriter; import java.io.StringWriter; @@ -35,12 +36,10 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -import static org.keycloak.client.admin.cli.util.AuthUtil.ensureToken; -import static org.keycloak.client.admin.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_STRING; -import static org.keycloak.client.admin.cli.util.ConfigUtil.credentialsAvailable; -import static org.keycloak.client.admin.cli.util.ConfigUtil.loadConfig; -import static org.keycloak.client.admin.cli.util.OsUtil.CMD; -import static org.keycloak.client.admin.cli.util.OsUtil.PROMPT; +import static org.keycloak.client.admin.cli.KcAdmMain.CMD; +import static org.keycloak.client.cli.util.ConfigUtil.credentialsAvailable; +import static org.keycloak.client.cli.util.ConfigUtil.loadConfig; +import static org.keycloak.client.cli.util.OsUtil.PROMPT; /** * @author Marko Strukelj @@ -290,7 +289,7 @@ public class AddRolesCmd extends AbstractAuthOptionsCmd { 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(" --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(" --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"); diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/ConfigCmd.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/ConfigCmd.java index 9bc0c346e5..4479de9847 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/ConfigCmd.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/ConfigCmd.java @@ -21,7 +21,7 @@ import java.io.StringWriter; import picocli.CommandLine.Command; -import static org.keycloak.client.admin.cli.util.OsUtil.CMD; +import static org.keycloak.client.admin.cli.KcAdmMain.CMD; /** diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/ConfigCredentialsCmd.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/ConfigCredentialsCmd.java index f62b448c1c..a89a4f32ee 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/ConfigCredentialsCmd.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/ConfigCredentialsCmd.java @@ -16,237 +16,20 @@ */ package org.keycloak.client.admin.cli.commands; -import org.keycloak.OAuth2Constants; -import org.keycloak.client.admin.cli.config.ConfigData; -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 org.keycloak.client.admin.cli.KcAdmMain; +import org.keycloak.client.cli.common.BaseConfigCredentialsCmd; 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 Marko Strukelj */ @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 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(); - } + public ConfigCredentialsCmd() { + super(KcAdmMain.COMMAND_STATE); } - @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(); - } } diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/ConfigTruststoreCmd.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/ConfigTruststoreCmd.java index 2b86df05f6..36abec0ad9 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/ConfigTruststoreCmd.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/ConfigTruststoreCmd.java @@ -16,126 +16,19 @@ */ package org.keycloak.client.admin.cli.commands; -import java.io.File; -import java.io.PrintWriter; -import java.io.StringWriter; +import org.keycloak.client.admin.cli.KcAdmMain; +import org.keycloak.client.cli.common.BaseConfigTruststoreCmd; 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 Marko Strukelj */ @Command(name = "truststore", description = "PATH [ARGUMENTS]") -public class ConfigTruststoreCmd extends AbstractAuthOptionsCmd { +public class ConfigTruststoreCmd extends BaseConfigTruststoreCmd { - @Parameters(arity = "0..1") - private String store; - - @Option(names = {"-d", "--delete"}, description = "Remove truststore configuration") - private boolean delete; - - @Override - protected boolean nothingToDo() { - return super.nothingToDo() && store == null && !delete; + public ConfigTruststoreCmd() { + super(KcAdmMain.COMMAND_STATE); } - @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(); - } } diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/CreateCmd.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/CreateCmd.java index a74ea74463..3240589186 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/CreateCmd.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/CreateCmd.java @@ -16,16 +16,17 @@ */ package org.keycloak.client.admin.cli.commands; +import org.keycloak.client.admin.cli.KcAdmMain; + import java.io.PrintWriter; import java.io.StringWriter; import picocli.CommandLine.Command; import picocli.CommandLine.Option; -import static org.keycloak.client.admin.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_STRING; -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; +import static org.keycloak.client.admin.cli.KcAdmMain.CMD; +import static org.keycloak.client.cli.util.OsUtil.OS_ARCH; +import static org.keycloak.client.cli.util.OsUtil.PROMPT; /** * @author Marko Strukelj @@ -91,7 +92,7 @@ public class CreateCmd extends AbstractRequestCmd { 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(" --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(" --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"); diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/DeleteCmd.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/DeleteCmd.java index a8ba2c27fc..829cfd9301 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/DeleteCmd.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/DeleteCmd.java @@ -16,14 +16,15 @@ */ package org.keycloak.client.admin.cli.commands; +import org.keycloak.client.admin.cli.KcAdmMain; + import java.io.PrintWriter; import java.io.StringWriter; import picocli.CommandLine.Command; -import static org.keycloak.client.admin.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_STRING; -import static org.keycloak.client.admin.cli.util.OsUtil.CMD; -import static org.keycloak.client.admin.cli.util.OsUtil.PROMPT; +import static org.keycloak.client.admin.cli.KcAdmMain.CMD; +import static org.keycloak.client.cli.util.OsUtil.PROMPT; /** @@ -55,7 +56,7 @@ public class DeleteCmd extends CreateCmd { 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(" --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(" --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"); diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/GetCmd.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/GetCmd.java index 367fb2cdd8..17ddb090b5 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/GetCmd.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/GetCmd.java @@ -16,15 +16,16 @@ */ package org.keycloak.client.admin.cli.commands; +import org.keycloak.client.admin.cli.KcAdmMain; + import java.io.PrintWriter; import java.io.StringWriter; import picocli.CommandLine.Command; import picocli.CommandLine.Option; -import static org.keycloak.client.admin.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_STRING; -import static org.keycloak.client.admin.cli.util.OsUtil.CMD; -import static org.keycloak.client.admin.cli.util.OsUtil.PROMPT; +import static org.keycloak.client.admin.cli.KcAdmMain.CMD; +import static org.keycloak.client.cli.util.OsUtil.PROMPT; /** * @author Marko Strukelj @@ -91,7 +92,7 @@ public class GetCmd extends AbstractRequestCmd { 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(" --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(" --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"); diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/GetRolesCmd.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/GetRolesCmd.java index 92088fa9b2..3568d45869 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/GetRolesCmd.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/GetRolesCmd.java @@ -16,11 +16,12 @@ */ 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.GroupOperations; import org.keycloak.client.admin.cli.operations.RoleOperations; import org.keycloak.client.admin.cli.operations.UserOperations; +import org.keycloak.client.cli.config.ConfigData; import java.io.PrintWriter; import java.io.StringWriter; @@ -28,13 +29,11 @@ import java.io.StringWriter; import picocli.CommandLine.Command; import picocli.CommandLine.Option; -import static org.keycloak.client.admin.cli.util.AuthUtil.ensureToken; -import static org.keycloak.client.admin.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_STRING; -import static org.keycloak.client.admin.cli.util.ConfigUtil.credentialsAvailable; -import static org.keycloak.client.admin.cli.util.ConfigUtil.loadConfig; -import static org.keycloak.client.admin.cli.util.HttpUtil.composeResourceUrl; -import static org.keycloak.client.admin.cli.util.OsUtil.CMD; -import static org.keycloak.client.admin.cli.util.OsUtil.PROMPT; +import static org.keycloak.client.admin.cli.KcAdmMain.CMD; +import static org.keycloak.client.cli.util.ConfigUtil.credentialsAvailable; +import static org.keycloak.client.cli.util.ConfigUtil.loadConfig; +import static org.keycloak.client.cli.util.HttpUtil.composeResourceUrl; +import static org.keycloak.client.cli.util.OsUtil.PROMPT; /** * @author Marko Strukelj @@ -325,7 +324,7 @@ public class GetRolesCmd extends GetCmd { 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(" --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(" --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"); diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/GlobalOptionsCmdHelper.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/GlobalOptionsCmdHelper.java new file mode 100644 index 0000000000..9ee04b41c3 --- /dev/null +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/GlobalOptionsCmdHelper.java @@ -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); + } + } + +} diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/HelpCmd.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/HelpCmd.java index ff675ab4e2..3a3ca05a1e 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/HelpCmd.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/HelpCmd.java @@ -21,7 +21,7 @@ import java.util.List; import picocli.CommandLine.Command; 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") public class HelpCmd implements Runnable { @@ -39,11 +39,11 @@ public class HelpCmd implements Runnable { if (args.size() > 1) { switch (args.get(1)) { case "credentials": { - printOut(ConfigCredentialsCmd.usage()); + printOut(new ConfigCredentialsCmd().help()); break outer; } case "truststore": { - printOut(ConfigTruststoreCmd.usage()); + printOut(new ConfigTruststoreCmd().help()); break outer; } } diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/KcAdmCmd.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/KcAdmCmd.java index 904c4e9209..3e3cdcfc29 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/KcAdmCmd.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/KcAdmCmd.java @@ -16,14 +16,16 @@ */ 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.StringWriter; import picocli.CommandLine.Command; -import static org.keycloak.client.admin.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_STRING; -import static org.keycloak.client.admin.cli.util.OsUtil.CMD; -import static org.keycloak.client.admin.cli.util.OsUtil.PROMPT; +import static org.keycloak.client.admin.cli.KcAdmMain.CMD; +import static org.keycloak.client.cli.util.OsUtil.PROMPT; @Command(name = "kcadm", header = { @@ -47,13 +49,18 @@ subcommands = { GetRolesCmd.class, SetPasswordCmd.class }) -public class KcAdmCmd extends AbstractGlobalOptionsCmd { +public class KcAdmCmd extends BaseGlobalOptionsCmd { @Override protected boolean nothingToDo() { return true; } + @Override + protected String help() { + return usage(); + } + public static String usage() { StringWriter sb = new StringWriter(); PrintWriter out = new PrintWriter(sb); @@ -76,7 +83,7 @@ public class KcAdmCmd extends AbstractGlobalOptionsCmd { out.println("Global options:"); out.println(" -x Print full stack trace when exiting with error"); 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("Commands: "); out.println(" config Set up credentials, and other configuration settings using the config file"); diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/NewObjectCmd.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/NewObjectCmd.java index 47bc1e884e..2ca8f0d040 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/NewObjectCmd.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/NewObjectCmd.java @@ -21,9 +21,10 @@ import com.fasterxml.jackson.databind.JsonNode; import picocli.CommandLine.Command; import picocli.CommandLine.Option; -import org.keycloak.client.admin.cli.common.AttributeOperation; -import org.keycloak.client.admin.cli.common.CmdStdinContext; -import org.keycloak.client.admin.cli.util.AccessibleBufferOutputStream; +import org.keycloak.client.admin.cli.CmdStdinContext; +import org.keycloak.client.cli.common.AttributeOperation; +import org.keycloak.client.cli.common.BaseGlobalOptionsCmd; +import org.keycloak.client.cli.util.AccessibleBufferOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -35,22 +36,20 @@ import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; -import static org.keycloak.client.admin.cli.common.AttributeOperation.Type.SET; -import static org.keycloak.client.admin.cli.util.IoUtil.copyStream; -import static org.keycloak.client.admin.cli.util.IoUtil.printErr; -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; -import static org.keycloak.client.admin.cli.util.OutputUtil.MAPPER; -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; +import static org.keycloak.client.cli.common.AttributeOperation.Type.SET; +import static org.keycloak.client.cli.util.IoUtil.copyStream; +import static org.keycloak.client.cli.util.IoUtil.printErr; +import static org.keycloak.client.cli.util.OsUtil.OS_ARCH; +import static org.keycloak.client.cli.util.OsUtil.PROMPT; +import static org.keycloak.client.cli.util.OutputUtil.MAPPER; +import static org.keycloak.client.cli.util.ParseUtil.parseKeyVal; +import static org.keycloak.client.admin.cli.KcAdmMain.CMD; /** * @author Marko Strukelj */ @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 '-'") String file; @@ -73,11 +72,11 @@ public class NewObjectCmd extends AbstractGlobalOptionsCmd { CmdStdinContext ctx = new CmdStdinContext<>(); if (file != null) { - ctx = parseFileOrStdin(file); + ctx = CmdStdinContext.parseFileOrStdin(file); } if (attrs.size() > 0) { - ctx = mergeAttributes(ctx, MAPPER.createObjectNode(), attrs); + ctx = CmdStdinContext.mergeAttributes(ctx, MAPPER.createObjectNode(), attrs); } if (body == null && ctx.getContent() != null) { diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/RemoveRolesCmd.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/RemoveRolesCmd.java index 959833f8a0..b07b5de404 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/RemoveRolesCmd.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/RemoveRolesCmd.java @@ -16,12 +16,13 @@ */ 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.GroupOperations; import org.keycloak.client.admin.cli.operations.LocalSearch; import org.keycloak.client.admin.cli.operations.RoleOperations; import org.keycloak.client.admin.cli.operations.UserOperations; +import org.keycloak.client.cli.config.ConfigData; import java.io.PrintWriter; import java.io.StringWriter; @@ -35,12 +36,10 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import picocli.CommandLine.Command; import picocli.CommandLine.Option; -import static org.keycloak.client.admin.cli.util.AuthUtil.ensureToken; -import static org.keycloak.client.admin.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_STRING; -import static org.keycloak.client.admin.cli.util.ConfigUtil.credentialsAvailable; -import static org.keycloak.client.admin.cli.util.ConfigUtil.loadConfig; -import static org.keycloak.client.admin.cli.util.OsUtil.CMD; -import static org.keycloak.client.admin.cli.util.OsUtil.PROMPT; +import static org.keycloak.client.admin.cli.KcAdmMain.CMD; +import static org.keycloak.client.cli.util.ConfigUtil.credentialsAvailable; +import static org.keycloak.client.cli.util.ConfigUtil.loadConfig; +import static org.keycloak.client.cli.util.OsUtil.PROMPT; /** * @author Marko Strukelj @@ -297,7 +296,7 @@ public class RemoveRolesCmd extends AbstractAuthOptionsCmd { 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(" --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(" --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"); diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/SetPasswordCmd.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/SetPasswordCmd.java index 038b305f9f..baa99fc828 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/SetPasswordCmd.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/SetPasswordCmd.java @@ -16,7 +16,8 @@ */ 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.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.resetUserPassword; -import static org.keycloak.client.admin.cli.util.AuthUtil.ensureToken; -import static org.keycloak.client.admin.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_STRING; -import static org.keycloak.client.admin.cli.util.ConfigUtil.credentialsAvailable; -import static org.keycloak.client.admin.cli.util.ConfigUtil.loadConfig; -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.PROMPT; +import static org.keycloak.client.cli.util.ConfigUtil.credentialsAvailable; +import static org.keycloak.client.cli.util.ConfigUtil.loadConfig; +import static org.keycloak.client.cli.util.IoUtil.readSecret; +import static org.keycloak.client.cli.util.OsUtil.PROMPT; +import static org.keycloak.client.admin.cli.KcAdmMain.CMD; /** * @author Marko Strukelj @@ -117,7 +116,7 @@ public class SetPasswordCmd extends AbstractAuthOptionsCmd { 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(" --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(" --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"); diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/UpdateCmd.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/UpdateCmd.java index c503ab9b21..11ccc18693 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/UpdateCmd.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/commands/UpdateCmd.java @@ -17,16 +17,17 @@ package org.keycloak.client.admin.cli.commands; +import org.keycloak.client.admin.cli.KcAdmMain; + import java.io.PrintWriter; import java.io.StringWriter; import picocli.CommandLine.Command; import picocli.CommandLine.Option; -import static org.keycloak.client.admin.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_STRING; -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; +import static org.keycloak.client.admin.cli.KcAdmMain.CMD; +import static org.keycloak.client.cli.util.OsUtil.OS_ARCH; +import static org.keycloak.client.cli.util.OsUtil.PROMPT; /** * @author Marko Strukelj @@ -97,7 +98,7 @@ public class UpdateCmd extends AbstractRequestCmd { 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(" --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(" --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"); diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/config/RealmConfigData.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/config/RealmConfigData.java deleted file mode 100644 index ac6fe64527..0000000000 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/config/RealmConfigData.java +++ /dev/null @@ -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 Marko Strukelj - */ -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; - } -} diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/operations/ClientOperations.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/operations/ClientOperations.java index 7106668241..4ffba3709b 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/operations/ClientOperations.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/operations/ClientOperations.java @@ -16,14 +16,12 @@ */ package org.keycloak.client.admin.cli.operations; -import static org.keycloak.client.admin.cli.util.HttpUtil.getIdForType; - /** * @author Marko Strukelj */ public class ClientOperations { 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"); } } diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/operations/GroupOperations.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/operations/GroupOperations.java index 144dd0b9a4..1235230949 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/operations/GroupOperations.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/operations/GroupOperations.java @@ -18,10 +18,9 @@ package org.keycloak.client.admin.cli.operations; 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.doPostJSON; -import static org.keycloak.client.admin.cli.util.HttpUtil.getIdForType; +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.doPostJSON; /** * @author Marko Strukelj @@ -29,11 +28,11 @@ import static org.keycloak.client.admin.cli.util.HttpUtil.getIdForType; public class GroupOperations { 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) { - 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) { diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/operations/OperationUtils.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/operations/OperationUtils.java new file mode 100644 index 0000000000..15245c565b --- /dev/null +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/operations/OperationUtils.java @@ -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 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 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 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(); + } + +} diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/operations/RoleOperations.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/operations/RoleOperations.java index 3dbbcf7bad..c49e361e29 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/operations/RoleOperations.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/operations/RoleOperations.java @@ -17,18 +17,17 @@ package org.keycloak.client.admin.cli.operations; 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 java.util.ArrayList; 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 Marko Strukelj */ @@ -38,7 +37,7 @@ public class RoleOperations { public static class LIST_OF_NODES extends ArrayList{}; 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) { @@ -60,11 +59,11 @@ public class RoleOperations { } 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) { - 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 getRealmRoles(String rootUrl, String realm, String auth) { diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/operations/UserOperations.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/operations/UserOperations.java index a0d3f6c2c3..1e9c8fbd07 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/operations/UserOperations.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/operations/UserOperations.java @@ -16,10 +16,10 @@ */ package org.keycloak.client.admin.cli.operations; -import org.keycloak.client.admin.cli.util.Headers; -import org.keycloak.client.admin.cli.util.HeadersBody; -import org.keycloak.client.admin.cli.util.HeadersBodyStatus; -import org.keycloak.client.admin.cli.util.HttpUtil; +import org.keycloak.client.cli.util.Headers; +import org.keycloak.client.cli.util.HeadersBody; +import org.keycloak.client.cli.util.HeadersBodyStatus; +import org.keycloak.client.cli.util.HttpUtil; import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.util.JsonSerialization; @@ -27,10 +27,9 @@ import java.io.ByteArrayInputStream; import java.io.IOException; 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.doPostJSON; -import static org.keycloak.client.admin.cli.util.HttpUtil.getIdForType; +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.doPostJSON; /** * @author Marko Strukelj @@ -91,7 +90,7 @@ public class UserOperations { } 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"}); } } diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/common/AttributeKey.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/common/AttributeKey.java similarity index 99% rename from integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/common/AttributeKey.java rename to integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/common/AttributeKey.java index 7cb0dc885f..51f394f3bf 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/common/AttributeKey.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/common/AttributeKey.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.client.admin.cli.common; +package org.keycloak.client.cli.common; import java.util.Collections; import java.util.LinkedList; diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/common/AttributeOperation.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/common/AttributeOperation.java similarity index 97% rename from integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/common/AttributeOperation.java rename to integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/common/AttributeOperation.java index 4db483e81f..bd0db9cf4e 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/common/AttributeOperation.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/common/AttributeOperation.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.client.admin.cli.common; +package org.keycloak.client.cli.common; /** * @author Marko Strukelj diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/common/BaseAuthOptionsCmd.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/common/BaseAuthOptionsCmd.java new file mode 100644 index 0000000000..6393e3bacf --- /dev/null +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/common/BaseAuthOptionsCmd.java @@ -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 Marko Strukelj + */ +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()); + } + +} diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/common/BaseConfigCredentialsCmd.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/common/BaseConfigCredentialsCmd.java new file mode 100644 index 0000000000..66cff02731 --- /dev/null +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/common/BaseConfigCredentialsCmd.java @@ -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 Marko Strukelj + */ +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(); + } +} diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/common/BaseConfigTruststoreCmd.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/common/BaseConfigTruststoreCmd.java new file mode 100644 index 0000000000..bc289fc31f --- /dev/null +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/common/BaseConfigTruststoreCmd.java @@ -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 Marko Strukelj + */ +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(); + } +} diff --git a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/AbstractGlobalOptionsCmd.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/common/BaseGlobalOptionsCmd.java similarity index 66% rename from integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/AbstractGlobalOptionsCmd.java rename to integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/common/BaseGlobalOptionsCmd.java index 5c9c7a7094..a120ea4307 100644 --- a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/AbstractGlobalOptionsCmd.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/common/BaseGlobalOptionsCmd.java @@ -1,16 +1,27 @@ -package org.keycloak.client.registration.cli.commands; - -import org.keycloak.client.registration.cli.Globals; +/* + * 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 picocli.CommandLine; import picocli.CommandLine.Option; -import static org.keycloak.client.registration.cli.util.IoUtil.printOut; +import static org.keycloak.client.cli.util.IoUtil.printOut; -/** - * @author Marko Strukelj - */ -public abstract class AbstractGlobalOptionsCmd implements Runnable { +public abstract class BaseGlobalOptionsCmd implements Runnable { @Option(names = "--help", description = "Print command specific help") @@ -38,9 +49,7 @@ public abstract class AbstractGlobalOptionsCmd implements Runnable { return false; } - protected String help() { - return KcRegCmd.usage(); - } + protected abstract String help(); @Override public void run() { diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/Globals.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/common/CommandState.java similarity index 69% rename from integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/Globals.java rename to integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/common/CommandState.java index d35d4a0a96..da6f6d7184 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/Globals.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/common/CommandState.java @@ -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. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,15 +14,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.client.admin.cli; -/** - * @author Marko Strukelj - */ -public class Globals { +package org.keycloak.client.cli.common; - public static boolean dumpTrace = false; +public interface CommandState { - public static boolean help = false; + String getCommand(); + + String getDefaultConfigFilePath(); } diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/ExecutionExceptionHandler.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/common/ExecutionExceptionHandler.java similarity index 96% rename from integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/ExecutionExceptionHandler.java rename to integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/common/ExecutionExceptionHandler.java index 352c5f4f89..d777177a22 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/ExecutionExceptionHandler.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/common/ExecutionExceptionHandler.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.client.admin.cli; +package org.keycloak.client.cli.common; import picocli.CommandLine; import picocli.CommandLine.ParseResult; diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/common/Globals.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/common/Globals.java new file mode 100644 index 0000000000..3afda5763a --- /dev/null +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/common/Globals.java @@ -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 Marko Strukelj + */ +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; + } + + +} diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/ShortErrorMessageHandler.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/common/ShortErrorMessageHandler.java similarity index 98% rename from integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/ShortErrorMessageHandler.java rename to integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/common/ShortErrorMessageHandler.java index b0c6d9bc16..e76cda5fb1 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/ShortErrorMessageHandler.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/common/ShortErrorMessageHandler.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.client.admin.cli; +package org.keycloak.client.cli.common; import java.io.PrintWriter; diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/config/ConfigData.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/config/ConfigData.java similarity index 99% rename from integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/config/ConfigData.java rename to integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/config/ConfigData.java index 9cdec9f129..f67c7fcc84 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/config/ConfigData.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/config/ConfigData.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.client.admin.cli.config; +package org.keycloak.client.cli.config; import com.fasterxml.jackson.annotation.JsonIgnore; import org.keycloak.util.JsonSerialization; diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/config/ConfigHandler.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/config/ConfigHandler.java similarity index 94% rename from integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/config/ConfigHandler.java rename to integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/config/ConfigHandler.java index fd318c569b..710bb2168b 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/config/ConfigHandler.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/config/ConfigHandler.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.client.admin.cli.config; +package org.keycloak.client.cli.config; /** * @author Marko Strukelj diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/config/ConfigUpdateOperation.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/config/ConfigUpdateOperation.java similarity index 94% rename from integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/config/ConfigUpdateOperation.java rename to integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/config/ConfigUpdateOperation.java index 15276a051f..b7b7c5e529 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/config/ConfigUpdateOperation.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/config/ConfigUpdateOperation.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.client.admin.cli.config; +package org.keycloak.client.cli.config; /** * @author Marko Strukelj diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/config/FileConfigHandler.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/config/FileConfigHandler.java similarity index 96% rename from integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/config/FileConfigHandler.java rename to integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/config/FileConfigHandler.java index cb76dfeec9..a7eb461984 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/config/FileConfigHandler.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/config/FileConfigHandler.java @@ -14,9 +14,9 @@ * See the License for the specific language governing permissions and * 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 java.io.ByteArrayInputStream; @@ -30,7 +30,7 @@ import java.nio.channels.OverlappingFileLockException; import java.nio.file.Path; 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 Marko Strukelj diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/config/InMemoryConfigHandler.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/config/InMemoryConfigHandler.java similarity index 96% rename from integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/config/InMemoryConfigHandler.java rename to integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/config/InMemoryConfigHandler.java index 7ac43775e7..3c6f9fdb1e 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/config/InMemoryConfigHandler.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/config/InMemoryConfigHandler.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.client.admin.cli.config; +package org.keycloak.client.cli.config; /** * @author Marko Strukelj diff --git a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/config/RealmConfigData.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/config/RealmConfigData.java similarity index 96% rename from integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/config/RealmConfigData.java rename to integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/config/RealmConfigData.java index 6eade23434..6e1a73d018 100644 --- a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/config/RealmConfigData.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/config/RealmConfigData.java @@ -14,8 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package org.keycloak.client.registration.cli.config; +package org.keycloak.client.cli.config; import org.keycloak.util.JsonSerialization; @@ -23,6 +22,8 @@ import java.io.IOException; import java.util.LinkedHashMap; import java.util.Map; +import com.fasterxml.jackson.annotation.JsonInclude; + /** * @author Marko Strukelj */ @@ -50,11 +51,12 @@ public class RealmConfigData { private Long sigExpiresAt; + @JsonInclude(JsonInclude.Include.NON_NULL) private String initialToken; + @JsonInclude(JsonInclude.Include.NON_EMPTY) private Map clients = new LinkedHashMap(); - public String serverUrl() { return serverUrl; } @@ -111,6 +113,14 @@ public class RealmConfigData { this.secret = secret; } + public String getGrantTypeForAuthentication() { + return grantTypeForAuthentication; + } + + public void setGrantTypeForAuthentication(String grantTypeForAuthentication) { + this.grantTypeForAuthentication = grantTypeForAuthentication; + } + public Long getExpiresAt() { return expiresAt; } @@ -127,14 +137,6 @@ public class RealmConfigData { this.refreshExpiresAt = refreshExpiresAt; } - public String getGrantTypeForAuthentication() { - return grantTypeForAuthentication; - } - - public void setGrantTypeForAuthentication(String grantTypeForAuthentication) { - this.grantTypeForAuthentication = grantTypeForAuthentication; - } - public Long getSigExpiresAt() { return sigExpiresAt; } @@ -194,13 +196,6 @@ public class RealmConfigData { refreshToken = source.refreshToken; expiresAt = source.expiresAt; refreshExpiresAt = source.refreshExpiresAt; - - mergeClients(source); - } - - public void mergeRegistrationTokens(RealmConfigData source) { - initialToken = source.initialToken; - mergeClients(source); } @Override diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/AccessibleBufferOutputStream.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/AccessibleBufferOutputStream.java similarity index 97% rename from integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/AccessibleBufferOutputStream.java rename to integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/AccessibleBufferOutputStream.java index 984784eb34..8afce36c70 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/AccessibleBufferOutputStream.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/AccessibleBufferOutputStream.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.client.admin.cli.util; +package org.keycloak.client.cli.util; import java.io.FilterOutputStream; import java.io.IOException; diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/AttributeException.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/AttributeException.java similarity index 96% rename from integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/AttributeException.java rename to integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/AttributeException.java index 721baf260e..7e310f54b5 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/AttributeException.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/AttributeException.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.client.admin.cli.util; +package org.keycloak.client.cli.util; /** * @author Marko Strukelj diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/AuthUtil.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/AuthUtil.java similarity index 91% rename from integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/AuthUtil.java rename to integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/AuthUtil.java index 125913fbe2..ea0b6eec33 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/AuthUtil.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/AuthUtil.java @@ -14,10 +14,10 @@ * See the License for the specific language governing permissions and * 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.admin.cli.config.RealmConfigData; +import org.keycloak.client.cli.config.ConfigData; +import org.keycloak.client.cli.config.RealmConfigData; import org.keycloak.common.util.KeystoreUtil; import org.keycloak.common.util.Time; import org.keycloak.jose.jws.JWSBuilder; @@ -33,24 +33,24 @@ import java.security.KeyPair; import java.util.UUID; import static java.lang.System.currentTimeMillis; -import static org.keycloak.client.admin.cli.util.ConfigUtil.checkAuthInfo; -import static org.keycloak.client.admin.cli.util.ConfigUtil.saveMergeConfig; -import static org.keycloak.client.admin.cli.util.HttpUtil.APPLICATION_FORM_URL_ENCODED; -import static org.keycloak.client.admin.cli.util.HttpUtil.APPLICATION_JSON; -import static org.keycloak.client.admin.cli.util.HttpUtil.doPost; -import static org.keycloak.client.admin.cli.util.HttpUtil.urlencode; +import static org.keycloak.client.cli.util.ConfigUtil.checkServerInfo; +import static org.keycloak.client.cli.util.ConfigUtil.saveMergeConfig; +import static org.keycloak.client.cli.util.HttpUtil.APPLICATION_FORM_URL_ENCODED; +import static org.keycloak.client.cli.util.HttpUtil.APPLICATION_JSON; +import static org.keycloak.client.cli.util.HttpUtil.doPost; +import static org.keycloak.client.cli.util.HttpUtil.urlencode; /** * @author Marko Strukelj */ public class AuthUtil { - public static String ensureToken(ConfigData config) { + public static String ensureToken(ConfigData config, String cmd) { if (config.getExternalToken() != null) { return config.getExternalToken(); } - checkAuthInfo(config); + checkServerInfo(config, cmd); RealmConfigData realmConfig = config.sessionRealmConfigData(); @@ -63,11 +63,11 @@ public class AuthUtil { // 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'"); + throw new RuntimeException("Session has expired. Login again with '" + cmd + " config credentials'"); } 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 { diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/ClassLoaderUtil.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/ClassLoaderUtil.java similarity index 98% rename from integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/ClassLoaderUtil.java rename to integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/ClassLoaderUtil.java index 990cf8182e..3ce4afaa08 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/ClassLoaderUtil.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/ClassLoaderUtil.java @@ -17,7 +17,7 @@ * */ -package org.keycloak.client.admin.cli.util; +package org.keycloak.client.cli.util; import java.io.File; import java.net.MalformedURLException; diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/ConfigUtil.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/ConfigUtil.java similarity index 82% rename from integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/ConfigUtil.java rename to integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/ConfigUtil.java index 9d6033cb38..ece5dbf26c 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/ConfigUtil.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/ConfigUtil.java @@ -14,14 +14,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.client.admin.cli.util; +package org.keycloak.client.cli.util; import org.keycloak.OAuth2Constants; -import org.keycloak.client.admin.cli.config.ConfigData; -import org.keycloak.client.admin.cli.config.ConfigHandler; -import org.keycloak.client.admin.cli.config.ConfigUpdateOperation; -import org.keycloak.client.admin.cli.config.InMemoryConfigHandler; -import org.keycloak.client.admin.cli.config.RealmConfigData; +import org.keycloak.client.cli.config.ConfigData; +import org.keycloak.client.cli.config.ConfigHandler; +import org.keycloak.client.cli.config.ConfigUpdateOperation; +import org.keycloak.client.cli.config.InMemoryConfigHandler; +import org.keycloak.client.cli.config.RealmConfigData; 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_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; public static ConfigHandler getHandler() { @@ -45,6 +41,15 @@ public class ConfigUtil { 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 -> { @@ -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) { - 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) { - 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) { // 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 diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/FilterUtil.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/FilterUtil.java similarity index 90% rename from integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/FilterUtil.java rename to integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/FilterUtil.java index 6a68f85d29..6dcf85ae1d 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/FilterUtil.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/FilterUtil.java @@ -14,18 +14,18 @@ * See the License for the specific language governing permissions and * 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.ArrayNode; 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.util.Iterator; -import static org.keycloak.client.admin.cli.util.OutputUtil.MAPPER; -import static org.keycloak.client.admin.cli.util.OutputUtil.convertToJsonNode; - /** * @author Marko Strukelj */ diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/Header.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/Header.java similarity index 95% rename from integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/Header.java rename to integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/Header.java index 6ba4a600cc..a90491a9a4 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/Header.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/Header.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.client.admin.cli.util; +package org.keycloak.client.cli.util; /** * @author Marko Strukelj diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/Headers.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/Headers.java similarity index 97% rename from integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/Headers.java rename to integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/Headers.java index 57fc034e5a..7a9b16ed28 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/Headers.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/Headers.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.client.admin.cli.util; +package org.keycloak.client.cli.util; import org.apache.http.entity.ContentType; diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/HeadersBody.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/HeadersBody.java similarity index 94% rename from integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/HeadersBody.java rename to integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/HeadersBody.java index eba553b0f8..e11417de3d 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/HeadersBody.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/HeadersBody.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.client.admin.cli.util; +package org.keycloak.client.cli.util; import org.apache.http.entity.ContentType; @@ -22,7 +22,7 @@ import java.io.ByteArrayOutputStream; import java.io.InputStream; 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 Marko Strukelj diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/HeadersBodyStatus.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/HeadersBodyStatus.java similarity index 98% rename from integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/HeadersBodyStatus.java rename to integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/HeadersBodyStatus.java index 9ce89651f5..8e82aac079 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/HeadersBodyStatus.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/HeadersBodyStatus.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.client.admin.cli.util; +package org.keycloak.client.cli.util; import org.keycloak.util.JsonSerialization; diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/httpcomponents/HttpDelete.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/HttpDelete.java similarity index 95% rename from integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/httpcomponents/HttpDelete.java rename to integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/HttpDelete.java index 51c1bcaa38..fd9a65a371 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/httpcomponents/HttpDelete.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/HttpDelete.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * 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.ThreadingBehavior; diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/HttpResponseException.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/HttpResponseException.java similarity index 96% rename from integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/HttpResponseException.java rename to integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/HttpResponseException.java index 692b38467a..f7ea7b383a 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/HttpResponseException.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/HttpResponseException.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.client.admin.cli.util; +package org.keycloak.client.cli.util; /** * @author Marko Strukelj diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/HttpUtil.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/HttpUtil.java similarity index 83% rename from integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/HttpUtil.java rename to integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/HttpUtil.java index 30eb9e9b7f..5af609f5a0 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/HttpUtil.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/HttpUtil.java @@ -14,10 +14,8 @@ * See the License for the specific language governing permissions and * 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.HttpHeaders; 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.ssl.SSLContextBuilder; 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 javax.net.ssl.SSLContext; @@ -53,12 +48,8 @@ import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Supplier; - -import static org.keycloak.common.util.ObjectUtil.capitalize; /** * @author Marko Strukelj @@ -69,7 +60,6 @@ public class HttpUtil { 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 UTF_8 = "utf-8"; - private static final String[] DEFAULT_QUERY_PARAMS = { "first", "0", "max", "2" }; private static HttpClient httpClient; private static SSLConnectionSocketFactory sslsf; @@ -436,57 +426,6 @@ public class HttpUtil { 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 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 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 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) { return value.substring(0, value.length()-1); } diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/IoUtil.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/IoUtil.java similarity index 99% rename from integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/IoUtil.java rename to integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/IoUtil.java index c2e49870b8..f2ececae02 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/IoUtil.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/IoUtil.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.client.admin.cli.util; +package org.keycloak.client.cli.util; import java.io.Console; import java.io.FileInputStream; diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/OsArch.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/OsArch.java similarity index 97% rename from integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/OsArch.java rename to integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/OsArch.java index 13cf424d87..6b63a8fa7b 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/OsArch.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/OsArch.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.client.admin.cli.util; +package org.keycloak.client.cli.util; /** * @author Marko Strukelj diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/OsUtil.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/OsUtil.java similarity index 93% rename from integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/OsUtil.java rename to integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/OsUtil.java index 3b70f47ad6..5398c7e2a2 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/OsUtil.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/OsUtil.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.client.admin.cli.util; +package org.keycloak.client.cli.util; /** * @author Marko Strukelj @@ -22,8 +22,6 @@ package org.keycloak.client.admin.cli.util; public class OsUtil { 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:\\>" : "$"; diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/OutputFormat.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/OutputFormat.java similarity index 94% rename from integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/OutputFormat.java rename to integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/OutputFormat.java index 31181cc296..64955f6c16 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/OutputFormat.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/OutputFormat.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.client.admin.cli.util; +package org.keycloak.client.cli.util; /** * @author Marko Strukelj diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/OutputUtil.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/OutputUtil.java similarity index 98% rename from integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/OutputUtil.java rename to integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/OutputUtil.java index 8e3c083e58..50a9c0740a 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/OutputUtil.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/OutputUtil.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * 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.databind.DeserializationFeature; diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/common/CmdStdinContext.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/ParseUtil.java similarity index 61% rename from integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/common/CmdStdinContext.java rename to integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/ParseUtil.java index 1c6752666a..2920ee8726 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/common/CmdStdinContext.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/ParseUtil.java @@ -14,31 +14,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.client.admin.cli.common; +package org.keycloak.client.cli.util; /** * @author Marko Strukelj */ -public class CmdStdinContext { +public class ParseUtil { - private T result; - private String content; + public static String[] parseKeyVal(String keyval) { + // we expect = as a separator + int pos = keyval.indexOf("="); + if (pos <= 0) { + throw new IllegalArgumentException("Invalid key=value parameter: [" + keyval + "]"); + } - public CmdStdinContext() {} + String [] parsed = new String[2]; + parsed[0] = keyval.substring(0, pos); + parsed[1] = keyval.substring(pos+1); - public T getResult() { - return result; - } - - public void setResult(T result) { - this.result = result; - } - - public String getContent() { - return content; - } - - public void setContent(String content) { - this.content = content; + return parsed; } } diff --git a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/ReturnFields.java b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/ReturnFields.java similarity index 99% rename from integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/ReturnFields.java rename to integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/ReturnFields.java index a185471457..0de307a24c 100644 --- a/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/admin/cli/util/ReturnFields.java +++ b/integration/client-cli/admin-cli/src/main/java/org/keycloak/client/cli/util/ReturnFields.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.client.admin.cli.util; +package org.keycloak.client.cli.util; import java.util.Collections; import java.util.HashMap; diff --git a/integration/client-cli/admin-cli/src/test/java/org/keycloak/client/admin/cli/util/ReflectionUtilTest.java b/integration/client-cli/admin-cli/src/test/java/org/keycloak/client/admin/cli/ReflectionUtilTest.java similarity index 97% rename from integration/client-cli/admin-cli/src/test/java/org/keycloak/client/admin/cli/util/ReflectionUtilTest.java rename to integration/client-cli/admin-cli/src/test/java/org/keycloak/client/admin/cli/ReflectionUtilTest.java index 76adcebe78..0ffd561db3 100644 --- a/integration/client-cli/admin-cli/src/test/java/org/keycloak/client/admin/cli/util/ReflectionUtilTest.java +++ b/integration/client-cli/admin-cli/src/test/java/org/keycloak/client/admin/cli/ReflectionUtilTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.keycloak.client.admin.cli.util; +package org.keycloak.client.admin.cli; import org.junit.Test; diff --git a/integration/client-cli/admin-cli/src/test/java/org/keycloak/client/admin/cli/util/MergeAttributesTest.java b/integration/client-cli/admin-cli/src/test/java/org/keycloak/client/cli/util/MergeAttributesTest.java similarity index 88% rename from integration/client-cli/admin-cli/src/test/java/org/keycloak/client/admin/cli/util/MergeAttributesTest.java rename to integration/client-cli/admin-cli/src/test/java/org/keycloak/client/cli/util/MergeAttributesTest.java index bb518e39f5..ab36853228 100644 --- a/integration/client-cli/admin-cli/src/test/java/org/keycloak/client/admin/cli/util/MergeAttributesTest.java +++ b/integration/client-cli/admin-cli/src/test/java/org/keycloak/client/cli/util/MergeAttributesTest.java @@ -14,22 +14,22 @@ * See the License for the specific language governing permissions and * 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.junit.Test; -import org.keycloak.client.admin.cli.common.AttributeOperation; -import org.keycloak.client.admin.cli.common.CmdStdinContext; +import org.keycloak.client.admin.cli.CmdStdinContext; +import org.keycloak.client.admin.cli.ReflectionUtil; +import org.keycloak.client.cli.common.AttributeOperation; import java.nio.charset.StandardCharsets; import java.util.LinkedList; import java.util.List; -import static org.keycloak.client.admin.cli.common.AttributeOperation.Type.DELETE; -import static org.keycloak.client.admin.cli.common.AttributeOperation.Type.SET; -import static org.keycloak.client.admin.cli.util.OutputUtil.MAPPER; -import static org.keycloak.client.admin.cli.util.ParseUtil.mergeAttributes; +import static org.keycloak.client.cli.common.AttributeOperation.Type.DELETE; +import static org.keycloak.client.cli.common.AttributeOperation.Type.SET; +import static org.keycloak.client.cli.util.OutputUtil.MAPPER; /** * @author Marko Strukelj @@ -71,7 +71,7 @@ public class MergeAttributesTest { CmdStdinContext ctx = new CmdStdinContext<>(); ctx.setResult(localNode); - ctx = mergeAttributes(ctx, MAPPER.createObjectNode(), attrs); + ctx = CmdStdinContext.mergeAttributes(ctx, MAPPER.createObjectNode(), attrs); System.out.println(ctx); String remoteJSON = "{\n" + diff --git a/integration/client-cli/admin-cli/src/test/java/org/keycloak/client/admin/cli/util/OuputUtilTest.java b/integration/client-cli/admin-cli/src/test/java/org/keycloak/client/cli/util/OuputUtilTest.java similarity index 91% rename from integration/client-cli/admin-cli/src/test/java/org/keycloak/client/admin/cli/util/OuputUtilTest.java rename to integration/client-cli/admin-cli/src/test/java/org/keycloak/client/cli/util/OuputUtilTest.java index d91b0b8e1a..6151018d5b 100644 --- a/integration/client-cli/admin-cli/src/test/java/org/keycloak/client/admin/cli/util/OuputUtilTest.java +++ b/integration/client-cli/admin-cli/src/test/java/org/keycloak/client/cli/util/OuputUtilTest.java @@ -15,9 +15,11 @@ * limitations under the License. */ -package org.keycloak.client.admin.cli.util; +package org.keycloak.client.cli.util; import org.junit.Test; +import org.keycloak.client.cli.util.OutputUtil; +import org.keycloak.client.cli.util.ReturnFields; import java.io.IOException; import java.util.ArrayList; diff --git a/integration/client-cli/admin-cli/src/test/java/org/keycloak/client/admin/cli/util/ReturnFieldsTest.java b/integration/client-cli/admin-cli/src/test/java/org/keycloak/client/cli/util/ReturnFieldsTest.java similarity index 98% rename from integration/client-cli/admin-cli/src/test/java/org/keycloak/client/admin/cli/util/ReturnFieldsTest.java rename to integration/client-cli/admin-cli/src/test/java/org/keycloak/client/cli/util/ReturnFieldsTest.java index bd2105be8f..218f6f120f 100644 --- a/integration/client-cli/admin-cli/src/test/java/org/keycloak/client/admin/cli/util/ReturnFieldsTest.java +++ b/integration/client-cli/admin-cli/src/test/java/org/keycloak/client/cli/util/ReturnFieldsTest.java @@ -14,10 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.keycloak.client.admin.cli.util; +package org.keycloak.client.cli.util; import org.junit.Assert; import org.junit.Test; +import org.keycloak.client.cli.util.ReturnFields; /** * @author Marko Strukelj diff --git a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/ParseUtil.java b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/CmdStdinContext.java similarity index 80% rename from integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/ParseUtil.java rename to integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/CmdStdinContext.java index 8d237dd987..4ed3b4cd2a 100644 --- a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/ParseUtil.java +++ b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/CmdStdinContext.java @@ -15,13 +15,10 @@ * 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.databind.exc.UnrecognizedPropertyException; -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.client.cli.common.AttributeOperation; +import org.keycloak.client.cli.util.AttributeException; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.oidc.OIDCClientRepresentation; import org.keycloak.util.JsonSerialization; @@ -29,46 +26,73 @@ import org.keycloak.util.JsonSerialization; import java.io.IOException; import java.util.List; -import static java.lang.System.arraycopy; -import static org.keycloak.client.registration.cli.util.IoUtil.readFileOrStdin; -import static org.keycloak.client.registration.cli.util.ReflectionUtil.setAttributes; +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException; + +import static org.keycloak.client.cli.util.IoUtil.readFileOrStdin; +import static org.keycloak.client.registration.cli.ReflectionUtil.setAttributes; /** * @author Marko Strukelj */ -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 TOKEN_OPTION_WARN = "You're using what looks like an OPTION as TOKEN: %s"; - public static String[] shift(String[] args) { - if (args.length == 1) - return new String[0]; - String [] nu = new String [args.length-1]; - arraycopy(args, 1, nu, 0, args.length-1); - return nu; + public CmdStdinContext() {} + + public EndpointType getEndpointType() { + return regType; } - public static String[] parseKeyVal(String keyval) { - // we expect = as a separator - int pos = keyval.indexOf("="); - if (pos <= 0) { - throw new RuntimeException("Invalid key=value parameter: [" + keyval + "]"); + 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(); } - - String [] parsed = new String[2]; - parsed[0] = keyval.substring(0, pos); - parsed[1] = keyval.substring(pos+1); - - return parsed; + return null; } public static CmdStdinContext parseFileOrStdin(String file, EndpointType type) { - + String content = readFileOrStdin(file).trim(); ClientRepresentation client = null; OIDCClientRepresentation oidcClient = null; - + if (type == null) { // guess the correct endpoint from content of the file if (content.startsWith("<")) { @@ -80,13 +104,13 @@ public class ParseUtil { try { client = JsonSerialization.readValue(content, ClientRepresentation.class); type = EndpointType.DEFAULT; - + } catch (JsonParseException e) { throw new RuntimeException("Failed to read the input document as JSON: " + e.getMessage(), e); } catch (Exception ignored) { // deliberately not logged } - + if (client == null) { // try parse as OIDCClientRepresentation try { @@ -98,14 +122,14 @@ public class ParseUtil { throw new RuntimeException("Failed to read the input document as JSON", e); } } - + } else if (content.length() == 0) { throw new RuntimeException("Document provided by --file option is empty"); } else { throw new RuntimeException("Unable to determine input document type. Use -e TYPE to specify the registration endpoint to use"); } } - + // check content type, making sure it can be parsed into .json if it's not saml xml if (content != null) { try { @@ -122,7 +146,7 @@ public class ParseUtil { throw new RuntimeException("Not a valid JSON document", e); } } - + CmdStdinContext ctx = new CmdStdinContext(); ctx.setEndpointType(type); ctx.setContent(content); @@ -158,7 +182,7 @@ public class ParseUtil { } catch (IOException e) { throw new RuntimeException("Failed to merge set attributes with configuration from file", e); } - + ctx.setContent(content); ctx.setClient(client); ctx.setOidcClient(oidcClient); diff --git a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/common/EndpointType.java b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/EndpointType.java similarity index 81% rename from integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/common/EndpointType.java rename to integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/EndpointType.java index 5114adacb0..698777f8db 100644 --- a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/common/EndpointType.java +++ b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/EndpointType.java @@ -15,7 +15,9 @@ * 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.HashSet; @@ -60,4 +62,16 @@ public enum EndpointType { public String getName() { 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); + } + } } diff --git a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/EndpointTypeConverter.java b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/EndpointTypeConverter.java index 80015edd23..276b2b2361 100644 --- a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/EndpointTypeConverter.java +++ b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/EndpointTypeConverter.java @@ -1,7 +1,5 @@ package org.keycloak.client.registration.cli; -import org.keycloak.client.registration.cli.common.EndpointType; - import picocli.CommandLine.ITypeConverter; /** diff --git a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/Globals.java b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/Globals.java deleted file mode 100644 index d266c7a953..0000000000 --- a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/Globals.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.keycloak.client.registration.cli; - -/** - * @author Marko Strukelj - */ -public class Globals { - - public static boolean dumpTrace = false; - - public static boolean help = false; - -} diff --git a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/KcRegMain.java b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/KcRegMain.java index 90330bf596..94b2a03905 100644 --- a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/KcRegMain.java +++ b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/KcRegMain.java @@ -1,46 +1,36 @@ package org.keycloak.client.registration.cli; -import org.keycloak.client.admin.cli.ExecutionExceptionHandler; -import org.keycloak.client.admin.cli.ShortErrorMessageHandler; +import org.keycloak.client.cli.common.CommandState; +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.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 Marko Strukelj */ public class KcRegMain { - public static void main(String [] args) { - String libDir = System.getProperty("kc.lib.dir"); - if (libDir == null) { - throw new RuntimeException("System property kc.lib.dir needs to be set"); + 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; } - ClassLoader cl = ClassLoaderUtil.resolveClassLoader(libDir); - Thread.currentThread().setContextClassLoader(cl); - CryptoIntegration.init(cl); + @Override + public String getDefaultConfigFilePath() { + return DEFAULT_CONFIG_FILE_PATH; + } - 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; + public static void main(String [] args) { + Globals.main(args, new KcRegCmd(), CMD, DEFAULT_CONFIG_FILE_STRING); } } diff --git a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/ReflectionUtil.java b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/ReflectionUtil.java similarity index 99% rename from integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/ReflectionUtil.java rename to integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/ReflectionUtil.java index 1fe500b82b..1e884884d0 100644 --- a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/ReflectionUtil.java +++ b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/ReflectionUtil.java @@ -15,11 +15,13 @@ * 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.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 java.io.IOException; diff --git a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/AbstractAuthOptionsCmd.java b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/AbstractAuthOptionsCmd.java index 436b2f2270..e6ceb00b0b 100644 --- a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/AbstractAuthOptionsCmd.java +++ b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/AbstractAuthOptionsCmd.java @@ -1,255 +1,22 @@ package org.keycloak.client.registration.cli.commands; -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.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 org.keycloak.client.cli.common.BaseAuthOptionsCmd; +import org.keycloak.client.registration.cli.KcRegMain; 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 Marko Strukelj */ -public abstract class AbstractAuthOptionsCmd extends AbstractGlobalOptionsCmd { - - 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; +public abstract class AbstractAuthOptionsCmd extends BaseAuthOptionsCmd { @Option(names = {"-t", "--token"}, description = "Initial / Registration access token to use)") - protected String 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; + public void setToken(String token) { + this.externalToken = token; } - protected void applyDefaultOptionValues() { - if (clientId == null) { - clientId = DEFAULT_CLIENT; - } + public AbstractAuthOptionsCmd() { + super(KcRegMain.COMMAND_STATE); } - 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; - } } diff --git a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/AttrsCmd.java b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/AttrsCmd.java index 9f3a036aef..5362a1ba16 100644 --- a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/AttrsCmd.java +++ b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/AttrsCmd.java @@ -1,8 +1,9 @@ package org.keycloak.client.registration.cli.commands; -import org.keycloak.client.registration.cli.common.AttributeKey; -import org.keycloak.client.registration.cli.common.EndpointType; -import org.keycloak.client.registration.cli.util.ReflectionUtil; +import org.keycloak.client.cli.common.AttributeKey; +import org.keycloak.client.cli.common.BaseGlobalOptionsCmd; +import org.keycloak.client.registration.cli.EndpointType; +import org.keycloak.client.registration.cli.ReflectionUtil; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.oidc.OIDCClientRepresentation; @@ -20,18 +21,18 @@ import picocli.CommandLine.Command; import picocli.CommandLine.Option; import picocli.CommandLine.Parameters; -import static org.keycloak.client.registration.cli.util.OsUtil.CMD; -import static org.keycloak.client.registration.cli.util.OsUtil.PROMPT; -import static org.keycloak.client.registration.cli.util.ReflectionUtil.getAttributeListWithJSonTypes; -import static org.keycloak.client.registration.cli.util.ReflectionUtil.isBasicType; -import static org.keycloak.client.registration.cli.util.ReflectionUtil.isListType; -import static org.keycloak.client.registration.cli.util.ReflectionUtil.isMapType; +import static org.keycloak.client.cli.util.OsUtil.PROMPT; +import static org.keycloak.client.registration.cli.KcRegMain.CMD; +import static org.keycloak.client.registration.cli.ReflectionUtil.getAttributeListWithJSonTypes; +import static org.keycloak.client.registration.cli.ReflectionUtil.isBasicType; +import static org.keycloak.client.registration.cli.ReflectionUtil.isListType; +import static org.keycloak.client.registration.cli.ReflectionUtil.isMapType; /** * @author Marko Strukelj */ @Command(name = "attrs", description = "[ATTRIBUTE] [--endpoint TYPE]") -public class AttrsCmd extends AbstractGlobalOptionsCmd { +public class AttrsCmd extends BaseGlobalOptionsCmd { CommandLine.Model.CommandSpec spec; diff --git a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/ConfigCmd.java b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/ConfigCmd.java index 22dbd3e1fe..3d127a144e 100644 --- a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/ConfigCmd.java +++ b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/ConfigCmd.java @@ -22,7 +22,7 @@ import java.io.StringWriter; import picocli.CommandLine.Command; -import static org.keycloak.client.registration.cli.util.OsUtil.CMD; +import static org.keycloak.client.registration.cli.KcRegMain.CMD; /** * @author Marko Strukelj diff --git a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/ConfigCredentialsCmd.java b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/ConfigCredentialsCmd.java index a0f16c9409..a9678f8d45 100644 --- a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/ConfigCredentialsCmd.java +++ b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/ConfigCredentialsCmd.java @@ -1,243 +1,18 @@ package org.keycloak.client.registration.cli.commands; -import org.keycloak.OAuth2Constants; -import org.keycloak.client.registration.cli.config.ConfigData; -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 org.keycloak.client.cli.common.BaseConfigCredentialsCmd; +import org.keycloak.client.registration.cli.KcRegMain; 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 Marko Strukelj */ @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 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(); - } + public ConfigCredentialsCmd() { + super(KcRegMain.COMMAND_STATE); } - @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(); - } } diff --git a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/ConfigInitialTokenCmd.java b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/ConfigInitialTokenCmd.java index 9bd9e72718..31fdcb0b3f 100644 --- a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/ConfigInitialTokenCmd.java +++ b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/ConfigInitialTokenCmd.java @@ -1,8 +1,9 @@ package org.keycloak.client.registration.cli.commands; -import org.keycloak.client.registration.cli.config.RealmConfigData; -import org.keycloak.client.registration.cli.util.IoUtil; -import org.keycloak.client.registration.cli.util.ParseUtil; +import org.keycloak.client.cli.config.RealmConfigData; +import org.keycloak.client.cli.util.IoUtil; +import org.keycloak.client.registration.cli.CmdStdinContext; +import org.keycloak.client.registration.cli.KcRegMain; import java.io.PrintWriter; import java.io.StringWriter; @@ -11,12 +12,11 @@ import picocli.CommandLine.Command; import picocli.CommandLine.Option; import picocli.CommandLine.Parameters; -import static org.keycloak.client.registration.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_STRING; -import static org.keycloak.client.registration.cli.util.ConfigUtil.saveMergeConfig; -import static org.keycloak.client.registration.cli.util.IoUtil.warnfOut; -import static org.keycloak.client.registration.cli.util.OsUtil.CMD; -import static org.keycloak.client.registration.cli.util.OsUtil.OS_ARCH; -import static org.keycloak.client.registration.cli.util.OsUtil.PROMPT; +import static org.keycloak.client.cli.util.ConfigUtil.saveMergeConfig; +import static org.keycloak.client.cli.util.IoUtil.warnfOut; +import static org.keycloak.client.cli.util.OsUtil.OS_ARCH; +import static org.keycloak.client.cli.util.OsUtil.PROMPT; +import static org.keycloak.client.registration.cli.KcRegMain.CMD; /** * @author Marko Strukelj @@ -34,7 +34,7 @@ public class ConfigInitialTokenCmd extends AbstractAuthOptionsCmd { @Override protected boolean nothingToDo() { - return noOptions() && token == null && !delete && !keepDomain; + return super.nothingToDo() && token == null && !delete && !keepDomain; } @Override @@ -60,7 +60,7 @@ public class ConfigInitialTokenCmd extends AbstractAuthOptionsCmd { } if (token != null && token.startsWith("-")) { - warnfOut(ParseUtil.TOKEN_OPTION_WARN, token); + warnfOut(CmdStdinContext.TOKEN_OPTION_WARN, token); } if (!delete && token == null) { @@ -106,7 +106,7 @@ public class ConfigInitialTokenCmd extends AbstractAuthOptionsCmd { 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(" --config Path to the config file (" + KcRegMain.DEFAULT_CONFIG_FILE_STRING + " by default)"); out.println(); out.println(" Command specific options:"); out.println(" --server SERVER Server endpoint url (e.g. 'http://localhost:8080')"); diff --git a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/ConfigRegistrationTokenCmd.java b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/ConfigRegistrationTokenCmd.java index 8d11b0a588..d78ddf56fc 100644 --- a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/ConfigRegistrationTokenCmd.java +++ b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/ConfigRegistrationTokenCmd.java @@ -1,7 +1,8 @@ package org.keycloak.client.registration.cli.commands; -import org.keycloak.client.registration.cli.config.RealmConfigData; -import org.keycloak.client.registration.cli.util.IoUtil; +import org.keycloak.client.registration.cli.KcRegMain; +import org.keycloak.client.cli.config.RealmConfigData; +import org.keycloak.client.cli.util.IoUtil; import java.io.PrintWriter; import java.io.StringWriter; @@ -10,11 +11,10 @@ import picocli.CommandLine.Command; import picocli.CommandLine.Option; import picocli.CommandLine.Parameters; -import static org.keycloak.client.registration.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_STRING; -import static org.keycloak.client.registration.cli.util.ConfigUtil.saveMergeConfig; -import static org.keycloak.client.registration.cli.util.OsUtil.CMD; -import static org.keycloak.client.registration.cli.util.OsUtil.OS_ARCH; -import static org.keycloak.client.registration.cli.util.OsUtil.PROMPT; +import static org.keycloak.client.cli.util.ConfigUtil.saveMergeConfig; +import static org.keycloak.client.cli.util.OsUtil.OS_ARCH; +import static org.keycloak.client.cli.util.OsUtil.PROMPT; +import static org.keycloak.client.registration.cli.KcRegMain.CMD; /** * @author Marko Strukelj @@ -30,7 +30,7 @@ public class ConfigRegistrationTokenCmd extends AbstractAuthOptionsCmd { @Override protected boolean nothingToDo() { - return noOptions() && token == null && !delete; + return super.nothingToDo() && token == null && !delete; } @Override @@ -97,7 +97,7 @@ public class ConfigRegistrationTokenCmd extends AbstractAuthOptionsCmd { 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(" --config Path to the config file (" + KcRegMain.DEFAULT_CONFIG_FILE_STRING + " by default)"); out.println(); out.println(" Command specific options:"); out.println(" --server SERVER Server endpoint url (e.g. 'http://localhost:8080')"); diff --git a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/ConfigTruststoreCmd.java b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/ConfigTruststoreCmd.java index 3e032515e7..2e199a9d99 100644 --- a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/ConfigTruststoreCmd.java +++ b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/ConfigTruststoreCmd.java @@ -16,131 +16,19 @@ */ package org.keycloak.client.registration.cli.commands; -import java.io.File; -import java.io.PrintWriter; -import java.io.StringWriter; +import org.keycloak.client.cli.common.BaseConfigTruststoreCmd; +import org.keycloak.client.registration.cli.KcRegMain; 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 Marko Strukelj */ @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") - private boolean delete; - - @Parameters(arity = "0..1") - private String truststorePath; - - @Override - protected boolean nothingToDo() { - return noOptions() && truststorePath == null && !delete; + public ConfigTruststoreCmd() { + super(KcRegMain.COMMAND_STATE); } - @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(); - } } diff --git a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/CreateCmd.java b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/CreateCmd.java index 605b88a1e9..fda405f3cf 100644 --- a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/CreateCmd.java +++ b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/CreateCmd.java @@ -17,12 +17,13 @@ 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.common.AttributeOperation; -import org.keycloak.client.registration.cli.common.CmdStdinContext; -import org.keycloak.client.registration.cli.common.EndpointType; -import org.keycloak.client.registration.cli.config.ConfigData; -import org.keycloak.client.registration.cli.util.HttpUtil; +import org.keycloak.client.registration.cli.KcRegMain; +import org.keycloak.client.cli.common.AttributeOperation; +import org.keycloak.client.cli.config.ConfigData; +import org.keycloak.client.cli.util.HttpUtil; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.oidc.OIDCClientRepresentation; import org.keycloak.util.JsonSerialization; @@ -39,28 +40,23 @@ import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException; import picocli.CommandLine.Command; import picocli.CommandLine.Option; -import static org.keycloak.client.registration.cli.common.AttributeOperation.Type.SET; -import static org.keycloak.client.registration.cli.common.EndpointType.DEFAULT; -import static org.keycloak.client.registration.cli.common.EndpointType.OIDC; -import static org.keycloak.client.registration.cli.common.EndpointType.SAML2; -import static org.keycloak.client.registration.cli.util.AuthUtil.ensureToken; -import static org.keycloak.client.registration.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_STRING; -import static org.keycloak.client.registration.cli.util.ConfigUtil.credentialsAvailable; -import static org.keycloak.client.registration.cli.util.ConfigUtil.loadConfig; -import static org.keycloak.client.registration.cli.util.ConfigUtil.saveMergeConfig; -import static org.keycloak.client.registration.cli.util.ConfigUtil.setRegistrationToken; -import static org.keycloak.client.registration.cli.util.HttpUtil.doPost; -import static org.keycloak.client.registration.cli.util.HttpUtil.getExpectedContentType; -import static org.keycloak.client.registration.cli.util.IoUtil.printErr; -import static org.keycloak.client.registration.cli.util.IoUtil.printOut; -import static org.keycloak.client.registration.cli.util.IoUtil.readFully; -import static org.keycloak.client.registration.cli.util.IoUtil.readSecret; -import static org.keycloak.client.registration.cli.util.OsUtil.CMD; -import static org.keycloak.client.registration.cli.util.OsUtil.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; +import static org.keycloak.client.cli.common.AttributeOperation.Type.SET; +import static org.keycloak.client.cli.util.ConfigUtil.credentialsAvailable; +import static org.keycloak.client.cli.util.ConfigUtil.loadConfig; +import static org.keycloak.client.cli.util.ConfigUtil.saveMergeConfig; +import static org.keycloak.client.cli.util.ConfigUtil.setRegistrationToken; +import static org.keycloak.client.cli.util.HttpUtil.doPost; +import static org.keycloak.client.cli.util.IoUtil.printErr; +import static org.keycloak.client.cli.util.IoUtil.printOut; +import static org.keycloak.client.cli.util.IoUtil.readFully; +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; +import static org.keycloak.client.cli.util.ParseUtil.parseKeyVal; +import static org.keycloak.client.registration.cli.EndpointType.DEFAULT; +import static org.keycloak.client.registration.cli.EndpointType.OIDC; +import static org.keycloak.client.registration.cli.EndpointType.SAML2; +import static org.keycloak.client.registration.cli.KcRegMain.CMD; /** * @author Marko Strukelj @@ -109,13 +105,13 @@ public class CreateCmd extends AbstractAuthOptionsCmd { } // if --token is specified read it - if ("-".equals(token)) { - token = readSecret("Enter Initial Access Token: "); + if ("-".equals(externalToken)) { + externalToken = readSecret("Enter Initial Access Token: "); } CmdStdinContext ctx = new CmdStdinContext(); if (file != null) { - ctx = parseFileOrStdin(file, regType); + ctx = CmdStdinContext.parseFileOrStdin(file, regType); } if (ctx.getEndpointType() == null) { @@ -126,22 +122,22 @@ public class CreateCmd extends AbstractAuthOptionsCmd { } 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(); config = copyWithServerInfo(config); - if (token == null) { + if (externalToken == null) { // if initial token is not set, try use the one from configuration - token = config.sessionRealmConfigData().getInitialToken(); + externalToken = config.sessionRealmConfigData().getInitialToken(); } setupTruststore(config); - String auth = token; + String auth = externalToken; if (auth == null) { config = ensureAuthInfo(config); config = copyWithServerInfo(config); @@ -199,7 +195,7 @@ public class CreateCmd extends AbstractAuthOptionsCmd { @Override protected boolean nothingToDo() { - return noOptions() && regType == null && file == null && rawSets.isEmpty(); + return super.nothingToDo() && regType == null && file == null && rawSets.isEmpty(); } @Override @@ -220,7 +216,7 @@ public class CreateCmd extends AbstractAuthOptionsCmd { 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(" --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(" --truststore PATH Path to a truststore containing trusted certificates"); out.println(" --trustpass PASSWORD Truststore password (prompted for if not specified and --truststore is used)"); diff --git a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/DeleteCmd.java b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/DeleteCmd.java index 3337cbb843..8011513ed1 100644 --- a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/DeleteCmd.java +++ b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/DeleteCmd.java @@ -17,8 +17,9 @@ package org.keycloak.client.registration.cli.commands; -import org.keycloak.client.registration.cli.config.ConfigData; -import org.keycloak.client.registration.cli.util.ParseUtil; +import org.keycloak.client.registration.cli.KcRegMain; +import org.keycloak.client.cli.config.ConfigData; +import org.keycloak.client.registration.cli.CmdStdinContext; import java.io.PrintWriter; import java.io.StringWriter; @@ -26,17 +27,15 @@ import java.io.StringWriter; import picocli.CommandLine.Command; import picocli.CommandLine.Parameters; -import static org.keycloak.client.registration.cli.util.AuthUtil.ensureToken; -import static org.keycloak.client.registration.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_STRING; -import static org.keycloak.client.registration.cli.util.ConfigUtil.credentialsAvailable; -import static org.keycloak.client.registration.cli.util.ConfigUtil.getRegistrationToken; -import static org.keycloak.client.registration.cli.util.ConfigUtil.loadConfig; -import static org.keycloak.client.registration.cli.util.ConfigUtil.saveMergeConfig; -import static org.keycloak.client.registration.cli.util.HttpUtil.doDelete; -import static org.keycloak.client.registration.cli.util.HttpUtil.urlencode; -import static org.keycloak.client.registration.cli.util.IoUtil.warnfErr; -import static org.keycloak.client.registration.cli.util.OsUtil.CMD; -import static org.keycloak.client.registration.cli.util.OsUtil.PROMPT; +import static org.keycloak.client.cli.util.ConfigUtil.credentialsAvailable; +import static org.keycloak.client.cli.util.ConfigUtil.getRegistrationToken; +import static org.keycloak.client.cli.util.ConfigUtil.loadConfig; +import static org.keycloak.client.cli.util.ConfigUtil.saveMergeConfig; +import static org.keycloak.client.cli.util.HttpUtil.doDelete; +import static org.keycloak.client.cli.util.HttpUtil.urlencode; +import static org.keycloak.client.cli.util.IoUtil.warnfErr; +import static org.keycloak.client.cli.util.OsUtil.PROMPT; +import static org.keycloak.client.registration.cli.KcRegMain.CMD; /** @@ -55,7 +54,7 @@ public class DeleteCmd extends AbstractAuthOptionsCmd { } if (clientId.startsWith("-")) { - warnfErr(ParseUtil.CLIENT_OPTION_WARN, clientId); + warnfErr(CmdStdinContext.CLIENT_OPTION_WARN, clientId); } String regType = "default"; @@ -63,14 +62,14 @@ public class DeleteCmd extends AbstractAuthOptionsCmd { ConfigData config = loadConfig(); config = copyWithServerInfo(config); - if (token == null) { + if (externalToken == null) { // 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); - String auth = token; + String auth = externalToken; if (auth == null) { config = ensureAuthInfo(config); config = copyWithServerInfo(config); @@ -94,7 +93,7 @@ public class DeleteCmd extends AbstractAuthOptionsCmd { @Override protected boolean nothingToDo() { - return noOptions() && clientId == null; + return super.nothingToDo() && clientId == null; } @Override @@ -114,7 +113,7 @@ public class DeleteCmd extends AbstractAuthOptionsCmd { 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(" --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(" --truststore PATH Path to a truststore containing trusted certificates"); out.println(" --trustpass PASSWORD Truststore password (prompted for if not specified and --truststore is used)"); diff --git a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/GetCmd.java b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/GetCmd.java index fd8ba9b5df..92dae5791d 100644 --- a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/GetCmd.java +++ b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/GetCmd.java @@ -17,9 +17,10 @@ package org.keycloak.client.registration.cli.commands; -import org.keycloak.client.registration.cli.config.ConfigData; -import org.keycloak.client.registration.cli.common.EndpointType; -import org.keycloak.client.registration.cli.util.ParseUtil; +import org.keycloak.client.registration.cli.KcRegMain; +import org.keycloak.client.cli.config.ConfigData; +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.idm.ClientRepresentation; import org.keycloak.representations.oidc.OIDCClientRepresentation; @@ -34,21 +35,19 @@ import picocli.CommandLine.Command; import picocli.CommandLine.Option; import picocli.CommandLine.Parameters; -import static org.keycloak.client.registration.cli.util.AuthUtil.ensureToken; -import static org.keycloak.client.registration.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_STRING; -import static org.keycloak.client.registration.cli.util.ConfigUtil.credentialsAvailable; -import static org.keycloak.client.registration.cli.util.ConfigUtil.getRegistrationToken; -import static org.keycloak.client.registration.cli.util.ConfigUtil.loadConfig; -import static org.keycloak.client.registration.cli.util.ConfigUtil.saveMergeConfig; -import static org.keycloak.client.registration.cli.util.ConfigUtil.setRegistrationToken; -import static org.keycloak.client.registration.cli.util.HttpUtil.APPLICATION_JSON; -import static org.keycloak.client.registration.cli.util.HttpUtil.doGet; -import static org.keycloak.client.registration.cli.util.HttpUtil.urlencode; -import static org.keycloak.client.registration.cli.util.IoUtil.warnfErr; -import static org.keycloak.client.registration.cli.util.IoUtil.printOut; -import static org.keycloak.client.registration.cli.util.IoUtil.readFully; -import static org.keycloak.client.registration.cli.util.OsUtil.CMD; -import static org.keycloak.client.registration.cli.util.OsUtil.PROMPT; +import static org.keycloak.client.cli.util.ConfigUtil.credentialsAvailable; +import static org.keycloak.client.cli.util.ConfigUtil.getRegistrationToken; +import static org.keycloak.client.cli.util.ConfigUtil.loadConfig; +import static org.keycloak.client.cli.util.ConfigUtil.saveMergeConfig; +import static org.keycloak.client.cli.util.ConfigUtil.setRegistrationToken; +import static org.keycloak.client.cli.util.HttpUtil.APPLICATION_JSON; +import static org.keycloak.client.cli.util.HttpUtil.doGet; +import static org.keycloak.client.cli.util.HttpUtil.urlencode; +import static org.keycloak.client.cli.util.IoUtil.printOut; +import static org.keycloak.client.cli.util.IoUtil.readFully; +import static org.keycloak.client.cli.util.IoUtil.warnfErr; +import static org.keycloak.client.cli.util.OsUtil.PROMPT; +import static org.keycloak.client.registration.cli.KcRegMain.CMD; /** * @author Marko Strukelj @@ -75,20 +74,20 @@ public class GetCmd extends AbstractAuthOptionsCmd { if (clientId.startsWith("-")) { - warnfErr(ParseUtil.CLIENT_OPTION_WARN, clientId); + warnfErr(CmdStdinContext.CLIENT_OPTION_WARN, clientId); } ConfigData config = loadConfig(); config = copyWithServerInfo(config); - if (token == null) { + if (externalToken == null) { // 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); - String auth = token; + String auth = externalToken; if (auth == null) { config = ensureAuthInfo(config); config = copyWithServerInfo(config); @@ -156,7 +155,7 @@ public class GetCmd extends AbstractAuthOptionsCmd { @Override protected boolean nothingToDo() { - return noOptions() && endpoint == null && clientId == null; + return super.nothingToDo() && endpoint == null && clientId == null; } @Override @@ -176,7 +175,7 @@ public class GetCmd extends AbstractAuthOptionsCmd { 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(" --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(" --truststore PATH Path to a truststore containing trusted certificates"); out.println(" --trustpass PASSWORD Truststore password (prompted for if not specified and --truststore is used)"); diff --git a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/HelpCmd.java b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/HelpCmd.java index ca8cc7e748..510c79302b 100644 --- a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/HelpCmd.java +++ b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/HelpCmd.java @@ -5,7 +5,7 @@ import java.util.List; import picocli.CommandLine.Command; import picocli.CommandLine.Parameters; -import static org.keycloak.client.registration.cli.util.IoUtil.printOut; +import static org.keycloak.client.cli.util.IoUtil.printOut; /** * @author Marko Strukelj @@ -27,7 +27,7 @@ public class HelpCmd implements Runnable { if (args.size() > 1) { switch (args.get(1)) { case "credentials": { - printOut(ConfigCredentialsCmd.usage()); + printOut(new ConfigCredentialsCmd().help()); break outer; } case "initial-token": { @@ -39,7 +39,7 @@ public class HelpCmd implements Runnable { break outer; } case "truststore": { - printOut(ConfigTruststoreCmd.usage()); + printOut(new ConfigTruststoreCmd().help()); break outer; } } diff --git a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/KcRegCmd.java b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/KcRegCmd.java index d2d2245abd..8e43fbc479 100644 --- a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/KcRegCmd.java +++ b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/KcRegCmd.java @@ -16,15 +16,16 @@ */ 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.StringWriter; import picocli.CommandLine.Command; -import static org.keycloak.client.registration.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_STRING; -import static org.keycloak.client.registration.cli.util.IoUtil.printOut; -import static org.keycloak.client.registration.cli.util.OsUtil.CMD; -import static org.keycloak.client.registration.cli.util.OsUtil.PROMPT; +import static org.keycloak.client.cli.util.OsUtil.PROMPT; +import static org.keycloak.client.registration.cli.KcRegMain.CMD; /** * @author Marko Strukelj @@ -49,13 +50,18 @@ subcommands = { AttrsCmd.class, UpdateTokenCmd.class }) -public class KcRegCmd extends AbstractGlobalOptionsCmd { - +public class KcRegCmd extends BaseGlobalOptionsCmd { + @Override protected boolean nothingToDo() { return true; } + @Override + protected String help() { + return usage(); + } + public static String usage() { StringWriter sb = new StringWriter(); PrintWriter out = new PrintWriter(sb); @@ -93,7 +99,7 @@ public class KcRegCmd extends AbstractGlobalOptionsCmd { out.println("Global options:"); out.println(" -x Print full stack trace when exiting with error"); 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(); out.println("Commands: "); diff --git a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/UpdateCmd.java b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/UpdateCmd.java index c5e3c9e480..f623692f28 100644 --- a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/UpdateCmd.java +++ b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/UpdateCmd.java @@ -24,13 +24,13 @@ import picocli.CommandLine.Command; import picocli.CommandLine.Option; 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.common.AttributeOperation; -import org.keycloak.client.registration.cli.config.ConfigData; -import org.keycloak.client.registration.cli.common.CmdStdinContext; -import org.keycloak.client.registration.cli.common.EndpointType; -import org.keycloak.client.registration.cli.util.ParseUtil; -import org.keycloak.client.registration.cli.util.ReflectionUtil; +import org.keycloak.client.registration.cli.ReflectionUtil; +import org.keycloak.client.registration.cli.KcRegMain; +import org.keycloak.client.cli.common.AttributeOperation; +import org.keycloak.client.cli.config.ConfigData; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.oidc.OIDCClientRepresentation; import org.keycloak.util.JsonSerialization; @@ -43,29 +43,25 @@ import java.util.ArrayList; import java.util.LinkedList; import java.util.List; -import static org.keycloak.client.registration.cli.common.AttributeOperation.Type.DELETE; -import static org.keycloak.client.registration.cli.common.AttributeOperation.Type.SET; -import static org.keycloak.client.registration.cli.util.AuthUtil.ensureToken; -import static org.keycloak.client.registration.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_STRING; -import static org.keycloak.client.registration.cli.util.ConfigUtil.credentialsAvailable; -import static org.keycloak.client.registration.cli.util.ConfigUtil.getRegistrationToken; -import static org.keycloak.client.registration.cli.util.ConfigUtil.loadConfig; -import static org.keycloak.client.registration.cli.util.ConfigUtil.saveMergeConfig; -import static org.keycloak.client.registration.cli.util.ConfigUtil.setRegistrationToken; -import static org.keycloak.client.registration.cli.common.EndpointType.DEFAULT; -import static org.keycloak.client.registration.cli.common.EndpointType.OIDC; -import static org.keycloak.client.registration.cli.util.HttpUtil.doGet; -import static org.keycloak.client.registration.cli.util.HttpUtil.doPut; -import static org.keycloak.client.registration.cli.util.HttpUtil.urlencode; -import static org.keycloak.client.registration.cli.util.IoUtil.printOut; -import static org.keycloak.client.registration.cli.util.IoUtil.warnfErr; -import static org.keycloak.client.registration.cli.util.IoUtil.readFully; -import static org.keycloak.client.registration.cli.util.HttpUtil.APPLICATION_JSON; -import static org.keycloak.client.registration.cli.util.OsUtil.CMD; -import static org.keycloak.client.registration.cli.util.OsUtil.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; +import static org.keycloak.client.cli.common.AttributeOperation.Type.DELETE; +import static org.keycloak.client.cli.common.AttributeOperation.Type.SET; +import static org.keycloak.client.cli.util.ConfigUtil.credentialsAvailable; +import static org.keycloak.client.cli.util.ConfigUtil.getRegistrationToken; +import static org.keycloak.client.cli.util.ConfigUtil.loadConfig; +import static org.keycloak.client.cli.util.ConfigUtil.saveMergeConfig; +import static org.keycloak.client.cli.util.ConfigUtil.setRegistrationToken; +import static org.keycloak.client.cli.util.HttpUtil.APPLICATION_JSON; +import static org.keycloak.client.cli.util.HttpUtil.doGet; +import static org.keycloak.client.cli.util.HttpUtil.doPut; +import static org.keycloak.client.cli.util.HttpUtil.urlencode; +import static org.keycloak.client.cli.util.IoUtil.printOut; +import static org.keycloak.client.cli.util.IoUtil.readFully; +import static org.keycloak.client.cli.util.IoUtil.warnfErr; +import static org.keycloak.client.cli.util.OsUtil.PROMPT; +import static org.keycloak.client.cli.util.ParseUtil.parseKeyVal; +import static org.keycloak.client.registration.cli.EndpointType.DEFAULT; +import static org.keycloak.client.registration.cli.EndpointType.OIDC; +import static org.keycloak.client.registration.cli.KcRegMain.CMD; /** * @author Marko Strukelj @@ -123,7 +119,7 @@ public class UpdateCmd extends AbstractAuthOptionsCmd { } if (clientId.startsWith("-")) { - warnfErr(ParseUtil.CLIENT_OPTION_WARN, clientId); + warnfErr(CmdStdinContext.CLIENT_OPTION_WARN, clientId); } if (file == null && attrs.size() == 0) { @@ -165,7 +161,7 @@ public class UpdateCmd extends AbstractAuthOptionsCmd { CmdStdinContext ctx = new CmdStdinContext(); if (file != null) { - ctx = parseFileOrStdin(file, regType); + ctx = CmdStdinContext.parseFileOrStdin(file, regType); regType = ctx.getEndpointType(); } @@ -185,7 +181,7 @@ public class UpdateCmd extends AbstractAuthOptionsCmd { final String server = config.getServerUrl(); 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 // but first see if it's overridden by --set, or maybe deliberately muted via -d registrationAccessToken boolean processed = false; @@ -193,25 +189,25 @@ public class UpdateCmd extends AbstractAuthOptionsCmd { if ("registrationAccessToken".equals(op.getKey().toString())) { processed = true; if (op.getType() == AttributeOperation.Type.SET) { - token = op.getValue(); + externalToken = op.getValue(); } // otherwise it's delete - meaning it should stay null break; } } 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 - token = getRegistrationToken(config.sessionRealmConfigData(), clientId); + externalToken = getRegistrationToken(config.sessionRealmConfigData(), clientId); } setupTruststore(config); - String auth = token; + String auth = externalToken; if (auth == null) { config = ensureAuthInfo(config); config = copyWithServerInfo(config); @@ -236,10 +232,10 @@ public class UpdateCmd extends AbstractAuthOptionsCmd { if (regType == DEFAULT) { ctxremote.setClient(JsonSerialization.readValue(json, ClientRepresentation.class)); - token = ctxremote.getClient().getRegistrationAccessToken(); + externalToken = ctxremote.getClient().getRegistrationAccessToken(); } else if (regType == OIDC) { ctxremote.setOidcClient(JsonSerialization.readValue(json, OIDCClientRepresentation.class)); - token = ctxremote.getOidcClient().getRegistrationAccessToken(); + externalToken = ctxremote.getOidcClient().getRegistrationAccessToken(); } } catch (JsonParseException 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 // that ensures optimistic locking semantics - if (token != null) { + if (externalToken != null) { // we use auth with doPost later - auth = "Bearer " + token; + auth = "Bearer " + externalToken; - String newToken = token; + String newToken = externalToken; String clientToUpdate = clientId; saveMergeConfig(cfg -> { setRegistrationToken(cfg.ensureRealmConfigData(server, realm), clientToUpdate, newToken); @@ -270,7 +266,7 @@ public class UpdateCmd extends AbstractAuthOptionsCmd { } if (attrs.size() > 0) { - ctx = mergeAttributes(ctx, attrs); + ctx = CmdStdinContext.mergeAttributes(ctx, attrs); } // now update @@ -280,14 +276,14 @@ public class UpdateCmd extends AbstractAuthOptionsCmd { if (regType == DEFAULT) { ClientRepresentation clirep = JsonSerialization.readValue(response, ClientRepresentation.class); outputResult(clirep); - token = clirep.getRegistrationAccessToken(); + externalToken = clirep.getRegistrationAccessToken(); } else if (regType == OIDC) { OIDCClientRepresentation clirep = JsonSerialization.readValue(response, OIDCClientRepresentation.class); outputResult(clirep); - token = clirep.getRegistrationAccessToken(); + externalToken = clirep.getRegistrationAccessToken(); } - String newToken = token; + String newToken = externalToken; String clientToUpdate = clientId; saveMergeConfig(cfg -> { setRegistrationToken(cfg.ensureRealmConfigData(server, realm), clientToUpdate, newToken); @@ -310,7 +306,7 @@ public class UpdateCmd extends AbstractAuthOptionsCmd { @Override 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 @@ -332,7 +328,7 @@ public class UpdateCmd extends AbstractAuthOptionsCmd { 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(" --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(" --truststore PATH Path to a truststore containing trusted certificates"); out.println(" --trustpass PASSWORD Truststore password (prompted for if not specified and --truststore is used)"); diff --git a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/UpdateTokenCmd.java b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/UpdateTokenCmd.java index 85f35054b2..219ae8fad6 100644 --- a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/UpdateTokenCmd.java +++ b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/commands/UpdateTokenCmd.java @@ -22,8 +22,9 @@ import com.fasterxml.jackson.core.type.TypeReference; import picocli.CommandLine.Command; import picocli.CommandLine.Parameters; -import org.keycloak.client.registration.cli.config.ConfigData; -import org.keycloak.client.registration.cli.util.ParseUtil; +import org.keycloak.client.registration.cli.KcRegMain; +import org.keycloak.client.cli.config.ConfigData; +import org.keycloak.client.registration.cli.CmdStdinContext; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.util.JsonSerialization; @@ -33,18 +34,16 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.util.List; -import static org.keycloak.client.registration.cli.util.AuthUtil.ensureToken; -import static org.keycloak.client.registration.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_STRING; -import static org.keycloak.client.registration.cli.util.ConfigUtil.loadConfig; -import static org.keycloak.client.registration.cli.util.ConfigUtil.saveMergeConfig; -import static org.keycloak.client.registration.cli.util.ConfigUtil.setRegistrationToken; -import static org.keycloak.client.registration.cli.util.HttpUtil.APPLICATION_JSON; -import static org.keycloak.client.registration.cli.util.HttpUtil.doGet; -import static org.keycloak.client.registration.cli.util.HttpUtil.doPost; -import static org.keycloak.client.registration.cli.util.IoUtil.printOut; -import static org.keycloak.client.registration.cli.util.IoUtil.warnfOut; -import static org.keycloak.client.registration.cli.util.OsUtil.CMD; -import static org.keycloak.client.registration.cli.util.OsUtil.PROMPT; +import static org.keycloak.client.cli.util.ConfigUtil.loadConfig; +import static org.keycloak.client.cli.util.ConfigUtil.saveMergeConfig; +import static org.keycloak.client.cli.util.ConfigUtil.setRegistrationToken; +import static org.keycloak.client.cli.util.HttpUtil.APPLICATION_JSON; +import static org.keycloak.client.cli.util.HttpUtil.doGet; +import static org.keycloak.client.cli.util.HttpUtil.doPost; +import static org.keycloak.client.cli.util.IoUtil.printOut; +import static org.keycloak.client.cli.util.IoUtil.warnfOut; +import static org.keycloak.client.cli.util.OsUtil.PROMPT; +import static org.keycloak.client.registration.cli.KcRegMain.CMD; /** * @author Marko Strukelj @@ -62,7 +61,7 @@ public class UpdateTokenCmd extends AbstractAuthOptionsCmd { } if (clientId.startsWith("-")) { - warnfOut(ParseUtil.CLIENT_OPTION_WARN, clientId); + warnfOut(CmdStdinContext.CLIENT_OPTION_WARN, clientId); } ConfigData config = loadConfig(); @@ -116,7 +115,7 @@ public class UpdateTokenCmd extends AbstractAuthOptionsCmd { @Override protected boolean nothingToDo() { - return noOptions() && clientId == null; + return super.nothingToDo() && clientId == null; } @Override @@ -136,7 +135,7 @@ public class UpdateTokenCmd extends AbstractAuthOptionsCmd { 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(" --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(" --truststore PATH Path to a truststore containing trusted certificates"); out.println(" --trustpass PASSWORD Truststore password (prompted for if not specified and --truststore is used)"); diff --git a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/common/AttributeKey.java b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/common/AttributeKey.java deleted file mode 100644 index 49e3ab4470..0000000000 --- a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/common/AttributeKey.java +++ /dev/null @@ -1,154 +0,0 @@ -package org.keycloak.client.registration.cli.common; - -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; - -/** - * @author Marko Strukelj - */ -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 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 parse(String key) { - - if (key == null || "".equals(key)) { - return Collections.emptyList(); - } - - List 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 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 + "]" : ""); - } - } -} diff --git a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/common/AttributeOperation.java b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/common/AttributeOperation.java deleted file mode 100644 index cfc3a87d02..0000000000 --- a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/common/AttributeOperation.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.keycloak.client.registration.cli.common; - -/** - * @author Marko Strukelj - */ -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 - } -} diff --git a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/common/CmdStdinContext.java b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/common/CmdStdinContext.java deleted file mode 100644 index b3b31a91cf..0000000000 --- a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/common/CmdStdinContext.java +++ /dev/null @@ -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 Marko Strukelj - */ -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; - } -} diff --git a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/common/ParsingContext.java b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/common/ParsingContext.java deleted file mode 100644 index f5fbb78044..0000000000 --- a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/common/ParsingContext.java +++ /dev/null @@ -1,113 +0,0 @@ -package org.keycloak.client.registration.cli.common; - - -/** - * An iterator wrapping command line - * - * @author Marko Strukelj - */ -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; - } -} diff --git a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/config/ConfigData.java b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/config/ConfigData.java deleted file mode 100644 index 0ae56e0441..0000000000 --- a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/config/ConfigData.java +++ /dev/null @@ -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 Marko Strukelj - */ -public class ConfigData { - - private String serverUrl; - - private String realm; - - private String truststore; - - private String trustpass; - - private Map> 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> getEndpoints() { - return endpoints; - } - - public void setEndpoints(Map> endpoints) { - for (Map.Entry> entry: endpoints.entrySet()) { - String endpoint = entry.getKey(); - for (Map.Entry 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 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 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> item: endpoints.entrySet()) { - - Map nuitems = new HashMap<>(); - Map curitems = item.getValue(); - - if (curitems != null) { - for (Map.Entry 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(); - } - } -} diff --git a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/config/ConfigHandler.java b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/config/ConfigHandler.java deleted file mode 100644 index 2b33c04560..0000000000 --- a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/config/ConfigHandler.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.keycloak.client.registration.cli.config; - -/** - * @author Marko Strukelj - */ -public interface ConfigHandler { - - void saveMergeConfig(ConfigUpdateOperation op); - - ConfigData loadConfig(); - -} diff --git a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/config/ConfigUpdateOperation.java b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/config/ConfigUpdateOperation.java deleted file mode 100644 index 98b0c85e33..0000000000 --- a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/config/ConfigUpdateOperation.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.keycloak.client.registration.cli.config; - -/** - * @author Marko Strukelj - */ -public interface ConfigUpdateOperation { - - void update(ConfigData data); - -} diff --git a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/config/FileConfigHandler.java b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/config/FileConfigHandler.java deleted file mode 100644 index 46e7a002ac..0000000000 --- a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/config/FileConfigHandler.java +++ /dev/null @@ -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 Marko Strukelj - */ -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); - } - } -} diff --git a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/config/InMemoryConfigHandler.java b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/config/InMemoryConfigHandler.java deleted file mode 100644 index 1d572b2005..0000000000 --- a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/config/InMemoryConfigHandler.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.keycloak.client.registration.cli.config; - - -/** - * @author Marko Strukelj - */ -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; - } -} diff --git a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/AttributeException.java b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/AttributeException.java deleted file mode 100644 index 30f3caa775..0000000000 --- a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/AttributeException.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.keycloak.client.registration.cli.util; - -/** - * @author Marko Strukelj - */ -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; - } -} diff --git a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/AuthUtil.java b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/AuthUtil.java deleted file mode 100644 index be0107b2da..0000000000 --- a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/AuthUtil.java +++ /dev/null @@ -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 Marko Strukelj - */ -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; - } -} diff --git a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/ClassLoaderUtil.java b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/ClassLoaderUtil.java deleted file mode 100644 index 72e2b2f5f2..0000000000 --- a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/ClassLoaderUtil.java +++ /dev/null @@ -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 Marek Posolda - */ -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()); - } - -} diff --git a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/ConfigUtil.java b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/ConfigUtil.java deleted file mode 100644 index 8d1050f2f8..0000000000 --- a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/ConfigUtil.java +++ /dev/null @@ -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 Marko Strukelj - */ -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); - } -} diff --git a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/DebugBufferedInputStream.java b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/DebugBufferedInputStream.java deleted file mode 100644 index fa200d660e..0000000000 --- a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/DebugBufferedInputStream.java +++ /dev/null @@ -1,78 +0,0 @@ -package org.keycloak.client.registration.cli.util; - -import java.io.BufferedInputStream; -import java.io.IOException; -import java.io.InputStream; - -/** - * @author Marko Strukelj - */ -public class DebugBufferedInputStream extends BufferedInputStream { - - public DebugBufferedInputStream(InputStream in) { - super(in); - } - - @Override - public synchronized int read() throws IOException { - log("read() >>>"); - int b = super.read(); - log("read() <<< " + (char) b + " (" + b + ")"); - return b; - } - - @Override - public synchronized int read(byte[] b, int off, int len) throws IOException { - log("read(buf, off, len) >>>"); - int c = super.read(b, off, len); - log("read(buf, off, len) <<< " + (c != -1 ? "[" + new String(b, off, c) + "]" : "-1")); - return c; - } - - @Override - public synchronized long skip(long n) throws IOException { - log("skip()"); - return super.skip(n); - } - - @Override - public synchronized int available() throws IOException { - log("available() >>>"); - int c = super.available(); - log("available() >>> " + c); - return c; - } - - @Override - public synchronized void mark(int readlimit) { - log("mark()"); - super.mark(readlimit); - } - - @Override - public synchronized void reset() throws IOException { - log("reset()"); - super.reset(); - } - - @Override - public boolean markSupported() { - log("markSupported()"); - return super.markSupported(); - } - - @Override - public void close() throws IOException { - log("close()"); - super.close(); - } - - @Override - public int read(byte[] b) throws IOException { - return read(b, 0, b.length); - } - - private void log(String msg) { - System.err.println(msg); - } -} diff --git a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/HttpUtil.java b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/HttpUtil.java deleted file mode 100644 index 6f2c58f764..0000000000 --- a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/HttpUtil.java +++ /dev/null @@ -1,210 +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.apache.http.Header; -import org.apache.http.HttpHeaders; -import org.apache.http.HttpResponse; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpDelete; -import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.methods.HttpPut; -import org.apache.http.client.methods.HttpRequestBase; -import org.apache.http.conn.ssl.SSLConnectionSocketFactory; -import org.apache.http.conn.ssl.TrustSelfSignedStrategy; -import org.apache.http.ssl.SSLContextBuilder; -import org.apache.http.ssl.SSLContexts; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.HttpClientBuilder; -import org.keycloak.client.registration.cli.common.EndpointType; -import org.keycloak.util.JsonSerialization; - -import javax.net.ssl.SSLContext; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.security.KeyManagementException; - -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.cert.CertificateException; -import java.util.Map; -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * @author Marko Strukelj - */ -public class HttpUtil { - - public static final String APPLICATION_XML = "application/xml"; - 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 UTF_8 = "utf-8"; - - private static HttpClient httpClient; - private static SSLConnectionSocketFactory sslsf; - private static final AtomicBoolean tlsWarningEmitted = new AtomicBoolean(); - - public static InputStream doGet(String url, String acceptType, String authorization) { - try { - HttpGet request = new HttpGet(url); - request.setHeader(HttpHeaders.ACCEPT, acceptType); - return doRequest(authorization, request); - } catch (IOException e) { - throw new RuntimeException("Failed to send request - " + e.getMessage(), e); - } - } - - public static InputStream doPost(String url, String contentType, String acceptType, String content, String authorization) { - try { - return doPostOrPut(contentType, acceptType, content, authorization, new HttpPost(url)); - } catch (IOException e) { - throw new RuntimeException("Failed to send request - " + e.getMessage(), e); - } - } - - public static InputStream doPut(String url, String contentType, String acceptType, String content, String authorization) { - try { - return doPostOrPut(contentType, acceptType, content, authorization, new HttpPut(url)); - } catch (IOException e) { - throw new RuntimeException("Failed to send request - " + e.getMessage(), e); - } - } - - public static void doDelete(String url, String authorization) { - try { - HttpDelete request = new HttpDelete(url); - doRequest(authorization, request); - } catch (IOException e) { - throw new RuntimeException("Failed to send request - " + e.getMessage(), e); - } - } - - private static InputStream doPostOrPut(String contentType, String acceptType, String content, String authorization, HttpEntityEnclosingRequestBase request) throws IOException { - request.setHeader(HttpHeaders.CONTENT_TYPE, contentType); - request.setHeader(HttpHeaders.ACCEPT, acceptType); - if (content != null) { - request.setEntity(new StringEntity(content)); - } - - return doRequest(authorization, request); - } - - private static InputStream doRequest(String authorization, HttpRequestBase request) throws IOException { - addAuth(request, authorization); - - HttpResponse response = getHttpClient().execute(request); - InputStream responseStream = null; - if (response.getEntity() != null) { - responseStream = response.getEntity().getContent(); - } - - int code = response.getStatusLine().getStatusCode(); - if (code >= 200 && code < 300) { - return responseStream; - } else { - Map error = null; - try { - Header header = response.getEntity().getContentType(); - if (header != null && APPLICATION_JSON.equals(header.getValue())) { - error = JsonSerialization.readValue(responseStream, Map.class); - } - } catch (Exception e) { - throw new RuntimeException("Failed to read error response - " + e.getMessage(), e); - } finally { - responseStream.close(); - } - - String message = null; - if (error != null) { - message = error.get("error_description") + " [" + error.get("error") + "]"; - } - throw new RuntimeException(message != null ? message : response.getStatusLine().getStatusCode() + " " + response.getStatusLine().getReasonPhrase()); - } - } - - private static void addAuth(HttpRequestBase request, String authorization) { - if (authorization != null) { - request.setHeader(HttpHeaders.AUTHORIZATION, authorization); - } - } - - public static HttpClient getHttpClient() { - if (httpClient == null) { - if (sslsf != null) { - httpClient = HttpClientBuilder.create().useSystemProperties().setSSLSocketFactory(sslsf).build(); - } else { - httpClient = HttpClientBuilder.create().useSystemProperties().build(); - } - } - return httpClient; - } - - public static String getExpectedContentType(EndpointType type) { - switch (type) { - case DEFAULT: - case OIDC: - return APPLICATION_JSON; - case SAML2: - return APPLICATION_XML; - default: - throw new RuntimeException("Unsupported endpoint type: " + type); - } - } - - public static String urlencode(String value) { - try { - return URLEncoder.encode(value, UTF_8); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException("Failed to urlencode", e); - } - } - - public static void setTruststore(File file, String password) throws CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException, KeyManagementException { - if (!file.isFile()) { - throw new RuntimeException("Truststore file not found: " + file.getAbsolutePath()); - } - SSLContext theContext = SSLContexts.custom() - .useProtocol("TLS") - .loadTrustMaterial(file, password == null ? null : password.toCharArray(), TrustSelfSignedStrategy.INSTANCE) - .build(); - sslsf = new SSLConnectionSocketFactory(theContext); - } - - public static void setSkipCertificateValidation() { - if (!tlsWarningEmitted.getAndSet(true)) { - // Since this is a static util, it may happen that TLS is setup many times in one command - // invocation (e.g. when a command requires logging in). However, we would like to - // prevent this warning from appearing multiple times. That's why we need to guard it with a boolean. - System.err.println("The server is configured to use TLS but there is no truststore specified."); - System.err.println("The tool will skip certificate validation. This is highly discouraged for production use cases"); - } - - SSLContextBuilder builder = new SSLContextBuilder(); - try { - builder.loadTrustMaterial(null, new TrustSelfSignedStrategy()); - sslsf = new SSLConnectionSocketFactory(builder.build()); - } catch (Exception e) { - throw new RuntimeException("Failed setting up TLS", e); - } - } -} diff --git a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/IoUtil.java b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/IoUtil.java deleted file mode 100644 index e1c38d38c8..0000000000 --- a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/IoUtil.java +++ /dev/null @@ -1,198 +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 java.io.Console; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.Charset; -import java.nio.file.FileSystem; -import java.nio.file.FileSystems; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.attribute.AclEntry; -import java.nio.file.attribute.AclEntryPermission; -import java.nio.file.attribute.AclEntryType; -import java.nio.file.attribute.AclFileAttributeView; -import java.nio.file.attribute.PosixFilePermission; -import java.nio.file.attribute.UserPrincipal; -import java.util.Formatter; -import java.util.HashSet; -import java.util.List; -import java.util.ListIterator; -import java.util.Set; - -import static java.nio.file.Files.createDirectories; -import static java.nio.file.Files.createFile; -import static java.nio.file.Files.isDirectory; -import static java.nio.file.Files.isRegularFile; - -/** - * @author Marko Strukelj - */ -public class IoUtil { - - public static String readFileOrStdin(String file) { - String content; - if ("-".equals(file)) { - content = readFully(System.in); - } else { - try (InputStream is = new FileInputStream(file)) { - content = readFully(is); - } catch (FileNotFoundException e) { - throw new RuntimeException("File not found: " + file); - } catch (IOException e) { - throw new RuntimeException("Failed to read file: " + file, e); - } - } - return content; - } - - public static void waitFor(long millis) { - try { - Thread.sleep(millis); - } catch (InterruptedException e) { - throw new RuntimeException("Interrupted"); - } - } - - public static String readSecret(String prompt) { - Console cons; - char[] passwd; - if ((cons = System.console()) != null && - (passwd = cons.readPassword("%s", prompt)) != null) { - return new String(passwd); - } - throw new RuntimeException("Console is not active, or no password provided"); - } - - public static String readFully(InputStream is) { - Charset charset = Charset.forName("utf-8"); - StringBuilder out = new StringBuilder(); - byte [] buf = new byte[8192]; - - int rc; - try { - while ((rc = is.read(buf)) != -1) { - out.append(new String(buf, 0, rc, charset)); - } - } catch (Exception e) { - throw new RuntimeException("Failed to read stream", e); - } - return out.toString(); - } - - public static void ensureFile(Path path) throws IOException { - - FileSystem fs = FileSystems.getDefault(); - Set supportedViews = fs.supportedFileAttributeViews(); - Path parent = path.getParent(); - - if (!isDirectory(parent)) { - createDirectories(parent); - // make sure only owner can read/write it - if (supportedViews.contains("posix")) { - setUnixPermissions(parent); - } else if (supportedViews.contains("acl")) { - setWindowsPermissions(parent); - } else { - warnErr("Failed to restrict access permissions on .keycloak directory: " + parent); - } - } - if (!isRegularFile(path)) { - createFile(path); - // make sure only owner can read/write it - if (FileSystems.getDefault().supportedFileAttributeViews().contains("posix")) { - setUnixPermissions(path); - } else if (supportedViews.contains("acl")) { - setWindowsPermissions(path); - } else { - warnErr("Failed to restrict access permissions on config file: " + path); - } - } - } - - private static void setUnixPermissions(Path path) throws IOException { - Set perms = new HashSet<>(); - perms.add(PosixFilePermission.OWNER_READ); - perms.add(PosixFilePermission.OWNER_WRITE); - if (isDirectory(path)) { - perms.add(PosixFilePermission.OWNER_EXECUTE); - } - Files.setPosixFilePermissions(path, perms); - } - - private static void setWindowsPermissions(Path path) throws IOException { - AclFileAttributeView view = Files.getFileAttributeView(path, AclFileAttributeView.class); - UserPrincipal owner = view.getOwner(); - List acl = view.getAcl(); - ListIterator it = acl.listIterator(); - while (it.hasNext()) { - AclEntry entry = it.next(); - if ("BUILTIN\\Administrators".equals(entry.principal().getName()) || "NT AUTHORITY\\SYSTEM".equals(entry.principal().getName())) { - continue; - } - it.remove(); - } - AclEntry entry = AclEntry.newBuilder() - .setType(AclEntryType.ALLOW) - .setPrincipal(owner) - .setPermissions(AclEntryPermission.READ_DATA, AclEntryPermission.WRITE_DATA, - AclEntryPermission.APPEND_DATA, AclEntryPermission.READ_NAMED_ATTRS, - AclEntryPermission.WRITE_NAMED_ATTRS, AclEntryPermission.EXECUTE, - AclEntryPermission.READ_ATTRIBUTES, AclEntryPermission.WRITE_ATTRIBUTES, - AclEntryPermission.DELETE, AclEntryPermission.READ_ACL, AclEntryPermission.SYNCHRONIZE) - .build(); - acl.add(entry); - view.setAcl(acl); - } - - public static void printOut(String msg) { - System.out.println(msg); - } - - public static void printErr(String msg) { - System.err.println(msg); - } - - public static void printfOut(String format, String ... params) { - System.out.println(new Formatter().format("WARN: " + format, params)); - } - - public static void warnOut(String msg) { - System.out.println("WARN: " + msg); - } - - public static void warnErr(String msg) { - System.err.println("WARN: " + msg); - } - - public static void warnfOut(String format, String ... params) { - System.out.println(new Formatter().format("WARN: " + format, params)); - } - - public static void warnfErr(String format, String ... params) { - System.err.println(new Formatter().format("WARN: " + format, params)); - } - - public static void logOut(String msg) { - System.out.println("LOG: " + msg); - } -} diff --git a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/OsArch.java b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/OsArch.java deleted file mode 100644 index 823d1fbb07..0000000000 --- a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/OsArch.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2014 Red Hat, Inc. and/or its affiliates. - * - * Licensed under the Eclipse Public License version 1.0, available at http://www.eclipse.org/legal/epl-v10.html - */ -package org.keycloak.client.registration.cli.util; - -/** - * @author Marko Strukelj - */ -public class OsArch { - - private String os; - private String arch; - private boolean legacy; - - public OsArch(String os, String arch) { - this(os, arch, false); - } - - public OsArch(String os, String arch, boolean legacy) { - this.os = os; - this.arch = arch; - this.legacy = legacy; - } - - public String os() { - return os; - } - - public String arch() { - return arch; - } - - public boolean isLegacy() { - return legacy; - } - - public boolean isWindows() { - return "win32".equals(os); - } - - public String envVar(String var) { - if (isWindows()) { - return "%" + var + "%"; - } else { - return "$" + var; - } - } - - public String path(String path) { - if (isWindows()) { - path = path.replaceAll("/", "\\\\"); - if (path.startsWith("~")) { - path = "%HOMEPATH%" + path.substring(1); - } - } - return path; - } -} \ No newline at end of file diff --git a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/OsUtil.java b/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/OsUtil.java deleted file mode 100644 index 1dd54b1348..0000000000 --- a/integration/client-cli/client-registration-cli/src/main/java/org/keycloak/client/registration/cli/util/OsUtil.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.keycloak.client.registration.cli.util; - -/** - * @author Marko Strukelj - */ -public class OsUtil { - - public static final OsArch OS_ARCH = determineOSAndArch(); - - public static final String CMD = OS_ARCH.isWindows() ? "kcreg.bat" : "kcreg.sh"; - - public static final String PROMPT = OS_ARCH.isWindows() ? "c:\\>" : "$"; - - public static final String EOL = OS_ARCH.isWindows() ? "\r\n" : "\n"; - - - public static OsArch determineOSAndArch() { - String os = System.getProperty("os.name").toLowerCase(); - String arch = System.getProperty("os.arch"); - - if (arch.equals("amd64")) { - arch = "x86_64"; - } - - if (os.startsWith("linux")) { - if (arch.equals("x86") || arch.equals("i386") || arch.equals("i586")) { - arch = "i686"; - } - return new OsArch("linux", arch); - } else if (os.startsWith("windows")) { - if (arch.equals("x86")) { - arch = "i386"; - } - if (os.indexOf("2008") != -1 || os.indexOf("2003") != -1 || os.indexOf("vista") != -1) { - return new OsArch("win32", arch, true); - } else { - return new OsArch("win32", arch); - } - } else if (os.startsWith("sunos")) { - return new OsArch("sunos5", "x86_64"); - } else if (os.startsWith("mac os x")) { - return new OsArch("osx", "x86_64"); - } else if (os.startsWith("freebsd")) { - return new OsArch("freebsd", arch); - } else if (os.startsWith("openbsd")) { - return new OsArch("openbsd", arch); - } - - // unsupported platform - throw new RuntimeException("Could not determine OS and architecture for this operating system: " + os); - } -} diff --git a/integration/client-cli/client-registration-cli/src/test/java/org/keycloak/client/registration/cli/util/ReflectionUtilTest.java b/integration/client-cli/client-registration-cli/src/test/java/org/keycloak/client/registration/cli/ReflectionUtilTest.java similarity index 96% rename from integration/client-cli/client-registration-cli/src/test/java/org/keycloak/client/registration/cli/util/ReflectionUtilTest.java rename to integration/client-cli/client-registration-cli/src/test/java/org/keycloak/client/registration/cli/ReflectionUtilTest.java index 2a20e54bcd..85423f0c31 100644 --- a/integration/client-cli/client-registration-cli/src/test/java/org/keycloak/client/registration/cli/util/ReflectionUtilTest.java +++ b/integration/client-cli/client-registration-cli/src/test/java/org/keycloak/client/registration/cli/ReflectionUtilTest.java @@ -1,11 +1,12 @@ -package org.keycloak.client.registration.cli.util; +package org.keycloak.client.registration.cli; import org.junit.Assert; import org.junit.Ignore; import org.junit.Test; -import org.keycloak.client.registration.cli.common.AttributeKey; -import org.keycloak.client.registration.cli.common.AttributeKey.Component; -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.common.AttributeKey.Component; +import org.keycloak.client.registration.cli.ReflectionUtil; import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; @@ -18,8 +19,8 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; -import static org.keycloak.client.registration.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.DELETE; +import static org.keycloak.client.cli.common.AttributeOperation.Type.SET; /** * @author Marko Strukelj diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/cli/exec/AbstractExec.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/cli/exec/AbstractExec.java index e7cd34de86..42eda58f73 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/cli/exec/AbstractExec.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/cli/exec/AbstractExec.java @@ -1,6 +1,6 @@ package org.keycloak.testsuite.cli.exec; -import org.keycloak.client.admin.cli.util.OsUtil; +import org.keycloak.client.cli.util.OsUtil; import org.keycloak.testsuite.cli.OsArch; import org.keycloak.testsuite.cli.OsUtils; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/FineGrainAdminUnitTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/FineGrainAdminUnitTest.java index 5031d27247..300ba1476c 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/FineGrainAdminUnitTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/FineGrainAdminUnitTest.java @@ -25,7 +25,7 @@ import org.keycloak.authorization.AuthorizationProvider; import org.keycloak.authorization.model.Policy; import org.keycloak.authorization.model.Resource; import org.keycloak.authorization.model.ResourceServer; -import org.keycloak.client.admin.cli.util.ConfigUtil; +import org.keycloak.client.cli.util.ConfigUtil; import org.keycloak.common.Profile; import org.keycloak.models.AdminRoles; import org.keycloak.models.ClientModel; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/admin/AbstractAdmCliTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/admin/AbstractAdmCliTest.java index a9afe73139..a69a060217 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/admin/AbstractAdmCliTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/admin/AbstractAdmCliTest.java @@ -3,9 +3,9 @@ package org.keycloak.testsuite.cli.admin; import org.junit.Assert; import org.keycloak.authentication.authenticators.client.ClientIdAndSecretAuthenticator; import org.keycloak.authentication.authenticators.client.JWTClientAuthenticator; -import org.keycloak.client.admin.cli.config.ConfigData; -import org.keycloak.client.admin.cli.config.FileConfigHandler; -import org.keycloak.client.admin.cli.config.RealmConfigData; +import org.keycloak.client.cli.config.ConfigData; +import org.keycloak.client.cli.config.FileConfigHandler; +import org.keycloak.client.cli.config.RealmConfigData; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.UserRepresentation; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/admin/KcAdmCreateTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/admin/KcAdmCreateTest.java index 9766aa9c60..106f4f53b5 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/admin/KcAdmCreateTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/admin/KcAdmCreateTest.java @@ -3,7 +3,7 @@ package org.keycloak.testsuite.cli.admin; import org.junit.Assert; import org.junit.Test; import org.keycloak.admin.client.resource.RealmResource; -import org.keycloak.client.admin.cli.config.FileConfigHandler; +import org.keycloak.client.cli.config.FileConfigHandler; import org.keycloak.models.IdentityProviderModel; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.testsuite.cli.KcAdmExec; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/admin/KcAdmSessionTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/admin/KcAdmSessionTest.java index d6c7611108..a8c686e580 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/admin/KcAdmSessionTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/admin/KcAdmSessionTest.java @@ -4,7 +4,7 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.node.ObjectNode; import org.junit.Assert; import org.junit.Test; -import org.keycloak.client.admin.cli.config.FileConfigHandler; +import org.keycloak.client.cli.config.FileConfigHandler; import org.keycloak.testsuite.cli.KcAdmExec; import org.keycloak.testsuite.util.TempFileResource; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/admin/KcAdmTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/admin/KcAdmTest.java index 9641aa7d84..17a8d3e58c 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/admin/KcAdmTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/admin/KcAdmTest.java @@ -2,9 +2,9 @@ package org.keycloak.testsuite.cli.admin; import org.junit.Assert; import org.junit.Test; -import org.keycloak.client.admin.cli.config.ConfigData; -import org.keycloak.client.admin.cli.config.FileConfigHandler; -import org.keycloak.client.admin.cli.config.RealmConfigData; +import org.keycloak.client.cli.config.ConfigData; +import org.keycloak.client.cli.config.FileConfigHandler; +import org.keycloak.client.cli.config.RealmConfigData; import org.keycloak.common.util.KeystoreUtil; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.testsuite.cli.KcAdmExec; @@ -18,8 +18,8 @@ import java.io.IOException; import java.util.List; import java.util.UUID; -import static org.keycloak.client.admin.cli.util.OsUtil.CMD; -import static org.keycloak.client.admin.cli.util.OsUtil.EOL; +import static org.keycloak.client.admin.cli.KcAdmMain.CMD; +import static org.keycloak.client.cli.util.OsUtil.EOL; import static org.keycloak.testsuite.cli.KcAdmExec.execute; /** @@ -67,7 +67,7 @@ public class KcAdmTest extends AbstractAdmCliTest { exe = KcAdmExec.execute("config credentials"); assertExitCodeAndStdErrSize(exe, 2, 0); Assert.assertTrue("help message returned", exe.stdoutLines().size() > 10); - Assert.assertEquals("help message", "Usage: " + CMD + " config credentials --server SERVER_URL --realm REALM --user USER [--password PASSWORD] [ARGUMENTS]", exe.stdoutLines().get(0)); + Assert.assertEquals("help message", " " + CMD + " config credentials --server SERVER_URL --realm REALM --user USER [--password PASSWORD] [ARGUMENTS]", exe.stdoutLines().get(1)); exe = KcAdmExec.execute("config truststore"); assertExitCodeAndStdErrSize(exe, 2, 0); @@ -184,9 +184,9 @@ public class KcAdmTest extends AbstractAdmCliTest { exe = KcAdmExec.execute("config credentials --help"); assertExitCodeAndStdErrSize(exe, 0, 0); - Assert.assertEquals("stdout first line", - "Usage: " + CMD + " config credentials --server SERVER_URL --realm REALM --user USER [--password PASSWORD] [ARGUMENTS]", - exe.stdoutLines().get(0)); + Assert.assertEquals("stdout line", + " " + CMD + " config credentials --server SERVER_URL --realm REALM --user USER [--password PASSWORD] [ARGUMENTS]", + exe.stdoutLines().get(1)); exe = KcAdmExec.execute("config truststore --help"); assertExitCodeAndStdErrSize(exe, 0, 0); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/admin/KcAdmTruststoreTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/admin/KcAdmTruststoreTest.java index 2e64c72517..fced3bfbfc 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/admin/KcAdmTruststoreTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/admin/KcAdmTruststoreTest.java @@ -2,9 +2,9 @@ package org.keycloak.testsuite.cli.admin; import org.junit.Assert; import org.junit.Test; -import org.keycloak.client.admin.cli.config.ConfigData; -import org.keycloak.client.admin.cli.config.FileConfigHandler; -import org.keycloak.client.admin.cli.util.OsUtil; +import org.keycloak.client.admin.cli.KcAdmMain; +import org.keycloak.client.cli.config.ConfigData; +import org.keycloak.client.cli.config.FileConfigHandler; import org.keycloak.testsuite.cli.KcAdmExec; import org.keycloak.testsuite.util.OAuthClient; import org.keycloak.testsuite.util.TempFileResource; @@ -12,8 +12,8 @@ import org.keycloak.testsuite.util.TempFileResource; import java.io.File; import java.io.IOException; -import static org.keycloak.client.admin.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_PATH; -import static org.keycloak.client.admin.cli.util.OsUtil.EOL; +import static org.keycloak.client.admin.cli.KcAdmMain.DEFAULT_CONFIG_FILE_PATH; +import static org.keycloak.client.cli.util.OsUtil.EOL; import static org.keycloak.testsuite.util.ServerURLs.AUTH_SERVER_SSL_REQUIRED; import static org.keycloak.testsuite.cli.KcAdmExec.execute; @@ -31,7 +31,7 @@ public class KcAdmTruststoreTest extends AbstractAdmCliTest { assertExitCodeAndStreamSizes(exe, 2, 0, 2); Assert.assertEquals("stderr first line", "Unsupported option: --no-config", exe.stderrLines().get(0)); - Assert.assertEquals("try help", "Try '" + OsUtil.CMD + " config truststore --help' for more information on the available options.", exe.stderrLines().get(1)); + Assert.assertEquals("try help", "Try '" + KcAdmMain.CMD + " config truststore --help' for more information on the available options.", exe.stderrLines().get(1)); // only run this test if ssl protected keycloak server is available if (!AUTH_SERVER_SSL_REQUIRED) { @@ -101,12 +101,12 @@ public class KcAdmTruststoreTest extends AbstractAdmCliTest { exe = execute("config truststore --delete '" + truststore.getAbsolutePath() + "'"); assertExitCodeAndStreamSizes(exe, 2, 0, 2); Assert.assertEquals("incompatible", "Option --delete is mutually exclusive with specifying a TRUSTSTORE", exe.stderrLines().get(0)); - Assert.assertEquals("try help", "Try '" + OsUtil.CMD + " config truststore --help' for more information on the available options.", exe.stderrLines().get(1)); + Assert.assertEquals("try help", "Try '" + KcAdmMain.CMD + " config truststore --help' for more information on the available options.", exe.stderrLines().get(1)); exe = execute("config truststore --delete --trustpass secret"); assertExitCodeAndStreamSizes(exe, 2, 0, 2); Assert.assertEquals("no truststore error", "Options --trustpass and --delete are mutually exclusive", exe.stderrLines().get(0)); - Assert.assertEquals("try help", "Try '" + OsUtil.CMD + " config truststore --help' for more information on the available options.", exe.stderrLines().get(1)); + Assert.assertEquals("try help", "Try '" + KcAdmMain.CMD + " config truststore --help' for more information on the available options.", exe.stderrLines().get(1)); FileConfigHandler cfghandler = new FileConfigHandler(); FileConfigHandler.setConfigFile(DEFAULT_CONFIG_FILE_PATH); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/admin/KcAdmUpdateTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/admin/KcAdmUpdateTest.java index 3f537d8353..002e3ec7e4 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/admin/KcAdmUpdateTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/admin/KcAdmUpdateTest.java @@ -2,7 +2,6 @@ package org.keycloak.testsuite.cli.admin; import org.junit.Assert; import org.junit.Test; -import org.keycloak.client.admin.cli.config.FileConfigHandler; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.testsuite.cli.KcAdmExec; import org.keycloak.testsuite.util.TempFileResource; @@ -18,6 +17,7 @@ import static org.hamcrest.Matchers.is; import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.broker.saml.SAMLIdentityProviderConfig; import org.keycloak.broker.saml.SAMLIdentityProviderFactory; +import org.keycloak.client.cli.config.FileConfigHandler; import org.keycloak.representations.idm.IdentityProviderRepresentation; import static org.hamcrest.MatcherAssert.assertThat; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/AbstractRegCliTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/AbstractRegCliTest.java index 30e5816a59..6d4f1686df 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/AbstractRegCliTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/AbstractRegCliTest.java @@ -6,9 +6,9 @@ import org.keycloak.admin.client.resource.ClientInitialAccessResource; import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.authentication.authenticators.client.ClientIdAndSecretAuthenticator; import org.keycloak.authentication.authenticators.client.JWTClientAuthenticator; -import org.keycloak.client.registration.cli.config.ConfigData; -import org.keycloak.client.registration.cli.config.FileConfigHandler; -import org.keycloak.client.registration.cli.config.RealmConfigData; +import org.keycloak.client.cli.config.ConfigData; +import org.keycloak.client.cli.config.FileConfigHandler; +import org.keycloak.client.cli.config.RealmConfigData; import org.keycloak.representations.idm.ClientInitialAccessCreatePresentation; import org.keycloak.representations.idm.ClientInitialAccessPresentation; import org.keycloak.representations.idm.ClientRepresentation; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/KcRegConfigTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/KcRegConfigTest.java index 6d0c766993..476dc50fd9 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/KcRegConfigTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/KcRegConfigTest.java @@ -2,14 +2,14 @@ package org.keycloak.testsuite.cli.registration; import org.junit.Assert; import org.junit.Test; -import org.keycloak.client.registration.cli.config.FileConfigHandler; +import org.keycloak.client.cli.config.FileConfigHandler; import org.keycloak.testsuite.cli.KcRegExec; import org.keycloak.testsuite.util.TempFileResource; import java.io.IOException; -import static org.keycloak.client.registration.cli.util.OsUtil.CMD; -import static org.keycloak.client.registration.cli.util.OsUtil.EOL; +import static org.keycloak.client.cli.util.OsUtil.EOL; +import static org.keycloak.client.registration.cli.KcRegMain.CMD; import static org.keycloak.testsuite.cli.KcRegExec.execute; /** diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/KcRegCreateTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/KcRegCreateTest.java index 53e9565139..52b88ff41c 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/KcRegCreateTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/KcRegCreateTest.java @@ -9,8 +9,8 @@ import org.keycloak.OAuth2Constants; import org.keycloak.admin.client.resource.ClientResource; import org.keycloak.admin.client.resource.ClientsResource; import org.keycloak.admin.client.resource.RealmResource; -import org.keycloak.client.registration.cli.config.ConfigData; -import org.keycloak.client.registration.cli.config.FileConfigHandler; +import org.keycloak.client.cli.config.ConfigData; +import org.keycloak.client.cli.config.FileConfigHandler; import org.keycloak.common.Profile; import org.keycloak.common.constants.ServiceAccountConstants; import org.keycloak.common.crypto.FipsMode; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/KcRegTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/KcRegTest.java index dc35e1e226..0f5752a76a 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/KcRegTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/KcRegTest.java @@ -3,9 +3,9 @@ package org.keycloak.testsuite.cli.registration; import org.junit.Assert; import org.junit.Assume; import org.junit.Test; -import org.keycloak.client.registration.cli.config.ConfigData; -import org.keycloak.client.registration.cli.config.FileConfigHandler; -import org.keycloak.client.registration.cli.config.RealmConfigData; +import org.keycloak.client.cli.config.ConfigData; +import org.keycloak.client.cli.config.FileConfigHandler; +import org.keycloak.client.cli.config.RealmConfigData; import org.keycloak.common.util.KeystoreUtil; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.testsuite.cli.KcRegExec; @@ -19,8 +19,8 @@ import java.io.IOException; import java.util.List; import java.util.UUID; -import static org.keycloak.client.registration.cli.util.OsUtil.CMD; -import static org.keycloak.client.registration.cli.util.OsUtil.EOL; +import static org.keycloak.client.cli.util.OsUtil.EOL; +import static org.keycloak.client.registration.cli.KcRegMain.CMD; import static org.keycloak.testsuite.util.ServerURLs.AUTH_SERVER_SSL_REQUIRED; import static org.keycloak.testsuite.cli.KcRegExec.execute; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/KcRegTruststoreTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/KcRegTruststoreTest.java index d6cb6af448..b1a74289e7 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/KcRegTruststoreTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/KcRegTruststoreTest.java @@ -2,9 +2,8 @@ package org.keycloak.testsuite.cli.registration; import org.junit.Assert; import org.junit.Test; -import org.keycloak.client.registration.cli.config.ConfigData; -import org.keycloak.client.registration.cli.config.FileConfigHandler; -import org.keycloak.client.registration.cli.util.OsUtil; +import org.keycloak.client.cli.config.ConfigData; +import org.keycloak.client.cli.config.FileConfigHandler; import org.keycloak.testsuite.cli.KcRegExec; import org.keycloak.testsuite.util.OAuthClient; import org.keycloak.testsuite.util.TempFileResource; @@ -12,9 +11,9 @@ import org.keycloak.testsuite.util.TempFileResource; import java.io.File; import java.io.IOException; -import static org.keycloak.client.registration.cli.util.ConfigUtil.DEFAULT_CONFIG_FILE_PATH; -import static org.keycloak.client.registration.cli.util.OsUtil.CMD; -import static org.keycloak.client.registration.cli.util.OsUtil.EOL; +import static org.keycloak.client.registration.cli.KcRegMain.DEFAULT_CONFIG_FILE_PATH; +import static org.keycloak.client.cli.util.OsUtil.EOL; +import static org.keycloak.client.registration.cli.KcRegMain.CMD; import static org.keycloak.testsuite.util.ServerURLs.AUTH_SERVER_SSL_REQUIRED; import static org.keycloak.testsuite.cli.KcRegExec.execute; @@ -32,7 +31,7 @@ public class KcRegTruststoreTest extends AbstractRegCliTest { assertExitCodeAndStreamSizes(exe, 2, 0, 2); Assert.assertEquals("stderr first line", "Unsupported option: --no-config", exe.stderrLines().get(0)); - Assert.assertEquals("try help", "Try '" + OsUtil.CMD + " config truststore --help' for more information on the available options.", exe.stderrLines().get(1)); + Assert.assertEquals("try help", "Try '" + CMD + " config truststore --help' for more information on the available options.", exe.stderrLines().get(1)); // only run the rest of this test if ssl protected keycloak server is available if (!AUTH_SERVER_SSL_REQUIRED) { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/KcRegUpdateTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/KcRegUpdateTest.java index 7c6d4c50e9..5b5810b265 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/KcRegUpdateTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/KcRegUpdateTest.java @@ -2,7 +2,7 @@ package org.keycloak.testsuite.cli.registration; import org.junit.Assert; import org.junit.Test; -import org.keycloak.client.registration.cli.config.FileConfigHandler; +import org.keycloak.client.cli.config.FileConfigHandler; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.testsuite.cli.KcRegExec; import org.keycloak.testsuite.util.TempFileResource; @@ -12,7 +12,7 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.Arrays; -import static org.keycloak.client.registration.cli.util.OsUtil.CMD; +import static org.keycloak.client.registration.cli.KcRegMain.CMD; import static org.keycloak.testsuite.cli.KcRegExec.execute; /** diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/KcRegUpdateTokenTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/KcRegUpdateTokenTest.java index 3a57aac7c6..9796c55658 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/KcRegUpdateTokenTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/KcRegUpdateTokenTest.java @@ -2,10 +2,10 @@ package org.keycloak.testsuite.cli.registration; import org.junit.Assert; import org.junit.Test; -import org.keycloak.client.registration.cli.config.ConfigData; -import org.keycloak.client.registration.cli.config.FileConfigHandler; -import org.keycloak.client.registration.cli.config.RealmConfigData; -import org.keycloak.client.registration.cli.util.ConfigUtil; +import org.keycloak.client.cli.config.ConfigData; +import org.keycloak.client.cli.config.FileConfigHandler; +import org.keycloak.client.cli.config.RealmConfigData; +import org.keycloak.client.cli.util.ConfigUtil; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.testsuite.cli.KcRegExec; import org.keycloak.testsuite.util.TempFileResource;