Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Bill Burke 2016-09-07 23:19:45 -04:00
commit b0bb0d36ab
65 changed files with 252 additions and 2930 deletions

View file

@ -1,102 +0,0 @@
/*
* Copyright 2016 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.common.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@SuppressWarnings("serial")
public class ConcurrentMultivaluedHashMap<K, V> extends ConcurrentHashMap<K, List<V>>
{
public void putSingle(K key, V value)
{
List<V> list = new CopyOnWriteArrayList<>();
list.add(value);
put(key, list);
}
public void addAll(K key, V... newValues)
{
for (V value : newValues)
{
add(key, value);
}
}
public void addAll(K key, List<V> valueList)
{
for (V value : valueList)
{
add(key, value);
}
}
public void addFirst(K key, V value)
{
List<V> list = get(key);
if (list == null)
{
add(key, value);
}
else
{
list.add(0, value);
}
}
public final void add(K key, V value)
{
getList(key).add(value);
}
public final void addMultiple(K key, Collection<V> values)
{
getList(key).addAll(values);
}
public V getFirst(K key)
{
List<V> list = get(key);
return list == null ? null : list.get(0);
}
public final List<V> getList(K key)
{
List<V> list = get(key);
if (list == null)
put(key, list = new CopyOnWriteArrayList<V>());
return list;
}
public void addAll(ConcurrentMultivaluedHashMap<K, V> other)
{
for (Entry<K, List<V>> entry : other.entrySet())
{
getList(entry.getKey()).addAll(entry.getValue());
}
}
}

View file

@ -25,7 +25,6 @@
</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"/>

View file

@ -33,6 +33,5 @@
<module name="javax.ws.rs.api"/>
<module name="org.apache.httpcomponents"/>
<module name="org.jboss.resteasy.resteasy-jaxrs"/>
<module name="javax.transaction.api"/>
</dependencies>
</module>

View file

@ -74,11 +74,6 @@
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.transaction</groupId>
<artifactId>jboss-transaction-api_1.2_spec</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>

View file

@ -18,28 +18,34 @@
package org.keycloak.models.cache.infinispan;
import org.infinispan.Cache;
import org.infinispan.notifications.Listener;
import org.infinispan.notifications.cachelistener.annotation.*;
import org.infinispan.notifications.cachelistener.event.*;
import org.jboss.logging.Logger;
import org.keycloak.Config;
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.cache.UserCache;
import org.keycloak.models.cache.UserCacheProviderFactory;
import org.keycloak.models.cache.CacheUserProvider;
import org.keycloak.models.cache.CacheUserProviderFactory;
import org.keycloak.models.cache.infinispan.entities.CachedUser;
import org.keycloak.models.cache.infinispan.entities.Revisioned;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class InfinispanUserCacheProviderFactory implements UserCacheProviderFactory {
public class InfinispanCacheUserProviderFactory implements CacheUserProviderFactory {
private static final Logger log = Logger.getLogger(InfinispanUserCacheProviderFactory.class);
private static final Logger log = Logger.getLogger(InfinispanCacheUserProviderFactory.class);
protected volatile UserCacheManager userCache;
@Override
public UserCache create(KeycloakSession session) {
public CacheUserProvider create(KeycloakSession session) {
lazyInit(session);
return new UserCacheSession(userCache, session);
}

View file

@ -28,7 +28,6 @@ import org.keycloak.models.UserConsentModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserCredentialValueModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.cache.CachedUserModel;
import org.keycloak.models.cache.infinispan.entities.CachedUser;
import org.keycloak.models.cache.infinispan.entities.CachedUserConsent;
import org.keycloak.models.utils.KeycloakModelUtils;
@ -40,13 +39,12 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class UserAdapter implements CachedUserModel {
public class UserAdapter implements UserModel {
protected UserModel updated;
protected CachedUser cached;
protected UserCacheSession userProviderCache;
@ -67,22 +65,6 @@ public class UserAdapter implements CachedUserModel {
if (updated == null) throw new IllegalStateException("Not found in database");
}
}
@Override
public void invalidate() {
getDelegateForUpdate();
}
@Override
public long getCacheTimestamp() {
return cached.getCacheTimestamp();
}
@Override
public ConcurrentHashMap getCachedWith() {
return cached.getCachedWith();
}
@Override
public String getId() {
if (updated != null) return updated.getId();

View file

@ -34,9 +34,7 @@ import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserProvider;
import org.keycloak.models.cache.CachedUserModel;
import org.keycloak.models.cache.OnUserCache;
import org.keycloak.models.cache.UserCache;
import org.keycloak.models.cache.CacheUserProvider;
import org.keycloak.models.cache.infinispan.entities.CachedFederatedIdentityLinks;
import org.keycloak.models.cache.infinispan.entities.CachedUser;
import org.keycloak.models.cache.infinispan.entities.CachedUserConsent;
@ -49,7 +47,7 @@ import java.util.*;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class UserCacheSession implements UserCache {
public class UserCacheSession implements CacheUserProvider {
protected static final Logger logger = Logger.getLogger(UserCacheSession.class);
protected UserCacheManager cache;
protected KeycloakSession session;
@ -75,6 +73,7 @@ public class UserCacheSession implements UserCache {
cache.clear();
}
@Override
public UserProvider getDelegate() {
if (!transactionActive) throw new IllegalStateException("Cannot access delegate without a transaction");
if (delegate != null) return delegate;
@ -90,20 +89,6 @@ public class UserCacheSession implements UserCache {
if (realm.isIdentityFederationEnabled()) invalidations.add(getFederatedIdentityLinksCacheKey(user.getId()));
}
@Override
public void evict(RealmModel realm, UserModel user) {
if (user instanceof CachedUserModel) {
((CachedUserModel)user).invalidate();
} else {
invalidations.add(user.getId());
if (user.getEmail() != null) invalidations.add(getUserByEmailCacheKey(realm.getId(), user.getEmail()));
invalidations.add(getUserByUsernameCacheKey(realm.getId(), user.getUsername()));
if (realm.isIdentityFederationEnabled()) invalidations.add(getFederatedIdentityLinksCacheKey(user.getId()));
}
}
protected void runInvalidations() {
for (String realmId : realmInvalidations) {
cache.invalidateRealmUsers(realmId, invalidations);
@ -162,13 +147,8 @@ public class UserCacheSession implements UserCache {
logger.trace("registered for invalidation return delegate");
return getDelegate().getUserById(id, realm);
}
if (managedUsers.containsKey(id)) {
logger.trace("return managedusers");
return managedUsers.get(id);
}
CachedUser cached = cache.get(id, CachedUser.class);
boolean wasCached = cached != null;
if (cached == null) {
logger.trace("not cached");
Long loaded = cache.getCurrentRevision(id);
@ -177,12 +157,19 @@ public class UserCacheSession implements UserCache {
logger.trace("delegate returning null");
return null;
}
if (managedUsers.containsKey(id)) {
logger.trace("return managedusers");
return managedUsers.get(id);
}
if (invalidations.contains(id)) return model;
cached = new CachedUser(loaded, realm, model);
cache.addRevisioned(cached, startupRevision);
} else if (managedUsers.containsKey(id)) {
logger.trace("return managedusers");
return managedUsers.get(id);
}
logger.trace("returning new cache adapter");
UserAdapter adapter = new UserAdapter(cached, this, session, realm);
if (!wasCached) onCache(realm, adapter);
managedUsers.put(id, adapter);
return adapter;
}
@ -236,7 +223,13 @@ public class UserCacheSession implements UserCache {
return managedUsers.get(userId);
}
UserAdapter adapter = getUserAdapter(realm, userId, loaded, model);
CachedUser cached = cache.get(userId, CachedUser.class);
if (cached == null) {
cached = new CachedUser(loaded, realm, model);
cache.addRevisioned(cached, startupRevision);
}
logger.trace("return new cache adapter");
UserAdapter adapter = new UserAdapter(cached, this, session, realm);
managedUsers.put(userId, adapter);
return adapter;
} else {
@ -251,26 +244,6 @@ public class UserCacheSession implements UserCache {
}
}
protected UserAdapter getUserAdapter(RealmModel realm, String userId, Long loaded, UserModel model) {
CachedUser cached = cache.get(userId, CachedUser.class);
boolean wasCached = cached != null;
if (cached == null) {
cached = new CachedUser(loaded, realm, model);
cache.addRevisioned(cached, startupRevision);
}
UserAdapter adapter = new UserAdapter(cached, this, session, realm);
if (!wasCached) {
onCache(realm, adapter);
}
return adapter;
}
private void onCache(RealmModel realm, UserAdapter adapter) {
((OnUserCache)getDelegate()).onCache(realm, adapter);
((OnUserCache)session.userCredentialManager()).onCache(realm, adapter);
}
@Override
public UserModel getUserByEmail(String email, RealmModel realm) {
if (email == null) return null;
@ -295,7 +268,12 @@ public class UserCacheSession implements UserCache {
if (invalidations.contains(userId)) return model;
if (managedUsers.containsKey(userId)) return managedUsers.get(userId);
UserAdapter adapter = getUserAdapter(realm, userId, loaded, model);
CachedUser cached = cache.get(userId, CachedUser.class);
if (cached == null) {
cached = new CachedUser(loaded, realm, model);
cache.addRevisioned(cached, startupRevision);
}
UserAdapter adapter = new UserAdapter(cached, this, session, realm);
managedUsers.put(userId, adapter);
return adapter;
} else {
@ -338,7 +316,12 @@ public class UserCacheSession implements UserCache {
if (invalidations.contains(userId)) return model;
if (managedUsers.containsKey(userId)) return managedUsers.get(userId);
UserAdapter adapter = getUserAdapter(realm, userId, loaded, model);
CachedUser cached = cache.get(userId, CachedUser.class);
if (cached == null) {
cached = new CachedUser(loaded, realm, model);
cache.addRevisioned(cached, startupRevision);
}
UserAdapter adapter = new UserAdapter(cached, this, session, realm);
managedUsers.put(userId, adapter);
return adapter;
} else {
@ -686,5 +669,4 @@ public class UserCacheSession implements UserCache {
getDelegate().preRemove(realm, component);
}
}

View file

@ -1,40 +0,0 @@
/*
* Copyright 2016 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.cache.infinispan.entities;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public abstract class AbstractExtendableRevisioned extends AbstractRevisioned {
protected ConcurrentHashMap cachedWith = new ConcurrentHashMap();
public AbstractExtendableRevisioned(Long revision, String id) {
super(revision, id);
}
/**
* Cache things along with this cachable object
*
* @return
*/
public ConcurrentHashMap getCachedWith() {
return cachedWith;
}
}

View file

