KEYCLOAK-19030 Implement HotRodConnectionProvider

This commit is contained in:
Martin Kanis 2021-06-26 17:35:40 +02:00 committed by Hynek Mlnařík
parent 36f7139bdd
commit af97849feb
24 changed files with 846 additions and 18 deletions

View file

@ -48,6 +48,10 @@
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-model-infinispan</artifactId> <artifactId>keycloak-model-infinispan</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-map-hot-rod</artifactId>
</dependency>
<!-- identity providers --> <!-- identity providers -->
<dependency> <dependency>
<groupId>org.twitter4j</groupId> <groupId>org.twitter4j</groupId>

View file

@ -200,6 +200,16 @@
</exclusion> </exclusion>
</exclusions> </exclusions>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-map-hot-rod</artifactId>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-saml-core</artifactId> <artifactId>keycloak-saml-core</artifactId>

52
model/hot-rod/pom.xml Normal file
View file

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>keycloak-model-pom</artifactId>
<groupId>org.keycloak</groupId>
<version>16.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-model-map-hot-rod</artifactId>
<name>Keycloak Model Hot Rod</name>
<description/>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-map</artifactId>
</dependency>
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-client-hotrod</artifactId>
</dependency>
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-query-dsl</artifactId>
</dependency>
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-remote-query-client</artifactId>
</dependency>
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-server-router</artifactId>
</dependency>
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-server-rest</artifactId>
</dependency>
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-server-runtime</artifactId>
</dependency>
<dependency>
<groupId>org.infinispan.protostream</groupId>
<artifactId>protostream-processor</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View file

@ -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 <a href="mailto:mkanis@redhat.com">Martin Kanis</a>
*/
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<Route<? extends RouteSource, ? extends RouteDestination>> 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);
}
}

View file

@ -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 <a href="mailto:mkanis@redhat.com">Martin Kanis</a>
*/
@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();
}

View file

@ -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 <a href="mailto:mkanis@redhat.com">Martin Kanis</a>
*/
public class DefaultHotRodConnectionProvider implements HotRodConnectionProvider {
private RemoteCacheManager remoteCacheManager;
public DefaultHotRodConnectionProvider(RemoteCacheManager remoteCacheManager) {
this.remoteCacheManager = remoteCacheManager;
}
@Override
public <K, V> RemoteCache<K, V> getRemoteCache(String name) {
return remoteCacheManager.getCache(name);
}
@Override
public void close() {
}
}

View file

@ -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 <a href="mailto:mkanis@redhat.com">Martin Kanis</a>
*/
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<String, String> 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));*/
}
}

View file

@ -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 <a href="mailto:mkanis@redhat.com">Martin Kanis</a>
*/
public interface HotRodConnectionProvider extends Provider {
/**
* Returns a remote Infinispan cache specified by the given name.
* @param name String Name of the remote cache.
* @param <K> key
* @param <V> value
* @return A remote Infinispan cache specified by name.
*/
<K, V> RemoteCache<K, V> getRemoteCache(String name);
}

View file

@ -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 <a href="mailto:mkanis@redhat.com">Martin Kanis</a>
*/
public interface HotRodConnectionProviderFactory extends ProviderFactory<HotRodConnectionProvider> {
}

View file

@ -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 <a href="mailto:mkanis@redhat.com">Martin Kanis</a>
*/
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<? extends Provider> getProviderClass() {
return HotRodConnectionProvider.class;
}
@Override
public Class<? extends ProviderFactory> getProviderFactoryClass() {
return HotRodConnectionProviderFactory.class;
}
}

View file

@ -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

View file

@ -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

View file

@ -0,0 +1,10 @@
<!-- Used for configuration of remote caches on the Infinispan server if the configureRemoteCaches option is set to true
in DefaultHotRodConnectionProviderFactory configuration. -->
<infinispan>
<cache-container>
<!-- Specify all remote caches that should be created on the Infinispan server. -->
<distributed-cache name="clients" mode="SYNC">
<encoding media-type="application/x-protostream"/>
</distributed-cache>
</cache-container>
</infinispan>

View file

@ -0,0 +1,12 @@
<!-- Configuration is used to start embedded Infinispan server in DefaultHotRodConnectionProviderFactory if
the embedded property is set tu true.-->
<infinispan>
<cache-container>
<transport stack="udp"/>
<!-- Specify all remote caches that should be created on the embedded Infinispan server. -->
<distributed-cache name="clients" mode="SYNC">
<encoding media-type="application/x-protostream"/>
</distributed-cache>
</cache-container>
</infinispan>

View file

@ -75,10 +75,5 @@
<artifactId>microprofile-metrics-api</artifactId> <artifactId>microprofile-metrics-api</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-server-hotrod</artifactId>
<version>${infinispan.version}</version>
</dependency>
</dependencies> </dependencies>
</project> </project>

View file

