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:
parent
f9d4566723
commit
52d205ca91
20 changed files with 414 additions and 36 deletions
16
docs/guides/src/main/server/all-provider-config.adoc
Normal file
16
docs/guides/src/main/server/all-provider-config.adoc
Normal 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>
|
|
@ -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>
|
||||||
|
|
||||||
|===
|
|===
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()) {
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue