From af97849feb03b6e692353eb9eedb39baa3568470 Mon Sep 17 00:00:00 2001 From: Martin Kanis Date: Sat, 26 Jun 2021 17:35:40 +0200 Subject: [PATCH] KEYCLOAK-19030 Implement HotRodConnectionProvider --- dependencies/server-all/pom.xml | 4 + .../server-feature-pack-dependencies/pom.xml | 10 ++ model/hot-rod/pom.xml | 52 +++++++ .../models/map/common/HotRodUtils.java | 87 +++++++++++ .../map/common/ProtoSchemaInitializer.java | 42 ++++++ .../DefaultHotRodConnectionProvider.java | 42 ++++++ ...efaultHotRodConnectionProviderFactory.java | 140 ++++++++++++++++++ .../connections/HotRodConnectionProvider.java | 36 +++++ .../HotRodConnectionProviderFactory.java | 26 ++++ .../map/connections/HotRodConnectionSpi.java | 50 +++++++ ...onnections.HotRodConnectionProviderFactory | 18 +++ .../services/org.keycloak.provider.Spi | 18 +++ .../src/main/resources/config/cacheConfig.xml | 10 ++ .../src/main/resources/config/infinispan.xml | 12 ++ model/infinispan/pom.xml | 5 - model/pom.xml | 1 + pom.xml | 55 ++++++- .../resources/META-INF/keycloak-server.json | 79 ++++++++-- testsuite/model/pom.xml | 17 ++- .../org/keycloak/testsuite/model/Config.java | 2 +- .../testsuite/model/HotRodServerRule.java | 28 +++- .../src/main/resources/hotrod/infinispan.xml | 9 ++ .../model/parameters/HotRodMapStorage.java | 113 ++++++++++++++ .../resources/META-INF/keycloak-server.json | 8 + 24 files changed, 846 insertions(+), 18 deletions(-) create mode 100644 model/hot-rod/pom.xml create mode 100644 model/hot-rod/src/main/java/org/keycloak/models/map/common/HotRodUtils.java create mode 100644 model/hot-rod/src/main/java/org/keycloak/models/map/common/ProtoSchemaInitializer.java create mode 100644 model/hot-rod/src/main/java/org/keycloak/models/map/connections/DefaultHotRodConnectionProvider.java create mode 100644 model/hot-rod/src/main/java/org/keycloak/models/map/connections/DefaultHotRodConnectionProviderFactory.java create mode 100644 model/hot-rod/src/main/java/org/keycloak/models/map/connections/HotRodConnectionProvider.java create mode 100644 model/hot-rod/src/main/java/org/keycloak/models/map/connections/HotRodConnectionProviderFactory.java create mode 100644 model/hot-rod/src/main/java/org/keycloak/models/map/connections/HotRodConnectionSpi.java create mode 100644 model/hot-rod/src/main/resources/META-INF/services/org.keycloak.models.map.connections.HotRodConnectionProviderFactory create mode 100644 model/hot-rod/src/main/resources/META-INF/services/org.keycloak.provider.Spi create mode 100644 model/hot-rod/src/main/resources/config/cacheConfig.xml create mode 100644 model/hot-rod/src/main/resources/config/infinispan.xml create mode 100644 testsuite/model/src/main/resources/hotrod/infinispan.xml create mode 100644 testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/HotRodMapStorage.java diff --git a/dependencies/server-all/pom.xml b/dependencies/server-all/pom.xml index 4888078c5e..088d48f1d1 100755 --- a/dependencies/server-all/pom.xml +++ b/dependencies/server-all/pom.xml @@ -48,6 +48,10 @@ org.keycloak keycloak-model-infinispan + + org.keycloak + keycloak-model-map-hot-rod + org.twitter4j diff --git a/distribution/feature-packs/server-feature-pack-dependencies/pom.xml b/distribution/feature-packs/server-feature-pack-dependencies/pom.xml index 9f3527e204..a8041d0564 100644 --- a/distribution/feature-packs/server-feature-pack-dependencies/pom.xml +++ b/distribution/feature-packs/server-feature-pack-dependencies/pom.xml @@ -200,6 +200,16 @@ + + org.keycloak + keycloak-model-map-hot-rod + + + * + * + + + org.keycloak keycloak-saml-core diff --git a/model/hot-rod/pom.xml b/model/hot-rod/pom.xml new file mode 100644 index 0000000000..74e27e0a04 --- /dev/null +++ b/model/hot-rod/pom.xml @@ -0,0 +1,52 @@ + + + + keycloak-model-pom + org.keycloak + 16.0.0-SNAPSHOT + + 4.0.0 + + keycloak-model-map-hot-rod + Keycloak Model Hot Rod + + + + + org.keycloak + keycloak-model-map + + + org.infinispan + infinispan-client-hotrod + + + org.infinispan + infinispan-query-dsl + + + org.infinispan + infinispan-remote-query-client + + + org.infinispan + infinispan-server-router + + + org.infinispan + infinispan-server-rest + + + org.infinispan + infinispan-server-runtime + + + org.infinispan.protostream + protostream-processor + provided + + + + diff --git a/model/hot-rod/src/main/java/org/keycloak/models/map/common/HotRodUtils.java b/model/hot-rod/src/main/java/org/keycloak/models/map/common/HotRodUtils.java new file mode 100644 index 0000000000..a6e7a2ff4f --- /dev/null +++ b/model/hot-rod/src/main/java/org/keycloak/models/map/common/HotRodUtils.java @@ -0,0 +1,87 @@ +/* + * 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.models.map.common; + +import org.infinispan.manager.DefaultCacheManager; +import org.infinispan.rest.RestServer; +import org.infinispan.rest.configuration.RestServerConfigurationBuilder; +import org.infinispan.server.configuration.endpoint.SinglePortServerConfigurationBuilder; +import org.infinispan.server.hotrod.HotRodServer; +import org.infinispan.server.hotrod.configuration.HotRodServerConfigurationBuilder; +import org.infinispan.server.router.RoutingTable; +import org.infinispan.server.router.configuration.SinglePortRouterConfiguration; +import org.infinispan.server.router.router.impl.singleport.SinglePortEndpointRouter; +import org.infinispan.server.router.routes.Route; +import org.infinispan.server.router.routes.RouteDestination; +import org.infinispan.server.router.routes.RouteSource; +import org.infinispan.server.router.routes.hotrod.HotRodServerRouteDestination; +import org.infinispan.server.router.routes.rest.RestServerRouteDestination; +import org.infinispan.server.router.routes.singleport.SinglePortRouteSource; + +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; + +/** + * @author Martin Kanis + */ +public class HotRodUtils { + /** + * Not suitable for a production usage. Only for development and test purposes. + * Also do not use in clustered environment. + * @param hotRodServer HotRodServer + * @param hotRodCacheManager DefaultCacheManager + * @param embeddedPort int + */ + public static void createHotRodMapStoreServer(HotRodServer hotRodServer, DefaultCacheManager hotRodCacheManager, int embeddedPort) { + HotRodServerConfigurationBuilder hotRodServerConfigurationBuilder = new HotRodServerConfigurationBuilder(); + hotRodServerConfigurationBuilder.startTransport(false); + hotRodServerConfigurationBuilder.port(embeddedPort); + hotRodServer.start(hotRodServerConfigurationBuilder.build(), hotRodCacheManager); + + RestServerConfigurationBuilder restServerConfigurationBuilder = new RestServerConfigurationBuilder(); + restServerConfigurationBuilder.startTransport(false); + restServerConfigurationBuilder.port(embeddedPort); + RestServer restServer = new RestServer(); + restServer.start(restServerConfigurationBuilder.build(), hotRodCacheManager); + + SinglePortRouteSource routeSource = new SinglePortRouteSource(); + Set> routes = new HashSet<>(); + routes.add(new Route<>(routeSource, new HotRodServerRouteDestination("hotrod", hotRodServer))); + routes.add(new Route<>(routeSource, new RestServerRouteDestination("rest", restServer))); + + SinglePortRouterConfiguration singlePortRouter = new SinglePortServerConfigurationBuilder().port(embeddedPort).build(); + SinglePortEndpointRouter endpointServer = new SinglePortEndpointRouter(singlePortRouter); + endpointServer.start(new RoutingTable(routes)); + } + + /** + * Not suitable for a production usage. Only for development and test purposes. + * Also do not use in clustered environment. + * @param embeddedPort int + */ + public static void createHotRodMapStoreServer(int embeddedPort) { + DefaultCacheManager hotRodCacheManager = null; + try { + hotRodCacheManager = new DefaultCacheManager("config/infinispan.xml"); + } catch (IOException e) { + new RuntimeException("Cannot initialize cache manager!", e); + } + + HotRodUtils.createHotRodMapStoreServer(new HotRodServer(), hotRodCacheManager, embeddedPort); + } +} diff --git a/model/hot-rod/src/main/java/org/keycloak/models/map/common/ProtoSchemaInitializer.java b/model/hot-rod/src/main/java/org/keycloak/models/map/common/ProtoSchemaInitializer.java new file mode 100644 index 0000000000..ec19172ae0 --- /dev/null +++ b/model/hot-rod/src/main/java/org/keycloak/models/map/common/ProtoSchemaInitializer.java @@ -0,0 +1,42 @@ +/* + * 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.models.map.common; + +import org.infinispan.protostream.GeneratedSchema; +import org.infinispan.protostream.annotations.AutoProtoSchemaBuilder; +//import org.keycloak.models.map.client.HotRodAttributeEntity; +//import org.keycloak.models.map.client.HotRodClientEntity; +//import org.keycloak.models.map.client.HotRodPair; +//import org.keycloak.models.map.client.HotRodProtocolMapperEntity; + +/** + * @author Martin Kanis + */ +@AutoProtoSchemaBuilder( + includeClasses = { + //HotRodAttributeEntity.class, + //HotRodClientEntity.class, + //HotRodProtocolMapperEntity.class, + //HotRodPair.class + }, + schemaFileName = "KeycloakHotRodMapStorage.proto", + schemaFilePath = "proto/", + schemaPackageName = "org.keycloak.models.map.storage.hotrod") +public interface ProtoSchemaInitializer extends GeneratedSchema { + ProtoSchemaInitializer INSTANCE = new ProtoSchemaInitializerImpl(); +} diff --git a/model/hot-rod/src/main/java/org/keycloak/models/map/connections/DefaultHotRodConnectionProvider.java b/model/hot-rod/src/main/java/org/keycloak/models/map/connections/DefaultHotRodConnectionProvider.java new file mode 100644 index 0000000000..1b289f1ac1 --- /dev/null +++ b/model/hot-rod/src/main/java/org/keycloak/models/map/connections/DefaultHotRodConnectionProvider.java @@ -0,0 +1,42 @@ +/* + * 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.models.map.connections; + +import org.infinispan.client.hotrod.RemoteCache; +import org.infinispan.client.hotrod.RemoteCacheManager; + +/** + * @author Martin Kanis + */ +public class DefaultHotRodConnectionProvider implements HotRodConnectionProvider { + + private RemoteCacheManager remoteCacheManager; + + public DefaultHotRodConnectionProvider(RemoteCacheManager remoteCacheManager) { + this.remoteCacheManager = remoteCacheManager; + } + + @Override + public RemoteCache getRemoteCache(String name) { + return remoteCacheManager.getCache(name); + } + + @Override + public void close() { + + } +} diff --git a/model/hot-rod/src/main/java/org/keycloak/models/map/connections/DefaultHotRodConnectionProviderFactory.java b/model/hot-rod/src/main/java/org/keycloak/models/map/connections/DefaultHotRodConnectionProviderFactory.java new file mode 100644 index 0000000000..4508095af2 --- /dev/null +++ b/model/hot-rod/src/main/java/org/keycloak/models/map/connections/DefaultHotRodConnectionProviderFactory.java @@ -0,0 +1,140 @@ +/* + * 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.models.map.connections; + +import org.infinispan.client.hotrod.RemoteCache; +import org.infinispan.client.hotrod.RemoteCacheManager; +import org.infinispan.client.hotrod.configuration.ClientIntelligence; +import org.infinispan.client.hotrod.configuration.ConfigurationBuilder; +import org.infinispan.commons.marshall.ProtoStreamMarshaller; +import org.infinispan.protostream.GeneratedSchema; +import org.infinispan.query.remote.client.ProtobufMetadataManagerConstants; +import org.jboss.logging.Logger; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; +//import org.keycloak.models.map.common.HotRodEntityDescriptor; +import org.keycloak.models.map.common.HotRodUtils; +import org.keycloak.models.map.common.ProtoSchemaInitializer; +//import org.keycloak.models.map.storage.hotRod.HotRodMapStorageProviderFactory; + +import java.net.URI; +import java.net.URISyntaxException; + +/** + * @author Martin Kanis + */ +public class DefaultHotRodConnectionProviderFactory implements HotRodConnectionProviderFactory { + + public static final String PROVIDER_ID = "default"; + + private static final Logger LOG = Logger.getLogger(DefaultHotRodConnectionProviderFactory.class); + + private org.keycloak.Config.Scope config; + + private RemoteCacheManager remoteCacheManager; + + @Override + public HotRodConnectionProvider create(KeycloakSession session) { + if (remoteCacheManager == null) { + lazyInit(); + } + return new DefaultHotRodConnectionProvider(remoteCacheManager); + } + + @Override + public void postInit(KeycloakSessionFactory factory) { + + } + + @Override + public void close() { + + } + + @Override + public String getId() { + return PROVIDER_ID; + } + + @Override + public void init(org.keycloak.Config.Scope config) { + this.config = config; + } + + public void lazyInit() { + if (config.getBoolean("embedded", false)) { + HotRodUtils.createHotRodMapStoreServer(config.getInt("embeddedPort", 11444)); + } + + ConfigurationBuilder remoteBuilder = new ConfigurationBuilder(); + remoteBuilder.addServer() + .host(config.get("host", "localhost")) + .port(config.getInt("port", 11444)) + .clientIntelligence(ClientIntelligence.HASH_DISTRIBUTION_AWARE) + .marshaller(new ProtoStreamMarshaller()); + + if (config.getBoolean("enableSecurity", true)) { + remoteBuilder.security() + .authentication() + .saslMechanism("SCRAM-SHA-512") + .username(config.get("username", "admin")) + .password(config.get("password", "admin")) + .realm(config.get("realm", "default")); + } + + boolean configureRemoteCaches = config.getBoolean("configureRemoteCaches", false); + if (configureRemoteCaches) { + configureRemoteCaches(remoteBuilder); + } + + remoteBuilder.addContextInitializer(ProtoSchemaInitializer.INSTANCE); + remoteCacheManager = new RemoteCacheManager(remoteBuilder.build()); + + if (configureRemoteCaches) { + // access the caches to force their creation + /*HotRodMapStorageProviderFactory.ENTITY_DESCRIPTOR_MAP.values().stream() + .map(HotRodEntityDescriptor::getCacheName) + .forEach(remoteCacheManager::getCache);*/ + } + + registerSchemata(ProtoSchemaInitializer.INSTANCE); + } + + private void registerSchemata(GeneratedSchema initializer) { + final RemoteCache protoMetadataCache = remoteCacheManager.getCache(ProtobufMetadataManagerConstants.PROTOBUF_METADATA_CACHE_NAME); + + protoMetadataCache.put(initializer.getProtoFileName(), initializer.getProtoFile()); + + String errors = protoMetadataCache.get(ProtobufMetadataManagerConstants.ERRORS_KEY_SUFFIX); + if (errors != null) { + throw new IllegalStateException("Some Protobuf schema files contain errors: " + errors + "\nSchema :\n" + initializer.getProtoFileName()); + } + } + + private void configureRemoteCaches(ConfigurationBuilder builder) { + URI uri; + try { + uri = DefaultHotRodConnectionProviderFactory.class.getClassLoader().getResource("config/cacheConfig.xml").toURI(); + } catch (URISyntaxException e) { + throw new RuntimeException("Cannot read the cache configuration!", e); + } + + /*HotRodMapStorageProviderFactory.ENTITY_DESCRIPTOR_MAP.values().stream() + .map(HotRodEntityDescriptor::getCacheName) + .forEach(name -> builder.remoteCache(name).configurationURI(uri));*/ + } +} diff --git a/model/hot-rod/src/main/java/org/keycloak/models/map/connections/HotRodConnectionProvider.java b/model/hot-rod/src/main/java/org/keycloak/models/map/connections/HotRodConnectionProvider.java new file mode 100644 index 0000000000..a7268dab6f --- /dev/null +++ b/model/hot-rod/src/main/java/org/keycloak/models/map/connections/HotRodConnectionProvider.java @@ -0,0 +1,36 @@ +/* + * 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.models.map.connections; + +import org.infinispan.client.hotrod.RemoteCache; +import org.keycloak.provider.Provider; + +/** + * @author Martin Kanis + */ +public interface HotRodConnectionProvider extends Provider { + + /** + * Returns a remote Infinispan cache specified by the given name. + * @param name String Name of the remote cache. + * @param key + * @param value + * @return A remote Infinispan cache specified by name. + */ + RemoteCache getRemoteCache(String name); +} diff --git a/model/hot-rod/src/main/java/org/keycloak/models/map/connections/HotRodConnectionProviderFactory.java b/model/hot-rod/src/main/java/org/keycloak/models/map/connections/HotRodConnectionProviderFactory.java new file mode 100644 index 0000000000..cba9798e94 --- /dev/null +++ b/model/hot-rod/src/main/java/org/keycloak/models/map/connections/HotRodConnectionProviderFactory.java @@ -0,0 +1,26 @@ +/* + * 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.models.map.connections; + +import org.keycloak.provider.ProviderFactory; + +/** + * @author Martin Kanis + */ +public interface HotRodConnectionProviderFactory extends ProviderFactory { +} diff --git a/model/hot-rod/src/main/java/org/keycloak/models/map/connections/HotRodConnectionSpi.java b/model/hot-rod/src/main/java/org/keycloak/models/map/connections/HotRodConnectionSpi.java new file mode 100644 index 0000000000..54be770f92 --- /dev/null +++ b/model/hot-rod/src/main/java/org/keycloak/models/map/connections/HotRodConnectionSpi.java @@ -0,0 +1,50 @@ +/* + * 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.models.map.connections; + +import org.keycloak.provider.Provider; +import org.keycloak.provider.ProviderFactory; +import org.keycloak.provider.Spi; + +/** + * @author Martin Kanis + */ +public class HotRodConnectionSpi implements Spi { + + public static final String NAME = "connectionsHotRod"; + + @Override + public boolean isInternal() { + return true; + } + + @Override + public String getName() { + return NAME; + } + + @Override + public Class getProviderClass() { + return HotRodConnectionProvider.class; + } + + @Override + public Class getProviderFactoryClass() { + return HotRodConnectionProviderFactory.class; + } +} diff --git a/model/hot-rod/src/main/resources/META-INF/services/org.keycloak.models.map.connections.HotRodConnectionProviderFactory b/model/hot-rod/src/main/resources/META-INF/services/org.keycloak.models.map.connections.HotRodConnectionProviderFactory new file mode 100644 index 0000000000..1d08505e2b --- /dev/null +++ b/model/hot-rod/src/main/resources/META-INF/services/org.keycloak.models.map.connections.HotRodConnectionProviderFactory @@ -0,0 +1,18 @@ +# +# 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. +# + +org.keycloak.models.map.connections.DefaultHotRodConnectionProviderFactory \ No newline at end of file diff --git a/model/hot-rod/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/model/hot-rod/src/main/resources/META-INF/services/org.keycloak.provider.Spi new file mode 100644 index 0000000000..bc573c7fd3 --- /dev/null +++ b/model/hot-rod/src/main/resources/META-INF/services/org.keycloak.provider.Spi @@ -0,0 +1,18 @@ +# +# 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. +# + +org.keycloak.models.map.connections.HotRodConnectionSpi \ No newline at end of file diff --git a/model/hot-rod/src/main/resources/config/cacheConfig.xml b/model/hot-rod/src/main/resources/config/cacheConfig.xml new file mode 100644 index 0000000000..351576ee74 --- /dev/null +++ b/model/hot-rod/src/main/resources/config/cacheConfig.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/model/hot-rod/src/main/resources/config/infinispan.xml b/model/hot-rod/src/main/resources/config/infinispan.xml new file mode 100644 index 0000000000..ce1efc99cf --- /dev/null +++ b/model/hot-rod/src/main/resources/config/infinispan.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + diff --git a/model/infinispan/pom.xml b/model/infinispan/pom.xml index d8c7f060b8..97558e91ac 100755 --- a/model/infinispan/pom.xml +++ b/model/infinispan/pom.xml @@ -75,10 +75,5 @@ microprofile-metrics-api test - - org.infinispan - infinispan-server-hotrod - ${infinispan.version} - diff --git a/model/pom.xml b/model/pom.xml index bbf98ce10e..a3932630ce 100755 --- a/model/pom.xml +++ b/model/pom.xml @@ -35,5 +35,6 @@ infinispan map build-processor + hot-rod diff --git a/pom.xml b/pom.xml index 676a95aa75..84d12637c8 100644 --- a/pom.xml +++ b/pom.xml @@ -78,6 +78,7 @@ 5.3.20.Final 5.3.20.Final 11.0.9.Final + 4.3.4.Final 2.12.1 ${jackson.version} ${jackson.databind.version} @@ -908,6 +909,53 @@ infinispan-jboss-marshalling ${infinispan.version} + + org.infinispan + infinispan-server-core + ${infinispan.version} + + + org.infinispan + infinispan-server-router + ${infinispan.version} + + + org.infinispan + infinispan-server-runtime + ${infinispan.version} + + + * + * + + + + + org.infinispan + infinispan-server-rest + ${infinispan.version} + + + org.infinispan + infinispan-client-hotrod + ${infinispan.version} + + + org.infinispan + infinispan-query-dsl + ${infinispan.version} + + + org.infinispan + infinispan-remote-query-client + ${infinispan.version} + + + org.infinispan.protostream + protostream-processor + provided + ${infinispan.protostream.processor.version} + org.jboss.marshalling jboss-marshalling @@ -1243,6 +1291,11 @@ keycloak-model-infinispan ${project.version} + + org.keycloak + keycloak-model-map-hot-rod + ${project.version} + org.keycloak launcher @@ -2036,4 +2089,4 @@ - \ No newline at end of file + diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json b/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json index 72957ed3c0..54323ee1f1 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json @@ -48,42 +48,90 @@ }, "realm": { - "provider": "${keycloak.realm.provider:jpa}" + "provider": "${keycloak.realm.provider:jpa}", + "map": { + "storage": { + "provider": "${keycloak.realm.map.storage.provider:concurrenthashmap}" + } + } }, "user": { - "provider": "${keycloak.user.provider:jpa}" + "provider": "${keycloak.user.provider:jpa}", + "map": { + "storage": { + "provider": "${keycloak.user.map.storage.provider:concurrenthashmap}" + } + } }, "client": { - "provider": "${keycloak.client.provider:jpa}" + "provider": "${keycloak.client.provider:jpa}", + "map": { + "storage": { + "provider": "${keycloak.client.map.storage.provider:concurrenthashmap}" + } + } }, "clientScope": { - "provider": "${keycloak.clientScope.provider:jpa}" + "provider": "${keycloak.clientScope.provider:jpa}", + "map": { + "storage": { + "provider": "${keycloak.clientScope.map.storage.provider:concurrenthashmap}" + } + } }, "group": { - "provider": "${keycloak.group.provider:jpa}" + "provider": "${keycloak.group.provider:jpa}", + "map": { + "storage": { + "provider": "${keycloak.group.map.storage.provider:concurrenthashmap}" + } + } }, "role": { - "provider": "${keycloak.role.provider:jpa}" + "provider": "${keycloak.role.provider:jpa}", + "map": { + "storage": { + "provider": "${keycloak.role.map.storage.provider:concurrenthashmap}" + } + } }, "authenticationSessions": { "provider": "${keycloak.authSession.provider:infinispan}", + "map": { + "storage": { + "provider": "${keycloak.authSession.map.storage.provider:concurrenthashmap}" + } + }, "infinispan": { "authSessionsLimit": "${keycloak.authSessions.limit:300}" } }, "userSessions": { - "provider": "${keycloak.userSession.provider:infinispan}" + "provider": "${keycloak.userSession.provider:infinispan}", + "map": { + "storage-user-sessions": { + "provider": "${keycloak.userSession.map.storage.provider:concurrenthashmap}" + }, + "storage-client-sessions": { + "provider": "${keycloak.userSession.map.storage.provider:concurrenthashmap}" + } + } }, "loginFailure": { - "provider": "${keycloak.loginFailure.provider:infinispan}" + "provider": "${keycloak.loginFailure.provider:infinispan}", + "map": { + "storage": { + "provider": "${keycloak.loginFailure.map.storage.provider:concurrenthashmap}" + } + } }, "mapStorage": { @@ -104,7 +152,12 @@ }, "authorizationPersister": { - "provider": "${keycloak.authorization.provider:jpa}" + "provider": "${keycloak.authorization.provider:jpa}", + "map": { + "storage": { + "provider": "${keycloak.authorization.map.storage.provider:concurrenthashmap}" + } + } }, "userCache": { @@ -192,6 +245,14 @@ } }, + "connectionsHotRod": { + "default": { + "embedded": "${keycloak.connectionsHotRod.embedded:true}", + "embeddedPort": "${keycloak.connectionsHotRod.embeddedPort:11444}", + "enableSecurity": "${keycloak.connectionsHotRod.enableSecurity:false}" + } + }, + "truststore": { "file": { "file": "${keycloak.truststore.file:target/dependency/keystore/keycloak.truststore}", diff --git a/testsuite/model/pom.xml b/testsuite/model/pom.xml index 93719dc98a..2bc5554c2e 100644 --- a/testsuite/model/pom.xml +++ b/testsuite/model/pom.xml @@ -75,7 +75,6 @@ ${jdbc.mvn.version} test - org.keycloak keycloak-model-jpa @@ -93,6 +92,14 @@ integration-arquillian-testsuite-providers ${project.version} + + org.keycloak + keycloak-model-map-hot-rod + + + org.infinispan + infinispan-server-core + @@ -266,6 +273,14 @@ Jpa,Map,ConcurrentHashMapStorage + + + hot-rod + + enabled + Jpa,Map,HotRodMapStorage + + diff --git a/testsuite/model/src/main/java/org/keycloak/testsuite/model/Config.java b/testsuite/model/src/main/java/org/keycloak/testsuite/model/Config.java index ca8b9d690a..b65713f8fe 100644 --- a/testsuite/model/src/main/java/org/keycloak/testsuite/model/Config.java +++ b/testsuite/model/src/main/java/org/keycloak/testsuite/model/Config.java @@ -133,7 +133,7 @@ public class Config implements ConfigProvider { @Override public Scope scope(String... scope) { StringBuilder sb = new StringBuilder(); - sb.append(prefix).append("."); + sb.append(prefix); for (String s : scope) { sb.append(s); sb.append("."); diff --git a/testsuite/model/src/main/java/org/keycloak/testsuite/model/HotRodServerRule.java b/testsuite/model/src/main/java/org/keycloak/testsuite/model/HotRodServerRule.java index 7313dc20c3..87a2cc7bfa 100644 --- a/testsuite/model/src/main/java/org/keycloak/testsuite/model/HotRodServerRule.java +++ b/testsuite/model/src/main/java/org/keycloak/testsuite/model/HotRodServerRule.java @@ -14,6 +14,7 @@ import org.infinispan.server.hotrod.configuration.HotRodServerConfiguration; import org.infinispan.server.hotrod.configuration.HotRodServerConfigurationBuilder; import org.junit.rules.ExternalResource; import org.keycloak.Config; +import org.keycloak.models.map.common.HotRodUtils; import java.io.IOException; @@ -39,7 +40,9 @@ public class HotRodServerRule extends ExternalResource { @Override protected void after() { - remoteCacheManager.stop(); + if (remoteCacheManager != null) { + remoteCacheManager.stop(); + } } public void createEmbeddedHotRodServer(Config.Scope config) { @@ -76,6 +79,29 @@ public class HotRodServerRule extends ExternalResource { LOGIN_FAILURE_CACHE_NAME, WORK_CACHE_NAME, ACTION_TOKEN_CACHE); } + public void createHotRodMapStoreServer() { + hotRodCacheManager = configureHotRodCacheManager("hotrod/infinispan.xml"); + hotRodServer = new HotRodServer(); + + HotRodUtils.createHotRodMapStoreServer(hotRodServer, hotRodCacheManager, 11444); + + org.infinispan.client.hotrod.configuration.ConfigurationBuilder remoteBuilder = new org.infinispan.client.hotrod.configuration.ConfigurationBuilder(); + org.infinispan.client.hotrod.configuration.Configuration cfg = remoteBuilder + .addServers(hotRodServer.getHost() + ":" + hotRodServer.getPort()).build(); + remoteCacheManager = new RemoteCacheManager(cfg); + } + + private DefaultCacheManager configureHotRodCacheManager(String configPath) { + DefaultCacheManager manager = null; + try { + manager = new DefaultCacheManager(configPath); + } catch (IOException e) { + new RuntimeException(e); + } + + return manager; + } + private void getCaches(String... cache) { for (String c: cache) { hotRodCacheManager.getCache(c, true); diff --git a/testsuite/model/src/main/resources/hotrod/infinispan.xml b/testsuite/model/src/main/resources/hotrod/infinispan.xml new file mode 100644 index 0000000000..00a78521ec --- /dev/null +++ b/testsuite/model/src/main/resources/hotrod/infinispan.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/HotRodMapStorage.java b/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/HotRodMapStorage.java new file mode 100644 index 0000000000..5c989aa7ad --- /dev/null +++ b/testsuite/model/src/test/java/org/keycloak/testsuite/model/parameters/HotRodMapStorage.java @@ -0,0 +1,113 @@ +/* + * Copyright 2020 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.model.parameters; + +import com.google.common.collect.ImmutableSet; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; +import org.keycloak.authorization.store.StoreFactorySpi; +import org.keycloak.models.DeploymentStateSpi; +import org.keycloak.models.UserLoginFailureSpi; +import org.keycloak.models.UserSessionSpi; +import org.keycloak.models.dblock.NoLockingDBLockProviderFactory; +import org.keycloak.models.map.authSession.MapRootAuthenticationSessionProviderFactory; +import org.keycloak.models.map.authorization.MapAuthorizationStoreFactory; +import org.keycloak.models.map.client.MapClientProviderFactory; +import org.keycloak.models.map.clientscope.MapClientScopeProviderFactory; +import org.keycloak.models.map.connections.DefaultHotRodConnectionProviderFactory; +import org.keycloak.models.map.connections.HotRodConnectionProviderFactory; +import org.keycloak.models.map.connections.HotRodConnectionSpi; +import org.keycloak.models.map.deploymentState.MapDeploymentStateProviderFactory; +import org.keycloak.models.map.group.MapGroupProviderFactory; +import org.keycloak.models.map.loginFailure.MapUserLoginFailureProviderFactory; +import org.keycloak.models.map.realm.MapRealmProviderFactory; +import org.keycloak.models.map.role.MapRoleProviderFactory; +import org.keycloak.models.map.storage.MapStorageSpi; +import org.keycloak.models.map.storage.chm.ConcurrentHashMapStorageProviderFactory; +//import org.keycloak.models.map.storage.hotRod.HotRodMapStorageProviderFactory; +import org.keycloak.models.map.user.MapUserProviderFactory; +import org.keycloak.models.map.userSession.MapUserSessionProviderFactory; +import org.keycloak.provider.ProviderFactory; +import org.keycloak.provider.Spi; +import org.keycloak.sessions.AuthenticationSessionSpi; +import org.keycloak.testsuite.model.Config; +import org.keycloak.testsuite.model.HotRodServerRule; +import org.keycloak.testsuite.model.KeycloakModelParameters; + +import java.util.Set; + +/** + * + * @author hmlnarik + */ +public class HotRodMapStorage extends KeycloakModelParameters { + + static final Set> ALLOWED_SPIS = ImmutableSet.>builder() + .add(HotRodConnectionSpi.class) + .build(); + + static final Set> ALLOWED_FACTORIES = ImmutableSet.>builder() + //.add(HotRodMapStorageProviderFactory.class) + .add(HotRodConnectionProviderFactory.class) + .add(ConcurrentHashMapStorageProviderFactory.class) + .build(); + + private static final String STORAGE_CONFIG = "storage.provider"; + + private HotRodServerRule hotRodServerRule = new HotRodServerRule(); + + @Override + public void updateConfig(Config cf) { + cf.spi(AuthenticationSessionSpi.PROVIDER_ID).provider(MapRootAuthenticationSessionProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID) + //.spi("client").provider(MapClientProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, HotRodMapStorageProviderFactory.PROVIDER_ID) + .spi("client").provider(MapClientProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID) + .spi("clientScope").provider(MapClientScopeProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID) + .spi("group").provider(MapGroupProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID) + .spi("realm").provider(MapRealmProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID) + .spi("role").provider(MapRoleProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID) + .spi(DeploymentStateSpi.NAME).provider(MapDeploymentStateProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID) + .spi(StoreFactorySpi.NAME).provider(MapAuthorizationStoreFactory.PROVIDER_ID).config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID) + .spi("user").provider(MapUserProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID) + .spi(UserSessionSpi.NAME).provider(MapUserSessionProviderFactory.PROVIDER_ID).config("storage-user-sessions.provider", ConcurrentHashMapStorageProviderFactory.PROVIDER_ID) + .config("storage-client-sessions.provider", ConcurrentHashMapStorageProviderFactory.PROVIDER_ID) + .spi(UserLoginFailureSpi.NAME).provider(MapUserLoginFailureProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID) + .spi("dblock").provider(NoLockingDBLockProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID); + + cf.spi(MapStorageSpi.NAME) + .provider(ConcurrentHashMapStorageProviderFactory.PROVIDER_ID) + .config("dir", "${project.build.directory:target}"); + + cf.spi(HotRodConnectionSpi.NAME).provider(DefaultHotRodConnectionProviderFactory.PROVIDER_ID) + .config("enableSecurity", "false") + .config("configureRemoteCaches", "false"); + } + + @Override + public void beforeSuite(Config cf) { + hotRodServerRule.createHotRodMapStoreServer(); + } + + @Override + public Statement classRule(Statement base, Description description) { + return hotRodServerRule.apply(base, description); + } + + public HotRodMapStorage() { + super(ALLOWED_SPIS, ALLOWED_FACTORIES); + } + +} diff --git a/testsuite/utils/src/main/resources/META-INF/keycloak-server.json b/testsuite/utils/src/main/resources/META-INF/keycloak-server.json index 396aecb071..6afe42d6d8 100755 --- a/testsuite/utils/src/main/resources/META-INF/keycloak-server.json +++ b/testsuite/utils/src/main/resources/META-INF/keycloak-server.json @@ -124,6 +124,14 @@ } }, + "connectionsHotRod": { + "default": { + "embedded": "${keycloak.connectionsHotRod.embedded:true}", + "embeddedPort": "${keycloak.connectionsHotRod.embeddedPort:11444}", + "enableSecurity": "${keycloak.connectionsHotRod.enableSecurity:false}" + } + }, + "scripting": { },