@ -35,5 +35,6 @@
<module>infinispan</module> <module>infinispan</module>
<module>map</module> <module>map</module>
<module>build-processor</module> <module>build-processor</module>
<module>hot-rod</module>
</modules> </modules>
</project> </project>

53
pom.xml
View file

@ -78,6 +78,7 @@
<hibernate.core.version>5.3.20.Final</hibernate.core.version> <hibernate.core.version>5.3.20.Final</hibernate.core.version>
<hibernate.c3p0.version>5.3.20.Final</hibernate.c3p0.version> <hibernate.c3p0.version>5.3.20.Final</hibernate.c3p0.version>
<infinispan.version>11.0.9.Final</infinispan.version> <infinispan.version>11.0.9.Final</infinispan.version>
<infinispan.protostream.processor.version>4.3.4.Final</infinispan.protostream.processor.version>
<jackson.version>2.12.1</jackson.version> <jackson.version>2.12.1</jackson.version>
<jackson.databind.version>${jackson.version}</jackson.databind.version> <jackson.databind.version>${jackson.version}</jackson.databind.version>
<jackson.annotations.version>${jackson.databind.version}</jackson.annotations.version> <jackson.annotations.version>${jackson.databind.version}</jackson.annotations.version>
@ -908,6 +909,53 @@
<artifactId>infinispan-jboss-marshalling</artifactId> <artifactId>infinispan-jboss-marshalling</artifactId>
<version>${infinispan.version}</version> <version>${infinispan.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-server-core</artifactId>
<version>${infinispan.version}</version>
</dependency>
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-server-router</artifactId>
<version>${infinispan.version}</version>
</dependency>
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-server-runtime</artifactId>
<version>${infinispan.version}</version>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-server-rest</artifactId>
<version>${infinispan.version}</version>
</dependency>
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-client-hotrod</artifactId>
<version>${infinispan.version}</version>
</dependency>
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-query-dsl</artifactId>
<version>${infinispan.version}</version>
</dependency>
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-remote-query-client</artifactId>
<version>${infinispan.version}</version>
</dependency>
<dependency>
<groupId>org.infinispan.protostream</groupId>
<artifactId>protostream-processor</artifactId>
<scope>provided</scope>
<version>${infinispan.protostream.processor.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.jboss.marshalling</groupId> <groupId>org.jboss.marshalling</groupId>
<artifactId>jboss-marshalling</artifactId> <artifactId>jboss-marshalling</artifactId>
@ -1243,6 +1291,11 @@
<artifactId>keycloak-model-infinispan</artifactId> <artifactId>keycloak-model-infinispan</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-map-hot-rod</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>launcher</artifactId> <artifactId>launcher</artifactId>

View file

@ -48,42 +48,90 @@
}, },
"realm": { "realm": {
"provider": "${keycloak.realm.provider:jpa}" "provider": "${keycloak.realm.provider:jpa}",
"map": {
"storage": {
"provider": "${keycloak.realm.map.storage.provider:concurrenthashmap}"
}
}
}, },
"user": { "user": {
"provider": "${keycloak.user.provider:jpa}" "provider": "${keycloak.user.provider:jpa}",
"map": {
"storage": {
"provider": "${keycloak.user.map.storage.provider:concurrenthashmap}"
}
}
}, },
"client": { "client": {
"provider": "${keycloak.client.provider:jpa}" "provider": "${keycloak.client.provider:jpa}",
"map": {
"storage": {
"provider": "${keycloak.client.map.storage.provider:concurrenthashmap}"
}
}
}, },
"clientScope": { "clientScope": {
"provider": "${keycloak.clientScope.provider:jpa}" "provider": "${keycloak.clientScope.provider:jpa}",
"map": {
"storage": {
"provider": "${keycloak.clientScope.map.storage.provider:concurrenthashmap}"
}
}
}, },
"group": { "group": {
"provider": "${keycloak.group.provider:jpa}" "provider": "${keycloak.group.provider:jpa}",
"map": {
"storage": {
"provider": "${keycloak.group.map.storage.provider:concurrenthashmap}"
}
}
}, },
"role": { "role": {
"provider": "${keycloak.role.provider:jpa}" "provider": "${keycloak.role.provider:jpa}",
"map": {
"storage": {
"provider": "${keycloak.role.map.storage.provider:concurrenthashmap}"
}
}
}, },
"authenticationSessions": { "authenticationSessions": {
"provider": "${keycloak.authSession.provider:infinispan}", "provider": "${keycloak.authSession.provider:infinispan}",
"map": {
"storage": {
"provider": "${keycloak.authSession.map.storage.provider:concurrenthashmap}"
}
},
"infinispan": { "infinispan": {
"authSessionsLimit": "${keycloak.authSessions.limit:300}" "authSessionsLimit": "${keycloak.authSessions.limit:300}"
} }
}, },
"userSessions": { "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": { "loginFailure": {
"provider": "${keycloak.loginFailure.provider:infinispan}" "provider": "${keycloak.loginFailure.provider:infinispan}",
"map": {
"storage": {
"provider": "${keycloak.loginFailure.map.storage.provider:concurrenthashmap}"
}
}
}, },
"mapStorage": { "mapStorage": {
@ -104,7 +152,12 @@
}, },
"authorizationPersister": { "authorizationPersister": {
"provider": "${keycloak.authorization.provider:jpa}" "provider": "${keycloak.authorization.provider:jpa}",
"map": {
"storage": {
"provider": "${keycloak.authorization.map.storage.provider:concurrenthashmap}"
}
}
}, },
"userCache": { "userCache": {
@ -192,6 +245,14 @@
} }
}, },
"connectionsHotRod": {
"default": {
"embedded": "${keycloak.connectionsHotRod.embedded:true}",
"embeddedPort": "${keycloak.connectionsHotRod.embeddedPort:11444}",
"enableSecurity": "${keycloak.connectionsHotRod.enableSecurity:false}"
}
},
"truststore": { "truststore": {
"file": { "file": {
"file": "${keycloak.truststore.file:target/dependency/keystore/keycloak.truststore}", "file": "${keycloak.truststore.file:target/dependency/keystore/keycloak.truststore}",

View file

@ -75,7 +75,6 @@
<version>${jdbc.mvn.version}</version> <version>${jdbc.mvn.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-model-jpa</artifactId> <artifactId>keycloak-model-jpa</artifactId>
@ -93,6 +92,14 @@
<artifactId>integration-arquillian-testsuite-providers</artifactId> <artifactId>integration-arquillian-testsuite-providers</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-map-hot-rod</artifactId>
</dependency>
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-server-core</artifactId>
</dependency>
</dependencies> </dependencies>
<build> <build>
@ -266,6 +273,14 @@
<keycloak.model.parameters>Jpa,Map,ConcurrentHashMapStorage</keycloak.model.parameters> <keycloak.model.parameters>Jpa,Map,ConcurrentHashMapStorage</keycloak.model.parameters>
</properties> </properties>
</profile> </profile>
<profile>
<id>hot-rod</id>
<properties>
<keycloak.profile.feature.map_storage>enabled</keycloak.profile.feature.map_storage>
<keycloak.model.parameters>Jpa,Map,HotRodMapStorage</keycloak.model.parameters>
</properties>
</profile>
</profiles> </profiles>
</project> </project>

View file

@ -133,7 +133,7 @@ public class Config implements ConfigProvider {
@Override @Override
public Scope scope(String... scope) { public Scope scope(String... scope) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append(prefix).append("."); sb.append(prefix);
for (String s : scope) { for (String s : scope) {
sb.append(s); sb.append(s);
sb.append("."); sb.append(".");

View file

@ -14,6 +14,7 @@ import org.infinispan.server.hotrod.configuration.HotRodServerConfiguration;
import org.infinispan.server.hotrod.configuration.HotRodServerConfigurationBuilder; import org.infinispan.server.hotrod.configuration.HotRodServerConfigurationBuilder;
import org.junit.rules.ExternalResource; import org.junit.rules.ExternalResource;
import org.keycloak.Config; import org.keycloak.Config;
import org.keycloak.models.map.common.HotRodUtils;
import java.io.IOException; import java.io.IOException;
@ -39,7 +40,9 @@ public class HotRodServerRule extends ExternalResource {
@Override @Override
protected void after() { protected void after() {
remoteCacheManager.stop(); if (remoteCacheManager != null) {
remoteCacheManager.stop();
}
} }
public void createEmbeddedHotRodServer(Config.Scope config) { 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); 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) { private void getCaches(String... cache) {
for (String c: cache) { for (String c: cache) {
hotRodCacheManager.getCache(c, true); hotRodCacheManager.getCache(c, true);

View file

@ -0,0 +1,9 @@
<infinispan>
<cache-container>
<transport stack="udp"/>
<distributed-cache name="clients" mode="SYNC">
<encoding media-type="application/x-protostream"/>
</distributed-cache>
</cache-container>
</infinispan>

View file

@ -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<Class<? extends Spi>> ALLOWED_SPIS = ImmutableSet.<Class<? extends Spi>>builder()
.add(HotRodConnectionSpi.class)
.build();
static final Set<Class<? extends ProviderFactory>> ALLOWED_FACTORIES = ImmutableSet.<Class<? extends ProviderFactory>>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);
}
}

View file

@ -124,6 +124,14 @@
} }
}, },
"connectionsHotRod": {
"default": {
"embedded": "${keycloak.connectionsHotRod.embedded:true}",
"embeddedPort": "${keycloak.connectionsHotRod.embeddedPort:11444}",
"enableSecurity": "${keycloak.connectionsHotRod.enableSecurity:false}"
}
},
"scripting": { "scripting": {
}, },