KEYCLOAK-11347 - MicroProfile-Config

This commit is contained in:
Dmitry Telegin 2019-12-09 16:52:53 +03:00 committed by stianst
parent d3e59bd0d1
commit 56aa14ffab
10 changed files with 383 additions and 125 deletions

View file

@ -32,6 +32,10 @@
</exclusion> </exclusion>
</exclusions> </exclusions>
</dependency> </dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-services</artifactId> <artifactId>keycloak-services</artifactId>

View file

@ -0,0 +1,58 @@
/*
* 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.quarkus;
import java.util.ArrayList;
import io.quarkus.runtime.configuration.DeploymentProfileConfigSource;
import io.quarkus.runtime.configuration.ExpandingConfigSource;
import org.eclipse.microprofile.config.spi.ConfigSource;
import org.eclipse.microprofile.config.spi.ConfigSourceProvider;
public class KeycloakConfigSourceProvider implements ConfigSourceProvider {
private static final String KEYCLOAK_CONFIG_FILE_PROP = "keycloak.config.file";
private static final String KEYCLOAK_CONFIG_FILE_ENV = "KEYCLOAK_CONFIG_FILE";
@Override
public Iterable<ConfigSource> getConfigSources(ClassLoader forClassLoader) {
ArrayList<ConfigSource> sources = new ArrayList<>();
sources.add(wrap(new KeycloakPropertiesConfigSource.InJar()));
String fileName = System.getProperty(KEYCLOAK_CONFIG_FILE_PROP);
if (fileName == null)
fileName = System.getenv(KEYCLOAK_CONFIG_FILE_ENV);
if (fileName != null)
sources.add(wrap(new KeycloakPropertiesConfigSource.InFileSystem(fileName)));
return sources;
}
private ConfigSource wrap(ConfigSource source) {
return ExpandingConfigSource.wrapper(new ExpandingConfigSource.Cache())
.compose(DeploymentProfileConfigSource.wrapper())
.apply(source);
}
}

View file

@ -0,0 +1,138 @@
/*
* 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.quarkus;
import java.io.BufferedReader;
import java.io.Closeable;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOError;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import io.smallrye.config.PropertiesConfigSource;
import static org.keycloak.provider.quarkus.MicroProfileConfigProvider.NS_KEYCLOAK;
import static org.keycloak.provider.quarkus.MicroProfileConfigProvider.NS_QUARKUS;
/**
* A configuration source for {@code keycloak.properties}.
*/
public abstract class KeycloakPropertiesConfigSource extends PropertiesConfigSource {
static final String KEYCLOAK_PROPERTIES = "keycloak.properties";
KeycloakPropertiesConfigSource(InputStream is, int ordinal) {
super(readProperties(is), KEYCLOAK_PROPERTIES, ordinal);
}
private static Map<String, String> readProperties(final InputStream is) {
if (is == null) {
return Collections.emptyMap();
}
try (Closeable ignored = is) {
try (InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8)) {
try (BufferedReader br = new BufferedReader(isr)) {
final Properties properties = new Properties();
properties.load(br);
return transform((Map<String, String>) (Map) properties);
}
}
} catch (IOException e) {
throw new IOError(e);
}
}
public static final class InJar extends KeycloakPropertiesConfigSource {
public InJar() {
super(openStream(), 245);
}
private static InputStream openStream() {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
if (cl == null) {
cl = KeycloakPropertiesConfigSource.class.getClassLoader();
}
InputStream is;
String fileName = "META-INF" + File.separator + KEYCLOAK_PROPERTIES;
if (cl == null) {
is = ClassLoader.getSystemResourceAsStream(fileName);
} else {
is = cl.getResourceAsStream(fileName);
}
return is;
}
}
public static final class InFileSystem extends KeycloakPropertiesConfigSource {
public InFileSystem(String fileName) {
super(openStream(fileName), 255);
}
private static InputStream openStream(String fileName) {
final Path path = Paths.get(fileName);
if (Files.exists(path)) {
try {
return Files.newInputStream(path);
} catch (NoSuchFileException | FileNotFoundException e) {
return null;
} catch (IOException e) {
throw new IOError(e);
}
} else {
return null;
}
}
}
private static Map<String, String> transform(Map<String, String> properties) {
Map<String, String> result = new HashMap<>(properties.size());
properties.keySet().forEach(k -> result.put(transformKey(k), properties.get(k)));
return result;
}
private static String transformKey(String key) {
String namespace, prefix = (key.split("\\."))[0];
switch (prefix) {
case "datasource":
case "http":
case "log":
namespace = NS_QUARKUS;
break;
default:
namespace = NS_KEYCLOAK;
}
return namespace + "." + key;
}
}

