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

View file

@ -18,9 +18,12 @@
package org.keycloak.quarkus.runtime.cli.command; package org.keycloak.quarkus.runtime.cli.command;
import org.keycloak.common.util.IoUtils; 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.cli.PropertyException;
import org.keycloak.quarkus.runtime.integration.jaxrs.QuarkusKeycloakApplication; import org.keycloak.quarkus.runtime.integration.jaxrs.QuarkusKeycloakApplication;
import org.keycloak.services.managers.ApplianceBootstrap; import org.keycloak.services.managers.ApplianceBootstrap;
import org.keycloak.services.resources.KeycloakApplication;
import picocli.CommandLine.ArgGroup; import picocli.CommandLine.ArgGroup;
import picocli.CommandLine.Command; import picocli.CommandLine.Command;
@ -97,7 +100,9 @@ public class BootstrapAdminService extends AbstractNonServerCommand {
@Override @Override
public void onStart(QuarkusKeycloakApplication application) { public void onStart(QuarkusKeycloakApplication application) {
//BootstrapAdmin bootstrap = spec.commandLine().getParent().getCommand(); //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; package org.keycloak.quarkus.runtime.cli.command;
import org.keycloak.common.util.IoUtils; 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.cli.PropertyException;
import org.keycloak.quarkus.runtime.integration.jaxrs.QuarkusKeycloakApplication; import org.keycloak.quarkus.runtime.integration.jaxrs.QuarkusKeycloakApplication;
import org.keycloak.services.managers.ApplianceBootstrap; import org.keycloak.services.managers.ApplianceBootstrap;
import org.keycloak.services.resources.KeycloakApplication;
import picocli.CommandLine.ArgGroup; import picocli.CommandLine.ArgGroup;
import picocli.CommandLine.Command; import picocli.CommandLine.Command;
@ -97,7 +100,9 @@ public class BootstrapAdminUser extends AbstractNonServerCommand {
@Override @Override
public void onStart(QuarkusKeycloakApplication application) { public void onStart(QuarkusKeycloakApplication application) {
//BootstrapAdmin bootstrap = spec.commandLine().getParent().getCommand(); //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(ExportPropertyMappers.getMappers());
MAPPERS.addAll(ImportPropertyMappers.getMappers()); MAPPERS.addAll(ImportPropertyMappers.getMappers());
MAPPERS.addAll(TruststorePropertyMappers.getMappers()); MAPPERS.addAll(TruststorePropertyMappers.getMappers());
MAPPERS.addAll(BootstrapAdminPropertyMappers.getMappers());
} }
public static ConfigValue getValue(ConfigSourceInterceptorContext context, String name) { 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) { public void createTemporaryMasterRealmAdminUser(String adminUserName, String adminPassword, /*Integer adminExpiration,*/ KeycloakSession session) {
KeycloakSessionTask task = session -> { new ApplianceBootstrap(session).createTemporaryMasterRealmAdminUser(adminUserName, adminPassword /*, adminExpiration*/, false);
new ApplianceBootstrap(session).createTemporaryMasterRealmAdminUser(adminUserName, adminPassword /*, adminExpiration*/, false);
};
runAdminTask(adminUserName, existingSession, task);
} }
public void createTemporaryMasterRealmAdminService(String clientId, String clientSecret, /*Integer adminExpiration,*/ KeycloakSession existingSession) { public void createTemporaryMasterRealmAdminService(String clientId, String clientSecret, /*Integer adminExpiration,*/ KeycloakSession session) {
KeycloakSessionTask task = session -> { new ApplianceBootstrap(session).createTemporaryMasterRealmAdminService(clientId, clientSecret /*, adminExpiration*/);
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) { private String getEnvOrProp(String envKey, String propKey) {

View file

@ -64,6 +64,13 @@ public class BootstrapAdminDistTest {
assertTrue(result.getErrorOutput().isEmpty(), result.getErrorOutput()); 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 @Test
@Launch({ "bootstrap-admin", "service", "--no-prompt" }) @Launch({ "bootstrap-admin", "service", "--no-prompt" })
void failServiceAccountNoSecret(LaunchResult result) { void failServiceAccountNoSecret(LaunchResult result) {

View file

@ -48,30 +48,19 @@ public class FipsDistTest {
} }
@Test @Test
void testFipsApprovedModePasswordFails(KeycloakDistribution dist) { void testFipsApprovedMode(KeycloakDistribution dist) {
runOnFipsEnabledDistribution(dist, () -> { runOnFipsEnabledDistribution(dist, () -> {
dist.setEnvVar("KC_BOOTSTRAP_ADMIN_USERNAME", "admin"); dist.setEnvVar("KC_BOOTSTRAP_ADMIN_USERNAME", "admin");
dist.setEnvVar("KC_BOOTSTRAP_ADMIN_PASSWORD", "admin"); dist.setEnvVar("KC_BOOTSTRAP_ADMIN_PASSWORD", "admin");
CLIResult cliResult = dist.run("start", "--fips-mode=strict"); CLIResult cliResult = dist.run("start", "--fips-mode=strict");
cliResult.assertStarted(); cliResult.assertMessage("password must be at least 112 bits");
cliResult.assertMessage(
"org.bouncycastle.crypto.fips.FipsUnapprovedOperationError: password must be at least 112 bits");
cliResult.assertMessage("Java security providers: [ \n" cliResult.assertMessage("Java security providers: [ \n"
+ " KC(" + BCFIPS_VERSION + " Approved Mode, FIPS-JVM: " + KeycloakFipsSecurityProvider.isSystemFipsEnabled() + ") version 1.0 - class org.keycloak.crypto.fips.KeycloakFipsSecurityProvider"); + " 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"); dist.setEnvVar("KC_BOOTSTRAP_ADMIN_PASSWORD", "adminadminadmin");
cliResult = dist.run("start", "--fips-mode=strict");
CLIResult cliResult = dist.run("start", "--fips-mode=strict");
cliResult.assertStarted(); 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"); 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 DEPRECATED. Set the format for the GELF timestamp field. Uses Java
SimpleDateFormat pattern. Default: yyyy-MM-dd HH:mm:ss,SSS. Available only SimpleDateFormat pattern. Default: yyyy-MM-dd HH:mm:ss,SSS. Available only
when GELF is activated. 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> --log-level <category:level>
The log level of the root category or a comma-separated list of individual 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 categories and their levels. For the root category, you don't need to
specify a category. Default: info. 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> --log-syslog-endpoint <host:port>
The IP address and port of the syslog server. Default: localhost:514. The IP address and port of the syslog server. Default: localhost:514.
Available only when Syslog is activated. Available only when Syslog is activated.

View file

@ -171,16 +171,10 @@ Logging:
DEPRECATED. Set the format for the GELF timestamp field. Uses Java DEPRECATED. Set the format for the GELF timestamp field. Uses Java
SimpleDateFormat pattern. Default: yyyy-MM-dd HH:mm:ss,SSS. Available only SimpleDateFormat pattern. Default: yyyy-MM-dd HH:mm:ss,SSS. Available only
when GELF is activated. 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> --log-level <category:level>
The log level of the root category or a comma-separated list of individual 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 categories and their levels. For the root category, you don't need to
specify a category. Default: info. 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> --log-syslog-endpoint <host:port>
The IP address and port of the syslog server. Default: localhost:514. The IP address and port of the syslog server. Default: localhost:514.
Available only when Syslog is activated. 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 headers to allow internal and external applications. If all applications use
the public URL this option should be enabled. Default: false. Available only the public URL this option should be enabled. Default: false. Available only
when hostname:v1 feature is enabled. 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 --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. 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 DEPRECATED. Set the format for the GELF timestamp field. Uses Java
SimpleDateFormat pattern. Default: yyyy-MM-dd HH:mm:ss,SSS. Available only SimpleDateFormat pattern. Default: yyyy-MM-dd HH:mm:ss,SSS. Available only
when GELF is activated. 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> --log-level <category:level>
The log level of the root category or a comma-separated list of individual 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 categories and their levels. For the root category, you don't need to
specify a category. Default: info. 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> --log-syslog-endpoint <host:port>
The IP address and port of the syslog server. Default: localhost:514. The IP address and port of the syslog server. Default: localhost:514.
Available only when Syslog is activated. 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 headers to allow internal and external applications. If all applications use
the public URL this option should be enabled. Default: false. Available only the public URL this option should be enabled. Default: false. Available only
when hostname:v1 feature is enabled. 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 --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. 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 DEPRECATED. Set the format for the GELF timestamp field. Uses Java
SimpleDateFormat pattern. Default: yyyy-MM-dd HH:mm:ss,SSS. Available only SimpleDateFormat pattern. Default: yyyy-MM-dd HH:mm:ss,SSS. Available only
when GELF is activated. 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> --log-level <category:level>
The log level of the root category or a comma-separated list of individual 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 categories and their levels. For the root category, you don't need to
specify a category. Default: info. 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> --log-syslog-endpoint <host:port>
The IP address and port of the syslog server. Default: localhost:514. The IP address and port of the syslog server. Default: localhost:514.
Available only when Syslog is activated. 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 headers to allow internal and external applications. If all applications use
the public URL this option should be enabled. Default: false. Available only the public URL this option should be enabled. Default: false. Available only
when hostname:v1 feature is enabled. 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 --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. 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 DEPRECATED. Set the format for the GELF timestamp field. Uses Java
SimpleDateFormat pattern. Default: yyyy-MM-dd HH:mm:ss,SSS. Available only SimpleDateFormat pattern. Default: yyyy-MM-dd HH:mm:ss,SSS. Available only
when GELF is activated. 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> --log-level <category:level>
The log level of the root category or a comma-separated list of individual 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 categories and their levels. For the root category, you don't need to
specify a category. Default: info. 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> --log-syslog-endpoint <host:port>
The IP address and port of the syslog server. Default: localhost:514. The IP address and port of the syslog server. Default: localhost:514.
Available only when Syslog is activated. Available only when Syslog is activated.