Allow exposing some initial provider config options via web site (#10572)

* Allow exposing some initial provider config options via web site

Co-authored-by: Stian Thorgersen <stian@redhat.com>

Closes #10571

* Include type to provider options, and hide build-icon column as it's not relevant

Co-authored-by: stianst <stianst@gmail.com>
This commit is contained in:
Pedro Igor 2022-04-19 03:01:42 -03:00 committed by GitHub
parent f9d4566723
commit 52d205ca91
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 414 additions and 36 deletions

View file

@ -0,0 +1,16 @@
<#import "/templates/guide.adoc" as template>
<#import "/templates/options.adoc" as opts>
<@template.guide
title="All provider configuration"
summary="Complete list of all the available provider configuration options">
<#list ctx.options.getProviderOptions() as spi, providers>
== ${spi}
<#list providers as provider, options>
=== ${provider}
<@opts.list options=options buildIcon=false></@opts.list>
</#list>
</#list>
</@template.guide>

View file

@ -4,10 +4,16 @@
</#list> </#list>
</#macro> </#macro>
<#macro list options> <#macro list options buildIcon=true>
<#if buildIcon>
[cols="12a,4,4,1",role="options"] [cols="12a,4,4,1",role="options"]
|=== |===
| |Type|Default| | |Type|Default|
<#else>
[cols="12a,4,4",role="options"]
|===
| |Type|Default
</#if>
<#list options as option> <#list options as option>
| |
@ -25,7 +31,7 @@
|<#if option.defaultValue?has_content>[.options-default]#${option.defaultValue!}#</#if> |<#if option.defaultValue?has_content>[.options-default]#${option.defaultValue!}#</#if>
|<#if option.build>icon:tools[role=options-build]</#if> <#if buildIcon>|<#if option.build>icon:tools[role=options-build]</#if></#if>
</#list> </#list>
|=== |===

View file

@ -62,12 +62,6 @@
<dependency> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-quarkus-server</artifactId> <artifactId>keycloak-quarkus-server</artifactId>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>

View file

@ -1,11 +1,24 @@
package org.keycloak.guides.maven; package org.keycloak.guides.maven;
import static org.keycloak.quarkus.runtime.configuration.Configuration.OPTION_PART_SEPARATOR;
import static org.keycloak.quarkus.runtime.configuration.Configuration.toDashCase;
import static org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider.NS_KEYCLOAK_PREFIX;
import org.apache.commons.lang3.ArrayUtils;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.ProviderManager;
import org.keycloak.provider.Spi;
import org.keycloak.quarkus.runtime.Providers;
import org.keycloak.quarkus.runtime.configuration.Configuration;
import org.keycloak.quarkus.runtime.configuration.mappers.ConfigCategory; import org.keycloak.quarkus.runtime.configuration.mappers.ConfigCategory;
import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper; import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper;
import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMappers; import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMappers;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -17,6 +30,7 @@ import java.util.stream.StreamSupport;
public class Options { public class Options {
private final Map<String, Option> options; private final Map<String, Option> options;
private final Map<String, Map<String, List<Option>>> providerOptions = new LinkedHashMap<>();
public Options() { public Options() {
options = PropertyMappers.getMappers().stream() options = PropertyMappers.getMappers().stream()
@ -25,6 +39,29 @@ public class Options {
.map(m -> new Option(m.getFrom(), m.getCategory(), m.isBuildTime(), m.getDescription(), m.getDefaultValue(), m.getExpectedValues())) .map(m -> new Option(m.getFrom(), m.getCategory(), m.isBuildTime(), m.getDescription(), m.getDefaultValue(), m.getExpectedValues()))
.sorted(Comparator.comparing(Option::getKey)) .sorted(Comparator.comparing(Option::getKey))
.collect(Collectors.toMap(Option::getKey, o -> o, (o1, o2) -> o1, LinkedHashMap::new)); // Need to ignore duplicate keys?? .collect(Collectors.toMap(Option::getKey, o -> o, (o1, o2) -> o1, LinkedHashMap::new)); // Need to ignore duplicate keys??
ProviderManager providerManager = Providers.getProviderManager(Thread.currentThread().getContextClassLoader());
for (Spi loadSpi : providerManager.loadSpis().stream().sorted(Comparator.comparing(Spi::getName)).collect(Collectors.toList())) {
for (ProviderFactory providerFactory : providerManager.load(loadSpi).stream().sorted(Comparator.comparing(ProviderFactory::getId)).collect(Collectors.toList())) {
List<ProviderConfigProperty> configMetadata = providerFactory.getConfigMetadata();
if (configMetadata == null) {
continue;
}
String optionPrefix = NS_KEYCLOAK_PREFIX + String.join(OPTION_PART_SEPARATOR, ArrayUtils.insert(0, new String[] {loadSpi.getName(), providerFactory.getId()}, "spi"));
List<Option> options = configMetadata.stream()
.map(m -> new Option(Configuration.toDashCase(optionPrefix.concat("-") + m.getName()), ConfigCategory.GENERAL, false,
m.getHelpText(),
m.getDefaultValue() == null ? "none" : m.getDefaultValue().toString(),
m.getOptions() == null ? (m.getType() == null ? Collections.emptyList() : Collections.singletonList(m.getType())) : m.getOptions()))
.sorted(Comparator.comparing(Option::getKey)).collect(Collectors.toList());
if (!options.isEmpty()) {
providerOptions.computeIfAbsent(toDashCase(loadSpi.getName()), k -> new LinkedHashMap<>()).put(toDashCase(providerFactory.getId()), options);
}
}
}
} }
public ConfigCategory[] getCategories() { public ConfigCategory[] getCategories() {
@ -48,6 +85,10 @@ public class Options {
return this.options.values().stream().filter(o -> o.getKey().matches(r)).collect(Collectors.toList()); return this.options.values().stream().filter(o -> o.getKey().matches(r)).collect(Collectors.toList());
} }
public Map<String, Map<String, List<Option>>> getProviderOptions() {
return providerOptions;
}
public class Option { public class Option {
private String key; private String key;

View file

@ -33,10 +33,13 @@ import org.keycloak.models.sessions.infinispan.events.RealmRemovedSessionEvent;
import org.keycloak.models.sessions.infinispan.util.InfinispanKeyGenerator; import org.keycloak.models.sessions.infinispan.util.InfinispanKeyGenerator;
import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.models.utils.PostMigrationEvent; import org.keycloak.models.utils.PostMigrationEvent;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.provider.ProviderConfigurationBuilder;
import org.keycloak.provider.ProviderEvent; import org.keycloak.provider.ProviderEvent;
import org.keycloak.provider.ProviderEventListener; import org.keycloak.provider.ProviderEventListener;
import org.keycloak.sessions.AuthenticationSessionProvider; import org.keycloak.sessions.AuthenticationSessionProvider;
import org.keycloak.sessions.AuthenticationSessionProviderFactory; import org.keycloak.sessions.AuthenticationSessionProviderFactory;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@ -95,6 +98,17 @@ public class InfinispanAuthenticationSessionProviderFactory implements Authentic
}); });
} }
@Override
public List<ProviderConfigProperty> getConfigMetadata() {
return ProviderConfigurationBuilder.create()
.property()
.name("authSessionsLimit")
.type("int")
.helpText("The maximum number of concurrent authentication sessions.")
.defaultValue(DEFAULT_AUTH_SESSIONS_LIMIT)
.add()
.build();
}
protected void registerClusterListeners(KeycloakSession session) { protected void registerClusterListeners(KeycloakSession session) {
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory(); KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();

View file

@ -21,10 +21,14 @@ import org.jboss.logging.Logger;
import org.keycloak.Config; import org.keycloak.Config;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.provider.ProviderConfigurationBuilder;
import org.keycloak.sessions.StickySessionEncoderProvider; import org.keycloak.sessions.StickySessionEncoderProvider;
import org.keycloak.sessions.StickySessionEncoderProviderFactory; import org.keycloak.sessions.StickySessionEncoderProviderFactory;
import static org.keycloak.models.sessions.infinispan.InfinispanAuthenticationSessionProviderFactory.PROVIDER_PRIORITY; import static org.keycloak.models.sessions.infinispan.InfinispanAuthenticationSessionProviderFactory.PROVIDER_PRIORITY;
import java.util.List;
/** /**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a> * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/ */
@ -71,4 +75,16 @@ public class InfinispanStickySessionEncoderProviderFactory implements StickySess
public int order() { public int order() {
return PROVIDER_PRIORITY; return PROVIDER_PRIORITY;
} }
@Override
public List<ProviderConfigProperty> getConfigMetadata() {
return ProviderConfigurationBuilder.create()
.property()
.name("shouldAttachRoute")
.type("boolean")
.helpText("If the route should be attached to cookies to reflect the node that owns a particular session.")
.defaultValue(true)
.add()
.build();
}
} }

View file

@ -17,12 +17,15 @@
package org.keycloak.connections.jpa.updater.liquibase.lock; package org.keycloak.connections.jpa.updater.liquibase.lock;
import java.util.List;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.Config; import org.keycloak.Config;
import org.keycloak.common.util.Time; import org.keycloak.common.util.Time;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.dblock.DBLockProviderFactory; import org.keycloak.models.dblock.DBLockProviderFactory;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.provider.ProviderConfigurationBuilder;
/** /**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a> * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@ -74,4 +77,14 @@ public class LiquibaseDBLockProviderFactory implements DBLockProviderFactory {
public int order() { public int order() {
return PROVIDER_PRIORITY; return PROVIDER_PRIORITY;
} }
@Override
public List<ProviderConfigProperty> getConfigMetadata() {
return ProviderConfigurationBuilder.create()
.property()
.name("lockWaitTimeout")
.type("int")
.helpText("The maximum time to wait when waiting to release a database lock.")
.add().build();
}
} }

View file

@ -17,6 +17,7 @@
package org.keycloak.quarkus.deployment; package org.keycloak.quarkus.deployment;
import static org.keycloak.quarkus.runtime.Providers.getProviderManager;
import static org.keycloak.quarkus.runtime.configuration.Configuration.getPropertyNames; import static org.keycloak.quarkus.runtime.configuration.Configuration.getPropertyNames;
import static org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider.NS_QUARKUS; import static org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider.NS_QUARKUS;
import static org.keycloak.quarkus.runtime.configuration.QuarkusPropertiesConfigSource.QUARKUS_PROPERTY_ENABLED; import static org.keycloak.quarkus.runtime.configuration.QuarkusPropertiesConfigSource.QUARKUS_PROPERTY_ENABLED;
@ -506,14 +507,7 @@ class KeycloakProcessor {
Map<String, ProviderFactory> preConfiguredProviders) { Map<String, ProviderFactory> preConfiguredProviders) {
Config.init(new MicroProfileConfigProvider()); Config.init(new MicroProfileConfigProvider());
ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
ProviderManager pm = getProviderManager(classLoader);
KeycloakDeploymentInfo keycloakDeploymentInfo = KeycloakDeploymentInfo.create()
.name("classpath")
.services()
// .themes() // handling of .jar based themes is already supported by Keycloak.x
.themeResources();
ProviderManager pm = new ProviderManager(keycloakDeploymentInfo, classLoader);
Map<Spi, Map<Class<? extends Provider>, Map<String, ProviderFactory>>> factories = new HashMap<>(); Map<Spi, Map<Class<? extends Provider>, Map<String, ProviderFactory>>> factories = new HashMap<>();
for (Spi spi : pm.loadSpis()) { for (Spi spi : pm.loadSpis()) {

View file

@ -0,0 +1,33 @@
/*
* 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 org.keycloak.provider.KeycloakDeploymentInfo;
import org.keycloak.provider.ProviderManager;
public final class Providers {
public static ProviderManager getProviderManager(ClassLoader classLoader) {
KeycloakDeploymentInfo keycloakDeploymentInfo = KeycloakDeploymentInfo.create()
.name("classpath")
.services()
.themeResources();
return new ProviderManager(keycloakDeploymentInfo, classLoader);
}
}

View file

@ -137,6 +137,25 @@ public final class Configuration {
return ARG_PREFIX + key; return ARG_PREFIX + key;
} }
public static String toDashCase(String key) {
StringBuilder sb = new StringBuilder(key.length());
boolean l = false;
for (int i = 0; i < key.length(); i++) {
char c = key.charAt(i);
if (l && Character.isUpperCase(c)) {
sb.append('-');
c = Character.toLowerCase(c);
l = false;
} else {
l = Character.isLowerCase(c);
}
sb.append(c);
}
return sb.toString();
}
private static String getValue(ConfigSource configSource, String name) { private static String getValue(ConfigSource configSource, String name) {
String value = configSource.getValue(name); String value = configSource.getValue(name);

View file

@ -18,6 +18,7 @@
package org.keycloak.quarkus.runtime.configuration; package org.keycloak.quarkus.runtime.configuration;
import static org.keycloak.quarkus.runtime.configuration.Configuration.OPTION_PART_SEPARATOR; import static org.keycloak.quarkus.runtime.configuration.Configuration.OPTION_PART_SEPARATOR;
import static org.keycloak.quarkus.runtime.configuration.Configuration.toDashCase;
import static org.keycloak.quarkus.runtime.configuration.Configuration.toEnvVarFormat; import static org.keycloak.quarkus.runtime.configuration.Configuration.toEnvVarFormat;
import java.util.Set; import java.util.Set;
@ -134,23 +135,4 @@ public class MicroProfileConfigProvider implements Config.ConfigProvider {
} }
} }
private static String toDashCase(String s) {
StringBuilder sb = new StringBuilder(s.length());
boolean l = false;
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (l && Character.isUpperCase(c)) {
sb.append('-');
c = Character.toLowerCase(c);
l = false;
} else {
l = Character.isLowerCase(c);
}
sb.append(c);
}
return sb.toString();
}
} }

View file

@ -65,6 +65,8 @@ import org.keycloak.models.*;
import org.keycloak.models.dblock.DBLockManager; import org.keycloak.models.dblock.DBLockManager;
import org.keycloak.models.dblock.DBLockProvider; import org.keycloak.models.dblock.DBLockProvider;
import org.keycloak.models.utils.RepresentationToModel; import org.keycloak.models.utils.RepresentationToModel;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.provider.ProviderConfigurationBuilder;
import org.keycloak.provider.ServerInfoAwareProviderFactory; import org.keycloak.provider.ServerInfoAwareProviderFactory;
import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.representations.idm.UserRepresentation;
@ -147,6 +149,30 @@ public class QuarkusJpaConnectionProviderFactory extends AbstractJpaConnectionPr
} }
} }
@Override
public List<ProviderConfigProperty> getConfigMetadata() {
return ProviderConfigurationBuilder.create()
.property()
.name("initializeEmpty")
.type("boolean")
.helpText("Initialize database if empty. If set to false the database has to be manually initialized. If you want to manually initialize the database set migrationStrategy to manual which will create a file with SQL commands to initialize the database.")
.defaultValue(true)
.add()
.property()
.name("migrationStrategy")
.type("string")
.helpText("Strategy to use to migrate database. Valid values are update, manual and validate. Update will automatically migrate the database schema. Manual will export the required changes to a file with SQL commands that you can manually execute on the database. Validate will simply check if the database is up-to-date.")
.options("update", "manual", "validate")
.defaultValue("update")
.add()
.property()
.name("migrationExport")
.type("string")
.helpText("Path for where to write manual database initialization/migration file.")
.add()
.build();
}
@Override @Override
protected EntityManagerFactory getEntityManagerFactory() { protected EntityManagerFactory getEntityManagerFactory() {
Instance<EntityManagerFactory> instance = Arc.container().select(EntityManagerFactory.class); Instance<EntityManagerFactory> instance = Arc.container().select(EntityManagerFactory.class);

View file

@ -17,6 +17,8 @@
package org.keycloak.provider; package org.keycloak.provider;
import java.util.Collections;
import java.util.List;
import org.keycloak.Config; import org.keycloak.Config;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.KeycloakSessionFactory;
@ -57,4 +59,12 @@ public interface ProviderFactory<T extends Provider> {
return 0; return 0;
} }
/**
* Returns the metadata for each configuration property supported by this factory.
*
* @return a list with the metadata for each configuration property supported by this factory
*/
default List<ProviderConfigProperty> getConfigMetadata() {
return Collections.emptyList();
}
} }