View file

@ -0,0 +1,113 @@
/*
* 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.quarkus;
import org.apache.commons.lang3.ArrayUtils;
import org.eclipse.microprofile.config.ConfigProvider;
import org.keycloak.Config;
public class MicroProfileConfigProvider implements Config.ConfigProvider {
public static final String NS_KEYCLOAK = "keycloak";
public static final String NS_QUARKUS = "quarkus";
private final org.eclipse.microprofile.config.Config config;
public MicroProfileConfigProvider() {
this.config = ConfigProvider.getConfig();
}
@Override
public String getProvider(String spi) {
return scope(spi).get("provider");
}
@Override
public Config.Scope scope(String... scope) {
return new MicroProfileScope(scope);
}
public class MicroProfileScope implements Config.Scope {
private final String[] scope;
private final String prefix;
public MicroProfileScope(String... scope) {
this.scope = scope;
this.prefix = String.join(".", ArrayUtils.insert(0, scope, NS_KEYCLOAK));
}
@Override
public String get(String key) {
return getValue(key, String.class, null);
}
@Override
public String get(String key, String defaultValue) {
return getValue(key, String.class, defaultValue);
}
@Override
public String[] getArray(String key) {
return getValue(key, String[].class, null);
}
@Override
public Integer getInt(String key) {
return getValue(key, Integer.class, null);
}
@Override
public Integer getInt(String key, Integer defaultValue) {
return getValue(key, Integer.class, defaultValue);
}
@Override
public Long getLong(String key) {
return getValue(key, Long.class, null);
}
@Override
public Long getLong(String key, Long defaultValue) {
return getValue(key, Long.class, defaultValue);
}
@Override
public Boolean getBoolean(String key) {
return getValue(key, Boolean.class, null);
}
@Override
public Boolean getBoolean(String key, Boolean defaultValue) {
return getValue(key, Boolean.class, defaultValue);
}
@Override
public Config.Scope scope(String... scope) {
return new MicroProfileScope(ArrayUtils.addAll(this.scope, scope));
}
private <T> T getValue(String key, Class<T> clazz, T defaultValue) {
T value = config.getOptionalValue(prefix + "." + key, clazz).orElse(defaultValue);
return value;
}
}
}

View file

@ -17,6 +17,16 @@
package org.keycloak.provider.quarkus; package org.keycloak.provider.quarkus;
public class JsonConfigProviderFactory extends org.keycloak.services.util.JsonConfigProviderFactory { import java.util.Optional;
import org.keycloak.Config;
import org.keycloak.config.ConfigProviderFactory;
public class MicroProfileConfigProviderFactory implements ConfigProviderFactory {
@Override
public Optional<Config.ConfigProvider> create() {
return Optional.of(new MicroProfileConfigProvider());
}
} }

View file

@ -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.quarkus.KeycloakConfigSourceProvider

View file

@ -15,4 +15,4 @@
# limitations under the License. # limitations under the License.
# #
org.keycloak.provider.quarkus.JsonConfigProviderFactory org.keycloak.provider.quarkus.MicroProfileConfigProviderFactory

View file

@ -1,123 +0,0 @@
{
"hostname": {
"provider": "request",
"fixed": {
"hostname": "localhost",
"httpPort": "-1",
"httpsPort": "-1"
}
},
"admin": {
"realm": "master"
},
"eventsStore": {
"provider": "${keycloak.eventsStore.provider:jpa}"
},
"eventsListener": {
"jboss-logging" : {
"success-level": "debug",
"error-level": "warn"
}
},
"realm": {
"provider": "${keycloak.realm.provider:jpa}"
},
"user": {
"provider": "${keycloak.user.provider:jpa}"
},
"userFederatedStorage": {
"provider": "${keycloak.userFederatedStorage.provider:jpa}"
},
"userSessionPersister": {
"provider": "${keycloak.userSessionPersister.provider:jpa}"
},
"authorizationPersister": {
"provider": "${keycloak.authorization.provider:jpa}"
},
"userCache": {
"default" : {
"enabled": true
}
},
"timer": {
"provider": "basic"
},
"theme": {
"staticMaxAge": "${keycloak.theme.staticMaxAge:2592000}",
"cacheTemplates": "${keycloak.theme.cacheTemplates:true}",
"cacheThemes": "${keycloak.theme.cacheThemes:true}",
"folder": {
"dir": "${keycloak.theme.dir}"
}
},
"scheduled": {
"interval": 900
},
"connectionsHttpClient": {
"default": {}
},
"connectionsJpa": {
"default": {
"url": "${keycloak.connectionsJpa.url:jdbc:h2:mem:test;DB_CLOSE_DELAY=-1}",
"driver": "${keycloak.connectionsJpa.driver:org.h2.Driver}",
"driverDialect": "${keycloak.connectionsJpa.driverDialect:}",
"user": "${keycloak.connectionsJpa.user:sa}",
"password": "${keycloak.connectionsJpa.password:}",
"initializeEmpty": true,
"migrationStrategy": "update",
"showSql": "${keycloak.connectionsJpa.showSql:false}",
"formatSql": "${keycloak.connectionsJpa.formatSql:true}",
"globalStatsInterval": "${keycloak.connectionsJpa.globalStatsInterval:-1}"
}
},
"realmCache": {
"default" : {
"enabled": true
}
},
"connectionsInfinispan": {
"default": {
"jgroupsUdpMcastAddr": "${keycloak.connectionsInfinispan.jgroupsUdpMcastAddr:234.56.78.90}",
"nodeName": "${keycloak.connectionsInfinispan.nodeName,jboss.node.name:}",
"siteName": "${keycloak.connectionsInfinispan.siteName,jboss.site.name:}",
"clustered": "${keycloak.connectionsInfinispan.clustered:false}",
"async": "${keycloak.connectionsInfinispan.async:false}",
"sessionsOwners": "${keycloak.connectionsInfinispan.sessionsOwners:1}",
"l1Lifespan": "${keycloak.connectionsInfinispan.l1Lifespan:600000}",
"remoteStoreEnabled": "${keycloak.connectionsInfinispan.remoteStoreEnabled:false}",
"remoteStoreHost": "${keycloak.connectionsInfinispan.remoteStoreServer:localhost}",
"remoteStorePort": "${keycloak.connectionsInfinispan.remoteStorePort:11222}",
"hotrodProtocolVersion": "${keycloak.connectionsInfinispan.hotrodProtocolVersion}",
"stack": "${keycloak.connectionsInfinispan.stack:udp}"
}
},
"scripting": {
},
"jta-lookup": {
"provider": "${keycloak.jta.lookup.provider:jboss}",
"jboss" : {
"enabled": true
}
}
}

View file

@ -0,0 +1,40 @@
# Main
admin.realm = master
scheduled.interval = 900
# Theme
theme.staticMaxAge = 2592000
theme.cacheThemes = true
theme.cacheTemplates = true
#theme.dir = ${keycloak.home.dir}/themes
# SPIs
eventsListener.jboss-logging.success-level = debug
eventsListener.jboss-logging.error-level = warn
connectionsJpa.default.url = jdbc:h2:mem:test;DB_CLOSE_DELAY=-1
connectionsJpa.default.driver = org.h2.Driver
connectionsJpa.default.user = sa
connectionsJpa.default.password = keycloak
connectionsJpa.default.initializeEmpty = true
connectionsJpa.default.migrationStrategy = update
connectionsJpa.default.showSql = false
connectionsJpa.default.formatSql = true
connectionsJpa.default.globalStatsInterval = -1
eventsStore.provider=jpa
realm.provider=jpa
user.provider=jpa
userFederatedStorage.provider=jpa
userSessionPersister.provider=jpa
authorizationPersister.provider=jpa
userCache.enabled=true
timer.provider=basic
hostname.provider = default
hostname.default.frontendUrl = ${keycloak.frontendUrl:}
hostname.default.forceBackendUrlToFrontendUrl = false