KEYCLOAK-17501 Add support for map storage in WildFly

This commit is contained in:
Hynek Mlnarik 2021-03-25 16:42:10 +01:00 committed by Hynek Mlnařík
parent ee315ecab1
commit 96501760e0
26 changed files with 235 additions and 16 deletions

View file

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
-->
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-model-map">
<properties>
<property name="jboss.api" value="private"/>
</properties>
<resources>
<artifact name="${org.keycloak:keycloak-model-map}"/>
</resources>
<dependencies>
<module name="javax.transaction.api"/>
<module name="org.keycloak.keycloak-common"/>
<module name="org.keycloak.keycloak-core"/>
<module name="org.keycloak.keycloak-server-spi"/>
<module name="org.keycloak.keycloak-server-spi-private"/>
<module name="org.jboss.logging"/>
<module name="org.javassist"/>
<module name="javax.api"/>
<module name="com.fasterxml.jackson.core.jackson-core"/>
<module name="com.fasterxml.jackson.core.jackson-annotations"/>
<module name="com.fasterxml.jackson.core.jackson-databind"/>
<module name="com.fasterxml.jackson.datatype.jackson-datatype-jdk8"/>
<module name="com.fasterxml.jackson.jaxrs.jackson-jaxrs-json-provider"/>
</dependencies>
</module>

View file

@ -34,6 +34,7 @@
<module name="org.keycloak.keycloak-server-spi" services="import"/>
<module name="org.keycloak.keycloak-server-spi-private" services="import"/>
<module name="org.keycloak.keycloak-model-jpa" services="import"/>
<module name="org.keycloak.keycloak-model-map" services="import"/>
<module name="org.keycloak.keycloak-model-infinispan" services="import"/>
<module name="org.keycloak.keycloak-saml-core-public" services="import"/>
<module name="org.keycloak.keycloak-saml-core" services="import"/>

View file

