Skip to content

Commit

Permalink
Add status option to kcadm for validity (keycloak#32883)
Browse files Browse the repository at this point in the history
Closes keycloak#23179

Signed-off-by: Keshav Deshpande <[email protected]>
  • Loading branch information
keshavprashantdeshpande authored Sep 24, 2024
1 parent 89ed021 commit 4e23b45
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,16 @@
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:[email protected]">Marko Strukelj</a>
Expand All @@ -32,4 +40,26 @@ public ConfigCredentialsCmd() {
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();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ public void process() {
if (keyPass == null) {
keyPass = System.getenv("KC_CLI_KEY_PASSWORD");
}

if (storePass == null) {
storePass = readPasswordFromConsole("keystore password");
if (keyPass == null) {
Expand Down Expand Up @@ -192,6 +192,7 @@ public String help() {
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.");
Expand All @@ -201,6 +202,8 @@ public String help() {
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();
Expand All @@ -222,6 +225,7 @@ public String help() {
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:");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
*/
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();
Expand All @@ -58,15 +60,15 @@ public static String ensureToken(ConfigData config, String cmd) {

// 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'");
}

Expand Down

0 comments on commit 4e23b45

Please sign in to comment.