From 56aa14ffab62c7cae478a26254d62eca5810a77a Mon Sep 17 00:00:00 2001 From: Dmitry Telegin Date: Mon, 9 Dec 2019 16:52:53 +0300 Subject: [PATCH] KEYCLOAK-11347 - MicroProfile-Config --- quarkus/extensions/pom.xml | 4 + .../quarkus/KeycloakConfigSourceProvider.java | 58 ++++++++ .../KeycloakPropertiesConfigSource.java | 138 ++++++++++++++++++ .../quarkus/MicroProfileConfigProvider.java | 113 ++++++++++++++ ...=> MicroProfileConfigProviderFactory.java} | 12 +- ...croprofile.config.spi.ConfigSourceProvider | 18 +++ .../org.keycloak.config.ConfigProviderFactory | 2 +- .../resources/META-INF/keycloak-server.json | 123 ---------------- .../resources/META-INF/keycloak.properties | 40 +++++ .../config/ConfigProviderFactory.java | 0 10 files changed, 383 insertions(+), 125 deletions(-) create mode 100644 quarkus/extensions/src/main/java/org/keycloak/provider/quarkus/KeycloakConfigSourceProvider.java create mode 100644 quarkus/extensions/src/main/java/org/keycloak/provider/quarkus/KeycloakPropertiesConfigSource.java create mode 100644 quarkus/extensions/src/main/java/org/keycloak/provider/quarkus/MicroProfileConfigProvider.java rename quarkus/extensions/src/main/java/org/keycloak/provider/quarkus/{JsonConfigProviderFactory.java => MicroProfileConfigProviderFactory.java} (68%) create mode 100644 quarkus/extensions/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSourceProvider delete mode 100755 quarkus/server/src/main/resources/META-INF/keycloak-server.json create mode 100644 quarkus/server/src/main/resources/META-INF/keycloak.properties rename {server-spi-private => services}/src/main/java/org/keycloak/config/ConfigProviderFactory.java (100%) diff --git a/quarkus/extensions/pom.xml b/quarkus/extensions/pom.xml index 636bac6b0ee..b36add129f2 100644 --- a/quarkus/extensions/pom.xml +++ b/quarkus/extensions/pom.xml @@ -32,6 +32,10 @@ + + org.apache.commons + commons-lang3 + org.keycloak keycloak-services diff --git a/quarkus/extensions/src/main/java/org/keycloak/provider/quarkus/KeycloakConfigSourceProvider.java b/quarkus/extensions/src/main/java/org/keycloak/provider/quarkus/KeycloakConfigSourceProvider.java new file mode 100644 index 00000000000..b0dfbc539cb --- /dev/null +++ b/quarkus/extensions/src/main/java/org/keycloak/provider/quarkus/KeycloakConfigSourceProvider.java @@ -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 getConfigSources(ClassLoader forClassLoader) { + + ArrayList 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); + } + +} diff --git a/quarkus/extensions/src/main/java/org/keycloak/provider/quarkus/KeycloakPropertiesConfigSource.java b/quarkus/extensions/src/main/java/org/keycloak/provider/quarkus/KeycloakPropertiesConfigSource.java new file mode 100644 index 00000000000..4b08a10e709 --- /dev/null +++ b/quarkus/extensions/src/main/java/org/keycloak/provider/quarkus/KeycloakPropertiesConfigSource.java @@ -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 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) (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 transform(Map properties) { + Map 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; + + } + +} diff --git a/quarkus/extensions/src/main/java/org/keycloak/provider/quarkus/MicroProfileConfigProvider.java b/quarkus/extensions/src/main/java/org/keycloak/provider/quarkus/MicroProfileConfigProvider.java new file mode 100644 index 00000000000..b6e7d78539a --- /dev/null +++ b/quarkus/extensions/src/main/java/org/keycloak/provider/quarkus/MicroProfileConfigProvider.java @@ -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 getValue(String key, Class clazz, T defaultValue) { + T value = config.getOptionalValue(prefix + "." + key, clazz).orElse(defaultValue); + return value; + } + + } + +} diff --git a/quarkus/extensions/src/main/java/org/keycloak/provider/quarkus/JsonConfigProviderFactory.java b/quarkus/extensions/src/main/java/org/keycloak/provider/quarkus/MicroProfileConfigProviderFactory.java similarity index 68% rename from quarkus/extensions/src/main/java/org/keycloak/provider/quarkus/JsonConfigProviderFactory.java rename to quarkus/extensions/src/main/java/org/keycloak/provider/quarkus/MicroProfileConfigProviderFactory.java index 1b59e8dc9c6..f2d00d5d106 100644 --- a/quarkus/extensions/src/main/java/org/keycloak/provider/quarkus/JsonConfigProviderFactory.java +++ b/quarkus/extensions/src/main/java/org/keycloak/provider/quarkus/MicroProfileConfigProviderFactory.java @@ -17,6 +17,16 @@ 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 create() { + return Optional.of(new MicroProfileConfigProvider()); + } } diff --git a/quarkus/extensions/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSourceProvider b/quarkus/extensions/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSourceProvider new file mode 100644 index 00000000000..4da2c04747f --- /dev/null +++ b/quarkus/extensions/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSourceProvider @@ -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 diff --git a/quarkus/extensions/src/main/resources/META-INF/services/org.keycloak.config.ConfigProviderFactory b/quarkus/extensions/src/main/resources/META-INF/services/org.keycloak.config.ConfigProviderFactory index 9923d616efa..2fd75d03637 100644 --- a/quarkus/extensions/src/main/resources/META-INF/services/org.keycloak.config.ConfigProviderFactory +++ b/quarkus/extensions/src/main/resources/META-INF/services/org.keycloak.config.ConfigProviderFactory @@ -15,4 +15,4 @@ # limitations under the License. # -org.keycloak.provider.quarkus.JsonConfigProviderFactory +org.keycloak.provider.quarkus.MicroProfileConfigProviderFactory diff --git a/quarkus/server/src/main/resources/META-INF/keycloak-server.json b/quarkus/server/src/main/resources/META-INF/keycloak-server.json deleted file mode 100755 index 9abad69c8bd..00000000000 --- a/quarkus/server/src/main/resources/META-INF/keycloak-server.json +++ /dev/null @@ -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 - } - - } -} diff --git a/quarkus/server/src/main/resources/META-INF/keycloak.properties b/quarkus/server/src/main/resources/META-INF/keycloak.properties new file mode 100644 index 00000000000..73241bfd9f0 --- /dev/null +++ b/quarkus/server/src/main/resources/META-INF/keycloak.properties @@ -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 diff --git a/server-spi-private/src/main/java/org/keycloak/config/ConfigProviderFactory.java b/services/src/main/java/org/keycloak/config/ConfigProviderFactory.java similarity index 100% rename from server-spi-private/src/main/java/org/keycloak/config/ConfigProviderFactory.java rename to services/src/main/java/org/keycloak/config/ConfigProviderFactory.java