@ -118,6 +118,13 @@
<include>licenses-${product.slot}/**</include>
</includes>
</fileSet>
<fileSet>
<directory>src/main/docs</directory>
<outputDirectory>docs</outputDirectory>
<includes>
<include>**</include>
</includes>
</fileSet>
</fileSets>
<files>

View file

@ -0,0 +1,35 @@
##
## CLI script to set Keycloak to use map storage rather than the standard JPA.
## The backend database is at this moment a ConcurrentHashMap-based storage
## which is suitable for dev and testing in standalone node. It does not
## support clustered deployments.
##
## Apply this file using the following command from the Keycloak root directory:
##
## bin/jboss-cli.sh --file=docs/examples/map-storage-concurrenthashmap.cli
##
## This will modify standalone/configuration/standalone.xml
##
embed-server
/subsystem=keycloak-server/spi=authorizationPersister:add(default-provider=map)
/subsystem=keycloak-server/spi=client:add(default-provider=map)
/subsystem=keycloak-server/spi=clientScope:add(default-provider=map)
/subsystem=keycloak-server/spi=group:add(default-provider=map)
/subsystem=keycloak-server/spi=realm:add(default-provider=map)
/subsystem=keycloak-server/spi=role:add(default-provider=map)
/subsystem=keycloak-server/spi=serverInfo:add(default-provider=map)
/subsystem=keycloak-server/spi=serverInfo/provider=map:add(enabled=true,properties={resourcesVersionSeed=1JZ379bzyOCFA})
/subsystem=keycloak-server/spi=user:add(default-provider=map)
## For dev and single-node purposes, these are set to "map".
## For clustered deployments, these should be "infinispan" as map storage does not support distributed storage yet
/subsystem=keycloak-server/spi=authenticationSessions:add(default-provider=map)
/subsystem=keycloak-server/spi=loginFailure:add(default-provider=map)
/subsystem=keycloak-server/spi=userSessions:add(default-provider=map)
/subsystem=keycloak-server/spi=mapStorage:add(default-provider=concurrenthashmap)
/subsystem=keycloak-server/spi=mapStorage/provider=concurrenthashmap:add(properties={dir="${jboss.server.data.dir}/map"},enabled=true)
quit

View file

@ -24,6 +24,7 @@ import org.keycloak.models.*;
import org.keycloak.models.sessions.infinispan.entities.ActionTokenValueEntity;
import org.keycloak.models.sessions.infinispan.entities.ActionTokenReducedKey;
import org.infinispan.Cache;
import static org.keycloak.models.sessions.infinispan.InfinispanAuthenticationSessionProviderFactory.PROVIDER_PRIORITY;
/**
*
@ -78,4 +79,8 @@ public class InfinispanActionTokenStoreProviderFactory implements ActionTokenSto
return "infinispan";
}
@Override
public int order() {
return PROVIDER_PRIORITY;
}
}

View file

@ -48,6 +48,7 @@ import org.jboss.logging.Logger;
public class InfinispanAuthenticationSessionProviderFactory implements AuthenticationSessionProviderFactory {
private static final Logger log = Logger.getLogger(InfinispanAuthenticationSessionProviderFactory.class);
public static final int PROVIDER_PRIORITY = 1;
private InfinispanKeyGenerator keyGenerator;
@ -177,4 +178,9 @@ public class InfinispanAuthenticationSessionProviderFactory implements Authentic
public String getId() {
return PROVIDER_ID;
}
@Override
public int order() {
return PROVIDER_PRIORITY;
}
}

View file

@ -28,6 +28,7 @@ import org.keycloak.models.CodeToTokenStoreProviderFactory;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.sessions.infinispan.entities.ActionTokenValueEntity;
import static org.keycloak.models.sessions.infinispan.InfinispanAuthenticationSessionProviderFactory.PROVIDER_PRIORITY;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@ -74,4 +75,9 @@ public class InfinispanCodeToTokenStoreProviderFactory implements CodeToTokenSto
public String getId() {
return "infinispan";
}
@Override
public int order() {
return PROVIDER_PRIORITY;
}
}

View file

@ -26,6 +26,7 @@ import org.keycloak.models.OAuth2DeviceTokenStoreProviderFactory;
import org.keycloak.models.sessions.infinispan.entities.ActionTokenValueEntity;
import java.util.function.Supplier;
import static org.keycloak.models.sessions.infinispan.InfinispanAuthenticationSessionProviderFactory.PROVIDER_PRIORITY;
/**
* @author <a href="mailto:h2-wada@nri.co.jp">Hiroyuki Wada</a>
@ -68,4 +69,9 @@ public class InfinispanOAuth2DeviceTokenStoreProviderFactory implements OAuth2De
public String getId() {
return "infinispan";
}
@Override
public int order() {
return PROVIDER_PRIORITY;
}
}

View file

@ -24,10 +24,10 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.SamlArtifactSessionMappingStoreProvider;
import org.keycloak.models.SamlArtifactSessionMappingStoreProviderFactory;
import org.keycloak.models.sessions.infinispan.entities.ActionTokenValueEntity;
import java.util.UUID;
import java.util.function.Supplier;
import static org.keycloak.models.sessions.infinispan.InfinispanAuthenticationSessionProviderFactory.PROVIDER_PRIORITY;
/**
* @author mhajas
@ -74,4 +74,9 @@ public class InfinispanSamlArtifactSessionMappingStoreProviderFactory implements
public String getId() {
return "infinispan";
}
@Override
public int order() {
return PROVIDER_PRIORITY;
}
}

View file

@ -31,6 +31,7 @@ import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.SingleUseTokenStoreProviderFactory;
import org.keycloak.models.sessions.infinispan.entities.ActionTokenValueEntity;
import org.keycloak.models.sessions.infinispan.util.InfinispanUtil;
import static org.keycloak.models.sessions.infinispan.InfinispanAuthenticationSessionProviderFactory.PROVIDER_PRIORITY;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@ -97,4 +98,9 @@ public class InfinispanSingleUseTokenStoreProviderFactory implements SingleUseTo
public String getId() {
return "infinispan";
}
@Override
public int order() {
return PROVIDER_PRIORITY;
}
}

View file

@ -23,6 +23,7 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.sessions.StickySessionEncoderProvider;
import org.keycloak.sessions.StickySessionEncoderProviderFactory;
import static org.keycloak.models.sessions.infinispan.InfinispanAuthenticationSessionProviderFactory.PROVIDER_PRIORITY;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@ -65,4 +66,9 @@ public class InfinispanStickySessionEncoderProviderFactory implements StickySess
public String getId() {
return "infinispan";
}
@Override
public int order() {
return PROVIDER_PRIORITY;
}
}

View file

@ -28,6 +28,7 @@ import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.TokenRevocationStoreProvider;
import org.keycloak.models.TokenRevocationStoreProviderFactory;
import org.keycloak.models.sessions.infinispan.entities.ActionTokenValueEntity;
import static org.keycloak.models.sessions.infinispan.InfinispanAuthenticationSessionProviderFactory.PROVIDER_PRIORITY;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@ -73,4 +74,8 @@ public class InfinispanTokenRevocationStoreProviderFactory implements TokenRevoc
return "infinispan";
}
@Override
public int order() {
return PROVIDER_PRIORITY;
}
}

View file

@ -50,6 +50,7 @@ import org.keycloak.models.utils.PostMigrationEvent;
import java.io.Serializable;
import java.util.Set;
import java.util.function.BiFunction;
import static org.keycloak.models.sessions.infinispan.InfinispanAuthenticationSessionProviderFactory.PROVIDER_PRIORITY;
/**
* @author <a href="mailto:mkanis@redhat.com">Martin Kanis</a>
@ -211,4 +212,9 @@ public class InfinispanUserLoginFailureProviderFactory implements UserLoginFailu
public String getId() {
return PROVIDER_ID;
}
@Override
public int order() {
return PROVIDER_PRIORITY;
}
}

View file

@ -65,6 +65,7 @@ import java.io.Serializable;
import java.util.Set;
import java.util.UUID;
import java.util.function.BiFunction;
import static org.keycloak.models.sessions.infinispan.InfinispanAuthenticationSessionProviderFactory.PROVIDER_PRIORITY;
public class InfinispanUserSessionProviderFactory implements UserSessionProviderFactory {
@ -334,5 +335,9 @@ public class InfinispanUserSessionProviderFactory implements UserSessionProvider
return PROVIDER_ID;
}
@Override
public int order() {
return PROVIDER_PRIORITY;
}
}

View file

@ -26,6 +26,7 @@ import org.keycloak.authorization.store.AuthorizationStoreFactory;
import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.connections.jpa.JpaConnectionProvider;
import org.keycloak.models.KeycloakSession;
import static org.keycloak.models.jpa.JpaRealmProviderFactory.PROVIDER_PRIORITY;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@ -55,4 +56,9 @@ public class JPAAuthorizationStoreFactory implements AuthorizationStoreFactory {
private EntityManager getEntityManager(KeycloakSession session) {
return session.getProvider(JpaConnectionProvider.class).getEntityManager();
}
@Override
public int order() {
return PROVIDER_PRIORITY;
}
}

View file

@ -25,6 +25,8 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import javax.persistence.EntityManager;
import static org.keycloak.models.jpa.JpaRealmProviderFactory.PROVIDER_ID;
import static org.keycloak.models.jpa.JpaRealmProviderFactory.PROVIDER_PRIORITY;
public class JpaClientProviderFactory implements ClientProviderFactory {
@ -39,7 +41,7 @@ public class JpaClientProviderFactory implements ClientProviderFactory {
@Override
public String getId() {
return "jpa";
return PROVIDER_ID;
}
@Override
@ -52,4 +54,9 @@ public class JpaClientProviderFactory implements ClientProviderFactory {
public void close() {
}
@Override
public int order() {
return PROVIDER_PRIORITY;
}
}

View file

@ -25,6 +25,8 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import javax.persistence.EntityManager;
import static org.keycloak.models.jpa.JpaRealmProviderFactory.PROVIDER_ID;
import static org.keycloak.models.jpa.JpaRealmProviderFactory.PROVIDER_PRIORITY;
public class JpaClientScopeProviderFactory implements ClientScopeProviderFactory {
@ -38,7 +40,7 @@ public class JpaClientScopeProviderFactory implements ClientScopeProviderFactory
@Override
public String getId() {
return "jpa";
return PROVIDER_ID;
}
@Override
@ -51,4 +53,9 @@ public class JpaClientScopeProviderFactory implements ClientScopeProviderFactory
public void close() {
}
@Override
public int order() {
return PROVIDER_PRIORITY;
}
}

View file

@ -25,6 +25,8 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import javax.persistence.EntityManager;
import static org.keycloak.models.jpa.JpaRealmProviderFactory.PROVIDER_ID;
import static org.keycloak.models.jpa.JpaRealmProviderFactory.PROVIDER_PRIORITY;
public class JpaGroupProviderFactory implements GroupProviderFactory {
@ -39,7 +41,7 @@ public class JpaGroupProviderFactory implements GroupProviderFactory {
@Override
public String getId() {
return "jpa";
return PROVIDER_ID;
}
@Override
@ -52,4 +54,9 @@ public class JpaGroupProviderFactory implements GroupProviderFactory {
public void close() {
}
@Override
public int order() {
return PROVIDER_PRIORITY;
}
}

View file

@ -40,6 +40,9 @@ public class JpaRealmProviderFactory implements RealmProviderFactory, ProviderEv
private Runnable onClose;
public static final String PROVIDER_ID = "jpa";
public static final int PROVIDER_PRIORITY = 1;
@Override
public void init(Config.Scope config) {
}
@ -52,7 +55,7 @@ public class JpaRealmProviderFactory implements RealmProviderFactory, ProviderEv
@Override
public String getId() {
return "jpa";
return PROVIDER_ID;
}
@Override
@ -83,4 +86,10 @@ public class JpaRealmProviderFactory implements RealmProviderFactory, ProviderEv
create(e.getKeycloakSession()).preRemove(realm, role);
}
}
@Override
public int order() {
return PROVIDER_PRIORITY;
}
}

View file

@ -25,6 +25,8 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import javax.persistence.EntityManager;
import static org.keycloak.models.jpa.JpaRealmProviderFactory.PROVIDER_ID;
import static org.keycloak.models.jpa.JpaRealmProviderFactory.PROVIDER_PRIORITY;
public class JpaRoleProviderFactory implements RoleProviderFactory {
@ -38,7 +40,7 @@ public class JpaRoleProviderFactory implements RoleProviderFactory {
@Override
public String getId() {
return "jpa";
return PROVIDER_ID;
}
@Override
@ -51,4 +53,9 @@ public class JpaRoleProviderFactory implements RoleProviderFactory {
public void close() {
}
@Override
public int order() {
return PROVIDER_PRIORITY;
}
}

View file

@ -24,6 +24,8 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.ServerInfoProvider;
import org.keycloak.models.ServerInfoProviderFactory;
import static org.keycloak.models.jpa.JpaRealmProviderFactory.PROVIDER_ID;
import static org.keycloak.models.jpa.JpaRealmProviderFactory.PROVIDER_PRIORITY;
public class JpaServerInfoProviderFactory implements ServerInfoProviderFactory {
@ -37,7 +39,7 @@ public class JpaServerInfoProviderFactory implements ServerInfoProviderFactory {
@Override
public String getId() {
return "jpa";
return PROVIDER_ID;
}
@Override
@ -49,4 +51,9 @@ public class JpaServerInfoProviderFactory implements ServerInfoProviderFactory {
@Override
public void close() {
}
@Override
public int order() {
return PROVIDER_PRIORITY;
}
}

View file

@ -25,6 +25,8 @@ import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.ProviderFactory;
import javax.persistence.EntityManager;
import static org.keycloak.models.jpa.JpaRealmProviderFactory.PROVIDER_ID;
import static org.keycloak.models.jpa.JpaRealmProviderFactory.PROVIDER_PRIORITY;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -43,7 +45,7 @@ public class JpaUserCredentialStoreFactory implements ProviderFactory<UserCreden
@Override
public String getId() {
return "jpa";
return PROVIDER_ID;
}
@Override
@ -56,4 +58,9 @@ public class JpaUserCredentialStoreFactory implements ProviderFactory<UserCreden
public void close() {
}
@Override
public int order() {
return PROVIDER_PRIORITY;
}
}

View file

@ -25,6 +25,8 @@ import org.keycloak.models.UserProvider;
import org.keycloak.models.UserProviderFactory;
import javax.persistence.EntityManager;
import static org.keycloak.models.jpa.JpaRealmProviderFactory.PROVIDER_ID;
import static org.keycloak.models.jpa.JpaRealmProviderFactory.PROVIDER_PRIORITY;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -43,7 +45,7 @@ public class JpaUserProviderFactory implements UserProviderFactory {
@Override
public String getId() {
return "jpa";
return PROVIDER_ID;
}
@Override
@ -56,4 +58,9 @@ public class JpaUserProviderFactory implements UserProviderFactory {
public void close() {
}
@Override
public int order() {
return PROVIDER_PRIORITY;
}
}

View file

@ -57,13 +57,24 @@ public class ConcurrentHashMapStorageProvider implements MapStorageProvider {
@Override
public void init(Scope config) {
File f = new File(config.get("dir"));
try {
this.storageDirectory = f.exists()
? f
: Files.createTempDirectory("storage-map-chm-").toFile();
} catch (IOException ex) {
final String dir = config.get("dir");
if (dir == null || dir.trim().isEmpty()) {
LOG.warn("No directory set, created objects will not survive server restart");
this.storageDirectory = null;
} else {
File f = new File(dir);
try {
Files.createDirectories(f.toPath());
if (f.exists()) {
this.storageDirectory = f;
} else {
LOG.warnf("Directory cannot be used, created objects will not survive server restart: %s", dir);
this.storageDirectory = null;
}
} catch (IOException ex) {
LOG.warnf("Directory cannot be used, created objects will not survive server restart: %s", dir);
this.storageDirectory = null;
}
}
}

View file

@ -225,7 +225,7 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory, Pr
String defaultProvider = Config.getProvider(spi.getName());
if (defaultProvider != null) {
if (getProviderFactory(spi.getProviderClass(), defaultProvider) == null) {
throw new RuntimeException("Failed to find provider " + provider + " for " + spi.getName());
throw new RuntimeException("Failed to find provider " + defaultProvider + " for " + spi.getName());
}
} else {
Map<String, ProviderFactory> factories = factoriesMap.get(spi.getProviderClass());

View file

@ -44,6 +44,7 @@ public class KeycloakProviderDependencyProcessor implements DeploymentUnitProces
private static final ModuleIdentifier KEYCLOAK_SERVER_SPI = ModuleIdentifier.create("org.keycloak.keycloak-server-spi");
private static final ModuleIdentifier KEYCLOAK_SERVER_SPI_PRIVATE = ModuleIdentifier.create("org.keycloak.keycloak-server-spi-private");
private static final ModuleIdentifier KEYCLOAK_JPA = ModuleIdentifier.create("org.keycloak.keycloak-model-jpa");
private static final ModuleIdentifier KEYCLOAK_MAP = ModuleIdentifier.create("org.keycloak.keycloak-model-map");
private static final ModuleIdentifier JAXRS = ModuleIdentifier.create("javax.ws.rs.api");
private static final ModuleIdentifier RESTEASY = ModuleIdentifier.create("org.jboss.resteasy.resteasy-jaxrs");
private static final ModuleIdentifier APACHE = ModuleIdentifier.create("org.apache.httpcomponents");
@ -70,6 +71,7 @@ public class KeycloakProviderDependencyProcessor implements DeploymentUnitProces
moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, RESTEASY, false, false, false, false));
moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, APACHE, false, false, false, false));
moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_JPA, false, false, false, false));
moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_MAP, false, false, false, false));
}
}