diff --git a/docs/guides/src/main/server/features.adoc b/docs/guides/src/main/server/features.adoc index c8cd814609..b705ba3632 100644 --- a/docs/guides/src/main/server/features.adoc +++ b/docs/guides/src/main/server/features.adoc @@ -4,7 +4,8 @@ <@tmpl.guide title="Enabling and disabling features" -summary="Understand how to configure Keycloak to use optional features"> +summary="Understand how to configure Keycloak to use optional features" +includedOptions="features features-*"> Keycloak has packed some functionality in features, some of them not enabled by default. These features include features that are in tech preview or deprecated features. In addition there are some features that are enabled by default, but can be disabled if you don't need them for your specific usage scenario. diff --git a/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/KeycloakProcessor.java b/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/KeycloakProcessor.java index 0ffd6d8c09..94591541d7 100644 --- a/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/KeycloakProcessor.java +++ b/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/KeycloakProcessor.java @@ -83,6 +83,7 @@ import org.jboss.logging.Logger; import org.jboss.resteasy.plugins.server.servlet.ResteasyContextParameters; import org.jboss.resteasy.spi.ResteasyDeployment; import org.keycloak.Config; +import org.keycloak.quarkus.runtime.QuarkusProfile; import org.keycloak.quarkus.runtime.configuration.PersistedConfigSource; import org.keycloak.quarkus.runtime.configuration.QuarkusPropertiesConfigSource; import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper; @@ -244,7 +245,7 @@ class KeycloakProcessor { @Record(ExecutionTime.RUNTIME_INIT) @BuildStep KeycloakSessionFactoryPreInitBuildItem configureProviders(KeycloakRecorder recorder) { - Profile.setInstance(recorder.createProfile()); + Profile.setInstance(new QuarkusProfile()); Map, Map>>> factories = new HashMap<>(); Map, String> defaultProviders = new HashMap<>(); Map preConfiguredProviders = new HashMap<>(); diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/KeycloakRecorder.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/KeycloakRecorder.java index b0b1cc9f18..0b3003993d 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/KeycloakRecorder.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/KeycloakRecorder.java @@ -17,11 +17,8 @@ package org.keycloak.quarkus.runtime; -import static org.keycloak.quarkus.runtime.configuration.Configuration.getBuildTimeProperty; - import java.util.List; import java.util.Map; -import java.util.Optional; import org.infinispan.configuration.parsing.ConfigurationBuilderHolder; import org.infinispan.configuration.parsing.ParserRegistry; @@ -78,31 +75,10 @@ public class KeycloakRecorder { Map, String> defaultProviders, Map preConfiguredProviders, Boolean reaugmented) { - Profile.setInstance(createProfile()); + Profile.setInstance(new QuarkusProfile()); QuarkusKeycloakSessionFactory.setInstance(new QuarkusKeycloakSessionFactory(factories, defaultProviders, preConfiguredProviders, reaugmented)); } - public static Profile createProfile() { - return new Profile(new Profile.PropertyResolver() { - @Override - public String resolve(String feature) { - if (feature.startsWith("keycloak.profile.feature")) { - feature = feature.replaceFirst("keycloak\\.profile\\.feature.", "kc\\.features-"); - } else { - feature = "kc.features"; - } - - Optional value = getBuildTimeProperty(feature); - - if (value.isPresent()) { - return value.get(); - } - - return Configuration.getRawValue(feature); - } - }); - } - public RuntimeValue createCacheInitializer(String config, ShutdownContext shutdownContext) { try { ConfigurationBuilderHolder builder = new ParserRegistry().parse(config); diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/QuarkusProfile.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/QuarkusProfile.java new file mode 100644 index 0000000000..a8e64a1b18 --- /dev/null +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/QuarkusProfile.java @@ -0,0 +1,97 @@ +/* + * Copyright 2021 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.quarkus.runtime; + +import static org.keycloak.quarkus.runtime.configuration.Configuration.getRawPersistedProperty; + +import org.keycloak.common.Profile; +import org.keycloak.quarkus.runtime.configuration.Configuration; + +public class QuarkusProfile extends Profile { + + public QuarkusProfile() { + super(new DefaultPropertyResolver()); + } + + private static class DefaultPropertyResolver implements PropertyResolver { + + @Override + public String resolve(String key) { + if (isFeaturePresent(key, getCurrentValue("kc.features"))) { + if (isPreviewProfileKey(key)) { + return Profile.Type.PREVIEW.name(); + } + + return "enabled"; + } + + if (isFeaturePresent(key, getCurrentValue("kc.features-disabled"))) { + if (!isPreviewProfileKey(key)) { + return "disabled"; + } + } + + return null; + } + + private boolean isFeaturePresent(String key, String features) { + if (features == null) { + return false; + } + + for (String feature : features.split(",")) { + if (isPreviewProfileKey(key)) { + try { + Profile.Type profileType = Profile.Type.valueOf(feature); + + if (Profile.Type.PREVIEW.equals(profileType)) { + return true; + } + } catch (IllegalArgumentException ignore) { + } + + return false; + } + + if (key.substring(key.lastIndexOf('.') + 1).toUpperCase().equals(feature)) { + return true; + } + } + + return false; + } + + private boolean isPreviewProfileKey(String key) { + return key.equals("keycloak.profile"); + } + + private String getCurrentValue(String name) { + String enabledFeatures = getRawPersistedProperty(name).orElse(null); + + if (enabledFeatures == null) { + enabledFeatures = Configuration.getRawValue(name); + } + + if (enabledFeatures == null) { + return null; + } + + return enabledFeatures.toUpperCase().replace('-', '_'); + } + } +} diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/cli/Picocli.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/cli/Picocli.java index 37bc691827..963c943f67 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/cli/Picocli.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/cli/Picocli.java @@ -35,7 +35,6 @@ import static picocli.CommandLine.Model.UsageMessageSpec.SECTION_KEY_COMMAND_LIS import java.io.File; import java.io.PrintWriter; import java.util.ArrayList; -import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -51,7 +50,6 @@ import org.keycloak.quarkus.runtime.cli.command.Build; import org.keycloak.quarkus.runtime.cli.command.Main; import org.keycloak.quarkus.runtime.cli.command.Start; import org.keycloak.quarkus.runtime.cli.command.StartDev; -import org.keycloak.common.Profile; import org.keycloak.quarkus.runtime.configuration.ConfigArgsConfigSource; import org.keycloak.quarkus.runtime.configuration.PersistedConfigSource; import org.keycloak.quarkus.runtime.configuration.mappers.ConfigCategory; @@ -295,43 +293,11 @@ public final class Picocli { if (includeBuildTime) { mappers.addAll(PropertyMappers.getBuildTimeMappers()); - addFeatureOptions(commandSpec); } addMappedOptionsToArgGroups(commandSpec, mappers); } - private static void addFeatureOptions(CommandSpec commandSpec) { - ArgGroupSpec.Builder featureGroupBuilder = ArgGroupSpec.builder() - .heading(ConfigCategory.FEATURE.getHeading() + ":") - .order(ConfigCategory.FEATURE.getOrder()) - .validate(false); - - String previewName = Profile.Type.PREVIEW.name().toLowerCase(); - - featureGroupBuilder.addArg(OptionSpec.builder(new String[] {"-ft", "--features"}) - .description("Enables all tech preview features.") - .paramLabel(previewName) - .completionCandidates(Collections.singleton(previewName)) - .parameterConsumer(PropertyMapperParameterConsumer.INSTANCE) - .type(String.class) - .build()); - - List expectedValues = asList("enabled", "disabled"); - - for (Profile.Feature feature : Profile.Feature.values()) { - featureGroupBuilder.addArg(OptionSpec.builder("--features-" + feature.name().toLowerCase()) - .description("Enables the " + feature.name() + " feature.") - .paramLabel(String.join("|", expectedValues)) - .type(String.class) - .parameterConsumer(PropertyMapperParameterConsumer.INSTANCE) - .completionCandidates(expectedValues) - .build()); - } - - commandSpec.addArgGroup(featureGroupBuilder.build()); - } - private static void addMappedOptionsToArgGroups(CommandSpec cSpec, List propertyMappers) { for(ConfigCategory category : ConfigCategory.values()) { List mappersInCategory = propertyMappers.stream() diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/cli/PropertyMapperParameterConsumer.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/cli/PropertyMapperParameterConsumer.java index df5cd81f2f..71a605fc26 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/cli/PropertyMapperParameterConsumer.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/cli/PropertyMapperParameterConsumer.java @@ -19,8 +19,12 @@ package org.keycloak.quarkus.runtime.cli; import static org.keycloak.quarkus.runtime.cli.Picocli.ARG_PREFIX; -import java.util.Iterator; +import java.util.List; import java.util.Stack; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +import org.keycloak.utils.StringUtil; import picocli.CommandLine; import picocli.CommandLine.Model.ArgSpec; @@ -79,21 +83,23 @@ public final class PropertyMapperParameterConsumer implements CommandLine.IParam } private boolean isExpectedValue(OptionSpec option, String value) { - Iterator expectedValues = option.completionCandidates().iterator(); + List expectedValues = StreamSupport.stream(option.completionCandidates().spliterator(), false).collect(Collectors.toList()); - if (!expectedValues.hasNext()) { + if (expectedValues.isEmpty()) { // accept any return true; } - while (expectedValues.hasNext()) { - String expectedValue = expectedValues.next(); + if (StringUtil.isBlank(value)) { + return false; + } - if (expectedValue.equals(value)) { - return true; + for (String v : value.split(",")) { + if (!expectedValues.contains(v)) { + return false; } } - return false; + return true; } } diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/cli/command/Build.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/cli/command/Build.java index 1899a76cd5..6ba55c89a5 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/cli/command/Build.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/cli/command/Build.java @@ -48,10 +48,10 @@ import picocli.CommandLine.Mixin; footerHeading = "Examples:", footer = " Optimize the server based on a profile configuration:%n%n" + " $ ${PARENT-COMMAND-FULL-NAME:-$PARENTCOMMAND} --profile=prod ${COMMAND-NAME} %n%n" - + " Change database settings:%n%n" - + " $ ${PARENT-COMMAND-FULL-NAME:-$PARENTCOMMAND} ${COMMAND-NAME} --db=postgres [--db-url][--db-username][--db-password]%n%n" + + " Change the database vendor:%n%n" + + " $ ${PARENT-COMMAND-FULL-NAME:-$PARENTCOMMAND} ${COMMAND-NAME} --db=postgres%n%n" + " Enable a feature:%n%n" - + " $ ${PARENT-COMMAND-FULL-NAME:-$PARENTCOMMAND} ${COMMAND-NAME} --features-=[enabled|disabled]%n%n" + + " $ ${PARENT-COMMAND-FULL-NAME:-$PARENTCOMMAND} ${COMMAND-NAME} --features=%n%n" + " Or alternatively, enable all tech preview features:%n%n" + " $ ${PARENT-COMMAND-FULL-NAME:-$PARENTCOMMAND} ${COMMAND-NAME} --features=preview%n%n" + " Enable metrics:%n%n" diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/FeaturePropertyMappers.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/FeaturePropertyMappers.java new file mode 100644 index 0000000000..bed711e066 --- /dev/null +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/FeaturePropertyMappers.java @@ -0,0 +1,44 @@ +package org.keycloak.quarkus.runtime.configuration.mappers; + +import java.util.ArrayList; +import java.util.List; +import org.keycloak.common.Profile; + +final class FeaturePropertyMappers { + + private FeaturePropertyMappers() { + } + + public static PropertyMapper[] getMappers() { + return new PropertyMapper[] { + builder() + .from("features") + .description("Enables a set of one or more features.") + .expectedValues(getFeatureValues()) + .paramLabel("feature") + .build(), + builder() + .from("features-disabled") + .expectedValues(getFeatureValues()) + .paramLabel("feature") + .description("Disables a set of one or more features.") + .build() + }; + } + + private static List getFeatureValues() { + List features = new ArrayList<>(); + + for (Profile.Feature value : Profile.Feature.values()) { + features.add(value.name().toLowerCase().replace('_', '-')); + } + + features.add(Profile.Type.PREVIEW.name().toLowerCase()); + + return features; + } + + private static PropertyMapper.Builder builder() { + return PropertyMapper.builder(ConfigCategory.FEATURE).isBuildTimeProperty(true); + } +} diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/PropertyMappers.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/PropertyMappers.java index 2cae71d736..481c754b28 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/PropertyMappers.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/PropertyMappers.java @@ -29,6 +29,7 @@ public final class PropertyMappers { MAPPERS.addAll(MetricsPropertyMappers.getMetricsPropertyMappers()); MAPPERS.addAll(ProxyPropertyMappers.getProxyPropertyMappers()); MAPPERS.addAll(VaultPropertyMappers.getVaultPropertyMappers()); + MAPPERS.addAll(FeaturePropertyMappers.getMappers()); } public static ConfigValue getValue(ConfigSourceInterceptorContext context, String name) { diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/EnableFeatureDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/EnableFeatureDistTest.java deleted file mode 100644 index 17c6c90d72..0000000000 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/EnableFeatureDistTest.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2021 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.it.cli.dist; - -import org.junit.jupiter.api.Test; -import org.keycloak.it.junit5.extension.CLIResult; -import org.keycloak.it.junit5.extension.DistributionTest; - -import io.quarkus.test.junit.main.Launch; -import io.quarkus.test.junit.main.LaunchResult; - -@DistributionTest -class EnableFeatureDistTest { - - @Test - @Launch({ "build", "--features=preview" }) - void testEnablePreviewFeatures(LaunchResult result) { - CLIResult cliResult = (CLIResult) result; - cliResult.assertMessage("Preview feature enabled: admin_fine_grained_authz"); - cliResult.assertMessage("Preview feature enabled: openshift_integration"); - cliResult.assertMessage("Preview feature enabled: scripts"); - cliResult.assertMessage("Preview feature enabled: token_exchange"); - } - - @Test - @Launch({ "build", "--features-token_exchange=enabled" }) - void testEnableSinglefeature(LaunchResult result) { - CLIResult cliResult = (CLIResult) result; - cliResult.assertMessage("Preview feature enabled: token_exchange"); - } -} diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/FeaturesDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/FeaturesDistTest.java index d0297959e7..89f63082f0 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/FeaturesDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/FeaturesDistTest.java @@ -3,23 +3,71 @@ package org.keycloak.it.cli.dist; import io.quarkus.test.junit.main.Launch; import io.quarkus.test.junit.main.LaunchResult; import org.hamcrest.CoreMatchers; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.it.junit5.extension.DistributionTest; +import org.keycloak.it.junit5.extension.RawDistOnly; +import org.keycloak.quarkus.runtime.cli.command.Build; +import org.keycloak.quarkus.runtime.cli.command.Start; import org.keycloak.quarkus.runtime.cli.command.StartDev; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertFalse; @DistributionTest +@RawDistOnly(reason = "Containers are immutable") +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class FeaturesDistTest { + @Test + @Launch({ Build.NAME, "--features=preview", "--cache=local"}) + @Order(1) + public void testEnableOnBuild(LaunchResult result) { + CLIResult cliResult = (CLIResult) result; + cliResult.assertBuild(); + assertPreviewFeaturesEnabled(cliResult); + } + + @Test + @Launch({ Start.NAME, "--http-enabled=true", "--hostname-strict=false"}) + @Order(2) + public void testFeatureEnabledOnStart(LaunchResult result) { + assertPreviewFeaturesEnabled((CLIResult) result); + } + @Test @Launch({StartDev.NAME, "--features=preview"}) - public void testPreviewFeaturesGetEnabledWhenCliArgIsSet(LaunchResult result) { + public void testEnablePreviewFeatures(LaunchResult result) { + CLIResult cliResult = (CLIResult) result; + cliResult.assertStartedDevMode(); + assertPreviewFeaturesEnabled((CLIResult) result); + } + + @Test + @Launch({StartDev.NAME, "--features=preview", "--features-disabled=token-exchange"}) + public void testEnablePrecedenceOverDisable(LaunchResult result) { + CLIResult cliResult = (CLIResult) result; + cliResult.assertStartedDevMode(); + assertPreviewFeaturesEnabled((CLIResult) result); + } + + @Test + @Launch({StartDev.NAME, "--features=token-exchange,admin-fine-grained-authz"}) + public void testEnableMultipleFeatures(LaunchResult result) { CLIResult cliResult = (CLIResult) result; cliResult.assertStartedDevMode(); assertThat(cliResult.getOutput(), CoreMatchers.allOf( + containsString("Preview feature enabled: admin_fine_grained_authz"), + containsString("Preview feature enabled: token_exchange"))); + assertFalse(cliResult.getOutput().contains("declarative-user-profile")); + } + + private void assertPreviewFeaturesEnabled(CLIResult result) { + assertThat(result.getOutput(), CoreMatchers.allOf( containsString("Preview feature enabled: admin_fine_grained_authz"), containsString("Preview feature enabled: openshift_integration"), containsString("Preview feature enabled: scripts"), diff --git a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testBuildHelp.approved.txt b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testBuildHelp.approved.txt index 126c430a36..4569e8f4b9 100644 --- a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testBuildHelp.approved.txt +++ b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testBuildHelp.approved.txt @@ -41,44 +41,9 @@ Database: Feature: ---features-account2 - Enables the ACCOUNT2 feature. ---features-account_api - Enables the ACCOUNT_API feature. ---features-admin2 - Enables the ADMIN2 feature. ---features-admin_fine_grained_authz - Enables the ADMIN_FINE_GRAINED_AUTHZ feature. ---features-authorization - Enables the AUTHORIZATION feature. ---features-ciba - Enables the CIBA feature. ---features-client_policies - Enables the CLIENT_POLICIES feature. ---features-declarative_user_profile - Enables the DECLARATIVE_USER_PROFILE feature. ---features-docker - Enables the DOCKER feature. ---features-dynamic_scopes - Enables the DYNAMIC_SCOPES feature. ---features-impersonation - Enables the IMPERSONATION feature. ---features-map_storage - Enables the MAP_STORAGE feature. ---features-openshift_integration - Enables the OPENSHIFT_INTEGRATION feature. ---features-par - Enables the PAR feature. ---features-scripts - Enables the SCRIPTS feature. ---features-token_exchange - Enables the TOKEN_EXCHANGE feature. ---features-upload_scripts - Enables the UPLOAD_SCRIPTS feature. ---features-web_authn - Enables the WEB_AUTHN feature. --ft, --features - Enables all tech preview features. +--features Enables a set of one or more features. +--features-disabled + Disables a set of one or more features. HTTP/TLS: @@ -102,13 +67,13 @@ Examples: $ kc.sh --profile=prod build - Change database settings: + Change the database vendor: - $ kc.sh build --db=postgres [--db-url][--db-username][--db-password] + $ kc.sh build --db=postgres Enable a feature: - $ kc.sh build --features-=[enabled|disabled] + $ kc.sh build --features= Or alternatively, enable all tech preview features: diff --git a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testStartDevHelpAll.approved.txt b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testStartDevHelpAll.approved.txt index 76be0df1d3..fffda48675 100644 --- a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testStartDevHelpAll.approved.txt +++ b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testStartDevHelpAll.approved.txt @@ -54,44 +54,9 @@ Database: Feature: ---features-account2 - Enables the ACCOUNT2 feature. ---features-account_api - Enables the ACCOUNT_API feature. ---features-admin2 - Enables the ADMIN2 feature. ---features-admin_fine_grained_authz - Enables the ADMIN_FINE_GRAINED_AUTHZ feature. ---features-authorization - Enables the AUTHORIZATION feature. ---features-ciba - Enables the CIBA feature. ---features-client_policies - Enables the CLIENT_POLICIES feature. ---features-declarative_user_profile - Enables the DECLARATIVE_USER_PROFILE feature. ---features-docker - Enables the DOCKER feature. ---features-dynamic_scopes - Enables the DYNAMIC_SCOPES feature. ---features-impersonation - Enables the IMPERSONATION feature. ---features-map_storage - Enables the MAP_STORAGE feature. ---features-openshift_integration - Enables the OPENSHIFT_INTEGRATION feature. ---features-par - Enables the PAR feature. ---features-scripts - Enables the SCRIPTS feature. ---features-token_exchange - Enables the TOKEN_EXCHANGE feature. ---features-upload_scripts - Enables the UPLOAD_SCRIPTS feature. ---features-web_authn - Enables the WEB_AUTHN feature. --ft, --features - Enables all tech preview features. +--features Enables a set of one or more features. +--features-disabled + Disables a set of one or more features. Hostname: