mirror of
https://github.com/keycloak/keycloak.git
synced 2026-01-10 15:32:05 -03:30
KEYCLOAK-12162 Modularize config backends (#6499)
* KEYCLOAK-12162 - Modularize configuration backends * - Use JsonSerialization - simplify backend selection (no fallbacks) * Remove unused org.wildfly.core:wildfly-controller dependency
This commit is contained in:
parent
623f347263
commit
79074aa380
@ -25,11 +25,15 @@
|
||||
</resources>
|
||||
|
||||
<dependencies>
|
||||
<module name="com.fasterxml.jackson.core.jackson-databind"/>
|
||||
<module name="javax.servlet.api"/>
|
||||
<module name="org.keycloak.keycloak-common"/>
|
||||
<module name="org.keycloak.keycloak-core"/>
|
||||
<module name="org.keycloak.keycloak-server-spi"/>
|
||||
<module name="org.keycloak.keycloak-server-spi-private"/>
|
||||
<module name="org.keycloak.keycloak-services"/>
|
||||
<module name="org.jboss.dmr"/>
|
||||
<module name="org.jboss.logging"/>
|
||||
<module name="org.jboss.modules"/>
|
||||
<module name="org.jboss.resteasy.resteasy-jaxrs"/>
|
||||
</dependencies>
|
||||
|
||||
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2019 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.config;
|
||||
|
||||
import java.util.Optional;
|
||||
import org.keycloak.Config;
|
||||
|
||||
public interface ConfigProviderFactory {
|
||||
|
||||
Optional<Config.ConfigProvider> create();
|
||||
|
||||
}
|
||||
@ -88,11 +88,6 @@
|
||||
<artifactId>twitter4j-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.wildfly.core</groupId>
|
||||
<artifactId>wildfly-controller</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
|
||||
@ -17,13 +17,10 @@
|
||||
package org.keycloak.services.resources;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.jboss.dmr.ModelNode;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.common.util.Resteasy;
|
||||
import org.keycloak.common.util.SystemEnvProperties;
|
||||
import org.keycloak.config.ConfigProviderFactory;
|
||||
import org.keycloak.exportimport.ExportImportManager;
|
||||
import org.keycloak.migration.MigrationModelManager;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
@ -53,7 +50,6 @@ import org.keycloak.services.scheduled.ClearExpiredEvents;
|
||||
import org.keycloak.services.scheduled.ClearExpiredUserSessions;
|
||||
import org.keycloak.services.scheduled.ClusterAwareScheduledTaskRunner;
|
||||
import org.keycloak.services.scheduled.ScheduledTaskRunner;
|
||||
import org.keycloak.services.util.JsonConfigProvider;
|
||||
import org.keycloak.services.util.ObjectMapperResolver;
|
||||
import org.keycloak.timer.TimerProvider;
|
||||
import org.keycloak.transaction.JtaTransactionManagerLookup;
|
||||
@ -63,20 +59,15 @@ import javax.servlet.ServletContext;
|
||||
import javax.transaction.SystemException;
|
||||
import javax.transaction.Transaction;
|
||||
import javax.ws.rs.core.Application;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
@ -88,15 +79,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class KeycloakApplication extends Application {
|
||||
// This param name is defined again in Keycloak Server Subsystem class
|
||||
// org.keycloak.subsystem.server.extension.KeycloakServerDeploymentProcessor. We have this value in
|
||||
// two places to avoid dependency between Keycloak Subsystem and Keycloak Services module.
|
||||
public static final String KEYCLOAK_CONFIG_PARAM_NAME = "org.keycloak.server-subsystem.Config";
|
||||
|
||||
public static final String KEYCLOAK_EMBEDDED = "keycloak.embedded";
|
||||
|
||||
public static final String SERVER_CONTEXT_CONFIG_PROPERTY_OVERRIDES = "keycloak.server.context.config.property-overrides";
|
||||
|
||||
public static final AtomicBoolean BOOTSTRAP_ADMIN_USER = new AtomicBoolean(false);
|
||||
|
||||
private static final Logger logger = Logger.getLogger(KeycloakApplication.class);
|
||||
@ -120,7 +105,7 @@ public class KeycloakApplication extends Application {
|
||||
embedded = true;
|
||||
}
|
||||
|
||||
loadConfig(context);
|
||||
loadConfig();
|
||||
|
||||
this.sessionFactory = createSessionFactory();
|
||||
|
||||
@ -279,59 +264,18 @@ public class KeycloakApplication extends Application {
|
||||
}
|
||||
}
|
||||
|
||||
public static void loadConfig(ServletContext context) {
|
||||
protected void loadConfig() {
|
||||
|
||||
ServiceLoader<ConfigProviderFactory> loader = ServiceLoader.load(ConfigProviderFactory.class, KeycloakApplication.class.getClassLoader());
|
||||
|
||||
try {
|
||||
JsonNode node = null;
|
||||
|
||||
String dmrConfig = loadDmrConfig(context);
|
||||
if (dmrConfig != null) {
|
||||
node = new ObjectMapper().readTree(dmrConfig);
|
||||
ServicesLogger.LOGGER.loadingFrom("standalone.xml or domain.xml");
|
||||
}
|
||||
|
||||
String configDir = System.getProperty("jboss.server.config.dir");
|
||||
if (node == null && configDir != null) {
|
||||
File f = new File(configDir + File.separator + "keycloak-server.json");
|
||||
if (f.isFile()) {
|
||||
ServicesLogger.LOGGER.loadingFrom(f.getAbsolutePath());
|
||||
node = new ObjectMapper().readTree(f);
|
||||
}
|
||||
}
|
||||
|
||||
if (node == null) {
|
||||
URL resource = Thread.currentThread().getContextClassLoader().getResource("META-INF/keycloak-server.json");
|
||||
if (resource != null) {
|
||||
ServicesLogger.LOGGER.loadingFrom(resource);
|
||||
node = new ObjectMapper().readTree(resource);
|
||||
}
|
||||
}
|
||||
|
||||
if (node != null) {
|
||||
Map<String, String> propertyOverridesMap = new HashMap<>();
|
||||
String propertyOverrides = context.getInitParameter(SERVER_CONTEXT_CONFIG_PROPERTY_OVERRIDES);
|
||||
if (context.getInitParameter(SERVER_CONTEXT_CONFIG_PROPERTY_OVERRIDES) != null) {
|
||||
JsonNode jsonObj = new ObjectMapper().readTree(propertyOverrides);
|
||||
jsonObj.fields().forEachRemaining(e -> propertyOverridesMap.put(e.getKey(), e.getValue().asText()));
|
||||
}
|
||||
Properties properties = new SystemEnvProperties(propertyOverridesMap);
|
||||
Config.init(new JsonConfigProvider(node, properties));
|
||||
} else {
|
||||
throw new RuntimeException("Keycloak config not found.");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to load config", e);
|
||||
ConfigProviderFactory factory = loader.iterator().next();
|
||||
logger.infov("Using ConfigProvider: {0}", factory.getClass().getName());
|
||||
Config.init(factory.create().orElseThrow(() -> new RuntimeException("Failed to load Keycloak configuration")));
|
||||
} catch (NoSuchElementException e) {
|
||||
throw new RuntimeException("No valid ConfigProvider found");
|
||||
}
|
||||
}
|
||||
|
||||
private static String loadDmrConfig(ServletContext context) {
|
||||
String dmrConfig = context.getInitParameter(KEYCLOAK_CONFIG_PARAM_NAME);
|
||||
if (dmrConfig == null) return null;
|
||||
|
||||
ModelNode dmrConfigNode = ModelNode.fromString(dmrConfig);
|
||||
if (dmrConfigNode.asPropertyList().isEmpty()) return null;
|
||||
|
||||
// note that we need to resolve expressions BEFORE we convert to JSON
|
||||
return dmrConfigNode.resolve().toJSONString(true);
|
||||
}
|
||||
|
||||
public static KeycloakSessionFactory createSessionFactory() {
|
||||
|
||||
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2019 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.services.util;
|
||||
|
||||
import org.keycloak.config.ConfigProviderFactory;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.Optional;
|
||||
import java.util.Properties;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.common.util.SystemEnvProperties;
|
||||
import org.keycloak.services.ServicesLogger;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
public abstract class JsonConfigProviderFactory implements ConfigProviderFactory {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(JsonConfigProviderFactory.class);
|
||||
|
||||
@Override
|
||||
public Optional<Config.ConfigProvider> create() {
|
||||
|
||||
JsonNode node = null;
|
||||
|
||||
try {
|
||||
String configDir = System.getProperty("jboss.server.config.dir");
|
||||
if (configDir != null) {
|
||||
File f = new File(configDir + File.separator + "keycloak-server.json");
|
||||
if (f.isFile()) {
|
||||
ServicesLogger.LOGGER.loadingFrom(f.getAbsolutePath());
|
||||
node = JsonSerialization.mapper.readTree(f);
|
||||
}
|
||||
}
|
||||
|
||||
if (node == null) {
|
||||
URL resource = Thread.currentThread().getContextClassLoader().getResource("META-INF/keycloak-server.json");
|
||||
if (resource != null) {
|
||||
ServicesLogger.LOGGER.loadingFrom(resource);
|
||||
node = JsonSerialization.mapper.readTree(resource);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOG.warn("Failed to load JSON config", e);
|
||||
}
|
||||
|
||||
return createJsonProvider(node);
|
||||
|
||||
}
|
||||
|
||||
protected Optional<Config.ConfigProvider> createJsonProvider(JsonNode node) {
|
||||
return Optional.ofNullable(node).map(n -> new JsonConfigProvider(n, getProperties()));
|
||||
}
|
||||
|
||||
protected Properties getProperties() {
|
||||
return new SystemEnvProperties();
|
||||
}
|
||||
|
||||
}
|
||||
@ -24,12 +24,10 @@ import java.security.cert.X509CRL;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
@ -46,7 +44,6 @@ import org.jboss.logging.Logger;
|
||||
import org.keycloak.common.util.BouncyIntegration;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.truststore.TruststoreProvider;
|
||||
import org.wildfly.security.x500.X500;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:brat000012001@gmail.com">Peter Nalyvayko</a>
|
||||
|
||||
@ -49,6 +49,7 @@ import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.services.filters.KeycloakSessionServletFilter;
|
||||
import org.keycloak.services.managers.ApplianceBootstrap;
|
||||
import org.keycloak.services.resources.KeycloakApplication;
|
||||
import org.keycloak.testsuite.JsonConfigProviderFactory;
|
||||
import org.keycloak.testsuite.KeycloakServer;
|
||||
import org.keycloak.testsuite.TestKeycloakSessionServletFilter;
|
||||
import org.keycloak.testsuite.utils.tls.TLSUtils;
|
||||
@ -69,7 +70,7 @@ import org.xnio.SslClientAuthMode;
|
||||
public class KeycloakOnUndertow implements DeployableContainer<KeycloakOnUndertowConfiguration> {
|
||||
|
||||
protected final Logger log = Logger.getLogger(this.getClass());
|
||||
|
||||
|
||||
private KeycloakUndertowJaxrsServer undertow;
|
||||
private KeycloakOnUndertowConfiguration configuration;
|
||||
private KeycloakSessionFactory sessionFactory;
|
||||
@ -90,7 +91,7 @@ public class KeycloakOnUndertow implements DeployableContainer<KeycloakOnUnderto
|
||||
di.addInitParameter(KeycloakApplication.KEYCLOAK_EMBEDDED, "true");
|
||||
if (configuration.getKeycloakConfigPropertyOverridesMap() != null) {
|
||||
try {
|
||||
di.addInitParameter(KeycloakApplication.SERVER_CONTEXT_CONFIG_PROPERTY_OVERRIDES,
|
||||
di.addInitParameter(JsonConfigProviderFactory.SERVER_CONTEXT_CONFIG_PROPERTY_OVERRIDES,
|
||||
JsonSerialization.writeValueAsString(configuration.getKeycloakConfigPropertyOverridesMap()));
|
||||
} catch (IOException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
|
||||
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2019 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.testsuite;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import javax.servlet.ServletContext;
|
||||
import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
||||
import org.keycloak.common.util.SystemEnvProperties;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
public class JsonConfigProviderFactory extends org.keycloak.services.util.JsonConfigProviderFactory {
|
||||
|
||||
public static final String SERVER_CONTEXT_CONFIG_PROPERTY_OVERRIDES = "keycloak.server.context.config.property-overrides";
|
||||
|
||||
@Override
|
||||
protected Properties getProperties() {
|
||||
return new SystemEnvProperties(getPropertyOverrides());
|
||||
}
|
||||
|
||||
private Map<String, String> getPropertyOverrides() {
|
||||
|
||||
ServletContext context = ResteasyProviderFactory.getContextData(ServletContext.class);
|
||||
Map<String, String> propertyOverridesMap = new HashMap<>();
|
||||
String propertyOverrides = context.getInitParameter(SERVER_CONTEXT_CONFIG_PROPERTY_OVERRIDES);
|
||||
|
||||
try {
|
||||
if (context.getInitParameter(SERVER_CONTEXT_CONFIG_PROPERTY_OVERRIDES) != null) {
|
||||
JsonNode jsonObj = JsonSerialization.mapper.readTree(propertyOverrides);
|
||||
jsonObj.fields().forEachRemaining(e -> propertyOverridesMap.put(e.getKey(), e.getValue().asText()));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
}
|
||||
|
||||
return propertyOverridesMap;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
org.keycloak.testsuite.JsonConfigProviderFactory
|
||||
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright 2019 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.provider.wildfly;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Properties;
|
||||
import javax.servlet.ServletContext;
|
||||
import org.jboss.dmr.ModelNode;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.common.util.Resteasy;
|
||||
import org.keycloak.common.util.SystemEnvProperties;
|
||||
import org.keycloak.services.ServicesLogger;
|
||||
import org.keycloak.services.util.JsonConfigProviderFactory;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
public class DMRConfigProviderFactory extends JsonConfigProviderFactory {
|
||||
|
||||
// This param name is defined again in Keycloak Server Subsystem class
|
||||
// org.keycloak.subsystem.server.extension.KeycloakServerDeploymentProcessor. We have this value in
|
||||
// two places to avoid dependency between Keycloak Subsystem and Keycloak Wildfly Extensions module.
|
||||
public static final String KEYCLOAK_CONFIG_PARAM_NAME = "org.keycloak.server-subsystem.Config";
|
||||
|
||||
public static final String SERVER_CONTEXT_CONFIG_PROPERTY_OVERRIDES = "keycloak.server.context.config.property-overrides";
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(DMRConfigProviderFactory.class);
|
||||
|
||||
@Override
|
||||
public Optional<Config.ConfigProvider> create() {
|
||||
|
||||
ServletContext context = Resteasy.getContextData(ServletContext.class);
|
||||
|
||||
JsonNode node = null;
|
||||
|
||||
try {
|
||||
String dmrConfig = loadDmrConfig(context);
|
||||
if (dmrConfig != null) {
|
||||
node = JsonSerialization.mapper.readTree(dmrConfig);
|
||||
ServicesLogger.LOGGER.loadingFrom("standalone.xml or domain.xml");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOG.warn("Failed to load DMR config", e);
|
||||
}
|
||||
|
||||
return createJsonProvider(node);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Properties getProperties() {
|
||||
return new SystemEnvProperties(getPropertyOverrides());
|
||||
}
|
||||
|
||||
private String loadDmrConfig(ServletContext context) {
|
||||
String dmrConfig = context.getInitParameter(KEYCLOAK_CONFIG_PARAM_NAME);
|
||||
if (dmrConfig == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ModelNode dmrConfigNode = ModelNode.fromString(dmrConfig);
|
||||
if (dmrConfigNode.asPropertyList().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// note that we need to resolve expressions BEFORE we convert to JSON
|
||||
return dmrConfigNode.resolve().toJSONString(true);
|
||||
}
|
||||
|
||||
private Map<String, String> getPropertyOverrides() {
|
||||
|
||||
ServletContext context = ResteasyProviderFactory.getContextData(ServletContext.class);
|
||||
Map<String, String> propertyOverridesMap = new HashMap<>();
|
||||
String propertyOverrides = context.getInitParameter(SERVER_CONTEXT_CONFIG_PROPERTY_OVERRIDES);
|
||||
|
||||
try {
|
||||
if (context.getInitParameter(SERVER_CONTEXT_CONFIG_PROPERTY_OVERRIDES) != null) {
|
||||
JsonNode jsonObj = JsonSerialization.mapper.readTree(propertyOverrides);
|
||||
jsonObj.fields().forEachRemaining(e -> propertyOverridesMap.put(e.getKey(), e.getValue().asText()));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
}
|
||||
|
||||
return propertyOverridesMap;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
#
|
||||
# Copyright 2019 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.
|
||||
#
|
||||
|
||||
org.keycloak.provider.wildfly.DMRConfigProviderFactory
|
||||
Loading…
x
Reference in New Issue
Block a user