fix: fail to start if the admin user can't be added (#31207)

also allowing the bootstrap options to be used by the cli, which
requires hidden options to stay hidden

and a minor refactoring for clarity

closes: #31160

Signed-off-by: Steve Hawkins <shawkins@redhat.com>
This commit is contained in:
Steven Hawkins 2024-07-18 10:27:48 -04:00 committed by GitHub
parent 14a9927e29
commit d970521415
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 43 additions and 112 deletions

View file

@ -157,11 +157,15 @@ public final class Help extends CommandLine.Help {
}
private boolean isVisible(OptionSpec option) {
if (option.description().length == 0) {
// do not show options without a description
if (option.description().length == 0 || option.hidden()) {
// do not show options without a description nor hidden
return false;
}
if (ALL_OPTIONS) {
return true;
}
String optionName = undecorateDuplicitOptionName(option.longestName());
OptionCategory category = null;
@ -172,30 +176,13 @@ public final class Help extends CommandLine.Help {
if (mapper == null) {
final var disabledMapper = PropertyMappers.getDisabledMapper(optionName);
final var isDisabledMapper = disabledMapper.isPresent();
// Show disabled mappers, which do not have a description when they're enabled
final var isEnabledWhenEmpty = isDisabledMapper && disabledMapper.get().getEnabledWhen().isEmpty();
if (isEnabledWhenEmpty) {
return true;
return disabledMapper.flatMap(PropertyMapper::getEnabledWhen).isEmpty();
}
if (ALL_OPTIONS && isDisabledMapper) {
return true;
}
// only filter mapped options, defaults to the hidden marker
return !option.hidden() && !isDisabledMapper;
}
boolean isUnsupportedOption = !PropertyMappers.isSupported(mapper);
if (isUnsupportedOption) {
// unsupported options removed from help if all options are not requested
return !option.hidden() && ALL_OPTIONS;
}
return !option.hidden();
return PropertyMappers.isSupported(mapper);
}
public static void setAllOptions(boolean allOptions) {

View file

@ -18,9 +18,12 @@
package org.keycloak.quarkus.runtime.cli.command;
import org.keycloak.common.util.IoUtils;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.quarkus.runtime.cli.PropertyException;
import org.keycloak.quarkus.runtime.integration.jaxrs.QuarkusKeycloakApplication;
import org.keycloak.services.managers.ApplianceBootstrap;
import org.keycloak.services.resources.KeycloakApplication;
import picocli.CommandLine.ArgGroup;
import picocli.CommandLine.Command;
@ -97,7 +100,9 @@ public class BootstrapAdminService extends AbstractNonServerCommand {
@Override
public void onStart(QuarkusKeycloakApplication application) {
//BootstrapAdmin bootstrap = spec.commandLine().getParent().getCommand();
application.createTemporaryMasterRealmAdminService(clientId, clientSecret, /*bootstrap.expiration,*/ null);
KeycloakSessionFactory sessionFactory = KeycloakApplication.getSessionFactory();
KeycloakModelUtils.runJobInTransaction(sessionFactory, session -> application
.createTemporaryMasterRealmAdminService(clientId, clientSecret, /* bootstrap.expiration, */ session));
}
}

View file

@ -18,9 +18,12 @@
package org.keycloak.quarkus.runtime.cli.command;
import org.keycloak.common.util.IoUtils;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.quarkus.runtime.cli.PropertyException;
import org.keycloak.quarkus.runtime.integration.jaxrs.QuarkusKeycloakApplication;
import org.keycloak.services.managers.ApplianceBootstrap;
import org.keycloak.services.resources.KeycloakApplication;
import picocli.CommandLine.ArgGroup;
import picocli.CommandLine.Command;
@ -97,7 +100,9 @@ public class BootstrapAdminUser extends AbstractNonServerCommand {
@Override
public void onStart(QuarkusKeycloakApplication application) {
//BootstrapAdmin bootstrap = spec.commandLine().getParent().getCommand();
application.createTemporaryMasterRealmAdminUser(username, password, /*bootstrap.expiration,*/ null);
KeycloakSessionFactory sessionFactory = KeycloakApplication.getSessionFactory();
KeycloakModelUtils.runJobInTransaction(sessionFactory, session -> application
.createTemporaryMasterRealmAdminUser(username, password, /* bootstrap.expiration, */ session));
}
}

View file

@ -64,6 +64,7 @@ public final class PropertyMappers {
MAPPERS.addAll(ExportPropertyMappers.getMappers());
MAPPERS.addAll(ImportPropertyMappers.getMappers());
MAPPERS.addAll(TruststorePropertyMappers.getMappers());
MAPPERS.addAll(BootstrapAdminPropertyMappers.getMappers());
}
public static ConfigValue getValue(ConfigSourceInterceptorContext context, String name) {

View file

@ -98,33 +98,12 @@ public class QuarkusKeycloakApplication extends KeycloakApplication {
}
}
public void createTemporaryMasterRealmAdminUser(String adminUserName, String adminPassword, /*Integer adminExpiration,*/ KeycloakSession existingSession) {
KeycloakSessionTask task = session -> {
public void createTemporaryMasterRealmAdminUser(String adminUserName, String adminPassword, /*Integer adminExpiration,*/ KeycloakSession session) {
new ApplianceBootstrap(session).createTemporaryMasterRealmAdminUser(adminUserName, adminPassword /*, adminExpiration*/, false);
};
runAdminTask(adminUserName, existingSession, task);
}
public void createTemporaryMasterRealmAdminService(String clientId, String clientSecret, /*Integer adminExpiration,*/ KeycloakSession existingSession) {
KeycloakSessionTask task = session -> {
public void createTemporaryMasterRealmAdminService(String clientId, String clientSecret, /*Integer adminExpiration,*/ KeycloakSession session) {
new ApplianceBootstrap(session).createTemporaryMasterRealmAdminService(clientId, clientSecret /*, adminExpiration*/);
};
runAdminTask(clientId, existingSession, task);
}
private void runAdminTask(String adminUserName, KeycloakSession existingSession, KeycloakSessionTask task) {
try {
if (existingSession == null) {
KeycloakSessionFactory sessionFactory = KeycloakApplication.getSessionFactory();
KeycloakModelUtils.runJobInTransaction(sessionFactory, task);
} else {
task.run(existingSession);
}
} catch (Throwable t) {
ServicesLogger.LOGGER.addUserFailed(t, adminUserName, Config.getAdminRealm());
}
}
private String getEnvOrProp(String envKey, String propKey) {

View file

@ -64,6 +64,13 @@ public class BootstrapAdminDistTest {
assertTrue(result.getErrorOutput().isEmpty(), result.getErrorOutput());
}
@Test
@Launch({ "start-dev", "--bootstrap-admin-password=MY_PASSWORD" })
void createAdminWithCliOptions(LaunchResult result) {
assertTrue(result.getErrorOutput().isEmpty(), result.getErrorOutput());
result.getOutput().contains("Created temporary admin user with username temp-admin");
}
@Test
@Launch({ "bootstrap-admin", "service", "--no-prompt" })
void failServiceAccountNoSecret(LaunchResult result) {

View file

@ -48,30 +48,19 @@ public class FipsDistTest {
}
@Test
void testFipsApprovedModePasswordFails(KeycloakDistribution dist) {
void testFipsApprovedMode(KeycloakDistribution dist) {
runOnFipsEnabledDistribution(dist, () -> {
dist.setEnvVar("KC_BOOTSTRAP_ADMIN_USERNAME", "admin");
dist.setEnvVar("KC_BOOTSTRAP_ADMIN_PASSWORD", "admin");
CLIResult cliResult = dist.run("start", "--fips-mode=strict");
cliResult.assertStarted();
cliResult.assertMessage(
"org.bouncycastle.crypto.fips.FipsUnapprovedOperationError: password must be at least 112 bits");
cliResult.assertMessage("password must be at least 112 bits");
cliResult.assertMessage("Java security providers: [ \n"
+ " KC(" + BCFIPS_VERSION + " Approved Mode, FIPS-JVM: " + KeycloakFipsSecurityProvider.isSystemFipsEnabled() + ") version 1.0 - class org.keycloak.crypto.fips.KeycloakFipsSecurityProvider");
});
}
@Test
void testFipsApprovedModePasswordSucceeds(KeycloakDistribution dist) {
runOnFipsEnabledDistribution(dist, () -> {
dist.setEnvVar("KC_BOOTSTRAP_ADMIN_USERNAME", "admin");
dist.setEnvVar("KC_BOOTSTRAP_ADMIN_PASSWORD", "adminadminadmin");
CLIResult cliResult = dist.run("start", "--fips-mode=strict");
cliResult = dist.run("start", "--fips-mode=strict");
cliResult.assertStarted();
cliResult.assertMessage("Java security providers: [ \n"
+ " KC(" + BCFIPS_VERSION + " Approved Mode, FIPS-JVM: " + KeycloakFipsSecurityProvider.isSystemFipsEnabled() + ") version 1.0 - class org.keycloak.crypto.fips.KeycloakFipsSecurityProvider");
cliResult.assertMessage("Created temporary admin user with username admin");
});
}

View file

@ -171,16 +171,10 @@ Logging:
DEPRECATED. Set the format for the GELF timestamp field. Uses Java
SimpleDateFormat pattern. Default: yyyy-MM-dd HH:mm:ss,SSS. Available only
when GELF is activated.
--log-gelf-version <version>
The GELF version to be used. Possible values are: 1.0, 1.1. Default: 1.1.
Available only when GELF is activated.
--log-level <category:level>
The log level of the root category or a comma-separated list of individual
categories and their levels. For the root category, you don't need to
specify a category. Default: info.
--log-syslog-app-name <name>
The app name used when formatting the message in RFC5424 format. Default:
keycloak. Available only when Syslog is activated.
--log-syslog-endpoint <host:port>
The IP address and port of the syslog server. Default: localhost:514.
Available only when Syslog is activated.

View file

@ -171,16 +171,10 @@ Logging:
DEPRECATED. Set the format for the GELF timestamp field. Uses Java
SimpleDateFormat pattern. Default: yyyy-MM-dd HH:mm:ss,SSS. Available only
when GELF is activated.
--log-gelf-version <version>
The GELF version to be used. Possible values are: 1.0, 1.1. Default: 1.1.
Available only when GELF is activated.
--log-level <category:level>
The log level of the root category or a comma-separated list of individual
categories and their levels. For the root category, you don't need to
specify a category. Default: info.
--log-syslog-app-name <name>
The app name used when formatting the message in RFC5424 format. Default:
keycloak. Available only when Syslog is activated.
--log-syslog-endpoint <host:port>
The IP address and port of the syslog server. Default: localhost:514.
Available only when Syslog is activated.

View file

@ -191,10 +191,6 @@ Hostname v1 (Deprecated):
headers to allow internal and external applications. If all applications use
the public URL this option should be enabled. Default: false. Available only
when hostname:v1 feature is enabled.
--hostname-strict-https <true|false>
DEPRECATED. Forces frontend URLs to use the 'https' scheme. If set to false,
the HTTP scheme is inferred from requests. Default: true. Available only
when hostname:v1 feature is enabled.
--hostname-url <url> DEPRECATED. Set the base URL for frontend URLs, including scheme, host, port
and path. Available only when hostname:v1 feature is enabled.
@ -385,16 +381,10 @@ Logging:
DEPRECATED. Set the format for the GELF timestamp field. Uses Java
SimpleDateFormat pattern. Default: yyyy-MM-dd HH:mm:ss,SSS. Available only
when GELF is activated.
--log-gelf-version <version>
The GELF version to be used. Possible values are: 1.0, 1.1. Default: 1.1.
Available only when GELF is activated.
--log-level <category:level>
The log level of the root category or a comma-separated list of individual
categories and their levels. For the root category, you don't need to
specify a category. Default: info.
--log-syslog-app-name <name>
The app name used when formatting the message in RFC5424 format. Default:
keycloak. Available only when Syslog is activated.
--log-syslog-endpoint <host:port>
The IP address and port of the syslog server. Default: localhost:514.
Available only when Syslog is activated.

View file

@ -192,10 +192,6 @@ Hostname v1 (Deprecated):
headers to allow internal and external applications. If all applications use
the public URL this option should be enabled. Default: false. Available only
when hostname:v1 feature is enabled.
--hostname-strict-https <true|false>
DEPRECATED. Forces frontend URLs to use the 'https' scheme. If set to false,
the HTTP scheme is inferred from requests. Default: true. Available only
when hostname:v1 feature is enabled.
--hostname-url <url> DEPRECATED. Set the base URL for frontend URLs, including scheme, host, port
and path. Available only when hostname:v1 feature is enabled.
@ -386,16 +382,10 @@ Logging:
DEPRECATED. Set the format for the GELF timestamp field. Uses Java
SimpleDateFormat pattern. Default: yyyy-MM-dd HH:mm:ss,SSS. Available only
when GELF is activated.
--log-gelf-version <version>
The GELF version to be used. Possible values are: 1.0, 1.1. Default: 1.1.
Available only when GELF is activated.
--log-level <category:level>
The log level of the root category or a comma-separated list of individual
categories and their levels. For the root category, you don't need to
specify a category. Default: info.
--log-syslog-app-name <name>
The app name used when formatting the message in RFC5424 format. Default:
keycloak. Available only when Syslog is activated.
--log-syslog-endpoint <host:port>
The IP address and port of the syslog server. Default: localhost:514.
Available only when Syslog is activated.

View file

@ -177,10 +177,6 @@ Hostname v1 (Deprecated):
headers to allow internal and external applications. If all applications use
the public URL this option should be enabled. Default: false. Available only
when hostname:v1 feature is enabled.
--hostname-strict-https <true|false>
DEPRECATED. Forces frontend URLs to use the 'https' scheme. If set to false,
the HTTP scheme is inferred from requests. Default: true. Available only
when hostname:v1 feature is enabled.
--hostname-url <url> DEPRECATED. Set the base URL for frontend URLs, including scheme, host, port
and path. Available only when hostname:v1 feature is enabled.
@ -337,16 +333,10 @@ Logging:
DEPRECATED. Set the format for the GELF timestamp field. Uses Java
SimpleDateFormat pattern. Default: yyyy-MM-dd HH:mm:ss,SSS. Available only
when GELF is activated.
--log-gelf-version <version>
The GELF version to be used. Possible values are: 1.0, 1.1. Default: 1.1.
Available only when GELF is activated.
--log-level <category:level>
The log level of the root category or a comma-separated list of individual
categories and their levels. For the root category, you don't need to
specify a category. Default: info.
--log-syslog-app-name <name>
The app name used when formatting the message in RFC5424 format. Default:
keycloak. Available only when Syslog is activated.
--log-syslog-endpoint <host:port>
The IP address and port of the syslog server. Default: localhost:514.
Available only when Syslog is activated.