@ -9,7 +9,6 @@ import java.io.Serializable;
public class AbstractRevisioned implements Revisioned, Serializable {
private String id;
private Long revision;
private final long cacheTimestamp = System.currentTimeMillis();
public AbstractRevisioned(Long revision, String id) {
this.revision = revision;
@ -31,12 +30,4 @@ public class AbstractRevisioned implements Revisioned, Serializable {
this.revision = revision;
}
/**
* When was this cached
*
* @return
*/
public long getCacheTimestamp() {
return cacheTimestamp;
}
}

View file

@ -36,7 +36,7 @@ import java.util.Set;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class CachedUser extends AbstractExtendableRevisioned implements InRealm {
public class CachedUser extends AbstractRevisioned implements InRealm {
private String realm;
private String username;
private Long createdTimestamp;

View file

@ -15,4 +15,4 @@
# limitations under the License.
#
org.keycloak.models.cache.infinispan.InfinispanUserCacheProviderFactory
org.keycloak.models.cache.infinispan.InfinispanCacheUserProviderFactory

View file

@ -101,19 +101,14 @@ public class ClusteredCacheBehaviorTest {
System.out.println("node1 create entry");
node1Cache.put("key", "node1");
System.out.println("node1 create entry");
node1Cache.put("key", "node111");
System.out.println("node2 create entry");
node2Cache.put("key", "node2");
System.out.println("node1 remove entry");
node1Cache.remove("key");
System.out.println("node2 remove entry");
node2Cache.remove("key");
System.out.println("node2 put entry");
node2Cache.put("key", "node2");
System.out.println("node2 evict entry");
@ -123,28 +118,6 @@ public class ClusteredCacheBehaviorTest {
node2Cache.putForExternalRead("key", "common");
System.out.println("node2 remove entry");
node2Cache.remove("key");
System.out.println("node1 remove entry");
node1Cache.remove("key");
// test remove non-existing node 2, existing node 1
System.out.println("Test non existent remove");
System.out.println("node1 create entry");
node1Cache.put("key", "value");
System.out.println("node2 remove non-existent entry");
System.out.println("exists?: " + node2Cache.containsKey("key"));
node2Cache.remove("key");
// test clear
System.out.println("Test clear cache");
System.out.println("add key to node 1, key2 to node2");
node1Cache.putForExternalRead("key", "value");
node2Cache.putForExternalRead("key", "value");
node2Cache.putForExternalRead("key2", "value");
System.out.println("Clear from node1");
node1Cache.clear();
System.out.println("node 2 exists key2?: " + node2Cache.containsKey("key2"));
System.out.println("node 2 exists key?: " + node2Cache.containsKey("key"));
}

View file

@ -17,8 +17,6 @@
package org.keycloak.connections.jpa;
import org.jboss.logging.Logger;
import javax.persistence.EntityManager;
/**
@ -26,7 +24,6 @@ import javax.persistence.EntityManager;
*/
public class DefaultJpaConnectionProvider implements JpaConnectionProvider {
private static final Logger logger = Logger.getLogger(DefaultJpaConnectionProvider.class);
private final EntityManager em;
public DefaultJpaConnectionProvider(EntityManager em) {
@ -40,7 +37,6 @@ public class DefaultJpaConnectionProvider implements JpaConnectionProvider {
@Override
public void close() {
logger.trace("DefaultJpaConnectionProvider close()");
em.close();
}

View file

@ -29,22 +29,12 @@ import java.util.Map;
import javax.naming.InitialContext;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.SynchronizationType;
import javax.sql.DataSource;
import javax.transaction.InvalidTransactionException;
import javax.transaction.Synchronization;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import javax.transaction.UserTransaction;
import org.hibernate.ejb.AvailableSettings;
import org.hibernate.engine.transaction.jta.platform.internal.AbstractJtaPlatform;
import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform;
import org.jboss.logging.Logger;
import org.keycloak.Config;
import org.keycloak.connections.jpa.updater.JpaUpdaterProvider;
import org.keycloak.connections.jpa.updater.liquibase.conn.LiquibaseConnectionProvider;
import org.keycloak.connections.jpa.util.JpaUtils;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
@ -55,7 +45,6 @@ import org.keycloak.provider.ServerInfoAwareProviderFactory;
import org.keycloak.models.dblock.DBLockManager;
import org.keycloak.ServerStartupError;
import org.keycloak.timer.TimerProvider;
import org.keycloak.transaction.JtaTransactionManagerLookup;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@ -72,28 +61,15 @@ public class DefaultJpaConnectionProviderFactory implements JpaConnectionProvide
private Config.Scope config;
private Map<String, String> operationalInfo;
private boolean jtaEnabled;
private JtaTransactionManagerLookup jtaLookup;
private KeycloakSessionFactory factory;
private Map<String,String> operationalInfo;
@Override
public JpaConnectionProvider create(KeycloakSession session) {
logger.trace("Create JpaConnectionProvider");
lazyInit(session);
EntityManager em = null;
if (!jtaEnabled) {
logger.trace("enlisting EntityManager in JpaKeycloakTransaction");
em = emf.createEntityManager();
} else {
em = emf.createEntityManager(SynchronizationType.SYNCHRONIZED);
}
EntityManager em = emf.createEntityManager();
em = PersistenceExceptionConverter.create(em);
if (!jtaEnabled) session.getTransactionManager().enlist(new JpaKeycloakTransaction(em));
session.getTransactionManager().enlist(new JpaKeycloakTransaction(em));
return new DefaultJpaConnectionProvider(em);
}
@ -116,25 +92,13 @@ public class DefaultJpaConnectionProviderFactory implements JpaConnectionProvide
@Override
public void postInit(KeycloakSessionFactory factory) {
this.factory = factory;
checkJtaEnabled(factory);
}
protected void checkJtaEnabled(KeycloakSessionFactory factory) {
jtaLookup = (JtaTransactionManagerLookup) factory.getProviderFactory(JtaTransactionManagerLookup.class);
if (jtaLookup != null) {
if (jtaLookup.getTransactionManager() != null) {
jtaEnabled = true;
}
}
}
private void lazyInit(KeycloakSession session) {
if (emf == null) {
synchronized (this) {
if (emf == null) {
KeycloakModelUtils.suspendJtaTransaction(session.getKeycloakSessionFactory(), () -> {
logger.debug("Initializing JPA connections");
Map<String, Object> properties = new HashMap<String, Object>();
@ -143,7 +107,7 @@ public class DefaultJpaConnectionProviderFactory implements JpaConnectionProvide
String dataSource = config.get("dataSource");
if (dataSource != null) {
if (config.getBoolean("jta", jtaEnabled)) {
if (config.getBoolean("jta", false)) {
properties.put(AvailableSettings.JTA_DATASOURCE, dataSource);
} else {
properties.put(AvailableSettings.NON_JTA_DATASOURCE, dataSource);
@ -175,7 +139,7 @@ public class DefaultJpaConnectionProviderFactory implements JpaConnectionProvide
properties.put("hibernate.format_sql", config.getBoolean("formatSql", true));
Connection connection = getConnection();
try {
try{
prepareOperationalInfo(connection);
String driverDialect = detectDialect(connection);
@ -191,21 +155,7 @@ public class DefaultJpaConnectionProviderFactory implements JpaConnectionProvide
}
logger.trace("Creating EntityManagerFactory");
logger.tracev("***** create EMF jtaEnabled {0} ", jtaEnabled);
if (jtaEnabled) {
properties.put(org.hibernate.cfg.AvailableSettings.JTA_PLATFORM, new AbstractJtaPlatform() {
@Override
protected TransactionManager locateTransactionManager() {
return jtaLookup.getTransactionManager();
}
@Override
protected UserTransaction locateUserTransaction() {
return null;
}
});
}
emf = JpaUtils.createEntityManagerFactory(session, unitName, properties, getClass().getClassLoader(), jtaEnabled);
emf = JpaUtils.createEntityManagerFactory(session, unitName, properties, getClass().getClassLoader());
logger.trace("EntityManagerFactory created");
if (globalStatsInterval != -1) {
@ -221,7 +171,6 @@ public class DefaultJpaConnectionProviderFactory implements JpaConnectionProvide
}
}
}
});
}
}
}
@ -236,7 +185,7 @@ public class DefaultJpaConnectionProviderFactory implements JpaConnectionProvide
try {
operationalInfo = new LinkedHashMap<>();
DatabaseMetaData md = connection.getMetaData();
operationalInfo.put("databaseUrl", md.getURL());
operationalInfo.put("databaseUrl",md.getURL());
operationalInfo.put("databaseUser", md.getUserName());
operationalInfo.put("databaseProduct", md.getDatabaseProductName() + " " + md.getDatabaseProductVersion());
operationalInfo.put("databaseDriver", md.getDriverName() + " " + md.getDriverVersion());
@ -387,7 +336,7 @@ public class DefaultJpaConnectionProviderFactory implements JpaConnectionProvide
}
@Override
public Map<String, String> getOperationalInfo() {
public Map<String,String> getOperationalInfo() {
return operationalInfo;
}

View file

@ -23,13 +23,14 @@ import java.sql.SQLException;
import liquibase.Liquibase;
import liquibase.exception.DatabaseException;
import liquibase.exception.LiquibaseException;
import liquibase.exception.LockException;
import liquibase.lockservice.LockService;
import org.jboss.logging.Logger;
import org.keycloak.connections.jpa.JpaConnectionProvider;
import org.keycloak.connections.jpa.JpaConnectionProviderFactory;
import org.keycloak.connections.jpa.updater.liquibase.conn.LiquibaseConnectionProvider;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.dblock.DBLockProvider;
import org.keycloak.models.utils.KeycloakModelUtils;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@ -56,7 +57,6 @@ public class LiquibaseDBLockProvider implements DBLockProvider {
this.session = session;
}
private void lazyInit() {
if (!initialized) {
LiquibaseConnectionProvider liquibaseProvider = session.getProvider(LiquibaseConnectionProvider.class);
@ -92,8 +92,6 @@ public class LiquibaseDBLockProvider implements DBLockProvider {
@Override
public void waitForLock() {
KeycloakModelUtils.suspendJtaTransaction(session.getKeycloakSessionFactory(), () -> {
lazyInit();
while (maxAttempts > 0) {
@ -113,20 +111,16 @@ public class LiquibaseDBLockProvider implements DBLockProvider {
throw re;
}
}
});
}
@Override
public void releaseLock() {
KeycloakModelUtils.suspendJtaTransaction(session.getKeycloakSessionFactory(), () -> {
lazyInit();
lockService.releaseLock();
lockService.reset();
factory.setHasLock(false);
});
}
@Override
@ -142,7 +136,6 @@ public class LiquibaseDBLockProvider implements DBLockProvider {
@Override
public void destroyLockInfo() {
KeycloakModelUtils.suspendJtaTransaction(session.getKeycloakSessionFactory(), () -> {
lazyInit();
try {
@ -153,14 +146,11 @@ public class LiquibaseDBLockProvider implements DBLockProvider {
logger.error("Failed to destroy lock table");
safeRollbackConnection();
}
});
}
@Override
public void close() {
KeycloakModelUtils.suspendJtaTransaction(session.getKeycloakSessionFactory(), () -> {
safeCloseConnection();
});
}
private void safeRollbackConnection() {

View file

@ -21,8 +21,6 @@ import org.hibernate.boot.registry.classloading.internal.ClassLoaderServiceImpl;
import org.hibernate.jpa.boot.internal.ParsedPersistenceXmlDescriptor;
import org.hibernate.jpa.boot.internal.PersistenceXmlParser;
import org.hibernate.jpa.boot.spi.Bootstrap;
import org.jboss.logging.Logger;
import org.keycloak.connections.jpa.DefaultJpaConnectionProviderFactory;
import org.keycloak.connections.jpa.entityprovider.JpaEntityProvider;
import org.keycloak.connections.jpa.entityprovider.ProxyClassLoader;
import org.keycloak.models.KeycloakSession;
@ -48,9 +46,8 @@ public class JpaUtils {
return (schema==null) ? tableName : schema + "." + tableName;
}
public static EntityManagerFactory createEntityManagerFactory(KeycloakSession session, String unitName, Map<String, Object> properties, ClassLoader classLoader, boolean jta) {
PersistenceUnitTransactionType txType = jta ? PersistenceUnitTransactionType.JTA : PersistenceUnitTransactionType.RESOURCE_LOCAL;
PersistenceXmlParser parser = new PersistenceXmlParser(new ClassLoaderServiceImpl(classLoader), txType);
public static EntityManagerFactory createEntityManagerFactory(KeycloakSession session, String unitName, Map<String, Object> properties, ClassLoader classLoader) {
PersistenceXmlParser parser = new PersistenceXmlParser(new ClassLoaderServiceImpl(classLoader), PersistenceUnitTransactionType.RESOURCE_LOCAL);
List<ParsedPersistenceXmlDescriptor> persistenceUnits = parser.doResolve(properties);
for (ParsedPersistenceXmlDescriptor persistenceUnit : persistenceUnits) {
if (persistenceUnit.getName().equals(unitName)) {
@ -61,7 +58,6 @@ public class JpaUtils {
}
// Now build the entity manager factory, supplying a proxy classloader, so Hibernate will be able
// to find and load the extra provided entities. Set the provided classloader as parent classloader.
persistenceUnit.setTransactionType(txType);
return Bootstrap.getEntityManagerFactoryBuilder(persistenceUnit, properties,
new ProxyClassLoader(providedEntities, classLoader)).build();
}

View file

@ -1,220 +0,0 @@
/*
* Copyright 2016 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.jpa;
import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.credential.CredentialModel;
import org.keycloak.credential.UserCredentialStore;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.jpa.entities.CredentialAttributeEntity;
import org.keycloak.models.jpa.entities.CredentialEntity;
import org.keycloak.models.jpa.entities.UserEntity;
import org.keycloak.models.utils.KeycloakModelUtils;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class JpaUserCredentialStore implements UserCredentialStore {
private final KeycloakSession session;
protected final EntityManager em;
public JpaUserCredentialStore(KeycloakSession session, EntityManager em) {
this.session = session;
this.em = em;
}
@Override
public void updateCredential(RealmModel realm, UserModel user, CredentialModel cred) {
CredentialEntity entity = em.find(CredentialEntity.class, cred.getId());
if (entity == null) return;
entity.setAlgorithm(cred.getAlgorithm());
entity.setCounter(cred.getCounter());
entity.setCreatedDate(cred.getCreatedDate());
entity.setDevice(cred.getDevice());
entity.setDigits(cred.getDigits());
entity.setHashIterations(cred.getHashIterations());
entity.setPeriod(cred.getPeriod());
entity.setSalt(cred.getSalt());
entity.setType(cred.getType());
entity.setValue(cred.getValue());
if (entity.getCredentialAttributes().isEmpty() && (cred.getConfig() == null || cred.getConfig().isEmpty())) {
} else {
MultivaluedHashMap<String, String> attrs = cred.getConfig();
MultivaluedHashMap<String, String> config = cred.getConfig();
if (config == null) config = new MultivaluedHashMap<>();
Iterator<CredentialAttributeEntity> it = entity.getCredentialAttributes().iterator();
while (it.hasNext()) {
CredentialAttributeEntity attr = it.next();
List<String> values = config.getList(attr.getName());
if (values == null || !values.contains(attr.getValue())) {
em.remove(attr);
it.remove();
} else {
attrs.add(attr.getName(), attr.getValue());
}
}
for (String key : config.keySet()) {
List<String> values = config.getList(key);
List<String> attrValues = attrs.getList(key);
for (String val : values) {
if (attrValues == null || !attrValues.contains(val)) {
CredentialAttributeEntity attr = new CredentialAttributeEntity();
attr.setId(KeycloakModelUtils.generateId());
attr.setValue(val);
attr.setName(key);
attr.setCredential(entity);
em.persist(attr);
entity.getCredentialAttributes().add(attr);
}
}
}
}
}
@Override
public CredentialModel createCredential(RealmModel realm, UserModel user, CredentialModel cred) {
CredentialEntity entity = new CredentialEntity();
String id = cred.getId() == null ? KeycloakModelUtils.generateId() : cred.getId();
entity.setId(id);
entity.setAlgorithm(cred.getAlgorithm());
entity.setCounter(cred.getCounter());
entity.setCreatedDate(cred.getCreatedDate());
entity.setDevice(cred.getDevice());
entity.setDigits(cred.getDigits());
entity.setHashIterations(cred.getHashIterations());
entity.setPeriod(cred.getPeriod());
entity.setSalt(cred.getSalt());
entity.setType(cred.getType());
entity.setValue(cred.getValue());
UserEntity userRef = em.getReference(UserEntity.class, user.getId());
entity.setUser(userRef);
em.persist(entity);
MultivaluedHashMap<String, String> config = cred.getConfig();
if (config != null || !config.isEmpty()) {
for (String key : config.keySet()) {
List<String> values = config.getList(key);
for (String val : values) {
CredentialAttributeEntity attr = new CredentialAttributeEntity();
attr.setId(KeycloakModelUtils.generateId());
attr.setValue(val);
attr.setName(key);
attr.setCredential(entity);
em.persist(attr);
entity.getCredentialAttributes().add(attr);
}
}
}
return toModel(entity);
}
@Override
public boolean removeStoredCredential(RealmModel realm, UserModel user, String id) {
CredentialEntity entity = em.find(CredentialEntity.class, id);
if (entity == null) return false;
em.remove(entity);
return true;
}
@Override
public CredentialModel getStoredCredentialById(RealmModel realm, UserModel user, String id) {
CredentialEntity entity = em.find(CredentialEntity.class, id);
if (entity == null) return null;
CredentialModel model = toModel(entity);
return model;
}
protected CredentialModel toModel(CredentialEntity entity) {
CredentialModel model = new CredentialModel();
model.setId(entity.getId());
model.setType(entity.getType());
model.setValue(entity.getValue());
model.setAlgorithm(entity.getAlgorithm());
model.setSalt(entity.getSalt());
model.setPeriod(entity.getPeriod());
model.setCounter(entity.getCounter());
model.setCreatedDate(entity.getCreatedDate());
model.setDevice(entity.getDevice());
model.setDigits(entity.getDigits());
MultivaluedHashMap<String, String> config = new MultivaluedHashMap<>();
model.setConfig(config);
for (CredentialAttributeEntity attr : entity.getCredentialAttributes()) {
config.add(attr.getName(), attr.getValue());
}
return model;
}
@Override
public List<CredentialModel> getStoredCredentials(RealmModel realm, UserModel user) {
UserEntity userEntity = em.getReference(UserEntity.class, user.getId());
TypedQuery<CredentialEntity> query = em.createNamedQuery("credentialByUser", CredentialEntity.class)
.setParameter("user", userEntity);
List<CredentialEntity> results = query.getResultList();
List<CredentialModel> rtn = new LinkedList<>();
for (CredentialEntity entity : results) {
rtn.add(toModel(entity));
}
return rtn;
}
@Override
public List<CredentialModel> getStoredCredentialsByType(RealmModel realm, UserModel user, String type) {
UserEntity userEntity = em.getReference(UserEntity.class, user.getId());
TypedQuery<CredentialEntity> query = em.createNamedQuery("credentialByUserAndType", CredentialEntity.class)
.setParameter("type", type)
.setParameter("user", userEntity);
List<CredentialEntity> results = query.getResultList();
List<CredentialModel> rtn = new LinkedList<>();
for (CredentialEntity entity : results) {
rtn.add(toModel(entity));
}
return rtn;
}
@Override
public CredentialModel getStoredCredentialByNameAndType(RealmModel realm, UserModel user, String name, String type) {
UserEntity userEntity = em.getReference(UserEntity.class, user.getId());
TypedQuery<CredentialEntity> query = em.createNamedQuery("credentialByNameAndType", CredentialEntity.class)
.setParameter("type", type)
.setParameter("device", name)
.setParameter("user", userEntity);
List<CredentialEntity> results = query.getResultList();
if (results.isEmpty()) return null;
return toModel(results.get(0));
}
@Override
public void close() {
}
}

View file

@ -1,59 +0,0 @@
/*
* Copyright 2016 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.jpa;
import org.keycloak.Config;
import org.keycloak.connections.jpa.JpaConnectionProvider;
import org.keycloak.credential.UserCredentialStore;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.ProviderFactory;
import javax.persistence.EntityManager;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class JpaUserCredentialStoreFactory implements ProviderFactory<UserCredentialStore> {
@Override
public void init(Config.Scope config) {
}
@Override
public void postInit(KeycloakSessionFactory factory) {
}
@Override
public String getId() {
return "jpa";
}
@Override
public UserCredentialStore create(KeycloakSession session) {
EntityManager em = session.getProvider(JpaConnectionProvider.class).getEntityManager();
return new JpaUserCredentialStore(session, em);
}
@Override
public void close() {
}
}

View file

@ -17,11 +17,7 @@
package org.keycloak.models.jpa;
import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.component.ComponentModel;
import org.keycloak.credential.CredentialInput;
import org.keycloak.credential.CredentialModel;
import org.keycloak.credential.UserCredentialStore;
import org.keycloak.models.ClientModel;
import org.keycloak.models.CredentialValidationOutput;
import org.keycloak.models.FederatedIdentityModel;
@ -38,8 +34,6 @@ import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserProvider;
import org.keycloak.models.jpa.entities.CredentialAttributeEntity;
import org.keycloak.models.jpa.entities.CredentialEntity;
import org.keycloak.models.jpa.entities.FederatedIdentityEntity;
import org.keycloak.models.jpa.entities.UserAttributeEntity;
import org.keycloak.models.jpa.entities.UserConsentEntity;
@ -54,9 +48,7 @@ import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@ -66,7 +58,7 @@ import java.util.Set;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class JpaUserProvider implements UserProvider, UserCredentialStore {
public class JpaUserProvider implements UserProvider {
private static final String EMAIL = "email";
private static final String USERNAME = "username";
@ -375,8 +367,6 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore {
.setParameter("realmId", realm.getId()).executeUpdate();
num = em.createNamedQuery("deleteFederatedIdentityByRealm")
.setParameter("realmId", realm.getId()).executeUpdate();
num = em.createNamedQuery("deleteCredentialAttributeByRealm")
.setParameter("realmId", realm.getId()).executeUpdate();
num = em.createNamedQuery("deleteCredentialsByRealm")
.setParameter("realmId", realm.getId()).executeUpdate();
num = em.createNamedQuery("deleteUserAttributesByRealm")
@ -401,10 +391,6 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore {
.setParameter("realmId", realm.getId())
.setParameter("link", link.getId())
.executeUpdate();
num = em.createNamedQuery("deleteCredentialAttributeByRealmAndLink")
.setParameter("realmId", realm.getId())
.setParameter("link", link.getId())
.executeUpdate();
num = em.createNamedQuery("deleteCredentialsByRealmAndLink")
.setParameter("realmId", realm.getId())
.setParameter("link", link.getId())
@ -730,174 +716,4 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore {
public void preRemove(RealmModel realm, ComponentModel component) {
}
@Override
public void updateCredential(RealmModel realm, UserModel user, CredentialModel cred) {
CredentialEntity entity = em.find(CredentialEntity.class, cred.getId());
if (entity == null) return;
entity.setAlgorithm(cred.getAlgorithm());
entity.setCounter(cred.getCounter());
entity.setCreatedDate(cred.getCreatedDate());
entity.setDevice(cred.getDevice());
entity.setDigits(cred.getDigits());
entity.setHashIterations(cred.getHashIterations());
entity.setPeriod(cred.getPeriod());
entity.setSalt(cred.getSalt());
entity.setType(cred.getType());
entity.setValue(cred.getValue());
if (entity.getCredentialAttributes().isEmpty() && (cred.getConfig() == null || cred.getConfig().isEmpty())) {
} else {
MultivaluedHashMap<String, String> attrs = cred.getConfig();
MultivaluedHashMap<String, String> config = cred.getConfig();
if (config == null) config = new MultivaluedHashMap<>();
Iterator<CredentialAttributeEntity> it = entity.getCredentialAttributes().iterator();
while (it.hasNext()) {
CredentialAttributeEntity attr = it.next();
List<String> values = config.getList(attr.getName());
if (values == null || !values.contains(attr.getValue())) {
em.remove(attr);
it.remove();
} else {
attrs.add(attr.getName(), attr.getValue());
}
}
for (String key : config.keySet()) {
List<String> values = config.getList(key);
List<String> attrValues = attrs.getList(key);
for (String val : values) {
if (attrValues == null || !attrValues.contains(val)) {
CredentialAttributeEntity attr = new CredentialAttributeEntity();
attr.setId(KeycloakModelUtils.generateId());
attr.setValue(val);
attr.setName(key);
attr.setCredential(entity);
em.persist(attr);
entity.getCredentialAttributes().add(attr);
}
}
}
}
}
@Override
public CredentialModel createCredential(RealmModel realm, UserModel user, CredentialModel cred) {
CredentialEntity entity = new CredentialEntity();
String id = cred.getId() == null ? KeycloakModelUtils.generateId() : cred.getId();
entity.setId(id);
entity.setAlgorithm(cred.getAlgorithm());
entity.setCounter(cred.getCounter());
entity.setCreatedDate(cred.getCreatedDate());
entity.setDevice(cred.getDevice());
entity.setDigits(cred.getDigits());
entity.setHashIterations(cred.getHashIterations());
entity.setPeriod(cred.getPeriod());
entity.setSalt(cred.getSalt());
entity.setType(cred.getType());
entity.setValue(cred.getValue());
UserEntity userRef = em.getReference(UserEntity.class, user.getId());
entity.setUser(userRef);
em.persist(entity);
MultivaluedHashMap<String, String> config = cred.getConfig();
if (config != null || !config.isEmpty()) {
for (String key : config.keySet()) {
List<String> values = config.getList(key);
for (String val : values) {
CredentialAttributeEntity attr = new CredentialAttributeEntity();
attr.setId(KeycloakModelUtils.generateId());
attr.setValue(val);
attr.setName(key);
attr.setCredential(entity);
em.persist(attr);
entity.getCredentialAttributes().add(attr);
}
}
}
return toModel(entity);
}
@Override
public boolean removeStoredCredential(RealmModel realm, UserModel user, String id) {
CredentialEntity entity = em.find(CredentialEntity.class, id);
if (entity == null) return false;
em.remove(entity);
return true;
}
@Override
public CredentialModel getStoredCredentialById(RealmModel realm, UserModel user, String id) {
CredentialEntity entity = em.find(CredentialEntity.class, id);
if (entity == null) return null;
CredentialModel model = toModel(entity);
return model;
}
protected CredentialModel toModel(CredentialEntity entity) {
CredentialModel model = new CredentialModel();
model.setId(entity.getId());
model.setType(entity.getType());
model.setValue(entity.getValue());
model.setAlgorithm(entity.getAlgorithm());
model.setSalt(entity.getSalt());
model.setPeriod(entity.getPeriod());
model.setCounter(entity.getCounter());
model.setCreatedDate(entity.getCreatedDate());
model.setDevice(entity.getDevice());
model.setDigits(entity.getDigits());
MultivaluedHashMap<String, String> config = new MultivaluedHashMap<>();
model.setConfig(config);
for (CredentialAttributeEntity attr : entity.getCredentialAttributes()) {
config.add(attr.getName(), attr.getValue());
}
return model;
}
@Override
public List<CredentialModel> getStoredCredentials(RealmModel realm, UserModel user) {
UserEntity userEntity = em.getReference(UserEntity.class, user.getId());
TypedQuery<CredentialEntity> query = em.createNamedQuery("credentialByUser", CredentialEntity.class)
.setParameter("user", userEntity);
List<CredentialEntity> results = query.getResultList();
List<CredentialModel> rtn = new LinkedList<>();
for (CredentialEntity entity : results) {
rtn.add(toModel(entity));
}
return rtn;
}
@Override
public List<CredentialModel> getStoredCredentialsByType(RealmModel realm, UserModel user, String type) {
UserEntity userEntity = em.getReference(UserEntity.class, user.getId());
TypedQuery<CredentialEntity> query = em.createNamedQuery("credentialByUserAndType", CredentialEntity.class)
.setParameter("type", type)
.setParameter("user", userEntity);
List<CredentialEntity> results = query.getResultList();
List<CredentialModel> rtn = new LinkedList<>();
for (CredentialEntity entity : results) {
rtn.add(toModel(entity));
}
return rtn;
}
@Override
public CredentialModel getStoredCredentialByNameAndType(RealmModel realm, UserModel user, String name, String type) {
UserEntity userEntity = em.getReference(UserEntity.class, user.getId());
TypedQuery<CredentialEntity> query = em.createNamedQuery("credentialByNameAndType", CredentialEntity.class)
.setParameter("type", type)
.setParameter("device", name)
.setParameter("user", userEntity);
List<CredentialEntity> results = query.getResultList();
if (results.isEmpty()) return null;
return toModel(results.get(0));
}
}

View file

@ -1,111 +0,0 @@
/*
* Copyright 2016 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.jpa.entities;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@NamedQueries({
@NamedQuery(name="getCredentialAttribute", query="select attr from CredentialAttributeEntity attr where attr.credential = :credential"),
@NamedQuery(name="deleteCredentialAttributeByCredential", query="delete from CredentialAttributeEntity attr where attr.credential = :credential"),
@NamedQuery(name="deleteCredentialAttributeByRealm", query="delete from CredentialAttributeEntity attr where attr.credential IN (select cred from CredentialEntity cred where cred.user IN (select u from UserEntity u where u.realmId=:realmId))"),
@NamedQuery(name="deleteCredentialAttributeByRealmAndLink", query="delete from CredentialAttributeEntity attr where attr.credential IN (select cred from CredentialEntity cred where cred.user IN (select u from UserEntity u where u.realmId=:realmId and u.federationLink=:link))"),
@NamedQuery(name="deleteCredentialAttributeByUser", query="delete from CredentialAttributeEntity attr where attr.credential IN (select cred from CredentialEntity cred where cred.user = :user)"),
})
@Table(name="CREDENTIAL_ATTRIBUTE")
@Entity
public class CredentialAttributeEntity {
@Id
@Column(name="ID", length = 36)
@Access(AccessType.PROPERTY) // we do this because relationships often fetch id, but not entity. This avoids an extra SQL
protected String id;
@ManyToOne(fetch= FetchType.LAZY)
@JoinColumn(name = "CREDENTIAL_ID")
protected CredentialEntity credential;
@Column(name = "NAME")
protected String name;
@Column(name = "VALUE")
protected String value;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public CredentialEntity getCredential() {
return credential;
}
public void setCredential(CredentialEntity credential) {
this.credential = credential;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null) return false;
if (!(o instanceof CredentialAttributeEntity)) return false;
CredentialAttributeEntity that = (CredentialAttributeEntity) o;
if (!id.equals(that.getId())) return false;
return true;
}
@Override
public int hashCode() {
return id.hashCode();
}
}

View file

@ -19,7 +19,6 @@ package org.keycloak.models.jpa.entities;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
@ -28,19 +27,14 @@ import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import java.util.ArrayList;
import java.util.Collection;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@NamedQueries({
@NamedQuery(name="credentialByUser", query="select cred from CredentialEntity cred where cred.user = :user"),
@NamedQuery(name="credentialByUserAndType", query="select cred from CredentialEntity cred where cred.user = :user and cred.type = :type"),
@NamedQuery(name="credentialByNameAndType", query="select cred from CredentialEntity cred where cred.user = :user and cred.type = :type and cred.device = :device"),
@NamedQuery(name="deleteCredentialsByRealm", query="delete from CredentialEntity cred where cred.user IN (select u from UserEntity u where u.realmId=:realmId)"),
@NamedQuery(name="deleteCredentialsByRealmAndLink", query="delete from CredentialEntity cred where cred.user IN (select u from UserEntity u where u.realmId=:realmId and u.federationLink=:link)")
@ -80,8 +74,6 @@ public class CredentialEntity {
@Column(name="PERIOD")
protected int period;
@OneToMany(cascade = CascadeType.REMOVE, fetch = FetchType.EAGER, orphanRemoval = true, mappedBy="credential")
protected Collection<CredentialAttributeEntity> credentialAttributes = new ArrayList<>();
public String getId() {
return id;
@ -179,14 +171,6 @@ public class CredentialEntity {
this.period = period;
}
public Collection<CredentialAttributeEntity> getCredentialAttributes() {
return credentialAttributes;
}
public void setCredentialAttributes(Collection<CredentialAttributeEntity> credentialAttributes) {
this.credentialAttributes = credentialAttributes;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;

View file

@ -1,218 +0,0 @@
/*
* Copyright 2016 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.storage.jpa;
import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.credential.CredentialModel;
import org.keycloak.credential.UserCredentialStore;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.storage.StorageId;
import org.keycloak.storage.jpa.entity.FederatedUserCredentialAttributeEntity;
import org.keycloak.storage.jpa.entity.FederatedUserCredentialEntity;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class JpaFederatedUserCredentialStore implements UserCredentialStore {
private final KeycloakSession session;
protected final EntityManager em;
public JpaFederatedUserCredentialStore(KeycloakSession session, EntityManager em) {
this.session = session;
this.em = em;
}
@Override
public void updateCredential(RealmModel realm, UserModel user, CredentialModel cred) {
FederatedUserCredentialEntity entity = em.find(FederatedUserCredentialEntity.class, cred.getId());
if (entity == null) return;
entity.setAlgorithm(cred.getAlgorithm());
entity.setCounter(cred.getCounter());
entity.setCreatedDate(cred.getCreatedDate());
entity.setDevice(cred.getDevice());
entity.setDigits(cred.getDigits());
entity.setHashIterations(cred.getHashIterations());
entity.setPeriod(cred.getPeriod());
entity.setSalt(cred.getSalt());
entity.setType(cred.getType());
entity.setValue(cred.getValue());
if (entity.getCredentialAttributes().isEmpty() && (cred.getConfig() == null || cred.getConfig().isEmpty())) {
} else {
MultivaluedHashMap<String, String> attrs = cred.getConfig();
MultivaluedHashMap<String, String> config = cred.getConfig();
if (config == null) config = new MultivaluedHashMap<>();
Iterator<FederatedUserCredentialAttributeEntity> it = entity.getCredentialAttributes().iterator();
while (it.hasNext()) {
FederatedUserCredentialAttributeEntity attr = it.next();
List<String> values = config.getList(attr.getName());
if (values == null || !values.contains(attr.getValue())) {
em.remove(attr);
it.remove();
} else {
attrs.add(attr.getName(), attr.getValue());
}
}
for (String key : config.keySet()) {
List<String> values = config.getList(key);
List<String> attrValues = attrs.getList(key);
for (String val : values) {
if (attrValues == null || !attrValues.contains(val)) {
FederatedUserCredentialAttributeEntity attr = new FederatedUserCredentialAttributeEntity();
attr.setId(KeycloakModelUtils.generateId());
attr.setValue(val);
attr.setName(key);
attr.setCredential(entity);
em.persist(attr);
entity.getCredentialAttributes().add(attr);
}
}
}
}
}
@Override
public CredentialModel createCredential(RealmModel realm, UserModel user, CredentialModel cred) {
FederatedUserCredentialEntity entity = new FederatedUserCredentialEntity();
String id = cred.getId() == null ? KeycloakModelUtils.generateId() : cred.getId();
entity.setId(id);
entity.setAlgorithm(cred.getAlgorithm());
entity.setCounter(cred.getCounter());
entity.setCreatedDate(cred.getCreatedDate());
entity.setDevice(cred.getDevice());
entity.setDigits(cred.getDigits());
entity.setHashIterations(cred.getHashIterations());
entity.setPeriod(cred.getPeriod());
entity.setSalt(cred.getSalt());
entity.setType(cred.getType());
entity.setValue(cred.getValue());
entity.setUserId(user.getId());
entity.setRealmId(realm.getId());
entity.setStorageProviderId(StorageId.resolveProviderId(user));
em.persist(entity);
MultivaluedHashMap<String, String> config = cred.getConfig();
if (config != null || !config.isEmpty()) {
for (String key : config.keySet()) {
List<String> values = config.getList(key);
for (String val : values) {
FederatedUserCredentialAttributeEntity attr = new FederatedUserCredentialAttributeEntity();
attr.setId(KeycloakModelUtils.generateId());
attr.setValue(val);
attr.setName(key);
attr.setCredential(entity);
em.persist(attr);
entity.getCredentialAttributes().add(attr);
}
}
}
return toModel(entity);
}
@Override
public boolean removeStoredCredential(RealmModel realm, UserModel user, String id) {
FederatedUserCredentialEntity entity = em.find(FederatedUserCredentialEntity.class, id);
if (entity == null) return false;
em.remove(entity);
return true;
}
@Override
public CredentialModel getStoredCredentialById(RealmModel realm, UserModel user, String id) {
FederatedUserCredentialEntity entity = em.find(FederatedUserCredentialEntity.class, id);
if (entity == null) return null;
CredentialModel model = toModel(entity);
return model;
}
protected CredentialModel toModel(FederatedUserCredentialEntity entity) {
CredentialModel model = new CredentialModel();
model.setId(entity.getId());
model.setType(entity.getType());
model.setValue(entity.getValue());
model.setAlgorithm(entity.getAlgorithm());
model.setSalt(entity.getSalt());
model.setPeriod(entity.getPeriod());
model.setCounter(entity.getCounter());
model.setCreatedDate(entity.getCreatedDate());
model.setDevice(entity.getDevice());
model.setDigits(entity.getDigits());
MultivaluedHashMap<String, String> config = new MultivaluedHashMap<>();
model.setConfig(config);
for (FederatedUserCredentialAttributeEntity attr : entity.getCredentialAttributes()) {
config.add(attr.getName(), attr.getValue());
}
return model;
}
@Override
public List<CredentialModel> getStoredCredentials(RealmModel realm, UserModel user) {
TypedQuery<FederatedUserCredentialEntity> query = em.createNamedQuery("federatedUserCredentialByUser", FederatedUserCredentialEntity.class)
.setParameter("userId", user.getId());
List<FederatedUserCredentialEntity> results = query.getResultList();
List<CredentialModel> rtn = new LinkedList<>();
for (FederatedUserCredentialEntity entity : results) {
rtn.add(toModel(entity));
}
return rtn;
}
@Override
public List<CredentialModel> getStoredCredentialsByType(RealmModel realm, UserModel user, String type) {
TypedQuery<FederatedUserCredentialEntity> query = em.createNamedQuery("federatedUserCredentialByUserAndType", FederatedUserCredentialEntity.class)
.setParameter("type", type)
.setParameter("userId", user.getId());
List<FederatedUserCredentialEntity> results = query.getResultList();
List<CredentialModel> rtn = new LinkedList<>();
for (FederatedUserCredentialEntity entity : results) {
rtn.add(toModel(entity));
}
return rtn;
}
@Override
public CredentialModel getStoredCredentialByNameAndType(RealmModel realm, UserModel user, String name, String type) {
TypedQuery<FederatedUserCredentialEntity> query = em.createNamedQuery("federatedUserCredentialByNameAndType", FederatedUserCredentialEntity.class)
.setParameter("type", type)
.setParameter("device", name)
.setParameter("userId", user.getId());
List<FederatedUserCredentialEntity> results = query.getResultList();
if (results.isEmpty()) return null;
return toModel(results.get(0));
}
@Override
public void close() {
}
}

View file

@ -18,8 +18,6 @@ package org.keycloak.storage.jpa;
import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.component.ComponentModel;
import org.keycloak.credential.CredentialModel;
import org.keycloak.credential.UserCredentialStore;
import org.keycloak.models.ClientModel;
import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.GroupModel;
@ -51,7 +49,6 @@ import org.keycloak.storage.jpa.entity.FederatedUserAttributeEntity;
import org.keycloak.storage.jpa.entity.FederatedUserConsentEntity;
import org.keycloak.storage.jpa.entity.FederatedUserConsentProtocolMapperEntity;
import org.keycloak.storage.jpa.entity.FederatedUserConsentRoleEntity;
import org.keycloak.storage.jpa.entity.FederatedUserCredentialAttributeEntity;
import org.keycloak.storage.jpa.entity.FederatedUserCredentialEntity;
import org.keycloak.storage.jpa.entity.FederatedUserGroupMembershipEntity;
import org.keycloak.storage.jpa.entity.FederatedUserRequiredActionEntity;
@ -62,7 +59,6 @@ import javax.persistence.TypedQuery;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
@ -79,8 +75,7 @@ public class JpaUserFederatedStorageProvider implements
UserCredentialsFederatedStorage,
UserGroupMembershipFederatedStorage,
UserRequiredActionsFederatedStorage,
UserRoleMappingsFederatedStorage,
UserCredentialStore {
UserRoleMappingsFederatedStorage {
private final KeycloakSession session;
protected EntityManager em;
@ -611,200 +606,6 @@ public class JpaUserFederatedStorageProvider implements
em.flush();
}
@Override
public boolean removeCredential(RealmModel realm, String id) {
return false;
}
@Override
public CredentialModel getCredentialById(String id) {
return null;
}
@Override
public List<CredentialModel> getCredentials(RealmModel realm) {
return null;
}
@Override
public List<CredentialModel> getUserCredentials(RealmModel realm, UserModel user) {
return null;
}
@Override
public List<CredentialModel> getCredentialsByType(RealmModel realm, UserModel user, String type) {
return null;
}
@Override
public CredentialModel getCredentialByNameAndType(RealmModel realm, UserModel user, String name, String type) {
return null;
}
@Override
public void updateCredential(RealmModel realm, UserModel user, CredentialModel cred) {
FederatedUserCredentialEntity entity = em.find(FederatedUserCredentialEntity.class, cred.getId());
if (entity == null) return;
entity.setAlgorithm(cred.getAlgorithm());
entity.setCounter(cred.getCounter());
entity.setCreatedDate(cred.getCreatedDate());
entity.setDevice(cred.getDevice());
entity.setDigits(cred.getDigits());
entity.setHashIterations(cred.getHashIterations());
entity.setPeriod(cred.getPeriod());
entity.setSalt(cred.getSalt());
entity.setType(cred.getType());
entity.setValue(cred.getValue());
if (entity.getCredentialAttributes().isEmpty() && (cred.getConfig() == null || cred.getConfig().isEmpty())) {
} else {
MultivaluedHashMap<String, String> attrs = cred.getConfig();
MultivaluedHashMap<String, String> config = cred.getConfig();
if (config == null) config = new MultivaluedHashMap<>();
Iterator<FederatedUserCredentialAttributeEntity> it = entity.getCredentialAttributes().iterator();
while (it.hasNext()) {
FederatedUserCredentialAttributeEntity attr = it.next();
List<String> values = config.getList(attr.getName());
if (values == null || !values.contains(attr.getValue())) {
em.remove(attr);
it.remove();
} else {
attrs.add(attr.getName(), attr.getValue());
}
}
for (String key : config.keySet()) {
List<String> values = config.getList(key);
List<String> attrValues = attrs.getList(key);
for (String val : values) {
if (attrValues == null || !attrValues.contains(val)) {
FederatedUserCredentialAttributeEntity attr = new FederatedUserCredentialAttributeEntity();
attr.setId(KeycloakModelUtils.generateId());
attr.setValue(val);
attr.setName(key);
attr.setCredential(entity);
em.persist(attr);
entity.getCredentialAttributes().add(attr);
}
}
}
}
}
@Override
public CredentialModel createCredential(RealmModel realm, UserModel user, CredentialModel cred) {
FederatedUserCredentialEntity entity = new FederatedUserCredentialEntity();
String id = cred.getId() == null ? KeycloakModelUtils.generateId() : cred.getId();
entity.setId(id);
entity.setAlgorithm(cred.getAlgorithm());
entity.setCounter(cred.getCounter());
entity.setCreatedDate(cred.getCreatedDate());
entity.setDevice(cred.getDevice());
entity.setDigits(cred.getDigits());
entity.setHashIterations(cred.getHashIterations());
entity.setPeriod(cred.getPeriod());
entity.setSalt(cred.getSalt());
entity.setType(cred.getType());
entity.setValue(cred.getValue());
entity.setUserId(user.getId());
entity.setRealmId(realm.getId());
entity.setStorageProviderId(StorageId.resolveProviderId(user));
em.persist(entity);
MultivaluedHashMap<String, String> config = cred.getConfig();
if (config != null || !config.isEmpty()) {
for (String key : config.keySet()) {
List<String> values = config.getList(key);
for (String val : values) {
FederatedUserCredentialAttributeEntity attr = new FederatedUserCredentialAttributeEntity();
attr.setId(KeycloakModelUtils.generateId());
attr.setValue(val);
attr.setName(key);
attr.setCredential(entity);
em.persist(attr);
entity.getCredentialAttributes().add(attr);
}
}
}
return toModel(entity);
}
@Override
public boolean removeStoredCredential(RealmModel realm, UserModel user, String id) {
FederatedUserCredentialEntity entity = em.find(FederatedUserCredentialEntity.class, id);
if (entity == null) return false;
em.remove(entity);
return true;
}
@Override
public CredentialModel getStoredCredentialById(RealmModel realm, UserModel user, String id) {
FederatedUserCredentialEntity entity = em.find(FederatedUserCredentialEntity.class, id);
if (entity == null) return null;
CredentialModel model = toModel(entity);
return model;
}
protected CredentialModel toModel(FederatedUserCredentialEntity entity) {
CredentialModel model = new CredentialModel();
model.setId(entity.getId());
model.setType(entity.getType());
model.setValue(entity.getValue());
model.setAlgorithm(entity.getAlgorithm());
model.setSalt(entity.getSalt());
model.setPeriod(entity.getPeriod());
model.setCounter(entity.getCounter());
model.setCreatedDate(entity.getCreatedDate());
model.setDevice(entity.getDevice());
model.setDigits(entity.getDigits());
MultivaluedHashMap<String, String> config = new MultivaluedHashMap<>();
model.setConfig(config);
for (FederatedUserCredentialAttributeEntity attr : entity.getCredentialAttributes()) {
config.add(attr.getName(), attr.getValue());
}
return model;
}
@Override
public List<CredentialModel> getStoredCredentials(RealmModel realm, UserModel user) {
TypedQuery<FederatedUserCredentialEntity> query = em.createNamedQuery("federatedUserCredentialByUser", FederatedUserCredentialEntity.class)
.setParameter("userId", user.getId());
List<FederatedUserCredentialEntity> results = query.getResultList();
List<CredentialModel> rtn = new LinkedList<>();
for (FederatedUserCredentialEntity entity : results) {
rtn.add(toModel(entity));
}
return rtn;
}
@Override
public List<CredentialModel> getStoredCredentialsByType(RealmModel realm, UserModel user, String type) {
TypedQuery<FederatedUserCredentialEntity> query = em.createNamedQuery("federatedUserCredentialByUserAndType", FederatedUserCredentialEntity.class)
.setParameter("type", type)
.setParameter("userId", user.getId());
List<FederatedUserCredentialEntity> results = query.getResultList();
List<CredentialModel> rtn = new LinkedList<>();
for (FederatedUserCredentialEntity entity : results) {
rtn.add(toModel(entity));
}
return rtn;
}
@Override
public CredentialModel getStoredCredentialByNameAndType(RealmModel realm, UserModel user, String name, String type) {
TypedQuery<FederatedUserCredentialEntity> query = em.createNamedQuery("federatedUserCredentialByNameAndType", FederatedUserCredentialEntity.class)
.setParameter("type", type)
.setParameter("device", name)
.setParameter("userId", user.getId());
List<FederatedUserCredentialEntity> results = query.getResultList();
if (results.isEmpty()) return null;
return toModel(results.get(0));
}
@Override
public void preRemove(RealmModel realm) {
int num = em.createNamedQuery("deleteFederatedUserConsentRolesByRealm")
@ -819,8 +620,6 @@ public class JpaUserFederatedStorageProvider implements
.setParameter("realmId", realm.getId()).executeUpdate();
num = em.createNamedQuery("deleteBrokerLinkByRealm")
.setParameter("realmId", realm.getId()).executeUpdate();
num = em.createNamedQuery("deleteFederatedCredentialAttributeByRealm")
.setParameter("realmId", realm.getId()).executeUpdate();
num = em.createNamedQuery("deleteFederatedUserCredentialsByRealm")
.setParameter("realmId", realm.getId()).executeUpdate();
num = em.createNamedQuery("deleteUserFederatedAttributesByRealm")
@ -843,10 +642,6 @@ public class JpaUserFederatedStorageProvider implements
.setParameter("realmId", realm.getId())
.setParameter("link", link.getId())
.executeUpdate();
num = em.createNamedQuery("deleteFederatedCredentialAttributeByRealmAndLink")
.setParameter("realmId", realm.getId())
.setParameter("link", link.getId())
.executeUpdate();
num = em.createNamedQuery("deleteFederatedUserCredentialsByRealmAndLink")
.setParameter("realmId", realm.getId())
.setParameter("link", link.getId())
@ -904,10 +699,6 @@ public class JpaUserFederatedStorageProvider implements
.setParameter("userId", user.getId())
.setParameter("realmId", realm.getId())
.executeUpdate();
em.createNamedQuery("deleteFederatedCredentialAttributeByUser")
.setParameter("userId", user.getId())
.setParameter("realmId", realm.getId())
.executeUpdate();
em.createNamedQuery("deleteFederatedUserCredentialByUser")
.setParameter("userId", user.getId())
.setParameter("realmId", realm.getId())
@ -946,9 +737,6 @@ public class JpaUserFederatedStorageProvider implements
em.createNamedQuery("deleteFederatedUserConsentsByStorageProvider")
.setParameter("storageProviderId", model.getId())
.executeUpdate();
em.createNamedQuery("deleteFederatedCredentialAttributeByStorageProvider")
.setParameter("storageProviderId", model.getId())
.executeUpdate();
em.createNamedQuery("deleteFederatedUserCredentialsByStorageProvider")
.setParameter("storageProviderId", model.getId())
.executeUpdate();

View file

@ -1,113 +0,0 @@
/*
* Copyright 2016 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.storage.jpa.entity;
import org.keycloak.models.jpa.entities.CredentialEntity;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@NamedQueries({
@NamedQuery(name="deleteFederatedCredentialAttributeByCredential", query="delete from FederatedUserCredentialAttributeEntity attr where attr.credential = :credential"),
@NamedQuery(name="deleteFederatedCredentialAttributeByStorageProvider", query="delete from FederatedUserCredentialAttributeEntity attr where attr.credential IN (select cred from FederatedUserCredentialEntity cred where cred.storageProviderId=:storageProviderId)"),
@NamedQuery(name="deleteFederatedCredentialAttributeByRealm", query="delete from FederatedUserCredentialAttributeEntity attr where attr.credential IN (select cred from FederatedUserCredentialEntity cred where cred.realmId=:realmId)"),
@NamedQuery(name="deleteFederatedCredentialAttributeByRealmAndLink", query="delete from FederatedUserCredentialAttributeEntity attr where attr.credential IN (select cred from FederatedUserCredentialEntity cred where cred.userId IN (select u.id from UserEntity u where u.realmId=:realmId and u.federationLink=:link))"),
@NamedQuery(name="deleteFederatedCredentialAttributeByUser", query="delete from FederatedUserCredentialAttributeEntity attr where attr.credential IN (select cred from FederatedUserCredentialEntity cred where cred.userId = :userId and cred.realmId = :realmId)"),
})
@Table(name="FED_CREDENTIAL_ATTRIBUTE")
@Entity
public class FederatedUserCredentialAttributeEntity {
@Id
@Column(name="ID", length = 36)
@Access(AccessType.PROPERTY) // we do this because relationships often fetch id, but not entity. This avoids an extra SQL
protected String id;
@ManyToOne(fetch= FetchType.LAZY)
@JoinColumn(name = "CREDENTIAL_ID")
protected FederatedUserCredentialEntity credential;
@Column(name = "NAME")
protected String name;
@Column(name = "VALUE")
protected String value;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public FederatedUserCredentialEntity getCredential() {
return credential;
}
public void setCredential(FederatedUserCredentialEntity credential) {
this.credential = credential;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null) return false;
if (!(o instanceof FederatedUserCredentialAttributeEntity)) return false;
FederatedUserCredentialAttributeEntity that = (FederatedUserCredentialAttributeEntity) o;
if (!id.equals(that.getId())) return false;
return true;
}
@Override
public int hashCode() {
return id.hashCode();
}
}

View file

@ -17,12 +17,10 @@
package org.keycloak.storage.jpa.entity;
import org.keycloak.models.jpa.entities.CredentialEntity;
import org.keycloak.models.jpa.entities.UserEntity;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
@ -31,10 +29,7 @@ import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import java.util.ArrayList;
import java.util.Collection;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -43,7 +38,6 @@ import java.util.Collection;
@NamedQueries({
@NamedQuery(name="federatedUserCredentialByUser", query="select cred from FederatedUserCredentialEntity cred where cred.userId = :userId"),
@NamedQuery(name="federatedUserCredentialByUserAndType", query="select cred from FederatedUserCredentialEntity cred where cred.userId = :userId and cred.type = :type"),
@NamedQuery(name="federatedUserCredentialByNameAndType", query="select cred from FederatedUserCredentialEntity cred where cred.userId = :userId and cred.type = :type and cred.device = :device"),
@NamedQuery(name="deleteFederatedUserCredentialByUser", query="delete from FederatedUserCredentialEntity cred where cred.userId = :userId and cred.realmId = :realmId"),
@NamedQuery(name="deleteFederatedUserCredentialByUserAndType", query="delete from FederatedUserCredentialEntity cred where cred.userId = :userId and cred.type = :type"),
@NamedQuery(name="deleteFederatedUserCredentialByUserAndTypeAndDevice", query="delete from FederatedUserCredentialEntity cred where cred.userId = :userId and cred.type = :type and cred.device = :device"),
@ -93,8 +87,6 @@ public class FederatedUserCredentialEntity {
protected int digits;
@Column(name="PERIOD")
protected int period;
@OneToMany(cascade = CascadeType.REMOVE, fetch = FetchType.EAGER, orphanRemoval = true, mappedBy="credential")
protected Collection<FederatedUserCredentialAttributeEntity> credentialAttributes = new ArrayList<>();
public String getId() {
@ -209,14 +201,6 @@ public class FederatedUserCredentialEntity {
this.period = period;
}
public Collection<FederatedUserCredentialAttributeEntity> getCredentialAttributes() {
return credentialAttributes;
}
public void setCredentialAttributes(Collection<FederatedUserCredentialAttributeEntity> credentialAttributes) {
this.credentialAttributes = credentialAttributes;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;

View file

@ -24,39 +24,4 @@
</addColumn>
</changeSet>
<changeSet author="bburke@redhat.com" id="2.2.0">
<createTable tableName="CREDENTIAL_ATTRIBUTE">
<column name="ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
<column name="CREDENTIAL_ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
<column name="NAME" type="VARCHAR(255)">
<constraints nullable="false"/>
</column>
<column name="VALUE" type="VARCHAR(4000)"/>
</createTable>
<createTable tableName="FED_CREDENTIAL_ATTRIBUTE">
<column name="ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
<column name="CREDENTIAL_ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
<column name="NAME" type="VARCHAR(255)">
<constraints nullable="false"/>
</column>
<column name="VALUE" type="VARCHAR(4000)"/>
</createTable>
<modifyDataType tableName="CREDENTIAL" columnName="VALUE" newDataType="VARCHAR(4000)"/>
<addForeignKeyConstraint baseColumnNames="CREDENTIAL_ID" baseTableName="FED_CREDENTIAL_ATTRIBUTE" constraintName="FK_FED_CRED_ATTR" referencedColumnNames="ID" referencedTableName="FED_USER_CREDENTIAL"/>
<addForeignKeyConstraint baseColumnNames="CREDENTIAL_ID" baseTableName="CREDENTIAL_ATTRIBUTE" constraintName="FK_CRED_ATTR" referencedColumnNames="ID" referencedTableName="CREDENTIAL"/>
</changeSet>
</databaseChangeLog>

View file

@ -22,7 +22,6 @@
<persistence-unit name="keycloak-default" transaction-type="RESOURCE_LOCAL">
<class>org.keycloak.models.jpa.entities.ClientEntity</class>
<class>org.keycloak.models.jpa.entities.CredentialEntity</class>
<class>org.keycloak.models.jpa.entities.CredentialAttributeEntity</class>
<class>org.keycloak.models.jpa.entities.RealmEntity</class>
<class>org.keycloak.models.jpa.entities.RealmAttributeEntity</class>
<class>org.keycloak.models.jpa.entities.RequiredCredentialEntity</class>
@ -75,7 +74,6 @@
<class>org.keycloak.storage.jpa.entity.FederatedUserConsentRoleEntity</class>
<class>org.keycloak.storage.jpa.entity.FederatedUserConsentProtocolMapperEntity</class>
<class>org.keycloak.storage.jpa.entity.FederatedUserCredentialEntity</class>
<class>org.keycloak.storage.jpa.entity.FederatedUserCredentialAttributeEntity</class>
<class>org.keycloak.storage.jpa.entity.FederatedUserGroupMembershipEntity</class>
<class>org.keycloak.storage.jpa.entity.FederatedUserRequiredActionEntity</class>
<class>org.keycloak.storage.jpa.entity.FederatedUserRoleMappingEntity</class>

View file

@ -24,7 +24,6 @@ import com.mongodb.QueryBuilder;
import org.keycloak.component.ComponentModel;
import org.keycloak.connections.mongo.api.MongoStore;
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
import org.keycloak.credential.CredentialInput;
import org.keycloak.models.ClientModel;
import org.keycloak.models.CredentialValidationOutput;
import org.keycloak.models.FederatedIdentityModel;
@ -636,5 +635,4 @@ public class MongoUserProvider implements UserProvider {
public void preRemove(RealmModel realm, ComponentModel component) {
}
}

View file

@ -1,58 +0,0 @@
/*
* Copyright 2016 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.component;
import org.keycloak.component.ComponentModel;
import org.keycloak.storage.UserStorageProviderModel;
import java.util.Comparator;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class PrioritizedComponentModel extends ComponentModel {
public static Comparator<ComponentModel> comparator = new Comparator<ComponentModel>() {
@Override
public int compare(ComponentModel o1, ComponentModel o2) {
return parsePriority(o1) - parsePriority(o2);
}
};
public PrioritizedComponentModel(ComponentModel copy) {
super(copy);
}
public PrioritizedComponentModel() {
}
public static int parsePriority(ComponentModel component) {
String priority = component.getConfig().getFirst("priority");
if (priority == null) return 0;
return Integer.valueOf(priority);
}
public int getPriority() {
return parsePriority(this);
}
public void setPriority(int priority) {
getConfig().putSingle("priority", Integer.toString(priority));
}
}

View file

@ -1,25 +0,0 @@
/*
* Copyright 2016 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.credential;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface CredentialInput {
String getType();
}

View file

@ -1,32 +0,0 @@
/*
* Copyright 2016 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.credential;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface CredentialInputUpdater {
boolean supportsCredentialType(String credentialType);
boolean updateCredential(RealmModel realm, UserModel user, CredentialInput input);
void disableCredentialType(RealmModel realm, UserModel user, String credentialType);
}

View file

@ -1,35 +0,0 @@
/*
* Copyright 2016 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.credential;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.provider.Provider;
import java.util.List;
import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface CredentialInputValidator {
boolean supportsCredentialType(String credentialType);
boolean isConfiguredFor(RealmModel realm, UserModel user, String credentialType);
boolean isValid(RealmModel realm, UserModel user, CredentialInput input);
}

View file

@ -1,158 +0,0 @@
/*
* Copyright 2016 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.credential;
import org.keycloak.common.util.MultivaluedHashMap;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
/**
* Used just in cases when we want to "directly" update or retrieve the hash or salt of user credential (For example during export/import)
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class CredentialModel implements Serializable {
public static final String PASSWORD = "password";
public static final String PASSWORD_HISTORY = "password-history";
public static final String PASSWORD_TOKEN = "password-token";
// Secret is same as password but it is not hashed
public static final String SECRET = "secret";
public static final String TOTP = "totp";
public static final String HOTP = "hotp";
public static final String CLIENT_CERT = "cert";
public static final String KERBEROS = "kerberos";
public static final String OTP = "otp";
private String id;
private String type;
private String value;
private String device;
private byte[] salt;
private int hashIterations;
private Long createdDate;
// otp stuff
private int counter;
private String algorithm;
private int digits;
private int period;
private MultivaluedHashMap<String, String> config;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getDevice() {
return device;
}
public void setDevice(String device) {
this.device = device;
}
public byte[] getSalt() {
return salt;
}
public void setSalt(byte[] salt) {
this.salt = salt;
}
public int getHashIterations() {
return hashIterations;
}
public void setHashIterations(int iterations) {
this.hashIterations = iterations;
}
public Long getCreatedDate() {
return createdDate;
}
public void setCreatedDate(Long createdDate) {
this.createdDate = createdDate;
}
public int getCounter() {
return counter;
}
public void setCounter(int counter) {
this.counter = counter;
}
public String getAlgorithm() {
return algorithm;
}
public void setAlgorithm(String algorithm) {
this.algorithm = algorithm;
}
public int getDigits() {
return digits;
}
public void setDigits(int digits) {
this.digits = digits;
}
public int getPeriod() {
return period;
}
public void setPeriod(int period) {
this.period = period;
}
public MultivaluedHashMap<String, String> getConfig() {
return config;
}
public void setConfig(MultivaluedHashMap<String, String> config) {
this.config = config;
}
}

View file

@ -1,31 +0,0 @@
/*
* Copyright 2016 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.credential;
import org.keycloak.provider.Provider;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface CredentialProvider extends Provider {
@Override
default
void close() {
}
}

View file

@ -1,85 +0,0 @@
/*
* Copyright 2016 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.credential;
import org.keycloak.Config;
import org.keycloak.component.ComponentFactory;
import org.keycloak.component.ComponentModel;
import org.keycloak.component.ComponentValidationException;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.storage.UserStorageProvider;
import java.util.Collections;
import java.util.List;
import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface CredentialProviderFactory<T extends CredentialProvider> extends ComponentFactory<T, CredentialProvider> {
/**
* called per Keycloak transaction.
*
* @param session
* @param model
* @return
*/
T create(KeycloakSession session, ComponentModel model);
/**
* This is the name of the provider and will be showed in the admin console as an option.
*
* @return
*/
@Override
String getId();
@Override
default void init(Config.Scope config) {
}
@Override
default void postInit(KeycloakSessionFactory factory) {
}
@Override
default void close() {
}
@Override
default String getHelpText() {
return "";
}
@Override
default List<ProviderConfigProperty> getConfigProperties() {
return Collections.EMPTY_LIST;
}
@Override
default void validateConfiguration(KeycloakSession session, ComponentModel config) throws ComponentValidationException {
}
}

View file

@ -1,37 +0,0 @@
/*
* Copyright 2016 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.credential;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.provider.Provider;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface UserCredentialStore extends Provider {
void updateCredential(RealmModel realm, UserModel user, CredentialModel cred);
CredentialModel createCredential(RealmModel realm, UserModel user, CredentialModel cred);
boolean removeStoredCredential(RealmModel realm, UserModel user, String id);
CredentialModel getStoredCredentialById(RealmModel realm, UserModel user, String id);
List<CredentialModel> getStoredCredentials(RealmModel realm, UserModel user);
List<CredentialModel> getStoredCredentialsByType(RealmModel realm, UserModel user, String type);
CredentialModel getStoredCredentialByNameAndType(RealmModel realm, UserModel user, String name, String type);
}

View file

@ -17,7 +17,6 @@
package org.keycloak.models;
import org.keycloak.models.cache.UserCache;
import org.keycloak.provider.Provider;
import org.keycloak.scripting.ScriptingProvider;
import org.keycloak.storage.federated.UserFederatedStorageProvider;
@ -75,7 +74,6 @@ public interface KeycloakSession {
Object removeAttribute(String attribute);
void setAttribute(String name, Object value);
void enlistForClose(Provider provider);
KeycloakSessionFactory getKeycloakSessionFactory();
@ -103,14 +101,7 @@ public interface KeycloakSession {
void close();
/**
* The user cache
*
* @return may be null if cache is disabled
*/
UserCache getUserCache();
/**
* A possibly cached view of all users in system.
* A cached view of all users in system.
*
* @return
*/
@ -124,10 +115,8 @@ public interface KeycloakSession {
*/
UserProvider userStorageManager();
UserCredentialManager userCredentialManager();
/**
* A possibly cached view of all users in system that does NOT include users available from the deprecated UserFederationProvider SPI.
* A cached view of all users in system that does NOT include users available from the deprecated UserFederationProvider SPI.
*/
UserProvider userStorage();
@ -140,7 +129,6 @@ public interface KeycloakSession {
/**
* Hybrid storage for UserStorageProviders that can't store a specific piece of keycloak data in their external storage.
* No cache in front.
*
* @return
*/

View file

@ -23,21 +23,6 @@ package org.keycloak.models;
*/
public interface KeycloakTransactionManager extends KeycloakTransaction {
enum JTAPolicy {
/**
* Do not interact with JTA at all
*
*/
NOT_SUPPORTED,
/**
* A new JTA Transaction will be created when Keycloak TM begin() is called. If an existing JTA transaction
* exists, it is suspended and resumed after the Keycloak transaction finishes.
*/
REQUIRES_NEW,
}
JTAPolicy getJTAPolicy();
void setJTAPolicy(JTAPolicy policy);
void enlist(KeycloakTransaction transaction);
void enlistAfterCompletion(KeycloakTransaction transaction);

View file

@ -1,37 +0,0 @@
/*
* Copyright 2016 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;
import org.keycloak.credential.CredentialInput;
import org.keycloak.credential.UserCredentialStore;
import java.util.List;
import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface UserCredentialManager extends UserCredentialStore {
boolean isValid(RealmModel realm, UserModel user, List<CredentialInput> inputs);
void updateCredential(RealmModel realm, UserModel user, CredentialInput input);
void disableCredential(RealmModel realm, UserModel user, String credentialType);
boolean isConfiguredFor(RealmModel realm, UserModel user, String type);
}

View file

@ -17,26 +17,23 @@
package org.keycloak.models;
import org.keycloak.credential.CredentialInput;
import org.keycloak.credential.CredentialModel;
import java.util.UUID;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class UserCredentialModel implements CredentialInput {
public static final String PASSWORD = CredentialModel.PASSWORD;
public static final String PASSWORD_HISTORY = CredentialModel.PASSWORD_HISTORY;
public static final String PASSWORD_TOKEN = CredentialModel.PASSWORD_TOKEN;
public class UserCredentialModel {
public static final String PASSWORD = "password";
public static final String PASSWORD_HISTORY = "password-history";
public static final String PASSWORD_TOKEN = "password-token";
// Secret is same as password but it is not hashed
public static final String SECRET = CredentialModel.SECRET;
public static final String TOTP = CredentialModel.TOTP;
public static final String HOTP = CredentialModel.HOTP;
public static final String CLIENT_CERT = CredentialModel.CLIENT_CERT;
public static final String KERBEROS = CredentialModel.KERBEROS;
public static final String SECRET = "secret";
public static final String TOTP = "totp";
public static final String HOTP = "hotp";
public static final String CLIENT_CERT = "cert";
public static final String KERBEROS = "kerberos";
protected String type;
protected String value;

View file

@ -19,7 +19,6 @@ package org.keycloak.models;
import org.jboss.logging.Logger;
import org.keycloak.component.ComponentModel;
import org.keycloak.credential.CredentialInput;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.policy.PasswordPolicyManagerProvider;
import org.keycloak.policy.PolicyError;

View file

@ -18,7 +18,6 @@
package org.keycloak.models;
import org.keycloak.component.ComponentModel;
import org.keycloak.credential.CredentialInput;
import org.keycloak.provider.Provider;
import org.keycloak.storage.user.UserCredentialValidatorProvider;
import org.keycloak.storage.user.UserLookupProvider;
@ -86,5 +85,4 @@ public interface UserProvider extends Provider,
void close();
void preRemove(RealmModel realm, ComponentModel component);
}

View file

@ -14,14 +14,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.models.cache;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserProvider;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface OnUserCache {
void onCache(RealmModel realm, CachedUserModel user);
public interface CacheUserProvider extends UserProvider {
void clear();
UserProvider getDelegate();
}

View file

@ -23,6 +23,6 @@ import org.keycloak.provider.ProviderFactory;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface UserCacheProviderFactory extends ProviderFactory<UserCache> {
public interface CacheUserProviderFactory extends ProviderFactory<CacheUserProvider> {
}

View file

@ -39,11 +39,11 @@ public class CacheUserProviderSpi implements Spi {
@Override
public Class<? extends Provider> getProviderClass() {
return UserCache.class;
return CacheUserProvider.class;
}
@Override
public Class<? extends ProviderFactory> getProviderFactoryClass() {
return UserCacheProviderFactory.class;
return CacheUserProviderFactory.class;
}
}

View file

@ -1,43 +0,0 @@
/*
* Copyright 2016 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.cache;
import org.keycloak.models.UserModel;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface CachedUserModel extends UserModel {
void invalidate();
/**
* When was the user loaded from database.
*
* @return
*/
long getCacheTimestamp();
/**
* Returns a map that contains custom things that are cached along with the user. You can write to this map.
*
* @return
*/
ConcurrentHashMap getCachedWith();
}

View file

@ -1,36 +0,0 @@
/*
* Copyright 2016 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.cache;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserProvider;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface UserCache extends UserProvider {
/**
* Evict user from cache.
*
* @param user
*/
void evict(RealmModel realm, UserModel user);
void clear();
}

View file

@ -44,14 +44,8 @@ import org.keycloak.models.UserModel;
import org.keycloak.representations.idm.CertificateRepresentation;
import org.keycloak.common.util.CertificateUtils;
import org.keycloak.common.util.PemUtils;
import org.keycloak.transaction.JtaTransactionManagerLookup;
import javax.crypto.spec.SecretKeySpec;
import javax.naming.InitialContext;
import javax.sql.DataSource;
import javax.transaction.InvalidTransactionException;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import java.io.IOException;
import java.io.StringWriter;
import java.security.Key;
@ -62,7 +56,6 @@ import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.sql.DriverManager;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@ -70,7 +63,6 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.function.Function;
/**
* Set of helper methods, which are useful in various model implementations.
@ -311,7 +303,6 @@ public final class KeycloakModelUtils {
}
}
public static String getMasterRealmAdminApplicationClientId(String realmName) {
return realmName + "-realm";
}
@ -660,33 +651,4 @@ public final class KeycloakModelUtils {
}
}
}
public static void suspendJtaTransaction(KeycloakSessionFactory factory, Runnable runnable) {
JtaTransactionManagerLookup lookup = (JtaTransactionManagerLookup)factory.getProviderFactory(JtaTransactionManagerLookup.class);
Transaction suspended = null;
try {
if (lookup != null) {
if (lookup.getTransactionManager() != null) {
try {
suspended = lookup.getTransactionManager().suspend();
} catch (SystemException e) {
throw new RuntimeException(e);
}
}
}
runnable.run();
} finally {
if (suspended != null) {
try {
lookup.getTransactionManager().resume(suspended);
} catch (InvalidTransactionException e) {
throw new RuntimeException(e);
} catch (SystemException e) {
throw new RuntimeException(e);
}
}
}
}
}

View file

@ -30,8 +30,6 @@ import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserConsentModel;
import org.keycloak.models.cache.CachedUserModel;
import org.keycloak.models.cache.OnUserCache;
import org.keycloak.storage.user.UserCredentialAuthenticationProvider;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.storage.user.UserCredentialValidatorProvider;
@ -58,7 +56,7 @@ import java.util.Set;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class UserStorageManager implements UserProvider, OnUserCache {
public class UserStorageManager implements UserProvider {
private static final Logger logger = Logger.getLogger(UserStorageManager.class);
@ -72,22 +70,22 @@ public class UserStorageManager implements UserProvider, OnUserCache {
return session.userLocalStorage();
}
public static List<UserStorageProviderModel> getStorageProviders(RealmModel realm) {
protected List<UserStorageProviderModel> getStorageProviders(RealmModel realm) {
return realm.getUserStorageProviders();
}
public static <T> T getFirstStorageProvider(KeycloakSession session, RealmModel realm, Class<T> type) {
protected <T> T getFirstStorageProvider(RealmModel realm, Class<T> type) {
for (UserStorageProviderModel model : getStorageProviders(realm)) {
UserStorageProviderFactory factory = (UserStorageProviderFactory)session.getKeycloakSessionFactory().getProviderFactory(UserStorageProvider.class, model.getProviderId());
if (Types.supports(type, factory, UserStorageProviderFactory.class)) {
return type.cast(getStorageProviderInstance(session, model, factory));
return type.cast(getStorageProviderInstance(model, factory));
}
}
return null;
}
public static UserStorageProvider getStorageProviderInstance(KeycloakSession session, UserStorageProviderModel model, UserStorageProviderFactory factory) {
private UserStorageProvider getStorageProviderInstance(UserStorageProviderModel model, UserStorageProviderFactory factory) {
UserStorageProvider instance = (UserStorageProvider)session.getAttribute(model.getId());
if (instance != null) return instance;
instance = factory.create(session, model);
@ -97,12 +95,12 @@ public class UserStorageManager implements UserProvider, OnUserCache {
}
public static <T> List<T> getStorageProviders(KeycloakSession session, RealmModel realm, Class<T> type) {
protected <T> List<T> getStorageProviders(RealmModel realm, Class<T> type) {
List<T> list = new LinkedList<>();
for (UserStorageProviderModel model : getStorageProviders(realm)) {
UserStorageProviderFactory factory = (UserStorageProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(UserStorageProvider.class, model.getProviderId());
if (Types.supports(type, factory, UserStorageProviderFactory.class)) {
list.add(type.cast(getStorageProviderInstance(session, model, factory)));
list.add(type.cast(getStorageProviderInstance(model, factory)));
}
@ -118,21 +116,21 @@ public class UserStorageManager implements UserProvider, OnUserCache {
@Override
public UserModel addUser(RealmModel realm, String username) {
UserRegistrationProvider registry = getFirstStorageProvider(session, realm, UserRegistrationProvider.class);
UserRegistrationProvider registry = getFirstStorageProvider(realm, UserRegistrationProvider.class);
if (registry != null) {
return registry.addUser(realm, username);
}
return localStorage().addUser(realm, username.toLowerCase());
}
public static UserStorageProvider getStorageProvider(KeycloakSession session, RealmModel realm, String componentId) {
public UserStorageProvider getStorageProvider(RealmModel realm, String componentId) {
ComponentModel model = realm.getComponent(componentId);
if (model == null) return null;
UserStorageProviderFactory factory = (UserStorageProviderFactory)session.getKeycloakSessionFactory().getProviderFactory(UserStorageProvider.class, model.getProviderId());
if (factory == null) {
throw new ModelException("Could not find UserStorageProviderFactory for: " + model.getProviderId());
}
return getStorageProviderInstance(session, new UserStorageProviderModel(model), factory);
return getStorageProviderInstance(new UserStorageProviderModel(model), factory);
}
@Override
@ -142,7 +140,7 @@ public class UserStorageManager implements UserProvider, OnUserCache {
if (storageId.getProviderId() == null) {
return localStorage().removeUser(realm, user);
}
UserRegistrationProvider registry = (UserRegistrationProvider)getStorageProvider(session, realm, storageId.getProviderId());
UserRegistrationProvider registry = (UserRegistrationProvider)getStorageProvider(realm, storageId.getProviderId());
if (registry == null) {
throw new ModelException("Could not resolve StorageProvider: " + storageId.getProviderId());
}
@ -235,7 +233,7 @@ public class UserStorageManager implements UserProvider, OnUserCache {
if (storageId.getProviderId() == null) {
return localStorage().getUserById(id, realm);
}
UserLookupProvider provider = (UserLookupProvider)getStorageProvider(session, realm, storageId.getProviderId());
UserLookupProvider provider = (UserLookupProvider)getStorageProvider(realm, storageId.getProviderId());
return provider.getUserById(id, realm);
}
@ -248,7 +246,7 @@ public class UserStorageManager implements UserProvider, OnUserCache {
public UserModel getUserByUsername(String username, RealmModel realm) {
UserModel user = localStorage().getUserByUsername(username, realm);
if (user != null) return user;
for (UserLookupProvider provider : getStorageProviders(session, realm, UserLookupProvider.class)) {
for (UserLookupProvider provider : getStorageProviders(realm, UserLookupProvider.class)) {
user = provider.getUserByUsername(username, realm);
if (user != null) return user;
}
@ -259,7 +257,7 @@ public class UserStorageManager implements UserProvider, OnUserCache {
public UserModel getUserByEmail(String email, RealmModel realm) {
UserModel user = localStorage().getUserByEmail(email, realm);
if (user != null) return user;
for (UserLookupProvider provider : getStorageProviders(session, realm, UserLookupProvider.class)) {
for (UserLookupProvider provider : getStorageProviders(realm, UserLookupProvider.class)) {
user = provider.getUserByEmail(email, realm);
if (user != null) return user;
}
@ -301,7 +299,7 @@ public class UserStorageManager implements UserProvider, OnUserCache {
@Override
public int getUsersCount(RealmModel realm) {
int size = localStorage().getUsersCount(realm);
for (UserQueryProvider provider : getStorageProviders(session, realm, UserQueryProvider.class)) {
for (UserQueryProvider provider : getStorageProviders(realm, UserQueryProvider.class)) {
size += provider.getUsersCount(realm);
}
return size;
@ -317,7 +315,7 @@ public class UserStorageManager implements UserProvider, OnUserCache {
List<UserQueryProvider> storageProviders = getStorageProviders(session, realm, UserQueryProvider.class);
List<UserQueryProvider> storageProviders = getStorageProviders(realm, UserQueryProvider.class);
// we can skip rest of method if there are no storage providers
if (storageProviders.isEmpty()) {
return pagedQuery.query(localStorage(), firstResult, maxResults);
@ -449,7 +447,7 @@ public class UserStorageManager implements UserProvider, OnUserCache {
@Override
public void grantToAllUsers(RealmModel realm, RoleModel role) {
// not federation-aware for now
List<UserRegistrationProvider> storageProviders = getStorageProviders(session, realm, UserRegistrationProvider.class);
List<UserRegistrationProvider> storageProviders = getStorageProviders(realm, UserRegistrationProvider.class);
LinkedList<UserRegistrationProvider> providers = new LinkedList<>();
providers.add(localStorage());
providers.addAll(storageProviders);
@ -484,7 +482,7 @@ public class UserStorageManager implements UserProvider, OnUserCache {
public void preRemove(RealmModel realm) {
localStorage().preRemove(realm);
getFederatedStorage().preRemove(realm);
for (UserStorageProvider provider : getStorageProviders(session, realm, UserStorageProvider.class)) {
for (UserStorageProvider provider : getStorageProviders(realm, UserStorageProvider.class)) {
provider.preRemove(realm);
}
}
@ -499,7 +497,7 @@ public class UserStorageManager implements UserProvider, OnUserCache {
public void preRemove(RealmModel realm, GroupModel group) {
localStorage().preRemove(realm, group);
getFederatedStorage().preRemove(realm, group);
for (UserStorageProvider provider : getStorageProviders(session, realm, UserStorageProvider.class)) {
for (UserStorageProvider provider : getStorageProviders(realm, UserStorageProvider.class)) {
provider.preRemove(realm, group);
}
}
@ -508,7 +506,7 @@ public class UserStorageManager implements UserProvider, OnUserCache {
public void preRemove(RealmModel realm, RoleModel role) {
localStorage().preRemove(realm, role);
getFederatedStorage().preRemove(realm, role);
for (UserStorageProvider provider : getStorageProviders(session, realm, UserStorageProvider.class)) {
for (UserStorageProvider provider : getStorageProviders(realm, UserStorageProvider.class)) {
provider.preRemove(realm, role);
}
}
@ -560,7 +558,7 @@ public class UserStorageManager implements UserProvider, OnUserCache {
if (toValidate.isEmpty()) return true;
UserStorageProvider provider = getStorageProvider(session, realm, StorageId.resolveProviderId(user));
UserStorageProvider provider = getStorageProvider(realm, StorageId.resolveProviderId(user));
if (!(provider instanceof UserCredentialValidatorProvider)) {
return false;
}
@ -574,7 +572,7 @@ public class UserStorageManager implements UserProvider, OnUserCache {
@Override
public CredentialValidationOutput validCredentials(KeycloakSession session, RealmModel realm, UserCredentialModel... input) {
List<UserCredentialAuthenticationProvider> providers = getStorageProviders(session, realm, UserCredentialAuthenticationProvider.class);
List<UserCredentialAuthenticationProvider> providers = getStorageProviders(realm, UserCredentialAuthenticationProvider.class);
if (providers.isEmpty()) return CredentialValidationOutput.failed();
CredentialValidationOutput result = null;
@ -608,23 +606,7 @@ public class UserStorageManager implements UserProvider, OnUserCache {
}
@Override
public void onCache(RealmModel realm, CachedUserModel user) {
if (StorageId.isLocalStorage(user)) {
if (session.userLocalStorage() instanceof OnUserCache) {
((OnUserCache)session.userLocalStorage()).onCache(realm, user);
}
} else {
Object provider = getStorageProvider(session, realm, StorageId.resolveProviderId(user));
if (provider != null && provider instanceof OnUserCache) {
((OnUserCache)provider).onCache(realm, user);
}
}
}
@Override
public void close() {
}
}

View file

@ -17,8 +17,14 @@
package org.keycloak.storage;
import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.component.ComponentModel;
import org.keycloak.component.PrioritizedComponentModel;
import org.keycloak.models.RealmModel;
import java.io.Serializable;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
/**
* Stored configuration of a User Storage provider instance.
@ -26,7 +32,14 @@ import org.keycloak.component.PrioritizedComponentModel;
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
* @author <a href="mailto:bburke@redhat.com">Bill Burke</a>
*/
public class UserStorageProviderModel extends PrioritizedComponentModel {
public class UserStorageProviderModel extends ComponentModel {
public static Comparator<UserStorageProviderModel> comparator = new Comparator<UserStorageProviderModel>() {
@Override
public int compare(UserStorageProviderModel o1, UserStorageProviderModel o2) {
return o1.getPriority() - o2.getPriority();
}
};
public UserStorageProviderModel() {
setProviderType(UserStorageProvider.class.getName());
@ -36,4 +49,14 @@ public class UserStorageProviderModel extends PrioritizedComponentModel {
super(copy);
}
public int getPriority() {
String priority = getConfig().getFirst("priority");
if (priority == null) return 0;
return Integer.valueOf(priority);
}
public void setPriority(int priority) {
getConfig().putSingle("priority", Integer.toString(priority));
}
}

View file

@ -16,7 +16,6 @@
*/
package org.keycloak.storage.federated;
import org.keycloak.credential.CredentialModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserCredentialValueModel;
@ -29,20 +28,8 @@ import java.util.List;
* @version $Revision: 1 $
*/
public interface UserCredentialsFederatedStorage {
// deprecated
void updateCredential(RealmModel realm, UserModel user, UserCredentialModel cred);
void updateCredential(RealmModel realm, UserModel user, UserCredentialValueModel cred);
void removeCredential(RealmModel realm, UserModel user, UserCredentialValueModel cred);
List<UserCredentialValueModel> getCredentials(RealmModel realm, UserModel user);
// new
void updateCredential(RealmModel realm, UserModel user, CredentialModel cred);
CredentialModel createCredential(RealmModel realm, UserModel user, CredentialModel cred);
boolean removeCredential(RealmModel realm, String id);
CredentialModel getCredentialById(String id);
List<CredentialModel> getCredentials(RealmModel realm);
List<CredentialModel> getUserCredentials(RealmModel realm, UserModel user);
List<CredentialModel> getCredentialsByType(RealmModel realm, UserModel user, String type);
CredentialModel getCredentialByNameAndType(RealmModel realm, UserModel user, String name, String type);
}

View file

@ -62,4 +62,3 @@ org.keycloak.models.cache.authorization.CachedStoreFactorySpi
org.keycloak.protocol.oidc.TokenIntrospectionSpi
org.keycloak.policy.PasswordPolicySpi
org.keycloak.policy.PasswordPolicyManagerSpi
org.keycloak.transaction.TransactionManagerLookupSpi

View file

@ -1,224 +0,0 @@
/*
* Copyright 2016 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.credential;
import org.jboss.logging.Logger;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.OTPPolicy;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.cache.CachedUserModel;
import org.keycloak.models.cache.OnUserCache;
import org.keycloak.models.utils.HmacOTP;
import org.keycloak.models.utils.TimeBasedOTP;
import java.util.Collections;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class LocalOTPCredentialManager implements CredentialInputValidator, CredentialInputUpdater, OnUserCache {
private static final Logger logger = Logger.getLogger(LocalOTPCredentialManager.class);
protected KeycloakSession session;
protected List<CredentialModel> getCachedCredentials(UserModel user, String type) {
if (!(user instanceof CachedUserModel)) return Collections.EMPTY_LIST;
CachedUserModel cached = (CachedUserModel)user;
List<CredentialModel> rtn = (List<CredentialModel>)cached.getCachedWith().get(LocalOTPCredentialManager.class.getName() + "." + type);
if (rtn == null) return Collections.EMPTY_LIST;
return rtn;
}
protected UserCredentialStore getCredentialStore() {
return session.userCredentialManager();
}
@Override
public void onCache(RealmModel realm, CachedUserModel user) {
List<CredentialModel> creds = getCredentialStore().getStoredCredentialsByType(realm, user, CredentialModel.TOTP);
user.getCachedWith().put(LocalOTPCredentialManager.class.getName() + "." + CredentialModel.TOTP, creds);
}
public LocalOTPCredentialManager(KeycloakSession session) {
this.session = session;
}
@Override
public boolean updateCredential(RealmModel realm, UserModel user, CredentialInput input) {
if (!supportsCredentialType(input.getType())) return false;
if (!(input instanceof UserCredentialModel)) {
logger.debug("Expected instance of UserCredentialModel for CredentialInput");
return false;
}
UserCredentialModel inputModel = (UserCredentialModel)input;
CredentialModel model = null;
if (inputModel.getDevice() != null) {
model = getCredentialStore().getStoredCredentialByNameAndType(realm, user, inputModel.getDevice(), CredentialModel.TOTP);
if (model == null) {
model = getCredentialStore().getStoredCredentialByNameAndType(realm, user, inputModel.getDevice(), CredentialModel.HOTP);
}
}
if (model == null) {
// delete all existing
disableCredentialType(realm, user, CredentialModel.OTP);
model = new CredentialModel();
}
OTPPolicy policy = realm.getOTPPolicy();
model.setDigits(policy.getDigits());
model.setCounter(policy.getInitialCounter());
model.setAlgorithm(policy.getAlgorithm());
model.setType(policy.getType());
model.setValue(inputModel.getValue());
model.setDevice(inputModel.getDevice());
model.setPeriod(policy.getPeriod());
if (model.getId() == null) {
getCredentialStore().createCredential(realm, user, model);
} else {
getCredentialStore().updateCredential(realm, user, model);
}
session.getUserCache().evict(realm, user);
return true;
}
@Override
public void disableCredentialType(RealmModel realm, UserModel user, String credentialType) {
boolean disableTOTP = false, disableHOTP = false;
if (CredentialModel.OTP.equals(credentialType)) {
disableTOTP = true;
disableHOTP = true;
} else if (CredentialModel.HOTP.equals(credentialType)) {
disableHOTP = true;
} else if (CredentialModel.TOTP.equals(credentialType)) {
disableTOTP = true;
}
if (disableHOTP) {
List<CredentialModel> hotp = getCredentialStore().getStoredCredentialsByType(realm, user, CredentialModel.HOTP);
for (CredentialModel cred : hotp) {
getCredentialStore().removeStoredCredential(realm, user, cred.getId());
}
}
if (disableTOTP) {
List<CredentialModel> totp = getCredentialStore().getStoredCredentialsByType(realm, user, CredentialModel.TOTP);
if (!totp.isEmpty()) {
for (CredentialModel cred : totp) {
getCredentialStore().removeStoredCredential(realm, user, cred.getId());
}
}
}
if (disableTOTP || disableHOTP) {
session.getUserCache().evict(realm, user);
}
}
@Override
public boolean supportsCredentialType(String credentialType) {
return CredentialModel.OTP.equals(credentialType)
|| CredentialModel.HOTP.equals(credentialType)
|| CredentialModel.TOTP.equals(credentialType);
}
@Override
public boolean isConfiguredFor(RealmModel realm, UserModel user, String credentialType) {
if (!supportsCredentialType(credentialType)) return false;
if (CredentialModel.OTP.equals(credentialType)) {
if (realm.getOTPPolicy().getType().equals(CredentialModel.HOTP)) {
return configuredForHOTP(realm, user);
} else {
return configuredForTOTP(realm, user);
}
} else if (CredentialModel.HOTP.equals(credentialType)) {
return configuredForHOTP(realm, user);
} else if (CredentialModel.TOTP.equals(credentialType)) {
return configuredForTOTP(realm, user);
} else {
return false;
}
}
protected boolean configuredForHOTP(RealmModel realm, UserModel user) {
return !getCredentialStore().getStoredCredentialsByType(realm, user, CredentialModel.HOTP).isEmpty();
}
protected boolean configuredForTOTP(RealmModel realm, UserModel user) {
return !getCachedCredentials(user, CredentialModel.TOTP).isEmpty()
|| !getCredentialStore().getStoredCredentialsByType(realm, user, CredentialModel.TOTP).isEmpty();
}
public static boolean validOTP(RealmModel realm, String token, String secret) {
OTPPolicy policy = realm.getOTPPolicy();
if (policy.getType().equals(UserCredentialModel.TOTP)) {
TimeBasedOTP validator = new TimeBasedOTP(policy.getAlgorithm(), policy.getDigits(), policy.getPeriod(), policy.getLookAheadWindow());
return validator.validateTOTP(token, secret.getBytes());
} else {
HmacOTP validator = new HmacOTP(policy.getDigits(), policy.getAlgorithm(), policy.getLookAheadWindow());
int c = validator.validateHOTP(token, secret, policy.getInitialCounter());
return c > -1;
}
}
@Override
public boolean isValid(RealmModel realm, UserModel user, CredentialInput input) {
if (! (input instanceof UserCredentialModel)) {
logger.debug("Expected instance of UserCredentialModel for CredentialInput");
return false;
}
String token = ((UserCredentialModel)input).getValue();
OTPPolicy policy = realm.getOTPPolicy();
if (realm.getOTPPolicy().getType().equals(CredentialModel.HOTP)) {
HmacOTP validator = new HmacOTP(policy.getDigits(), policy.getAlgorithm(), policy.getLookAheadWindow());
for (CredentialModel cred : getCredentialStore().getStoredCredentialsByType(realm, user, CredentialModel.HOTP)) {
int counter = validator.validateHOTP(token, cred.getValue(), cred.getCounter());
if (counter < 0) continue;
cred.setCounter(counter);
getCredentialStore().updateCredential(realm, user, cred);
return true;
}
} else {
TimeBasedOTP validator = new TimeBasedOTP(policy.getAlgorithm(), policy.getDigits(), policy.getPeriod(), policy.getLookAheadWindow());
List<CredentialModel> creds = getCachedCredentials(user, CredentialModel.TOTP);
if (creds.isEmpty()) {
creds = getCredentialStore().getStoredCredentialsByType(realm, user, CredentialModel.TOTP);
} else {
logger.debugv("Cache hit for TOTP for user {0}", user.getUsername());
}
for (CredentialModel cred : creds) {
if (validator.validateTOTP(token, cred.getValue().getBytes())) {
return true;
}
}
}
return false;
}
}

View file

@ -1,249 +0,0 @@
/*
* Copyright 2016 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.credential;
import org.keycloak.common.util.reflections.Types;
import org.keycloak.component.ComponentModel;
import org.keycloak.component.PrioritizedComponentModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserCredentialManager;
import org.keycloak.models.UserModel;
import org.keycloak.models.cache.CachedUserModel;
import org.keycloak.models.cache.OnUserCache;
import org.keycloak.storage.StorageId;
import org.keycloak.storage.UserStorageManager;
import org.keycloak.storage.UserStorageProvider;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class UserCredentialStoreManager implements UserCredentialManager, OnUserCache {
protected KeycloakSession session;
public UserCredentialStoreManager(KeycloakSession session) {
this.session = session;
}
protected UserCredentialStore getStoreForUser(UserModel user) {
if (StorageId.isLocalStorage(user)) {
return (UserCredentialStore)session.userLocalStorage();
} else {
return (UserCredentialStore)session.userFederatedStorage();
}
}
@Override
public void updateCredential(RealmModel realm, UserModel user, CredentialModel cred) {
getStoreForUser(user).updateCredential(realm, user, cred);
}
@Override
public CredentialModel createCredential(RealmModel realm, UserModel user, CredentialModel cred) {
return getStoreForUser(user).createCredential(realm, user, cred);
}
@Override
public boolean removeStoredCredential(RealmModel realm, UserModel user, String id) {
return getStoreForUser(user).removeStoredCredential(realm, user, id);
}
@Override
public CredentialModel getStoredCredentialById(RealmModel realm, UserModel user, String id) {
return getStoreForUser(user).getStoredCredentialById(realm, user, id);
}
@Override
public List<CredentialModel> getStoredCredentials(RealmModel realm, UserModel user) {
return getStoreForUser(user).getStoredCredentials(realm, user);
}
@Override
public List<CredentialModel> getStoredCredentialsByType(RealmModel realm, UserModel user, String type) {
return getStoreForUser(user).getStoredCredentialsByType(realm, user, type);
}
@Override
public CredentialModel getStoredCredentialByNameAndType(RealmModel realm, UserModel user, String name, String type) {
return getStoreForUser(user).getStoredCredentialByNameAndType(realm, user, name, type);
}
@Override
public boolean isValid(RealmModel realm, UserModel user, List<CredentialInput> inputs) {
List<CredentialInput> toValidate = new LinkedList<>();
toValidate.addAll(inputs);
if (!StorageId.isLocalStorage(user)) {
String providerId = StorageId.resolveProviderId(user);
UserStorageProvider provider = UserStorageManager.getStorageProvider(session, realm, providerId);
if (provider instanceof CredentialInputValidator) {
Iterator<CredentialInput> it = toValidate.iterator();
while (it.hasNext()) {
CredentialInput input = it.next();
CredentialInputValidator validator = (CredentialInputValidator)provider;
if (validator.supportsCredentialType(input.getType()) && validator.isValid(realm, user, input)) {
it.remove();
}
}
}
}
if (toValidate.isEmpty()) return true;
List<ComponentModel> components = getCredentialProviderComponents(realm);
for (ComponentModel component : components) {
CredentialProviderFactory factory = (CredentialProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(CredentialProvider.class, component.getProviderId());
if (!Types.supports(CredentialInputValidator.class, factory, CredentialProviderFactory.class)) continue;
Iterator<CredentialInput> it = toValidate.iterator();
while (it.hasNext()) {
CredentialInput input = it.next();
CredentialInputValidator validator = (CredentialInputValidator)session.getAttribute(component.getId());
if (validator == null) {
validator = (CredentialInputValidator)factory.create(session, component);
session.setAttribute(component.getId(), validator);
}
if (validator.supportsCredentialType(input.getType()) && validator.isValid(realm, user, input)) {
it.remove();
}
}
}
return toValidate.isEmpty();
}
protected List<ComponentModel> getCredentialProviderComponents(RealmModel realm) {
List<ComponentModel> components = realm.getComponents(realm.getId(), CredentialProvider.class.getName());
if (components.isEmpty()) return components;
List<ComponentModel> copy = new LinkedList<>();
copy.addAll(components);
Collections.sort(copy, PrioritizedComponentModel.comparator);
return copy;
}
@Override
public void updateCredential(RealmModel realm, UserModel user, CredentialInput input) {
if (!StorageId.isLocalStorage(user)) {
String providerId = StorageId.resolveProviderId(user);
UserStorageProvider provider = UserStorageManager.getStorageProvider(session, realm, providerId);
if (provider instanceof CredentialInputUpdater) {
CredentialInputUpdater updater = (CredentialInputUpdater)provider;
if (updater.supportsCredentialType(input.getType())) {
if (updater.updateCredential(realm, user, input)) return;
}
}
}
List<ComponentModel> components = getCredentialProviderComponents(realm);
for (ComponentModel component : components) {
CredentialProviderFactory factory = (CredentialProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(CredentialProvider.class, component.getProviderId());
if (!Types.supports(CredentialInputUpdater.class, factory, CredentialProviderFactory.class)) continue;
CredentialInputUpdater updater = (CredentialInputUpdater)session.getAttribute(component.getId());
if (updater == null) {
updater = (CredentialInputUpdater)factory.create(session, component);
session.setAttribute(component.getId(), updater);
}
if (!updater.supportsCredentialType(input.getType())) continue;
if (updater.updateCredential(realm, user, input)) return;
}
}
@Override
public void disableCredential(RealmModel realm, UserModel user, String credentialType) {
if (!StorageId.isLocalStorage(user)) {
String providerId = StorageId.resolveProviderId(user);
UserStorageProvider provider = UserStorageManager.getStorageProvider(session, realm, providerId);
if (provider instanceof CredentialInputUpdater) {
CredentialInputUpdater updater = (CredentialInputUpdater)provider;
if (updater.supportsCredentialType(credentialType)) {
updater.disableCredentialType(realm, user, credentialType);
}
}
}
List<ComponentModel> components = getCredentialProviderComponents(realm);
for (ComponentModel component : components) {
CredentialProviderFactory factory = (CredentialProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(CredentialProvider.class, component.getProviderId());
if (!Types.supports(CredentialInputUpdater.class, factory, CredentialProviderFactory.class)) continue;
CredentialInputUpdater updater = (CredentialInputUpdater)session.getAttribute(component.getId());
if (updater == null) {
updater = (CredentialInputUpdater)factory.create(session, component);
session.setAttribute(component.getId(), updater);
}
if (!updater.supportsCredentialType(credentialType)) continue;
updater.disableCredentialType(realm, user, credentialType);
}
}
@Override
public boolean isConfiguredFor(RealmModel realm, UserModel user, String type) {
if (!StorageId.isLocalStorage(user)) {
String providerId = StorageId.resolveProviderId(user);
UserStorageProvider provider = UserStorageManager.getStorageProvider(session, realm, providerId);
if (provider instanceof CredentialInputValidator) {
CredentialInputValidator validator = (CredentialInputValidator)provider;
if (validator.supportsCredentialType(type) && validator.isConfiguredFor(realm, user, type)) {
return true;
}
}
}
List<ComponentModel> components = getCredentialProviderComponents(realm);
for (ComponentModel component : components) {
CredentialProviderFactory factory = (CredentialProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(CredentialProvider.class, component.getProviderId());
if (!Types.supports(CredentialInputUpdater.class, factory, CredentialProviderFactory.class)) continue;
CredentialInputValidator validator = (CredentialInputValidator)session.getAttribute(component.getId());
if (validator == null) {
validator = (CredentialInputValidator)factory.create(session, component);
session.setAttribute(component.getId(), validator);
}
if (validator.supportsCredentialType(type) && validator.isConfiguredFor(realm, user, type)) {
return true;
}
}
return false;
}
@Override
public void onCache(RealmModel realm, CachedUserModel user) {
List<ComponentModel> components = getCredentialProviderComponents(realm);
for (ComponentModel component : components) {
CredentialProviderFactory factory = (CredentialProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(CredentialProvider.class, component.getProviderId());
if (!Types.supports(OnUserCache.class, factory, CredentialProviderFactory.class)) continue;
OnUserCache validator = (OnUserCache)session.getAttribute(component.getId());
if (validator == null) {
validator = (OnUserCache)factory.create(session, component);
session.setAttribute(component.getId(), validator);
}
validator.onCache(realm, user);
}
}
@Override
public void close() {
}
}

View file

@ -16,18 +16,15 @@
*/
package org.keycloak.services;
import org.keycloak.credential.UserCredentialStore;
import org.keycloak.credential.UserCredentialStoreManager;
import org.keycloak.models.*;
import org.keycloak.models.cache.CacheRealmProvider;
import org.keycloak.models.cache.UserCache;
import org.keycloak.models.cache.CacheUserProvider;
import org.keycloak.provider.Provider;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.scripting.ScriptingProvider;
import org.keycloak.storage.UserStorageManager;
import org.keycloak.storage.federated.UserFederatedStorageProvider;
import javax.transaction.TransactionManager;
import java.util.*;
/**
@ -43,7 +40,6 @@ public class DefaultKeycloakSession implements KeycloakSession {
private RealmProvider model;
private UserProvider userModel;
private UserStorageManager userStorageManager;
private UserCredentialStoreManager userCredentialStorageManager;
private ScriptingProvider scriptingProvider;
private UserSessionProvider sessionProvider;
private UserFederationManager federationManager;
@ -52,7 +48,7 @@ public class DefaultKeycloakSession implements KeycloakSession {
public DefaultKeycloakSession(DefaultKeycloakSessionFactory factory) {
this.factory = factory;
this.transactionManager = new DefaultKeycloakTransactionManager(this);
this.transactionManager = new DefaultKeycloakTransactionManager();
federationManager = new UserFederationManager(this);
context = new DefaultKeycloakContext(this);
}
@ -72,7 +68,7 @@ public class DefaultKeycloakSession implements KeycloakSession {
}
private UserProvider getUserProvider() {
UserCache cache = getProvider(UserCache.class);
CacheUserProvider cache = getProvider(CacheUserProvider.class);
if (cache != null) {
return cache;
} else {
@ -80,12 +76,6 @@ public class DefaultKeycloakSession implements KeycloakSession {
}
}
@Override
public UserCache getUserCache() {
return getProvider(UserCache.class);
}
@Override
public void enlistForClose(Provider provider) {
closable.add(provider);
@ -135,12 +125,6 @@ public class DefaultKeycloakSession implements KeycloakSession {
return userStorageManager;
}
@Override
public UserCredentialManager userCredentialManager() {
if (userCredentialStorageManager == null) userCredentialStorageManager = new UserCredentialStoreManager(this);
return userCredentialStorageManager;
}
@Override
public UserProvider userStorage() {
if (userModel == null) {

View file

@ -49,6 +49,7 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory, Pr
private Map<Class<? extends Provider>, String> provider = new HashMap<>();
private volatile Map<Class<? extends Provider>, Map<String, ProviderFactory>> factoriesMap = new HashMap<>();
protected CopyOnWriteArrayList<ProviderEventListener> listeners = new CopyOnWriteArrayList<>();
private TransactionManager tm;
// TODO: Likely should be changed to int and use Time.currentTime() to be compatible with all our "time" reps
protected long serverStartupTimestamp;
@ -96,6 +97,8 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory, Pr
// make the session factory ready for hot deployment
ProviderManagerRegistry.SINGLETON.setDeployer(this);
JtaTransactionManagerLookup lookup = (JtaTransactionManagerLookup)getProviderFactory(JtaTransactionManagerLookup.class);
if (lookup != null) tm = lookup.getTransactionManager();
}
protected Map<Class<? extends Provider>, Map<String, ProviderFactory>> getFactoriesCopy() {
Map<Class<? extends Provider>, Map<String, ProviderFactory>> copy = new HashMap<>();
@ -279,6 +282,9 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory, Pr
public KeycloakSession create() {
KeycloakSession session = new DefaultKeycloakSession(this);
if (tm != null) {
session.getTransactionManager().enlist(new JtaTransactionWrapper(tm));
}
return session;
}

View file

@ -16,13 +16,9 @@
*/
package org.keycloak.services;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakTransaction;
import org.keycloak.models.KeycloakTransactionManager;
import org.keycloak.transaction.JtaTransactionManagerLookup;
import org.keycloak.transaction.JtaTransactionWrapper;
import javax.transaction.TransactionManager;
import java.util.LinkedList;
import java.util.List;
@ -38,12 +34,6 @@ public class DefaultKeycloakTransactionManager implements KeycloakTransactionMan
private List<KeycloakTransaction> afterCompletion = new LinkedList<KeycloakTransaction>();
private boolean active;
private boolean rollback;
private KeycloakSession session;
private JTAPolicy jtaPolicy = JTAPolicy.REQUIRES_NEW;
public DefaultKeycloakTransactionManager(KeycloakSession session) {
this.session = session;
}
@Override
public void enlist(KeycloakTransaction transaction) {
@ -72,31 +62,12 @@ public class DefaultKeycloakTransactionManager implements KeycloakTransactionMan
prepare.add(transaction);
}
@Override
public JTAPolicy getJTAPolicy() {
return jtaPolicy;
}
@Override
public void setJTAPolicy(JTAPolicy policy) {
jtaPolicy = policy;
}
@Override
public void begin() {
if (active) {
throw new IllegalStateException("Transaction already active");
}
if (jtaPolicy == JTAPolicy.REQUIRES_NEW) {
JtaTransactionManagerLookup jtaLookup = session.getProvider(JtaTransactionManagerLookup.class);
TransactionManager tm = jtaLookup.getTransactionManager();
if (tm != null) {
enlist(new JtaTransactionWrapper(tm));
}
}
for (KeycloakTransaction tx : transactions) {
tx.begin();
}

View file

@ -45,13 +45,10 @@ import org.keycloak.services.scheduled.ClusterAwareScheduledTaskRunner;
import org.keycloak.services.util.JsonConfigProvider;
import org.keycloak.services.util.ObjectMapperResolver;
import org.keycloak.timer.TimerProvider;
import org.keycloak.transaction.JtaTransactionManagerLookup;
import org.keycloak.util.JsonSerialization;
import org.keycloak.common.util.SystemEnvProperties;
import javax.servlet.ServletContext;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriInfo;
@ -158,28 +155,11 @@ public class KeycloakApplication extends Application {
// Migrate model, bootstrap master realm, import realms and create admin user. This is done with acquired dbLock
protected ExportImportManager migrateAndBootstrap() {
ExportImportManager exportImportManager;
logger.debug("Calling migrateModel");
migrateModel();
logger.debug("bootstrap");
KeycloakSession session = sessionFactory.create();
try {
session.getTransactionManager().begin();
JtaTransactionManagerLookup lookup = (JtaTransactionManagerLookup) sessionFactory.getProviderFactory(JtaTransactionManagerLookup.class);
if (lookup != null) {
if (lookup.getTransactionManager() != null) {
try {
Transaction transaction = lookup.getTransactionManager().getTransaction();
logger.debugv("bootstrap current transaction? {0}", transaction != null);
if (transaction != null) {
logger.debugv("bootstrap current transaction status? {0}", transaction.getStatus());
}
} catch (SystemException e) {
throw new RuntimeException(e);
}
}
}
ApplianceBootstrap applianceBootstrap = new ApplianceBootstrap(session);
exportImportManager = new ExportImportManager(session);

View file

@ -34,6 +34,8 @@ import org.keycloak.events.admin.OperationType;
import org.keycloak.events.admin.ResourceType;
import org.keycloak.exportimport.ClientDescriptionConverter;
import org.keycloak.exportimport.ClientDescriptionConverterFactory;
import org.keycloak.jose.jws.JWSBuilder;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.models.ClientModel;
import org.keycloak.models.GroupModel;
import org.keycloak.models.KeycloakSession;
@ -42,7 +44,7 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.cache.CacheRealmProvider;
import org.keycloak.models.cache.UserCache;
import org.keycloak.models.cache.CacheUserProvider;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.models.utils.RepresentationToModel;
@ -53,6 +55,7 @@ import org.keycloak.representations.idm.AdminEventRepresentation;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.EventRepresentation;
import org.keycloak.representations.idm.GroupRepresentation;
import org.keycloak.representations.idm.IdentityProviderRepresentation;
import org.keycloak.representations.idm.PartialImportRepresentation;
import org.keycloak.representations.idm.RealmEventsConfigRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
@ -64,6 +67,7 @@ import org.keycloak.services.ServicesLogger;
import org.keycloak.services.managers.UsersSyncManager;
import org.keycloak.services.ErrorResponse;
import org.keycloak.services.resources.admin.RealmAuth.Resource;
import org.keycloak.timer.TimerProvider;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
@ -81,6 +85,8 @@ import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriInfo;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.text.ParseException;
import java.text.SimpleDateFormat;
@ -860,7 +866,7 @@ public class RealmAdminResource {
public void clearUserCache() {
auth.requireManage();
UserCache cache = session.getProvider(UserCache.class);
CacheUserProvider cache = session.getProvider(CacheUserProvider.class);
if (cache != null) {
cache.clear();
}

View file

@ -184,8 +184,6 @@ public class UsersResource {
return ErrorResponse.exists("User is read only!");
} catch (ModelException me) {
return ErrorResponse.exists("Could not update user!");
} catch (Exception me) { // JPA may be committed by JTA which can't
return ErrorResponse.exists("Could not update user!");
}
}

View file

@ -16,9 +16,7 @@
*/
package org.keycloak.transaction;
import org.jboss.logging.Logger;
import org.keycloak.models.KeycloakTransaction;
import org.keycloak.storage.UserStorageManager;
import javax.transaction.InvalidTransactionException;
import javax.transaction.NotSupportedException;
@ -33,22 +31,16 @@ import javax.transaction.UserTransaction;
* @version $Revision: 1 $
*/
public class JtaTransactionWrapper implements KeycloakTransaction {
private static final Logger logger = Logger.getLogger(JtaTransactionWrapper.class);
protected TransactionManager tm;
protected Transaction ut;
protected Transaction suspended;
protected Exception ended;
public JtaTransactionWrapper(TransactionManager tm) {
this.tm = tm;
try {
suspended = tm.suspend();
logger.debug("new JtaTransactionWrapper");
logger.debugv("was existing? {0}", suspended != null);
tm.begin();
ut = tm.getTransaction();
//ended = new Exception();
} catch (Exception e) {
throw new RuntimeException(e);
}
@ -61,20 +53,16 @@ public class JtaTransactionWrapper implements KeycloakTransaction {
@Override
public void commit() {
try {
logger.debug("JtaTransactionWrapper commit");
tm.commit();
ut.commit();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
end();
}
}
@Override
public void rollback() {
try {
logger.debug("JtaTransactionWrapper rollback");
tm.rollback();
ut.rollback();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
@ -86,7 +74,7 @@ public class JtaTransactionWrapper implements KeycloakTransaction {
@Override
public void setRollbackOnly() {
try {
tm.setRollbackOnly();
ut.setRollbackOnly();
} catch (Exception e) {
throw new RuntimeException(e);
}
@ -95,7 +83,7 @@ public class JtaTransactionWrapper implements KeycloakTransaction {
@Override
public boolean getRollbackOnly() {
try {
return tm.getStatus() == Status.STATUS_MARKED_ROLLBACK;
return ut.getStatus() == Status.STATUS_MARKED_ROLLBACK;
} catch (Exception e) {
throw new RuntimeException(e);
}
@ -104,28 +92,15 @@ public class JtaTransactionWrapper implements KeycloakTransaction {
@Override
public boolean isActive() {
try {
return tm.getStatus() == Status.STATUS_ACTIVE;
return ut.getStatus() == Status.STATUS_ACTIVE;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/*
@Override
protected void finalize() throws Throwable {
if (ended != null) {
logger.error("TX didn't close at position", ended);
}
}
*/
protected void end() {
ended = null;
logger.debug("JtaTransactionWrapper end");
if (suspended != null) {
try {
logger.debug("JtaTransactionWrapper resuming suspended");
tm.resume(suspended);
} catch (Exception e) {
throw new RuntimeException(e);

View file

@ -18,4 +18,5 @@
org.keycloak.exportimport.ClientDescriptionConverterSpi
org.keycloak.wellknown.WellKnownSpi
org.keycloak.services.clientregistration.ClientRegistrationSpi
org.keycloak.transaction.TransactionManagerLookupSpi

View file

@ -29,7 +29,7 @@
<password>sa</password>
</security>
</datasource>
<datasource jndi-name="java:jboss/datasources/KeycloakDS" pool-name="KeycloakDS" enabled="true" use-java-context="true">
<datasource jndi-name="java:jboss/datasources/KeycloakDS" jta="false" pool-name="KeycloakDS" enabled="true" use-java-context="true">
<?KEYCLOAK_DS_CONNECTION_URL?>
<driver>h2</driver>
<security>