Add status option to kcadm for validity (#32883)

Closes #23179

Signed-off-by: Keshav Deshpande <keshavprashantdeshpande@gmail.com>
This commit is contained in:
keshavprashantdeshpande 2024-09-24 13:18:54 +02:00 committed by GitHub
parent 89ed021718
commit 4e23b450be
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 40 additions and 4 deletions

View file

@ -19,8 +19,16 @@ package org.keycloak.client.admin.cli.commands;
import org.keycloak.client.admin.cli.KcAdmMain;
import org.keycloak.client.cli.common.BaseConfigCredentialsCmd;
import org.keycloak.client.cli.config.ConfigData;
import picocli.CommandLine;
import picocli.CommandLine.Command;
import static java.lang.System.currentTimeMillis;
import static org.keycloak.client.cli.util.AuthUtil.AUTH_BUFFER_TIME;
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.printOut;
/**
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
@ -32,4 +40,26 @@ public class ConfigCredentialsCmd extends BaseConfigCredentialsCmd {
super(KcAdmMain.COMMAND_STATE);
}
@CommandLine.Option(names = "--status", description = "Validity of the connection with server")
boolean status;
@Override
protected boolean nothingToDo() {
return super.nothingToDo() && !status;
}
@Override
public void process() {
if (status) {
ConfigData config = loadConfig();
long now = currentTimeMillis();
if (credentialsAvailable(config) && now + AUTH_BUFFER_TIME < config.sessionRealmConfigData().getExpiresAt()) {
printOut("Logged in (server: " + config.getServerUrl() + ", realm: " + config.getRealm() + ", expired: false, timeToExpiry: " + (config.sessionRealmConfigData().getExpiresAt() - now) / 1000 + "s from now)");
} else {
printOut("You are not logged in");
}
} else {
super.process();
}
}
}

View file

@ -138,7 +138,7 @@ public class BaseConfigCredentialsCmd extends BaseAuthOptionsCmd {
if (keyPass == null) {
keyPass = System.getenv("KC_CLI_KEY_PASSWORD");
}
if (storePass == null) {
storePass = readPasswordFromConsole("keystore password");
if (keyPass == null) {
@ -192,6 +192,7 @@ public class BaseConfigCredentialsCmd extends BaseAuthOptionsCmd {
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(" " + getCommand() + " config credentials --status");
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.");
@ -201,6 +202,8 @@ public class BaseConfigCredentialsCmd extends BaseAuthOptionsCmd {
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("If validity of the authentication needs to be checked use the --status option. This would only check the status of the existing config and does not update the config.");
out.println("Other arguments which are passed along with status are ignored");
out.println();
out.println("Arguments:");
out.println();
@ -222,6 +225,7 @@ public class BaseConfigCredentialsCmd extends BaseAuthOptionsCmd {
out.println(" --keypass PASSWORD Key password (prompted for if not specified, --keystore is used without --storepass, and KC_CLI_KEY_PASSWORD");
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(" --status Checks the validity of the existing connection (Note: It does not update the config)");
out.println();
out.println();
out.println("Examples:");

View file

@ -45,6 +45,8 @@ import static org.keycloak.client.cli.util.HttpUtil.urlencode;
*/
public class AuthUtil {
public static final int AUTH_BUFFER_TIME = 5000;
public static String ensureToken(ConfigData config, String cmd) {
if (config.getExternalToken() != null) {
return config.getExternalToken();
@ -58,15 +60,15 @@ public class AuthUtil {
// check expires of access_token against time
// if it's less than 5s to expiry, renew it
if (realmConfig.getExpiresAt() - now < 5000) {
if (realmConfig.getExpiresAt() - now < AUTH_BUFFER_TIME) {
// 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) {
if (realmConfig.getRefreshExpiresAt() != null && realmConfig.getRefreshExpiresAt() - now < AUTH_BUFFER_TIME) {
throw new RuntimeException("Session has expired. Login again with '" + cmd + " config credentials'");
}
if (realmConfig.getSigExpiresAt() != null && realmConfig.getSigExpiresAt() - now < 5000) {
if (realmConfig.getSigExpiresAt() != null && realmConfig.getSigExpiresAt() - now < AUTH_BUFFER_TIME) {
throw new RuntimeException("Session has expired. Login again with '" + cmd + " config credentials'");
}