View file

@ -30,11 +30,14 @@ import org.keycloak.common.util.EnvUtil;
import org.keycloak.common.util.KeystoreUtil; import org.keycloak.common.util.KeystoreUtil;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.provider.ProviderConfigurationBuilder;
import org.keycloak.truststore.TruststoreProvider; import org.keycloak.truststore.TruststoreProvider;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.security.KeyStore; import java.security.KeyStore;
import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.util.EntityUtils; import org.apache.http.util.EntityUtils;
@ -225,6 +228,86 @@ public class DefaultHttpClientFactory implements HttpClientFactory {
} }
@Override
public List<ProviderConfigProperty> getConfigMetadata() {
return ProviderConfigurationBuilder.create()
.property()
.name("socket-timeout-millis")
.type("long")
.helpText("Socket inactivity timeout.")
.defaultValue(5000L)
.add()
.property()
.name("establish-connection-timeout-millis")
.type("long")
.helpText("When trying to make an initial socket connection, what is the timeout?")
.defaultValue(-1L)
.add()
.property()
.name("max-pooled-per-route")
.type("int")
.helpText("Assigns maximum connection per route value.")
.defaultValue(64)
.add()
.property()
.name("connection-pool-size")
.type("int")
.helpText("Assigns maximum total connection value.")
.add()
.property()
.name("connection-ttl-millis")
.type("long")
.helpText("Sets maximum time, in milliseconds, to live for persistent connections.")
.defaultValue(-1L)
.add()
.property()
.name("reuse-connections")
.type("boolean")
.helpText("If connections should be reused.")
.defaultValue(true)
.add()
.property()
.name("max-connection-idle-time-millis")
.type("long")
.helpText("Sets the time, in milliseconds, for evicting idle connections from the pool.")
.defaultValue(900000)
.add()
.property()
.name("disable-cookies")
.type("boolean")
.helpText("Disables state (cookie) management.")
.defaultValue(true)
.add()
.property()
.name("client-keystore")
.type("string")
.helpText("The file path of the key store from where the key material is going to be read from to set-up TLS connections.")
.add()
.property()
.name("client-keystore-password")
.type("string")
.helpText("The key store password.")
.add()
.property()
.name("client-key-password")
.type("string")
.helpText("The key password.")
.defaultValue(-1L)
.add()
.property()
.name("disable-trust-manager")
.type("boolean")
.helpText("Disable trust management and hostname verification. NOTE this is a security hole, so only set this option if you cannot or do not want to verify the identity of the host you are communicating with.")
.defaultValue(false)
.add()
.property()
.name("proxy-mappings")
.type("string")
.helpText("Denotes the combination of a regex based hostname pattern and a proxy-uri in the form of hostnamePattern;proxyUri.")
.add()
.build();
}
private boolean getBooleanConfigWithSysPropFallback(String key, boolean defaultValue) { private boolean getBooleanConfigWithSysPropFallback(String key, boolean defaultValue) {
Boolean value = config.getBoolean(key); Boolean value = config.getBoolean(key);
if (value == null) { if (value == null) {

View file

@ -6,10 +6,13 @@ import org.keycloak.Config;
import org.keycloak.common.Version; import org.keycloak.common.Version;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.platform.Platform; import org.keycloak.platform.Platform;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.provider.ProviderConfigurationBuilder;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
public class GzipResourceEncodingProviderFactory implements ResourceEncodingProviderFactory { public class GzipResourceEncodingProviderFactory implements ResourceEncodingProviderFactory {
@ -47,6 +50,18 @@ public class GzipResourceEncodingProviderFactory implements ResourceEncodingProv
return "gzip"; return "gzip";
} }
@Override
public List<ProviderConfigProperty> getConfigMetadata() {
return ProviderConfigurationBuilder.create()
.property()
.name("excludedContentTypes")
.type("string")
.helpText("A space separated list of content-types to exclude from encoding.")
.defaultValue("image/png image/jpeg")
.add()
.build();
}
private synchronized File initCacheDir() { private synchronized File initCacheDir() {
if (cacheDir != null) { if (cacheDir != null) {
return cacheDir; return cacheDir;

View file

@ -24,9 +24,14 @@ import org.keycloak.events.EventListenerProviderFactory;
import org.keycloak.events.EventType; import org.keycloak.events.EventType;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.provider.ProviderConfigurationBuilder;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
/** /**
@ -80,4 +85,27 @@ public class EmailEventListenerProviderFactory implements EventListenerProviderF
return "email"; return "email";
} }
@Override
public List<ProviderConfigProperty> getConfigMetadata() {
String[] supportedEvents = Arrays.stream(EventType.values())
.map(EventType::name)
.map(String::toLowerCase)
.sorted(Comparator.naturalOrder())
.toArray(String[]::new);
return ProviderConfigurationBuilder.create()
.property()
.name("include-events")
.type("string")
.helpText("A comma-separated list of events that should be sent via email to the user's account.")
.options(supportedEvents)
.defaultValue("All events")
.add()
.property()
.name("exclude-events")
.type("string")
.helpText("A comma-separated list of events that should not be sent via email to the user's account.")
.options(supportedEvents)
.add()
.build();
}
} }

View file

@ -17,12 +17,17 @@
package org.keycloak.events.log; package org.keycloak.events.log;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.Config; import org.keycloak.Config;
import org.keycloak.events.EventListenerProvider; import org.keycloak.events.EventListenerProvider;
import org.keycloak.events.EventListenerProviderFactory; import org.keycloak.events.EventListenerProviderFactory;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.provider.ProviderConfigurationBuilder;
/** /**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a> * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@ -61,4 +66,28 @@ public class JBossLoggingEventListenerProviderFactory implements EventListenerPr
return ID; return ID;
} }
@Override
public List<ProviderConfigProperty> getConfigMetadata() {
String[] logLevels = Arrays.stream(Logger.Level.values())
.map(Logger.Level::name)
.map(String::toLowerCase)
.sorted(Comparator.naturalOrder())
.toArray(String[]::new);
return ProviderConfigurationBuilder.create()
.property()
.name("success-level")
.type("string")
.helpText("The log level for success messages.")
.options(logLevels)
.defaultValue("debug")
.add()
.property()
.name("error-level")
.type("string")
.helpText("The log level for error messages.")
.options(logLevels)
.defaultValue("warn")
.add()
.build();
}
} }

View file

@ -19,6 +19,7 @@ package org.keycloak.protocol.oidc;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.List;
import java.util.Map; import java.util.Map;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
@ -26,6 +27,8 @@ import org.keycloak.Config;
import org.keycloak.common.util.FindFile; import org.keycloak.common.util.FindFile;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.provider.ProviderConfigurationBuilder;
import org.keycloak.util.JsonSerialization; import org.keycloak.util.JsonSerialization;
import org.keycloak.wellknown.WellKnownProvider; import org.keycloak.wellknown.WellKnownProvider;
import org.keycloak.wellknown.WellKnownProviderFactory; import org.keycloak.wellknown.WellKnownProviderFactory;
@ -88,6 +91,23 @@ public class OIDCWellKnownProviderFactory implements WellKnownProviderFactory {
return 100; return 100;
} }
@Override
public List<ProviderConfigProperty> getConfigMetadata() {
return ProviderConfigurationBuilder.create()
.property()
.name("openid-configuration-override")
.type("string")
.helpText("The file path from where the metadata should be loaded from. You can use an absolute file path or, if the file is in the server classpath, use the 'classpath:' prefix to load the file from the classpath.")
.add()
.property()
.name("include-client-scopes")
.type("boolean")
.helpText("If client scopes should be used to calculate the list of supported scopes.")
.defaultValue(true)
.add()
.build();
}
protected Map<String, Object> getOpenidConfigOverride() { protected Map<String, Object> getOpenidConfigOverride() {
return openidConfigOverride; return openidConfigOverride;
} }

View file

@ -17,9 +17,12 @@
*/ */
package org.keycloak.protocol.oidc.grants.ciba.channel; package org.keycloak.protocol.oidc.grants.ciba.channel;
import java.util.List;
import org.keycloak.Config.Scope; import org.keycloak.Config.Scope;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.provider.ProviderConfigurationBuilder;
/** /**
* @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a> * @author <a href="mailto:takashi.norimatsu.ws@hitachi.com">Takashi Norimatsu</a>
@ -52,4 +55,15 @@ public class HttpAuthenticationChannelProviderFactory implements AuthenticationC
public String getId() { public String getId() {
return PROVIDER_ID; return PROVIDER_ID;
} }
@Override
public List<ProviderConfigProperty> getConfigMetadata() {
return ProviderConfigurationBuilder.create()
.property()
.name("httpAuthenticationChannelUri")
.type("string")
.helpText("The HTTP(S) URI of the authentication channel.")
.add()
.build();
}
} }

View file

@ -21,6 +21,8 @@ import org.jboss.logging.Logger;
import org.keycloak.Config; import org.keycloak.Config;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.provider.ProviderConfigurationBuilder;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
@ -36,9 +38,11 @@ import java.security.SignatureException;
import java.security.cert.Certificate; import java.security.cert.Certificate;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import javax.security.auth.x500.X500Principal; import javax.security.auth.x500.X500Principal;
@ -127,7 +131,28 @@ public class FileTruststoreProviderFactory implements TruststoreProviderFactory
return "file"; return "file";
} }
@Override
public List<ProviderConfigProperty> getConfigMetadata() {
return ProviderConfigurationBuilder.create()
.property()
.name("file")
.type("string")
.helpText("The file path of the trust store from where the certificates are going to be read from to validate TLS connections.")
.add()
.property()
.name("password")
.type("string")
.helpText("The trust store password.")
.add()
.property()
.name("hostname-verification-policy")
.type("string")
.helpText("The hostname verification policy.")
.options(Arrays.stream(HostnameVerificationPolicy.values()).map(HostnameVerificationPolicy::name).map(String::toLowerCase).toArray(String[]::new))
.defaultValue(HostnameVerificationPolicy.WILDCARD.name().toLowerCase())
.add()
.build();
}
private static class TruststoreCertificatesLoader { private static class TruststoreCertificatesLoader {