Added JPA connection provider and added basic support for multiple transactions to KeycloakSession
This commit is contained in:
parent
8cc7676335
commit
d625fb014c
120 changed files with 1867 additions and 1291 deletions
|
@ -25,6 +25,12 @@
|
|||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-connections-jpa</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-audit-api</artifactId>
|
||||
|
|
|
@ -41,44 +41,28 @@ public class JpaAuditProvider implements AuditProvider {
|
|||
|
||||
@Override
|
||||
public void clear() {
|
||||
beginTx();
|
||||
em.createQuery("delete from EventEntity").executeUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear(String realmId) {
|
||||
beginTx();
|
||||
em.createQuery("delete from EventEntity where realmId = :realmId").setParameter("realmId", realmId).executeUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear(String realmId, long olderThan) {
|
||||
beginTx();
|
||||
em.createQuery("delete from EventEntity where realmId = :realmId and time < :time").setParameter("realmId", realmId).setParameter("time", olderThan).executeUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEvent(Event event) {
|
||||
if (includedEvents.contains(event.getEvent())) {
|
||||
beginTx();
|
||||
em.persist(convert(event));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (tx != null) {
|
||||
tx.commit();
|
||||
}
|
||||
|
||||
em.close();
|
||||
}
|
||||
|
||||
private void beginTx() {
|
||||
if (tx == null) {
|
||||
tx = em.getTransaction();
|
||||
tx.begin();
|
||||
}
|
||||
}
|
||||
|
||||
static EventEntity convert(Event o) {
|
||||
|
|
|
@ -4,6 +4,7 @@ import org.keycloak.Config;
|
|||
import org.keycloak.audit.AuditProvider;
|
||||
import org.keycloak.audit.AuditProviderFactory;
|
||||
import org.keycloak.audit.EventType;
|
||||
import org.keycloak.connections.jpa.JpaConnectionProvider;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.util.JpaUtils;
|
||||
|
||||
|
@ -18,19 +19,17 @@ import java.util.Set;
|
|||
public class JpaAuditProviderFactory implements AuditProviderFactory {
|
||||
|
||||
public static final String ID = "jpa";
|
||||
private EntityManagerFactory emf;
|
||||
|
||||
private Set<EventType> includedEvents = new HashSet<EventType>();
|
||||
|
||||
@Override
|
||||
public AuditProvider create(KeycloakSession session) {
|
||||
return new JpaAuditProvider(emf.createEntityManager(), includedEvents);
|
||||
JpaConnectionProvider connection = session.getProvider(JpaConnectionProvider.class);
|
||||
return new JpaAuditProvider(connection.getEntityManager(), includedEvents);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
emf = Persistence.createEntityManagerFactory("jpa-keycloak-audit-store", JpaUtils.getHibernateProperties());
|
||||
|
||||
String[] include = config.getArray("include-events");
|
||||
if (include != null) {
|
||||
for (String i : include) {
|
||||
|
@ -52,7 +51,6 @@ public class JpaAuditProviderFactory implements AuditProviderFactory {
|
|||
|
||||
@Override
|
||||
public void close() {
|
||||
emf.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
package org.keycloak.audit.jpa;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.keycloak.audit.tests.AbstractAuditProviderTest;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
@Ignore
|
||||
public class JpaAuditProviderTest extends AbstractAuditProviderTest {
|
||||
|
||||
@Override
|
||||
|
|
|
@ -25,6 +25,12 @@
|
|||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-connections-mongo</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-audit-api</artifactId>
|
||||
|
|
|
@ -1,19 +1,15 @@
|
|||
package org.keycloak.audit.mongo;
|
||||
|
||||
import com.mongodb.DB;
|
||||
import com.mongodb.MongoClient;
|
||||
import com.mongodb.MongoCredential;
|
||||
import com.mongodb.ServerAddress;
|
||||
import com.mongodb.DBCollection;
|
||||
import com.mongodb.WriteConcern;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.audit.AuditProvider;
|
||||
import org.keycloak.audit.AuditProviderFactory;
|
||||
import org.keycloak.audit.EventType;
|
||||
import org.keycloak.connections.mongo.MongoConnectionProvider;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
|
@ -25,45 +21,21 @@ public class MongoAuditProviderFactory implements AuditProviderFactory {
|
|||
protected static final Logger logger = Logger.getLogger(MongoAuditProviderFactory.class);
|
||||
|
||||
public static final String ID = "mongo";
|
||||
private MongoClient client;
|
||||
private DB db;
|
||||
|
||||
private Set<EventType> includedEvents = new HashSet<EventType>();
|
||||
|
||||
@Override
|
||||
public AuditProvider create(KeycloakSession session) {
|
||||
return new MongoAuditProvider(db.getCollection("audit"), includedEvents);
|
||||
MongoConnectionProvider connection = session.getProvider(MongoConnectionProvider.class);
|
||||
|
||||
DBCollection collection = connection.getDB().getCollection("audit");
|
||||
collection.setWriteConcern(WriteConcern.UNACKNOWLEDGED);
|
||||
|
||||
return new MongoAuditProvider(collection, includedEvents);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
try {
|
||||
String host = config.get("host", ServerAddress.defaultHost());
|
||||
int port = config.getInt("port", ServerAddress.defaultPort());
|
||||
String dbName = config.get("db", "keycloak-audit");
|
||||
boolean clearOnStartup = config.getBoolean("clearOnStartup", false);
|
||||
|
||||
String user = config.get("user");
|
||||
String password = config.get("password");
|
||||
if (user != null && password != null) {
|
||||
MongoCredential credential = MongoCredential.createMongoCRCredential(user, dbName, password.toCharArray());
|
||||
client = new MongoClient(new ServerAddress(host, port), Collections.singletonList(credential));
|
||||
} else {
|
||||
client = new MongoClient(host, port);
|
||||
}
|
||||
|
||||
client.setWriteConcern(WriteConcern.UNACKNOWLEDGED);
|
||||
db = client.getDB(dbName);
|
||||
|
||||
if (clearOnStartup) {
|
||||
db.getCollection("audit").drop();
|
||||
}
|
||||
|
||||
logger.infof("Initialized mongo audit. host: %s, port: %d, db: %s, clearOnStartup: %b", host, port, dbName, clearOnStartup);
|
||||
} catch (UnknownHostException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
String[] include = config.getArray("include-events");
|
||||
if (include != null) {
|
||||
for (String i : include) {
|
||||
|
@ -85,7 +57,6 @@ public class MongoAuditProviderFactory implements AuditProviderFactory {
|
|||
|
||||
@Override
|
||||
public void close() {
|
||||
client.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
package org.keycloak.audit.mongo;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.keycloak.audit.tests.AbstractAuditProviderTest;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
@Ignore
|
||||
public class MongoAuditProviderTest extends AbstractAuditProviderTest {
|
||||
|
||||
@Override
|
||||
|
|
39
connections/jpa/pom.xml
Executable file
39
connections/jpa/pom.xml
Executable file
|
@ -0,0 +1,39 @@
|
|||
<?xml version="1.0"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.0-beta-4-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-connections-jpa</artifactId>
|
||||
<name>Keycloak Connections JPA</name>
|
||||
<description/>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-model-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate.javax.persistence</groupId>
|
||||
<artifactId>hibernate-jpa-2.0-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
<artifactId>hibernate-entitymanager</artifactId>
|
||||
<version>${hibernate.entitymanager.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
|
@ -0,0 +1,26 @@
|
|||
package org.keycloak.connections.jpa;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class DefaultJpaConnectionProvider implements JpaConnectionProvider {
|
||||
|
||||
private final EntityManager em;
|
||||
|
||||
public DefaultJpaConnectionProvider(EntityManager em) {
|
||||
this.em = em;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityManager getEntityManager() {
|
||||
return em;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
em.close();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package org.keycloak.connections.jpa;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.util.JpaUtils;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
import javax.persistence.Persistence;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class DefaultJpaConnectionProviderFactory implements JpaConnectionProviderFactory {
|
||||
|
||||
private EntityManagerFactory emf;
|
||||
|
||||
@Override
|
||||
public JpaConnectionProvider create(KeycloakSession session) {
|
||||
EntityManager em = emf.createEntityManager();
|
||||
em = PersistenceExceptionConverter.create(em);
|
||||
session.getTransaction().enlist(new JpaKeycloakTransaction(em));
|
||||
return new DefaultJpaConnectionProvider(em);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
emf.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "default";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
String unitName = config.get("unitName", "jpa-keycloak-identity-store");
|
||||
emf = Persistence.createEntityManagerFactory(unitName, JpaUtils.getHibernateProperties());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package org.keycloak.connections.jpa;
|
||||
|
||||
import org.keycloak.provider.Provider;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public interface JpaConnectionProvider extends Provider {
|
||||
|
||||
EntityManager getEntityManager();
|
||||
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package org.keycloak.connections.jpa;
|
||||
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public interface JpaConnectionProviderFactory extends ProviderFactory<JpaConnectionProvider> {
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package org.keycloak.connections.jpa;
|
||||
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.keycloak.provider.Spi;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class JpaConnectionSpi implements Spi {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "connectionsJpa";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Provider> getProviderClass() {
|
||||
return JpaConnectionProvider.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends ProviderFactory> getProviderFactoryClass() {
|
||||
return JpaConnectionProviderFactory.class;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
package org.keycloak.connections.jpa;
|
||||
|
||||
import org.keycloak.models.KeycloakTransaction;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class JpaKeycloakTransaction implements KeycloakTransaction {
|
||||
|
||||
protected EntityManager em;
|
||||
|
||||
public JpaKeycloakTransaction(EntityManager em) {
|
||||
this.em = em;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void begin() {
|
||||
em.getTransaction().begin();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commit() {
|
||||
try {
|
||||
em.getTransaction().commit();
|
||||
} catch (PersistenceException e) {
|
||||
throw PersistenceExceptionConverter.convert(e.getCause() != null ? e.getCause() : e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback() {
|
||||
em.getTransaction().rollback();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRollbackOnly() {
|
||||
em.getTransaction().setRollbackOnly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getRollbackOnly() {
|
||||
return em.getTransaction().getRollbackOnly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return em.getTransaction().isActive();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package org.keycloak.connections.jpa;
|
||||
|
||||
import org.hibernate.exception.ConstraintViolationException;
|
||||
import org.keycloak.models.ModelDuplicateException;
|
||||
import org.keycloak.models.ModelException;
|
||||
|
||||
import javax.persistence.EntityExistsException;
|
||||
import javax.persistence.EntityManager;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class PersistenceExceptionConverter implements InvocationHandler {
|
||||
|
||||
private EntityManager em;
|
||||
|
||||
public static EntityManager create(EntityManager em) {
|
||||
return (EntityManager) Proxy.newProxyInstance(EntityManager.class.getClassLoader(), new Class[]{EntityManager.class}, new PersistenceExceptionConverter(em));
|
||||
}
|
||||
|
||||
private PersistenceExceptionConverter(EntityManager em) {
|
||||
this.em = em;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
try {
|
||||
return method.invoke(em, args);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw convert(e.getCause());
|
||||
}
|
||||
}
|
||||
|
||||
public static ModelException convert(Throwable t) {
|
||||
if (t.getCause() != null && t.getCause() instanceof ConstraintViolationException) {
|
||||
throw new ModelDuplicateException(t);
|
||||
} if (t instanceof EntityExistsException) {
|
||||
throw new ModelDuplicateException(t);
|
||||
} else {
|
||||
throw new ModelException(t);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
org.keycloak.connections.jpa.DefaultJpaConnectionProviderFactory
|
|
@ -0,0 +1 @@
|
|||
org.keycloak.connections.jpa.JpaConnectionSpi
|
37
connections/mongo/pom.xml
Executable file
37
connections/mongo/pom.xml
Executable file
|
@ -0,0 +1,37 @@
|
|||
<?xml version="1.0"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.0-beta-4-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-connections-mongo</artifactId>
|
||||
<name>Keycloak Connections Mongo</name>
|
||||
<description/>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-model-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mongodb</groupId>
|
||||
<artifactId>mongo-java-driver</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
|
@ -0,0 +1,96 @@
|
|||
package org.keycloak.connections.mongo;
|
||||
|
||||
import com.mongodb.DB;
|
||||
import com.mongodb.MongoClient;
|
||||
import com.mongodb.MongoCredential;
|
||||
import com.mongodb.ServerAddress;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.connections.mongo.api.MongoStore;
|
||||
import org.keycloak.connections.mongo.impl.MongoStoreImpl;
|
||||
import org.keycloak.connections.mongo.impl.context.TransactionMongoStoreInvocationContext;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class DefaultMongoConnectionFactoryProvider implements MongoConnectionProviderFactory {
|
||||
|
||||
// TODO Make configurable
|
||||
private String[] entities = new String[]{
|
||||
"org.keycloak.models.mongo.keycloak.entities.MongoRealmEntity",
|
||||
"org.keycloak.models.mongo.keycloak.entities.MongoUserEntity",
|
||||
"org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity",
|
||||
"org.keycloak.models.entities.RequiredCredentialEntity",
|
||||
"org.keycloak.models.entities.AuthenticationProviderEntity",
|
||||
"org.keycloak.models.entities.CredentialEntity",
|
||||
"org.keycloak.models.entities.SocialLinkEntity",
|
||||
"org.keycloak.models.entities.AuthenticationLinkEntity",
|
||||
"org.keycloak.models.mongo.keycloak.entities.MongoApplicationEntity",
|
||||
"org.keycloak.models.mongo.keycloak.entities.MongoOAuthClientEntity",
|
||||
"org.keycloak.models.sessions.mongo.entities.MongoUsernameLoginFailureEntity",
|
||||
"org.keycloak.models.sessions.mongo.entities.MongoUserSessionEntity"
|
||||
};
|
||||
|
||||
private static final Logger logger = Logger.getLogger(DefaultMongoConnectionFactoryProvider.class);
|
||||
|
||||
private MongoClient client;
|
||||
|
||||
private MongoStore mongoStore;
|
||||
private DB db;
|
||||
|
||||
@Override
|
||||
public MongoConnectionProvider create(KeycloakSession session) {
|
||||
TransactionMongoStoreInvocationContext invocationContext = new TransactionMongoStoreInvocationContext(mongoStore);
|
||||
session.getTransaction().enlist(new MongoKeycloakTransaction(invocationContext));
|
||||
return new DefaultMongoConnectionProvider(db, mongoStore, invocationContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
try {
|
||||
String host = config.get("host", ServerAddress.defaultHost());
|
||||
int port = config.getInt("port", ServerAddress.defaultPort());
|
||||
String dbName = config.get("db", "keycloak");
|
||||
boolean clearOnStartup = config.getBoolean("clearOnStartup", false);
|
||||
|
||||
String user = config.get("user");
|
||||
String password = config.get("password");
|
||||
if (user != null && password != null) {
|
||||
MongoCredential credential = MongoCredential.createMongoCRCredential(user, dbName, password.toCharArray());
|
||||
client = new MongoClient(new ServerAddress(host, port), Collections.singletonList(credential));
|
||||
} else {
|
||||
client = new MongoClient(host, port);
|
||||
}
|
||||
|
||||
this.db = client.getDB(dbName);
|
||||
|
||||
this.mongoStore = new MongoStoreImpl(db, clearOnStartup, getManagedEntities());
|
||||
|
||||
logger.infof("Initialized mongo model. host: %s, port: %d, db: %s, clearOnStartup: %b", host, port, dbName, clearOnStartup);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private Class[] getManagedEntities() throws ClassNotFoundException {
|
||||
Class[] entityClasses = new Class[entities.length];
|
||||
for (int i = 0; i < entities.length; i++) {
|
||||
entityClasses[i] = Thread.currentThread().getContextClassLoader().loadClass(entities[i]);
|
||||
}
|
||||
return entityClasses;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
client.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "default";
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package org.keycloak.connections.mongo;
|
||||
|
||||
import com.mongodb.DB;
|
||||
import org.keycloak.connections.mongo.api.MongoStore;
|
||||
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class DefaultMongoConnectionProvider implements MongoConnectionProvider {
|
||||
|
||||
private DB db;
|
||||
private MongoStore mongoStore;
|
||||
private MongoStoreInvocationContext invocationContext;
|
||||
|
||||
public DefaultMongoConnectionProvider(DB db, MongoStore mongoStore, MongoStoreInvocationContext invocationContext) {
|
||||
this.db = db;
|
||||
this.mongoStore = mongoStore;
|
||||
this.invocationContext = invocationContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DB getDB() {
|
||||
return db;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MongoStore getMongoStore() {
|
||||
return mongoStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MongoStoreInvocationContext getInvocationContext() {
|
||||
return invocationContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package org.keycloak.connections.mongo;
|
||||
|
||||
import com.mongodb.DB;
|
||||
import org.keycloak.connections.mongo.api.MongoStore;
|
||||
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.provider.Provider;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public interface MongoConnectionProvider extends Provider {
|
||||
|
||||
DB getDB();
|
||||
|
||||
MongoStore getMongoStore();
|
||||
|
||||
MongoStoreInvocationContext getInvocationContext();
|
||||
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package org.keycloak.connections.mongo;
|
||||
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public interface MongoConnectionProviderFactory extends ProviderFactory<MongoConnectionProvider> {
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package org.keycloak.connections.mongo;
|
||||
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.keycloak.provider.Spi;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class MongoConnectionSpi implements Spi {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "connectionsMongo";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Provider> getProviderClass() {
|
||||
return MongoConnectionProvider.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends ProviderFactory> getProviderFactoryClass() {
|
||||
return MongoConnectionProviderFactory.class;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
package org.keycloak.models.mongo.keycloak.adapters;
|
||||
package org.keycloak.connections.mongo;
|
||||
|
||||
import com.mongodb.MongoException;
|
||||
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.connections.mongo.impl.MongoStoreImpl;
|
||||
import org.keycloak.models.KeycloakTransaction;
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.models.mongo.impl.MongoStoreImpl;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
|
@ -1,4 +1,4 @@
|
|||
package org.keycloak.models.mongo.api;
|
||||
package org.keycloak.connections.mongo.api;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Inherited;
|
|
@ -1,4 +1,4 @@
|
|||
package org.keycloak.models.mongo.api;
|
||||
package org.keycloak.connections.mongo.api;
|
||||
|
||||
/**
|
||||
* Base interface for object, which is persisted in Mongo
|
|
@ -1,4 +1,4 @@
|
|||
package org.keycloak.models.mongo.api;
|
||||
package org.keycloak.connections.mongo.api;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
|
@ -1,6 +1,6 @@
|
|||
package org.keycloak.models.mongo.api;
|
||||
package org.keycloak.connections.mongo.api;
|
||||
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
||||
|
||||
/**
|
||||
* Entity with Id
|
|
@ -1,4 +1,4 @@
|
|||
package org.keycloak.models.mongo.api;
|
||||
package org.keycloak.connections.mongo.api;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Inherited;
|
|
@ -1,4 +1,4 @@
|
|||
package org.keycloak.models.mongo.api;
|
||||
package org.keycloak.connections.mongo.api;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Inherited;
|
|
@ -1,7 +1,7 @@
|
|||
package org.keycloak.models.mongo.api;
|
||||
package org.keycloak.connections.mongo.api;
|
||||
|
||||
import com.mongodb.DBObject;
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
||||
|
||||
import java.util.List;
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
package org.keycloak.models.mongo.api.context;
|
||||
package org.keycloak.connections.mongo.api.context;
|
||||
|
||||
import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
|
||||
import org.keycloak.models.mongo.api.MongoStore;
|
||||
import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
|
||||
import org.keycloak.connections.mongo.api.MongoStore;
|
||||
|
||||
/**
|
||||
* Context, which provides callback methods to be invoked by MongoStore
|
|
@ -1,4 +1,4 @@
|
|||
package org.keycloak.models.mongo.api.context;
|
||||
package org.keycloak.connections.mongo.api.context;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
|
@ -1,4 +1,4 @@
|
|||
package org.keycloak.models.mongo.api.types;
|
||||
package org.keycloak.connections.mongo.api.types;
|
||||
|
||||
/**
|
||||
* SPI object to convert object from application type to database type and vice versa. Shouldn't be directly used by application.
|
|
@ -1,4 +1,4 @@
|
|||
package org.keycloak.models.mongo.api.types;
|
||||
package org.keycloak.connections.mongo.api.types;
|
||||
|
||||
import java.util.List;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package org.keycloak.models.mongo.api.types;
|
||||
package org.keycloak.connections.mongo.api.types;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
|
@ -1,6 +1,5 @@
|
|||
package org.keycloak.models.mongo.impl;
|
||||
package org.keycloak.connections.mongo.impl;
|
||||
|
||||
import org.keycloak.models.mongo.api.MongoEntity;
|
||||
import org.keycloak.models.utils.reflection.Property;
|
||||
|
||||
import java.util.Collection;
|
|
@ -1,4 +1,4 @@
|
|||
package org.keycloak.models.mongo.impl;
|
||||
package org.keycloak.connections.mongo.impl;
|
||||
|
||||
import com.mongodb.BasicDBList;
|
||||
import com.mongodb.BasicDBObject;
|
||||
|
@ -8,31 +8,29 @@ import com.mongodb.DBCursor;
|
|||
import com.mongodb.DBObject;
|
||||
import com.mongodb.MongoException;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.models.ModelException;
|
||||
import org.keycloak.connections.mongo.api.MongoCollection;
|
||||
import org.keycloak.connections.mongo.api.MongoEntity;
|
||||
import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
|
||||
import org.keycloak.connections.mongo.api.MongoIndex;
|
||||
import org.keycloak.connections.mongo.api.MongoIndexes;
|
||||
import org.keycloak.connections.mongo.api.MongoStore;
|
||||
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.connections.mongo.api.context.MongoTask;
|
||||
import org.keycloak.connections.mongo.api.types.Mapper;
|
||||
import org.keycloak.connections.mongo.api.types.MapperContext;
|
||||
import org.keycloak.connections.mongo.api.types.MapperRegistry;
|
||||
import org.keycloak.connections.mongo.impl.types.BasicDBListMapper;
|
||||
import org.keycloak.connections.mongo.impl.types.BasicDBObjectMapper;
|
||||
import org.keycloak.connections.mongo.impl.types.BasicDBObjectToMapMapper;
|
||||
import org.keycloak.connections.mongo.impl.types.EnumToStringMapper;
|
||||
import org.keycloak.connections.mongo.impl.types.ListMapper;
|
||||
import org.keycloak.connections.mongo.impl.types.MapMapper;
|
||||
import org.keycloak.connections.mongo.impl.types.MongoEntityMapper;
|
||||
import org.keycloak.connections.mongo.impl.types.SimpleMapper;
|
||||
import org.keycloak.connections.mongo.impl.types.StringToEnumMapper;
|
||||
import org.keycloak.models.ModelDuplicateException;
|
||||
import org.keycloak.models.mongo.api.MongoCollection;
|
||||
import org.keycloak.models.mongo.api.MongoEntity;
|
||||
import org.keycloak.models.mongo.api.MongoField;
|
||||
import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
|
||||
import org.keycloak.models.mongo.api.MongoIndex;
|
||||
import org.keycloak.models.mongo.api.MongoIndexes;
|
||||
import org.keycloak.models.mongo.api.MongoStore;
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.models.mongo.api.context.MongoTask;
|
||||
import org.keycloak.models.mongo.api.types.Mapper;
|
||||
import org.keycloak.models.mongo.api.types.MapperContext;
|
||||
import org.keycloak.models.mongo.api.types.MapperRegistry;
|
||||
import org.keycloak.models.mongo.impl.types.BasicDBListMapper;
|
||||
import org.keycloak.models.mongo.impl.types.BasicDBObjectMapper;
|
||||
import org.keycloak.models.mongo.impl.types.BasicDBObjectToMapMapper;
|
||||
import org.keycloak.models.mongo.impl.types.EnumToStringMapper;
|
||||
import org.keycloak.models.mongo.impl.types.ListMapper;
|
||||
import org.keycloak.models.mongo.impl.types.MapMapper;
|
||||
import org.keycloak.models.mongo.impl.types.MongoEntityMapper;
|
||||
import org.keycloak.models.mongo.impl.types.SimpleMapper;
|
||||
import org.keycloak.models.mongo.impl.types.StringToEnumMapper;
|
||||
import org.keycloak.models.ModelException;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.models.utils.reflection.AnnotatedPropertyCriteria;
|
||||
import org.keycloak.models.utils.reflection.Property;
|
||||
import org.keycloak.models.utils.reflection.PropertyQueries;
|
||||
|
||||
|
@ -41,7 +39,6 @@ import java.util.Date;
|
|||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
|
@ -1,10 +1,9 @@
|
|||
package org.keycloak.models.mongo.impl.context;
|
||||
package org.keycloak.connections.mongo.impl.context;
|
||||
|
||||
import org.keycloak.models.mongo.api.MongoEntity;
|
||||
import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
|
||||
import org.keycloak.models.mongo.api.MongoStore;
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.models.mongo.api.context.MongoTask;
|
||||
import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
|
||||
import org.keycloak.connections.mongo.api.MongoStore;
|
||||
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.connections.mongo.api.context.MongoTask;
|
||||
|
||||
/**
|
||||
* Context, which is not doing any postponing of tasks and does not cache anything
|
|
@ -1,4 +1,9 @@
|
|||
package org.keycloak.models.mongo.impl.context;
|
||||
package org.keycloak.connections.mongo.impl.context;
|
||||
|
||||
import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
|
||||
import org.keycloak.connections.mongo.api.MongoStore;
|
||||
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.connections.mongo.api.context.MongoTask;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
@ -6,11 +11,6 @@ import java.util.LinkedHashSet;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
|
||||
import org.keycloak.models.mongo.api.MongoStore;
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.models.mongo.api.context.MongoTask;
|
||||
|
||||
/**
|
||||
* Invocation context, which has some very basic support for transactions, and is able to cache loaded objects.
|
||||
* It always execute all pending update tasks before start searching for other objects
|
|
@ -1,9 +1,9 @@
|
|||
package org.keycloak.models.mongo.impl.types;
|
||||
package org.keycloak.connections.mongo.impl.types;
|
||||
|
||||
import com.mongodb.BasicDBList;
|
||||
import org.keycloak.models.mongo.api.types.Mapper;
|
||||
import org.keycloak.models.mongo.api.types.MapperContext;
|
||||
import org.keycloak.models.mongo.api.types.MapperRegistry;
|
||||
import org.keycloak.connections.mongo.api.types.Mapper;
|
||||
import org.keycloak.connections.mongo.api.types.MapperContext;
|
||||
import org.keycloak.connections.mongo.api.types.MapperRegistry;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
|
@ -1,22 +1,21 @@
|
|||
package org.keycloak.models.mongo.impl.types;
|
||||
package org.keycloak.connections.mongo.impl.types;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
|
||||
import org.keycloak.connections.mongo.api.types.Mapper;
|
||||
import org.keycloak.connections.mongo.api.types.MapperContext;
|
||||
import org.keycloak.connections.mongo.api.types.MapperRegistry;
|
||||
import org.keycloak.connections.mongo.impl.EntityInfo;
|
||||
import org.keycloak.connections.mongo.impl.MongoStoreImpl;
|
||||
import org.keycloak.models.utils.reflection.Property;
|
||||
import org.keycloak.models.utils.reflection.Types;
|
||||
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.models.mongo.api.MongoEntity;
|
||||
import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
|
||||
import org.keycloak.models.mongo.api.types.Mapper;
|
||||
import org.keycloak.models.mongo.api.types.MapperContext;
|
||||
import org.keycloak.models.mongo.api.types.MapperRegistry;
|
||||
import org.keycloak.models.mongo.impl.MongoStoreImpl;
|
||||
import org.keycloak.models.mongo.impl.EntityInfo;
|
||||
import org.keycloak.models.utils.reflection.Property;
|
||||
import org.keycloak.models.utils.reflection.Types;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
|
@ -1,12 +1,12 @@
|
|||
package org.keycloak.models.mongo.impl.types;
|
||||
package org.keycloak.connections.mongo.impl.types;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import org.keycloak.connections.mongo.api.types.Mapper;
|
||||
import org.keycloak.connections.mongo.api.types.MapperContext;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import org.keycloak.models.mongo.api.types.Mapper;
|
||||
import org.keycloak.models.mongo.api.types.MapperContext;
|
||||
|
||||
/**
|
||||
* For now, there is support just for convert to Map<String, String>
|
||||
*
|
|
@ -1,7 +1,7 @@
|
|||
package org.keycloak.models.mongo.impl.types;
|
||||
package org.keycloak.connections.mongo.impl.types;
|
||||
|
||||
import org.keycloak.models.mongo.api.types.Mapper;
|
||||
import org.keycloak.models.mongo.api.types.MapperContext;
|
||||
import org.keycloak.connections.mongo.api.types.Mapper;
|
||||
import org.keycloak.connections.mongo.api.types.MapperContext;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
|
@ -1,9 +1,9 @@
|
|||
package org.keycloak.models.mongo.impl.types;
|
||||
package org.keycloak.connections.mongo.impl.types;
|
||||
|
||||
import com.mongodb.BasicDBList;
|
||||
import org.keycloak.models.mongo.api.types.Mapper;
|
||||
import org.keycloak.models.mongo.api.types.MapperContext;
|
||||
import org.keycloak.models.mongo.api.types.MapperRegistry;
|
||||
import org.keycloak.connections.mongo.api.types.Mapper;
|
||||
import org.keycloak.connections.mongo.api.types.MapperContext;
|
||||
import org.keycloak.connections.mongo.api.types.MapperRegistry;
|
||||
|
||||
import java.util.List;
|
||||
|
|
@ -1,12 +1,12 @@
|
|||
package org.keycloak.models.mongo.impl.types;
|
||||
package org.keycloak.connections.mongo.impl.types;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import org.keycloak.connections.mongo.api.types.Mapper;
|
||||
import org.keycloak.connections.mongo.api.types.MapperContext;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import org.keycloak.models.mongo.api.types.Mapper;
|
||||
import org.keycloak.models.mongo.api.types.MapperContext;
|
||||
|
||||
/**
|
||||
* For now, we support just convert from Map<String, String>
|
||||
*
|
|
@ -1,11 +1,11 @@
|
|||
package org.keycloak.models.mongo.impl.types;
|
||||
package org.keycloak.connections.mongo.impl.types;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import org.keycloak.models.mongo.api.types.Mapper;
|
||||
import org.keycloak.models.mongo.api.types.MapperContext;
|
||||
import org.keycloak.models.mongo.api.types.MapperRegistry;
|
||||
import org.keycloak.models.mongo.impl.MongoStoreImpl;
|
||||
import org.keycloak.models.mongo.impl.EntityInfo;
|
||||
import org.keycloak.connections.mongo.api.types.Mapper;
|
||||
import org.keycloak.connections.mongo.api.types.MapperContext;
|
||||
import org.keycloak.connections.mongo.api.types.MapperRegistry;
|
||||
import org.keycloak.connections.mongo.impl.EntityInfo;
|
||||
import org.keycloak.connections.mongo.impl.MongoStoreImpl;
|
||||
import org.keycloak.models.utils.reflection.Property;
|
||||
|
||||
import java.util.Collection;
|
|
@ -1,7 +1,7 @@
|
|||
package org.keycloak.models.mongo.impl.types;
|
||||
package org.keycloak.connections.mongo.impl.types;
|
||||
|
||||
import org.keycloak.models.mongo.api.types.Mapper;
|
||||
import org.keycloak.models.mongo.api.types.MapperContext;
|
||||
import org.keycloak.connections.mongo.api.types.Mapper;
|
||||
import org.keycloak.connections.mongo.api.types.MapperContext;
|
||||
|
||||
/**
|
||||
* Just returns input
|
|
@ -1,7 +1,7 @@
|
|||
package org.keycloak.models.mongo.impl.types;
|
||||
package org.keycloak.connections.mongo.impl.types;
|
||||
|
||||
import org.keycloak.models.mongo.api.types.Mapper;
|
||||
import org.keycloak.models.mongo.api.types.MapperContext;
|
||||
import org.keycloak.connections.mongo.api.types.Mapper;
|
||||
import org.keycloak.connections.mongo.api.types.MapperContext;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
|
@ -0,0 +1 @@
|
|||
org.keycloak.connections.mongo.DefaultMongoConnectionFactoryProvider
|
|
@ -0,0 +1 @@
|
|||
org.keycloak.connections.mongo.MongoConnectionSpi
|
31
connections/pom.xml
Executable file
31
connections/pom.xml
Executable file
|
@ -0,0 +1,31 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.0-beta-4-SNAPSHOT</version>
|
||||
</parent>
|
||||
<name>Connections Parent</name>
|
||||
<description/>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-connections-pom</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<modules>
|
||||
<module>jpa</module>
|
||||
<module>mongo</module>
|
||||
</modules>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
|
@ -173,62 +173,62 @@
|
|||
</plugin>
|
||||
|
||||
<!-- Postpone tests to "integration-test" phase, so that we can bootstrap embedded mongo on 27018 before running tests -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>test</id>
|
||||
<phase>integration-test</phase>
|
||||
<goals>
|
||||
<goal>test</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<systemPropertyVariables>
|
||||
<keycloak.model.mongo.host>${keycloak.model.mongo.host}</keycloak.model.mongo.host>
|
||||
<keycloak.model.mongo.port>${keycloak.model.mongo.port}</keycloak.model.mongo.port>
|
||||
<keycloak.model.mongo.db>${keycloak.model.mongo.db}</keycloak.model.mongo.db>
|
||||
<keycloak.model.mongo.clearOnStartup>${keycloak.model.mongo.clearOnStartup}</keycloak.model.mongo.clearOnStartup>
|
||||
<keycloak.model.mongo.bindIp>${keycloak.model.mongo.bindIp}</keycloak.model.mongo.bindIp>
|
||||
</systemPropertyVariables>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>default-test</id>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!--<plugin>-->
|
||||
<!--<groupId>org.apache.maven.plugins</groupId>-->
|
||||
<!--<artifactId>maven-surefire-plugin</artifactId>-->
|
||||
<!--<executions>-->
|
||||
<!--<execution>-->
|
||||
<!--<id>test</id>-->
|
||||
<!--<phase>integration-test</phase>-->
|
||||
<!--<goals>-->
|
||||
<!--<goal>test</goal>-->
|
||||
<!--</goals>-->
|
||||
<!--<configuration>-->
|
||||
<!--<systemPropertyVariables>-->
|
||||
<!--<keycloak.model.mongo.host>${keycloak.model.mongo.host}</keycloak.model.mongo.host>-->
|
||||
<!--<keycloak.model.mongo.port>${keycloak.model.mongo.port}</keycloak.model.mongo.port>-->
|
||||
<!--<keycloak.model.mongo.db>${keycloak.model.mongo.db}</keycloak.model.mongo.db>-->
|
||||
<!--<keycloak.model.mongo.clearOnStartup>${keycloak.model.mongo.clearOnStartup}</keycloak.model.mongo.clearOnStartup>-->
|
||||
<!--<keycloak.model.mongo.bindIp>${keycloak.model.mongo.bindIp}</keycloak.model.mongo.bindIp>-->
|
||||
<!--</systemPropertyVariables>-->
|
||||
<!--</configuration>-->
|
||||
<!--</execution>-->
|
||||
<!--<execution>-->
|
||||
<!--<id>default-test</id>-->
|
||||
<!--<configuration>-->
|
||||
<!--<skip>true</skip>-->
|
||||
<!--</configuration>-->
|
||||
<!--</execution>-->
|
||||
<!--</executions>-->
|
||||
<!--</plugin>-->
|
||||
|
||||
<!-- Embedded mongo -->
|
||||
<plugin>
|
||||
<groupId>com.github.joelittlejohn.embedmongo</groupId>
|
||||
<artifactId>embedmongo-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>start-mongodb</id>
|
||||
<phase>pre-integration-test</phase>
|
||||
<goals>
|
||||
<goal>start</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<port>${keycloak.model.mongo.port}</port>
|
||||
<logging>file</logging>
|
||||
<logFile>${project.build.directory}/mongodb.log</logFile>
|
||||
<bindIp>${keycloak.model.mongo.bindIp}</bindIp>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>stop-mongodb</id>
|
||||
<phase>post-integration-test</phase>
|
||||
<goals>
|
||||
<goal>stop</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!--<plugin>-->
|
||||
<!--<groupId>com.github.joelittlejohn.embedmongo</groupId>-->
|
||||
<!--<artifactId>embedmongo-maven-plugin</artifactId>-->
|
||||
<!--<executions>-->
|
||||
<!--<execution>-->
|
||||
<!--<id>start-mongodb</id>-->
|
||||
<!--<phase>pre-integration-test</phase>-->
|
||||
<!--<goals>-->
|
||||
<!--<goal>start</goal>-->
|
||||
<!--</goals>-->
|
||||
<!--<configuration>-->
|
||||
<!--<port>${keycloak.model.mongo.port}</port>-->
|
||||
<!--<logging>file</logging>-->
|
||||
<!--<logFile>${project.build.directory}/mongodb.log</logFile>-->
|
||||
<!--<bindIp>${keycloak.model.mongo.bindIp}</bindIp>-->
|
||||
<!--</configuration>-->
|
||||
<!--</execution>-->
|
||||
<!--<execution>-->
|
||||
<!--<id>stop-mongodb</id>-->
|
||||
<!--<phase>post-integration-test</phase>-->
|
||||
<!--<goals>-->
|
||||
<!--<goal>stop</goal>-->
|
||||
<!--</goals>-->
|
||||
<!--</execution>-->
|
||||
<!--</executions>-->
|
||||
<!--</plugin>-->
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.keycloak.exportimport;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.keycloak.exportimport.io.directory.TmpDirExportImportIOProvider;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
|
||||
|
@ -8,6 +9,7 @@ import org.keycloak.models.KeycloakSessionFactory;
|
|||
*
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
@Ignore
|
||||
public class JPAToMongoExportImportTest extends ExportImportTestBase {
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.keycloak.exportimport;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Ignore;
|
||||
import org.keycloak.exportimport.io.zip.EncryptedZIPIOProvider;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
|
||||
|
@ -11,6 +12,7 @@ import java.io.File;
|
|||
*
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
@Ignore
|
||||
public class MongoToJPAExportImportTest extends ExportImportTestBase {
|
||||
|
||||
private static final String zipFile = "keycloak-export.zip";
|
||||
|
|
|
@ -11,9 +11,8 @@ import java.util.Set;
|
|||
* @version $Revision: 1 $
|
||||
*/
|
||||
public interface KeycloakSession {
|
||||
// Note: The reason there are so many query methods here is for layering a cache on top of an persistent KeycloakSession
|
||||
|
||||
KeycloakTransaction getTransaction();
|
||||
KeycloakTransactionManager getTransaction();
|
||||
|
||||
<T extends Provider> T getProvider(Class<T> clazz);
|
||||
|
||||
|
@ -43,5 +42,4 @@ public interface KeycloakSession {
|
|||
|
||||
void close();
|
||||
|
||||
void enlist(KeycloakTransaction transaction);
|
||||
}
|
||||
|
|
|
@ -10,4 +10,5 @@ public interface KeycloakTransaction {
|
|||
void rollback();
|
||||
void setRollbackOnly();
|
||||
boolean getRollbackOnly();
|
||||
boolean isActive();}
|
||||
boolean isActive();
|
||||
}
|
||||
|
|
11
model/api/src/main/java/org/keycloak/models/KeycloakTransactionManager.java
Executable file
11
model/api/src/main/java/org/keycloak/models/KeycloakTransactionManager.java
Executable file
|
@ -0,0 +1,11 @@
|
|||
package org.keycloak.models;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public interface KeycloakTransactionManager extends KeycloakTransaction {
|
||||
|
||||
void enlist(KeycloakTransaction transaction);
|
||||
|
||||
}
|
|
@ -13,8 +13,6 @@ import java.util.Set;
|
|||
public interface ModelProvider extends Provider {
|
||||
// Note: The reason there are so many query methods here is for layering a cache on top of an persistent KeycloakSession
|
||||
|
||||
KeycloakTransaction getTransaction();
|
||||
|
||||
RealmModel createRealm(String name);
|
||||
RealmModel createRealm(String id, String name);
|
||||
RealmModel getRealm(String id);
|
||||
|
|
|
@ -12,12 +12,10 @@ import java.util.Set;
|
|||
*/
|
||||
public interface UserSessionProvider extends Provider {
|
||||
|
||||
KeycloakTransaction getTransaction();
|
||||
|
||||
UserSessionModel createUserSession(RealmModel realm, UserModel user, String ipAddress);
|
||||
UserSessionModel getUserSession(RealmModel realm, String id);
|
||||
List<UserSessionModel> getUserSessions(RealmModel realm, UserModel user);
|
||||
Set<UserSessionModel> getUserSessions(RealmModel realm, ClientModel client);
|
||||
List<UserSessionModel> getUserSessions(RealmModel realm, ClientModel client);
|
||||
int getActiveUserSessions(RealmModel realm, ClientModel client);
|
||||
void removeUserSession(RealmModel realm, UserSessionModel session);
|
||||
void removeUserSessions(RealmModel realm, UserModel user);
|
||||
|
|
|
@ -9,9 +9,11 @@ import org.keycloak.provider.Spi;
|
|||
*/
|
||||
public class UserSessionSpi implements Spi {
|
||||
|
||||
public static final String NAME = "userSessions";
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "userSessions";
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -34,7 +34,6 @@ public class DefaultCacheModelProvider implements CacheModelProvider {
|
|||
protected KeycloakCache cache;
|
||||
protected KeycloakSession session;
|
||||
protected ModelProvider delegate;
|
||||
protected KeycloakTransaction transactionDelegate;
|
||||
protected boolean transactionActive;
|
||||
protected boolean setRollbackOnly;
|
||||
|
||||
|
@ -54,6 +53,8 @@ public class DefaultCacheModelProvider implements CacheModelProvider {
|
|||
public DefaultCacheModelProvider(KeycloakCache cache, KeycloakSession session) {
|
||||
this.cache = cache;
|
||||
this.session = session;
|
||||
|
||||
session.getTransaction().enlist(getTransaction());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -61,13 +62,6 @@ public class DefaultCacheModelProvider implements CacheModelProvider {
|
|||
if (!transactionActive) throw new IllegalStateException("Cannot access delegate without a transaction");
|
||||
if (delegate != null) return delegate;
|
||||
delegate = session.getProvider(ModelProvider.class);
|
||||
transactionDelegate = delegate.getTransaction();
|
||||
if (!transactionDelegate.isActive()) {
|
||||
transactionDelegate.begin();
|
||||
if (setRollbackOnly) {
|
||||
transactionDelegate.setRollbackOnly();
|
||||
}
|
||||
}
|
||||
return delegate;
|
||||
}
|
||||
|
||||
|
@ -115,8 +109,7 @@ public class DefaultCacheModelProvider implements CacheModelProvider {
|
|||
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeycloakTransaction getTransaction() {
|
||||
private KeycloakTransaction getTransaction() {
|
||||
return new KeycloakTransaction() {
|
||||
@Override
|
||||
public void begin() {
|
||||
|
@ -126,33 +119,21 @@ public class DefaultCacheModelProvider implements CacheModelProvider {
|
|||
@Override
|
||||
public void commit() {
|
||||
if (delegate == null) return;
|
||||
try {
|
||||
delegate.getTransaction().commit();
|
||||
if (clearAll) {
|
||||
cache.clear();
|
||||
}
|
||||
} finally {
|
||||
runInvalidations();
|
||||
if (clearAll) {
|
||||
cache.clear();
|
||||
}
|
||||
runInvalidations();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback() {
|
||||
setRollbackOnly = true;
|
||||
if (delegate == null) return;
|
||||
try {
|
||||
delegate.getTransaction().rollback();
|
||||
} finally {
|
||||
runInvalidations();
|
||||
}
|
||||
runInvalidations();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRollbackOnly() {
|
||||
setRollbackOnly = true;
|
||||
if (delegate == null) return;
|
||||
delegate.getTransaction().setRollbackOnly();
|
||||
setRollbackOnly = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -24,9 +24,9 @@ import java.util.Set;
|
|||
public class NoCacheModelProvider implements CacheModelProvider {
|
||||
protected KeycloakSession session;
|
||||
protected ModelProvider delegate;
|
||||
protected KeycloakTransaction transactionDelegate;
|
||||
protected boolean transactionActive;
|
||||
protected boolean setRollbackOnly;
|
||||
// protected KeycloakTransaction transactionDelegate;
|
||||
// protected boolean transactionActive;
|
||||
// protected boolean setRollbackOnly;
|
||||
|
||||
public NoCacheModelProvider(KeycloakSession session) {
|
||||
this.session = session;
|
||||
|
@ -34,16 +34,16 @@ public class NoCacheModelProvider implements CacheModelProvider {
|
|||
|
||||
@Override
|
||||
public ModelProvider getDelegate() {
|
||||
if (!transactionActive) throw new IllegalStateException("Cannot access delegate without a transaction");
|
||||
// if (!transactionActive) throw new IllegalStateException("Cannot access delegate without a transaction");
|
||||
if (delegate != null) return delegate;
|
||||
delegate = session.getProvider(ModelProvider.class);
|
||||
transactionDelegate = delegate.getTransaction();
|
||||
if (!transactionDelegate.isActive()) {
|
||||
transactionDelegate.begin();
|
||||
if (setRollbackOnly) {
|
||||
transactionDelegate.setRollbackOnly();
|
||||
}
|
||||
}
|
||||
// transactionDelegate = delegate.getTransaction();
|
||||
// if (!transactionDelegate.isActive()) {
|
||||
// transactionDelegate.begin();
|
||||
// if (setRollbackOnly) {
|
||||
// transactionDelegate.setRollbackOnly();
|
||||
// }
|
||||
// }
|
||||
return delegate;
|
||||
}
|
||||
|
||||
|
@ -63,53 +63,6 @@ public class NoCacheModelProvider implements CacheModelProvider {
|
|||
public void registerOAuthClientInvalidation(String id) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeycloakTransaction getTransaction() {
|
||||
return new KeycloakTransaction() {
|
||||
@Override
|
||||
public void begin() {
|
||||
transactionActive = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commit() {
|
||||
if (delegate == null) return;
|
||||
try {
|
||||
delegate.getTransaction().commit();
|
||||
} finally {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback() {
|
||||
setRollbackOnly = true;
|
||||
if (delegate == null) return;
|
||||
try {
|
||||
delegate.getTransaction().rollback();
|
||||
} finally {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRollbackOnly() {
|
||||
setRollbackOnly = true;
|
||||
if (delegate == null) return;
|
||||
delegate.getTransaction().setRollbackOnly();
|
||||
setRollbackOnly = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getRollbackOnly() {
|
||||
return setRollbackOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return transactionActive;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmModel createRealm(String name) {
|
||||
return getDelegate().createRealm(name);
|
||||
|
|
|
@ -35,6 +35,11 @@
|
|||
<artifactId>keycloak-model-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-connections-jpa</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-invalidation-cache-model</artifactId>
|
||||
|
@ -79,19 +84,19 @@
|
|||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-model-tests</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-model-tests</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<classifier>tests</classifier>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!--<dependency>-->
|
||||
<!--<groupId>org.keycloak</groupId>-->
|
||||
<!--<artifactId>keycloak-model-tests</artifactId>-->
|
||||
<!--<version>${project.version}</version>-->
|
||||
<!--<scope>test</scope>-->
|
||||
<!--</dependency>-->
|
||||
<!--<dependency>-->
|
||||
<!--<groupId>org.keycloak</groupId>-->
|
||||
<!--<artifactId>keycloak-model-tests</artifactId>-->
|
||||
<!--<version>${project.version}</version>-->
|
||||
<!--<classifier>tests</classifier>-->
|
||||
<!--<scope>test</scope>-->
|
||||
<!--</dependency>-->
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
|
@ -111,34 +116,34 @@
|
|||
</plugin>
|
||||
|
||||
<!-- Test jar used in export-import -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>package-tests-jar</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>test-jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!--<plugin>-->
|
||||
<!--<groupId>org.apache.maven.plugins</groupId>-->
|
||||
<!--<artifactId>maven-jar-plugin</artifactId>-->
|
||||
<!--<executions>-->
|
||||
<!--<execution>-->
|
||||
<!--<id>package-tests-jar</id>-->
|
||||
<!--<phase>package</phase>-->
|
||||
<!--<goals>-->
|
||||
<!--<goal>test-jar</goal>-->
|
||||
<!--</goals>-->
|
||||
<!--</execution>-->
|
||||
<!--</executions>-->
|
||||
<!--</plugin>-->
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>default-test</id>
|
||||
<configuration>
|
||||
<dependenciesToScan>
|
||||
<dependency>org.keycloak:keycloak-model-tests</dependency>
|
||||
</dependenciesToScan>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!--<plugin>-->
|
||||
<!--<groupId>org.apache.maven.plugins</groupId>-->
|
||||
<!--<artifactId>maven-surefire-plugin</artifactId>-->
|
||||
<!--<executions>-->
|
||||
<!--<execution>-->
|
||||
<!--<id>default-test</id>-->
|
||||
<!--<configuration>-->
|
||||
<!--<dependenciesToScan>-->
|
||||
<!--<dependency>org.keycloak:keycloak-model-tests</dependency>-->
|
||||
<!--</dependenciesToScan>-->
|
||||
<!--</configuration>-->
|
||||
<!--</execution>-->
|
||||
<!--</executions>-->
|
||||
<!--</plugin>-->
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
|
|
@ -37,12 +37,6 @@ public class JpaModelProvider implements ModelProvider {
|
|||
public JpaModelProvider(KeycloakSession session, EntityManager em) {
|
||||
this.session = session;
|
||||
this.em = em;
|
||||
this.em = PersistenceExceptionConverter.create(em);
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeycloakTransaction getTransaction() {
|
||||
return new JpaKeycloakTransaction(em);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -148,8 +142,6 @@ public class JpaModelProvider implements ModelProvider {
|
|||
|
||||
@Override
|
||||
public void close() {
|
||||
if (em.getTransaction().isActive()) em.getTransaction().rollback();
|
||||
if (em.isOpen()) em.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
package org.keycloak.models.jpa;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.connections.jpa.JpaConnectionProvider;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ModelProvider;
|
||||
import org.keycloak.models.ModelProviderFactory;
|
||||
import org.keycloak.util.JpaUtils;
|
||||
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
import javax.persistence.Persistence;
|
||||
import javax.persistence.EntityManager;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
|
@ -15,11 +14,8 @@ import javax.persistence.Persistence;
|
|||
*/
|
||||
public class JpaModelProviderFactory implements ModelProviderFactory {
|
||||
|
||||
protected EntityManagerFactory emf;
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
emf = Persistence.createEntityManagerFactory("jpa-keycloak-identity-store", JpaUtils.getHibernateProperties());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -29,12 +25,12 @@ public class JpaModelProviderFactory implements ModelProviderFactory {
|
|||
|
||||
@Override
|
||||
public ModelProvider create(KeycloakSession session) {
|
||||
return new JpaModelProvider(session, emf.createEntityManager());
|
||||
EntityManager em = session.getProvider(JpaConnectionProvider.class).getEntityManager();
|
||||
return new JpaModelProvider(session, em);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
emf.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -37,6 +37,12 @@
|
|||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-connections-mongo</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-invalidation-cache-model</artifactId>
|
||||
|
@ -86,13 +92,13 @@
|
|||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-model-tests</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<classifier>tests</classifier>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!--<dependency>-->
|
||||
<!--<groupId>org.keycloak</groupId>-->
|
||||
<!--<artifactId>keycloak-model-tests</artifactId>-->
|
||||
<!--<version>${project.version}</version>-->
|
||||
<!--<classifier>tests</classifier>-->
|
||||
<!--<scope>test</scope>-->
|
||||
<!--</dependency>-->
|
||||
</dependencies>
|
||||
|
||||
<properties>
|
||||
|
@ -115,65 +121,65 @@
|
|||
</plugin>
|
||||
|
||||
<!-- Postpone tests to "integration-test" phase, so that we can bootstrap embedded mongo on 27018 before running tests -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>test</id>
|
||||
<phase>integration-test</phase>
|
||||
<goals>
|
||||
<goal>test</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<systemPropertyVariables>
|
||||
<keycloak.model.mongo.host>${keycloak.model.mongo.host}</keycloak.model.mongo.host>
|
||||
<keycloak.model.mongo.port>${keycloak.model.mongo.port}</keycloak.model.mongo.port>
|
||||
<keycloak.model.mongo.db>${keycloak.model.mongo.db}</keycloak.model.mongo.db>
|
||||
<keycloak.model.mongo.clearOnStartup>${keycloak.model.mongo.clearOnStartup}</keycloak.model.mongo.clearOnStartup>
|
||||
<keycloak.model.mongo.bindIp>${keycloak.model.mongo.bindIp}</keycloak.model.mongo.bindIp>
|
||||
</systemPropertyVariables>
|
||||
<dependenciesToScan>
|
||||
<dependency>org.keycloak:keycloak-model-tests</dependency>
|
||||
</dependenciesToScan>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>default-test</id>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!--<plugin>-->
|
||||
<!--<groupId>org.apache.maven.plugins</groupId>-->
|
||||
<!--<artifactId>maven-surefire-plugin</artifactId>-->
|
||||
<!--<executions>-->
|
||||
<!--<execution>-->
|
||||
<!--<id>test</id>-->
|
||||
<!--<phase>integration-test</phase>-->
|
||||
<!--<goals>-->
|
||||
<!--<goal>test</goal>-->
|
||||
<!--</goals>-->
|
||||
<!--<configuration>-->
|
||||
<!--<systemPropertyVariables>-->
|
||||
<!--<keycloak.model.mongo.host>${keycloak.model.mongo.host}</keycloak.model.mongo.host>-->
|
||||
<!--<keycloak.model.mongo.port>${keycloak.model.mongo.port}</keycloak.model.mongo.port>-->
|
||||
<!--<keycloak.model.mongo.db>${keycloak.model.mongo.db}</keycloak.model.mongo.db>-->
|
||||
<!--<keycloak.model.mongo.clearOnStartup>${keycloak.model.mongo.clearOnStartup}</keycloak.model.mongo.clearOnStartup>-->
|
||||
<!--<keycloak.model.mongo.bindIp>${keycloak.model.mongo.bindIp}</keycloak.model.mongo.bindIp>-->
|
||||
<!--</systemPropertyVariables>-->
|
||||
<!--<dependenciesToScan>-->
|
||||
<!--<dependency>org.keycloak:keycloak-model-tests</dependency>-->
|
||||
<!--</dependenciesToScan>-->
|
||||
<!--</configuration>-->
|
||||
<!--</execution>-->
|
||||
<!--<execution>-->
|
||||
<!--<id>default-test</id>-->
|
||||
<!--<configuration>-->
|
||||
<!--<skip>true</skip>-->
|
||||
<!--</configuration>-->
|
||||
<!--</execution>-->
|
||||
<!--</executions>-->
|
||||
<!--</plugin>-->
|
||||
|
||||
<!-- Embedded mongo -->
|
||||
<plugin>
|
||||
<groupId>com.github.joelittlejohn.embedmongo</groupId>
|
||||
<artifactId>embedmongo-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>start-mongodb</id>
|
||||
<phase>pre-integration-test</phase>
|
||||
<goals>
|
||||
<goal>start</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<port>${keycloak.model.mongo.port}</port>
|
||||
<logging>file</logging>
|
||||
<logFile>${project.build.directory}/mongodb.log</logFile>
|
||||
<bindIp>${keycloak.model.mongo.bindIp}</bindIp>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>stop-mongodb</id>
|
||||
<phase>post-integration-test</phase>
|
||||
<goals>
|
||||
<goal>stop</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!--<plugin>-->
|
||||
<!--<groupId>com.github.joelittlejohn.embedmongo</groupId>-->
|
||||
<!--<artifactId>embedmongo-maven-plugin</artifactId>-->
|
||||
<!--<executions>-->
|
||||
<!--<execution>-->
|
||||
<!--<id>start-mongodb</id>-->
|
||||
<!--<phase>pre-integration-test</phase>-->
|
||||
<!--<goals>-->
|
||||
<!--<goal>start</goal>-->
|
||||
<!--</goals>-->
|
||||
<!--<configuration>-->
|
||||
<!--<port>${keycloak.model.mongo.port}</port>-->
|
||||
<!--<logging>file</logging>-->
|
||||
<!--<logFile>${project.build.directory}/mongodb.log</logFile>-->
|
||||
<!--<bindIp>${keycloak.model.mongo.bindIp}</bindIp>-->
|
||||
<!--</configuration>-->
|
||||
<!--</execution>-->
|
||||
<!--<execution>-->
|
||||
<!--<id>stop-mongodb</id>-->
|
||||
<!--<phase>post-integration-test</phase>-->
|
||||
<!--<goals>-->
|
||||
<!--<goal>stop</goal>-->
|
||||
<!--</goals>-->
|
||||
<!--</execution>-->
|
||||
<!--</executions>-->
|
||||
<!--</plugin>-->
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package org.keycloak.models.mongo.keycloak.adapters;
|
||||
|
||||
import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
|
||||
import org.keycloak.models.mongo.api.MongoStore;
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
|
||||
import org.keycloak.connections.mongo.api.MongoStore;
|
||||
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
|
|
|
@ -7,7 +7,7 @@ import org.keycloak.models.ClientModel;
|
|||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoApplicationEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity;
|
||||
import org.keycloak.models.mongo.utils.MongoModelUtils;
|
||||
|
|
|
@ -6,8 +6,8 @@ import org.keycloak.models.ModelProvider;
|
|||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.entities.ClientEntity;
|
||||
import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
|
||||
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity;
|
||||
import org.keycloak.models.mongo.utils.MongoModelUtils;
|
||||
|
||||
|
|
|
@ -3,37 +3,27 @@ package org.keycloak.models.mongo.keycloak.adapters;
|
|||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.QueryBuilder;
|
||||
import org.keycloak.connections.mongo.api.MongoStore;
|
||||
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.models.ApplicationModel;
|
||||
import org.keycloak.models.AuthenticationLinkModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakTransaction;
|
||||
import org.keycloak.models.ModelProvider;
|
||||
import org.keycloak.models.OAuthClientModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.SocialLinkModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.models.UsernameLoginFailureModel;
|
||||
import org.keycloak.models.entities.SocialLinkEntity;
|
||||
import org.keycloak.models.mongo.api.MongoStore;
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.models.mongo.impl.context.TransactionMongoStoreInvocationContext;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoApplicationEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoOAuthClientEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoRealmEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoUserEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoUserSessionEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoUsernameLoginFailureEntity;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.util.Time;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -45,21 +35,13 @@ import java.util.regex.Pattern;
|
|||
public class MongoModelProvider implements ModelProvider {
|
||||
|
||||
private final MongoStoreInvocationContext invocationContext;
|
||||
private final MongoKeycloakTransaction transaction;
|
||||
private final KeycloakSession session;
|
||||
private final MongoStore mongoStore;
|
||||
|
||||
public MongoModelProvider(KeycloakSession session, MongoStore mongoStore) {
|
||||
public MongoModelProvider(KeycloakSession session, MongoStore mongoStore, MongoStoreInvocationContext invocationContext) {
|
||||
this.session = session;
|
||||
this.mongoStore = mongoStore;
|
||||
// this.invocationContext = new SimpleMongoStoreInvocationContext(mongoStore);
|
||||
this.invocationContext = new TransactionMongoStoreInvocationContext(mongoStore);
|
||||
this.transaction = new MongoKeycloakTransaction(invocationContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeycloakTransaction getTransaction() {
|
||||
return transaction;
|
||||
this.invocationContext = invocationContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -325,138 +307,5 @@ public class MongoModelProvider implements ModelProvider {
|
|||
|
||||
return new OAuthClientAdapter(session, realm, clientEntity, invocationContext);
|
||||
}
|
||||
//
|
||||
// @Override
|
||||
// public UsernameLoginFailureModel getUserLoginFailure(String username, RealmModel realm) {
|
||||
// DBObject query = new QueryBuilder()
|
||||
// .and("username").is(username)
|
||||
// .and("realmId").is(realm.getId())
|
||||
// .get();
|
||||
// MongoUsernameLoginFailureEntity user = getMongoStore().loadSingleEntity(MongoUsernameLoginFailureEntity.class, query, invocationContext);
|
||||
//
|
||||
// if (user == null) {
|
||||
// return null;
|
||||
// } else {
|
||||
// return new UsernameLoginFailureAdapter(invocationContext, user);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public UsernameLoginFailureModel addUserLoginFailure(String username, RealmModel realm) {
|
||||
// UsernameLoginFailureModel userLoginFailure = getUserLoginFailure(username, realm);
|
||||
// if (userLoginFailure != null) {
|
||||
// return userLoginFailure;
|
||||
// }
|
||||
//
|
||||
// MongoUsernameLoginFailureEntity userEntity = new MongoUsernameLoginFailureEntity();
|
||||
// userEntity.setUsername(username);
|
||||
// userEntity.setRealmId(realm.getId());
|
||||
//
|
||||
// getMongoStore().insertEntity(userEntity, invocationContext);
|
||||
// return new UsernameLoginFailureAdapter(invocationContext, userEntity);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public List<UsernameLoginFailureModel> getAllUserLoginFailures(RealmModel realm) {
|
||||
// DBObject query = new QueryBuilder()
|
||||
// .and("realmId").is(realm.getId())
|
||||
// .get();
|
||||
// List<MongoUsernameLoginFailureEntity> failures = getMongoStore().loadEntities(MongoUsernameLoginFailureEntity.class, query, invocationContext);
|
||||
//
|
||||
// List<UsernameLoginFailureModel> result = new ArrayList<UsernameLoginFailureModel>();
|
||||
//
|
||||
// if (failures == null) return result;
|
||||
// for (MongoUsernameLoginFailureEntity failure : failures) {
|
||||
// result.add(new UsernameLoginFailureAdapter(invocationContext, failure));
|
||||
// }
|
||||
//
|
||||
// return result;
|
||||
// }
|
||||
|
||||
// @Override
|
||||
// public UserSessionModel createUserSession(RealmModel realm, UserModel user, String ipAddress) {
|
||||
// MongoUserSessionEntity entity = new MongoUserSessionEntity();
|
||||
// entity.setRealmId(realm.getId());
|
||||
// entity.setUser(user.getId());
|
||||
// entity.setIpAddress(ipAddress);
|
||||
//
|
||||
// int currentTime = Time.currentTime();
|
||||
//
|
||||
// entity.setStarted(currentTime);
|
||||
// entity.setLastSessionRefresh(currentTime);
|
||||
//
|
||||
// getMongoStore().insertEntity(entity, invocationContext);
|
||||
// return new UserSessionAdapter(entity, realm, invocationContext);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public UserSessionModel getUserSession(String id, RealmModel realm) {
|
||||
// MongoUserSessionEntity entity = getMongoStore().loadEntity(MongoUserSessionEntity.class, id, invocationContext);
|
||||
// if (entity == null) {
|
||||
// return null;
|
||||
// } else {
|
||||
// return new UserSessionAdapter(entity, realm, invocationContext);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public List<UserSessionModel> getUserSessions(UserModel user, RealmModel realm) {
|
||||
// DBObject query = new BasicDBObject("user", user.getId());
|
||||
// List<UserSessionModel> sessions = new LinkedList<UserSessionModel>();
|
||||
// for (MongoUserSessionEntity e : getMongoStore().loadEntities(MongoUserSessionEntity.class, query, invocationContext)) {
|
||||
// sessions.add(new UserSessionAdapter(e, realm, invocationContext));
|
||||
// }
|
||||
// return sessions;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public Set<UserSessionModel> getUserSessions(RealmModel realm, ClientModel client) {
|
||||
// DBObject query = new QueryBuilder()
|
||||
// .and("associatedClientIds").is(client.getId())
|
||||
// .get();
|
||||
// List<MongoUserSessionEntity> sessions = getMongoStore().loadEntities(MongoUserSessionEntity.class, query, invocationContext);
|
||||
//
|
||||
// Set<UserSessionModel> result = new HashSet<UserSessionModel>();
|
||||
// for (MongoUserSessionEntity session : sessions) {
|
||||
// result.add(new UserSessionAdapter(session, realm, invocationContext));
|
||||
// }
|
||||
// return result;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public int getActiveUserSessions(RealmModel realm, ClientModel client) {
|
||||
// return getUserSessions(realm, client).size();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void removeUserSession(UserSessionModel session) {
|
||||
// getMongoStore().removeEntity(((UserSessionAdapter) session).getMongoEntity(), invocationContext);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void removeUserSessions(RealmModel realm, UserModel user) {
|
||||
// DBObject query = new BasicDBObject("user", user.getId());
|
||||
// getMongoStore().removeEntities(MongoUserSessionEntity.class, query, invocationContext);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void removeUserSessions(RealmModel realm) {
|
||||
// DBObject query = new BasicDBObject("realmId", realm.getId());
|
||||
// getMongoStore().removeEntities(MongoUserSessionEntity.class, query, invocationContext);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void removeExpiredUserSessions(RealmModel realm) {
|
||||
// int currentTime = Time.currentTime();
|
||||
// DBObject query = new QueryBuilder()
|
||||
// .and("started").lessThan(currentTime - realm.getSsoSessionMaxLifespan())
|
||||
// .get();
|
||||
//
|
||||
// getMongoStore().removeEntities(MongoUserSessionEntity.class, query, invocationContext);
|
||||
// query = new QueryBuilder()
|
||||
// .and("lastSessionRefresh").lessThan(currentTime - realm.getSsoSessionIdleTimeout())
|
||||
// .get();
|
||||
//
|
||||
// getMongoStore().removeEntities(MongoUserSessionEntity.class, query, invocationContext);
|
||||
// }
|
||||
}
|
||||
|
|
|
@ -1,31 +1,11 @@
|
|||
package org.keycloak.models.mongo.keycloak.adapters;
|
||||
|
||||
import com.mongodb.DB;
|
||||
import com.mongodb.MongoClient;
|
||||
import com.mongodb.MongoCredential;
|
||||
import com.mongodb.ServerAddress;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.connections.mongo.MongoConnectionProvider;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ModelProvider;
|
||||
import org.keycloak.models.ModelProviderFactory;
|
||||
import org.keycloak.models.entities.AuthenticationLinkEntity;
|
||||
import org.keycloak.models.entities.AuthenticationProviderEntity;
|
||||
import org.keycloak.models.entities.CredentialEntity;
|
||||
import org.keycloak.models.entities.RequiredCredentialEntity;
|
||||
import org.keycloak.models.entities.SocialLinkEntity;
|
||||
import org.keycloak.models.mongo.api.MongoStore;
|
||||
import org.keycloak.models.mongo.impl.MongoStoreImpl;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoApplicationEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoOAuthClientEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoRealmEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoUserEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoUserSessionEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoUsernameLoginFailureEntity;
|
||||
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* KeycloakSessionFactory implementation based on MongoDB
|
||||
|
@ -35,25 +15,6 @@ import java.util.Collections;
|
|||
public class MongoModelProviderFactory implements ModelProviderFactory {
|
||||
protected static final Logger logger = Logger.getLogger(MongoModelProviderFactory.class);
|
||||
|
||||
private static final Class<?>[] MANAGED_ENTITY_TYPES = (Class<?>[]) new Class<?>[]{
|
||||
MongoRealmEntity.class,
|
||||
MongoUserEntity.class,
|
||||
MongoRoleEntity.class,
|
||||
RequiredCredentialEntity.class,
|
||||
AuthenticationProviderEntity.class,
|
||||
CredentialEntity.class,
|
||||
SocialLinkEntity.class,
|
||||
AuthenticationLinkEntity.class,
|
||||
MongoApplicationEntity.class,
|
||||
MongoOAuthClientEntity.class,
|
||||
MongoUsernameLoginFailureEntity.class,
|
||||
MongoUserSessionEntity.class
|
||||
};
|
||||
|
||||
private MongoClient client;
|
||||
|
||||
private MongoStore mongoStore;
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "mongo";
|
||||
|
@ -61,40 +22,16 @@ public class MongoModelProviderFactory implements ModelProviderFactory {
|
|||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
try {
|
||||
String host = config.get("host", ServerAddress.defaultHost());
|
||||
int port = config.getInt("port", ServerAddress.defaultPort());
|
||||
String dbName = config.get("db", "keycloak");
|
||||
boolean clearOnStartup = config.getBoolean("clearOnStartup", false);
|
||||
|
||||
String user = config.get("user");
|
||||
String password = config.get("password");
|
||||
if (user != null && password != null) {
|
||||
MongoCredential credential = MongoCredential.createMongoCRCredential(user, dbName, password.toCharArray());
|
||||
client = new MongoClient(new ServerAddress(host, port), Collections.singletonList(credential));
|
||||
} else {
|
||||
client = new MongoClient(host, port);
|
||||
}
|
||||
|
||||
DB db = client.getDB(dbName);
|
||||
|
||||
this.mongoStore = new MongoStoreImpl(db, clearOnStartup, MANAGED_ENTITY_TYPES);
|
||||
|
||||
logger.infof("Initialized mongo model. host: %s, port: %d, db: %s, clearOnStartup: %b", host, port, dbName, clearOnStartup);
|
||||
} catch (UnknownHostException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelProvider create(KeycloakSession session) {
|
||||
return new MongoModelProvider(session, mongoStore);
|
||||
MongoConnectionProvider connection = session.getProvider(MongoConnectionProvider.class);
|
||||
return new MongoModelProvider(session, connection.getMongoStore(), connection.getInvocationContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
this.client.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import org.keycloak.models.ApplicationModel;
|
|||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.OAuthClientModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoOAuthClientEntity;
|
||||
|
||||
/**
|
||||
|
|
|
@ -3,6 +3,7 @@ package org.keycloak.models.mongo.keycloak.adapters;
|
|||
import com.mongodb.DBObject;
|
||||
import com.mongodb.QueryBuilder;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.models.ApplicationModel;
|
||||
import org.keycloak.models.AuthenticationProviderModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
|
@ -18,11 +19,9 @@ import org.keycloak.models.SocialLinkModel;
|
|||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserCredentialValueModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UsernameLoginFailureModel;
|
||||
import org.keycloak.models.entities.AuthenticationProviderEntity;
|
||||
import org.keycloak.models.entities.RequiredCredentialEntity;
|
||||
import org.keycloak.models.entities.SocialLinkEntity;
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoApplicationEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoOAuthClientEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoRealmEntity;
|
||||
|
|
|
@ -11,7 +11,7 @@ import org.keycloak.models.KeycloakSession;
|
|||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleContainerModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoApplicationEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoRealmEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package org.keycloak.models.mongo.keycloak.adapters;
|
||||
|
||||
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.models.ApplicationModel;
|
||||
import org.keycloak.models.AuthenticationLinkModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.PasswordPolicy;
|
||||
import org.keycloak.models.RealmModel;
|
||||
|
@ -12,7 +12,6 @@ import org.keycloak.models.UserCredentialValueModel;
|
|||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.entities.AuthenticationLinkEntity;
|
||||
import org.keycloak.models.entities.CredentialEntity;
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoUserEntity;
|
||||
import org.keycloak.models.mongo.utils.MongoModelUtils;
|
||||
|
|
|
@ -1,15 +1,12 @@
|
|||
package org.keycloak.models.mongo.keycloak.entities;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.QueryBuilder;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.connections.mongo.api.MongoCollection;
|
||||
import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
|
||||
import org.keycloak.connections.mongo.api.MongoIndex;
|
||||
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.models.entities.ApplicationEntity;
|
||||
import org.keycloak.models.mongo.api.MongoCollection;
|
||||
import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
|
||||
import org.keycloak.models.mongo.api.MongoIndex;
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
|
@ -25,14 +22,5 @@ public class MongoApplicationEntity extends ApplicationEntity implements MongoId
|
|||
.and("applicationId").is(getId())
|
||||
.get();
|
||||
context.getMongoStore().removeEntities(MongoRoleEntity.class, query, context);
|
||||
|
||||
// Remove all session associations
|
||||
query = new QueryBuilder()
|
||||
.and("associatedClientIds").is(getId())
|
||||
.get();
|
||||
List<MongoUserSessionEntity> sessions = context.getMongoStore().loadEntities(MongoUserSessionEntity.class, query, context);
|
||||
for (MongoUserSessionEntity session : sessions) {
|
||||
context.getMongoStore().pullItemFromList(session, "associatedClientIds", getId(), context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
package org.keycloak.models.mongo.keycloak.entities;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.QueryBuilder;
|
||||
import org.keycloak.connections.mongo.api.MongoCollection;
|
||||
import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
|
||||
import org.keycloak.connections.mongo.api.MongoIndex;
|
||||
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.models.entities.OAuthClientEntity;
|
||||
import org.keycloak.models.mongo.api.MongoCollection;
|
||||
import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
|
||||
import org.keycloak.models.mongo.api.MongoIndex;
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
|
@ -23,9 +21,5 @@ public class MongoOAuthClientEntity extends OAuthClientEntity implements MongoId
|
|||
DBObject query = new QueryBuilder()
|
||||
.and("associatedClientIds").is(getId())
|
||||
.get();
|
||||
List<MongoUserSessionEntity> sessions = context.getMongoStore().loadEntities(MongoUserSessionEntity.class, query, context);
|
||||
for (MongoUserSessionEntity session : sessions) {
|
||||
context.getMongoStore().pullItemFromList(session, "associatedClientIds", getId(), context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,10 +3,10 @@ package org.keycloak.models.mongo.keycloak.entities;
|
|||
import com.mongodb.DBObject;
|
||||
import com.mongodb.QueryBuilder;
|
||||
import org.keycloak.models.entities.RealmEntity;
|
||||
import org.keycloak.models.mongo.api.MongoCollection;
|
||||
import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
|
||||
import org.keycloak.models.mongo.api.MongoIndex;
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.connections.mongo.api.MongoCollection;
|
||||
import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
|
||||
import org.keycloak.connections.mongo.api.MongoIndex;
|
||||
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
|
@ -32,8 +32,5 @@ public class MongoRealmEntity extends RealmEntity implements MongoIdentifiableEn
|
|||
|
||||
// Remove all clients of this realm
|
||||
context.getMongoStore().removeEntities(MongoOAuthClientEntity.class, query, context);
|
||||
|
||||
// Remove all sessions of this realm
|
||||
context.getMongoStore().removeEntities(MongoUserSessionEntity.class, query, context);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,12 +4,12 @@ import com.mongodb.DBObject;
|
|||
import com.mongodb.QueryBuilder;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.models.entities.RoleEntity;
|
||||
import org.keycloak.models.mongo.api.MongoCollection;
|
||||
import org.keycloak.models.mongo.api.MongoField;
|
||||
import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
|
||||
import org.keycloak.models.mongo.api.MongoIndex;
|
||||
import org.keycloak.models.mongo.api.MongoStore;
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.connections.mongo.api.MongoCollection;
|
||||
import org.keycloak.connections.mongo.api.MongoField;
|
||||
import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
|
||||
import org.keycloak.connections.mongo.api.MongoIndex;
|
||||
import org.keycloak.connections.mongo.api.MongoStore;
|
||||
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
package org.keycloak.models.mongo.keycloak.entities;
|
||||
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.QueryBuilder;
|
||||
import org.keycloak.connections.mongo.api.MongoCollection;
|
||||
import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
|
||||
import org.keycloak.connections.mongo.api.MongoIndex;
|
||||
import org.keycloak.connections.mongo.api.MongoIndexes;
|
||||
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.models.entities.UserEntity;
|
||||
import org.keycloak.models.mongo.api.MongoCollection;
|
||||
import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
|
||||
import org.keycloak.models.mongo.api.MongoIndex;
|
||||
import org.keycloak.models.mongo.api.MongoIndexes;
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
|
@ -29,10 +27,5 @@ public class MongoUserEntity extends UserEntity implements MongoIdentifiableEnti
|
|||
|
||||
@Override
|
||||
public void afterRemove(MongoStoreInvocationContext invocationContext) {
|
||||
DBObject query = new QueryBuilder()
|
||||
.and("userId").is(getId())
|
||||
.get();
|
||||
|
||||
invocationContext.getMongoStore().removeEntities(MongoUserSessionEntity.class, query, invocationContext);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
package org.keycloak.models.mongo.utils;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.QueryBuilder;
|
||||
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.entities.ClientEntity;
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.models.mongo.keycloak.adapters.ClientAdapter;
|
||||
import org.keycloak.models.mongo.keycloak.adapters.UserAdapter;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoUserEntity;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
|
|
|
@ -31,12 +31,13 @@
|
|||
<module>mongo</module>
|
||||
<module>tests</module>
|
||||
|
||||
<module>sessions-jpa</module>
|
||||
<module>sessions-mem</module>
|
||||
<module>sessions-mongo</module>
|
||||
|
||||
<!--<module>hybrid</module>-->
|
||||
<!--<module>realms-jpa</module>-->
|
||||
<!--<module>users-jpa</module>-->
|
||||
<!--<module>sessions-jpa</module>-->
|
||||
<!--<module>tests-hybrid</module>-->
|
||||
</modules>
|
||||
</project>
|
||||
|
|
|
@ -24,11 +24,13 @@
|
|||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-model-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-model-hybrid</artifactId>
|
||||
<artifactId>keycloak-connections-jpa</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate.javax.persistence</groupId>
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
package org.keycloak.models.sessions.jpa;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.sessions.SessionProvider;
|
||||
import org.keycloak.models.sessions.SessionProviderFactory;
|
||||
import org.keycloak.util.JpaUtils;
|
||||
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
import javax.persistence.Persistence;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class JpaSessionProviderFactory implements SessionProviderFactory {
|
||||
|
||||
protected EntityManagerFactory emf;
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
String persistenceUnit = config.get("persistenceUnit", "jpa-keycloak-identity-store");
|
||||
emf = Persistence.createEntityManagerFactory(persistenceUnit, JpaUtils.getHibernateProperties());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "jpa";
|
||||
}
|
||||
|
||||
@Override
|
||||
public SessionProvider create(KeycloakSession session) {
|
||||
return new JpaUserSessionProvider(emf.createEntityManager());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
emf.close();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,36 +1,38 @@
|
|||
package org.keycloak.models.sessions.jpa;
|
||||
|
||||
import org.keycloak.models.KeycloakTransaction;
|
||||
import org.keycloak.models.sessions.LoginFailure;
|
||||
import org.keycloak.models.sessions.Session;
|
||||
import org.keycloak.models.sessions.SessionProvider;
|
||||
import org.keycloak.models.sessions.jpa.entities.ClientUserSessionAssociationEntity;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.models.UserSessionProvider;
|
||||
import org.keycloak.models.UsernameLoginFailureModel;
|
||||
import org.keycloak.models.sessions.jpa.entities.UserSessionEntity;
|
||||
import org.keycloak.models.sessions.jpa.entities.UsernameLoginFailureEntity;
|
||||
import org.keycloak.util.Time;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.Query;
|
||||
import javax.persistence.TypedQuery;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class JpaUserSessionProvider implements SessionProvider {
|
||||
public class JpaUserSessionProvider implements UserSessionProvider {
|
||||
|
||||
protected final KeycloakSession session;
|
||||
|
||||
protected final EntityManager em;
|
||||
|
||||
public JpaUserSessionProvider(EntityManager em) {
|
||||
this.em = PersistenceExceptionConverter.create(em);
|
||||
public JpaUserSessionProvider(KeycloakSession session, EntityManager em) {
|
||||
this.session = session;
|
||||
this.em = em;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoginFailure getUserLoginFailure(String username, String realm) {
|
||||
public UsernameLoginFailureModel getUserLoginFailure(RealmModel realm, String username) {
|
||||
String id = username + "-" + realm;
|
||||
UsernameLoginFailureEntity entity = em.find(UsernameLoginFailureEntity.class, id);
|
||||
if (entity == null) return null;
|
||||
|
@ -38,23 +40,21 @@ public class JpaUserSessionProvider implements SessionProvider {
|
|||
}
|
||||
|
||||
@Override
|
||||
public LoginFailure addUserLoginFailure(String username, String realm) {
|
||||
LoginFailure model = getUserLoginFailure(username, realm);
|
||||
public UsernameLoginFailureModel addUserLoginFailure(RealmModel realm, String username) {
|
||||
UsernameLoginFailureModel model = getUserLoginFailure(realm, username);
|
||||
if (model != null) return model;
|
||||
String id = username + "-" + realm;
|
||||
UsernameLoginFailureEntity entity = new UsernameLoginFailureEntity();
|
||||
entity.setId(id);
|
||||
entity.setUsername(username);
|
||||
entity.setRealm(realm);
|
||||
entity.setRealmId(realm.getId());
|
||||
em.persist(entity);
|
||||
return new UsernameLoginFailureAdapter(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<LoginFailure> getAllUserLoginFailures(String realm) {
|
||||
public List<UsernameLoginFailureModel> getAllUserLoginFailures(RealmModel realm) {
|
||||
TypedQuery<UsernameLoginFailureEntity> query = em.createNamedQuery("getAllFailures", UsernameLoginFailureEntity.class);
|
||||
List<UsernameLoginFailureEntity> entities = query.getResultList();
|
||||
List<LoginFailure> models = new ArrayList<LoginFailure>();
|
||||
List<UsernameLoginFailureModel> models = new ArrayList<UsernameLoginFailureModel>();
|
||||
for (UsernameLoginFailureEntity entity : entities) {
|
||||
models.add(new UsernameLoginFailureAdapter(entity));
|
||||
}
|
||||
|
@ -62,10 +62,10 @@ public class JpaUserSessionProvider implements SessionProvider {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Session createUserSession(String realm, String id, String user, String ipAddress) {
|
||||
public UserSessionModel createUserSession(RealmModel realm, UserModel user, String ipAddress) {
|
||||
UserSessionEntity entity = new UserSessionEntity();
|
||||
entity.setRealmId(realm);
|
||||
entity.setUserId(user);
|
||||
entity.setRealmId(realm.getId());
|
||||
entity.setUserId(user.getId());
|
||||
entity.setIpAddress(ipAddress);
|
||||
|
||||
int currentTime = Time.currentTime();
|
||||
|
@ -74,82 +74,110 @@ public class JpaUserSessionProvider implements SessionProvider {
|
|||
entity.setLastSessionRefresh(currentTime);
|
||||
|
||||
em.persist(entity);
|
||||
return new UserSessionAdapter(em, realm, entity);
|
||||
return new UserSessionAdapter(session, em, realm, entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Session getUserSession(String id, String realm) {
|
||||
public UserSessionModel getUserSession(RealmModel realm, String id) {
|
||||
UserSessionEntity entity = em.find(UserSessionEntity.class, id);
|
||||
return entity != null ? new UserSessionAdapter(em, realm, entity) : null;
|
||||
return entity != null ? new UserSessionAdapter(session, em, realm, entity) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Session> getUserSessionsByUser(String user, String realm) {
|
||||
List<Session> sessions = new LinkedList<Session>();
|
||||
for (UserSessionEntity e : em.createNamedQuery("getUserSessionByUser", UserSessionEntity.class)
|
||||
.setParameter("userId", user).getResultList()) {
|
||||
sessions.add(new UserSessionAdapter(em, realm, e));
|
||||
public List<UserSessionModel> getUserSessions(RealmModel realm, UserModel user) {
|
||||
List<UserSessionModel> sessions = new LinkedList<UserSessionModel>();
|
||||
TypedQuery<UserSessionEntity> query = em.createNamedQuery("getUserSessionByUser", UserSessionEntity.class)
|
||||
.setParameter("realmId", realm.getId())
|
||||
.setParameter("userId", user.getId());
|
||||
for (UserSessionEntity e : query.getResultList()) {
|
||||
sessions.add(new UserSessionAdapter(session, em, realm, e));
|
||||
}
|
||||
return sessions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Session> getUserSessionsByClient(String realm, String client) {
|
||||
Set<Session> list = new HashSet<Session>();
|
||||
TypedQuery<ClientUserSessionAssociationEntity> query = em.createNamedQuery("getClientUserSessionByClient", ClientUserSessionAssociationEntity.class);
|
||||
query.setParameter("clientId", client);
|
||||
List<ClientUserSessionAssociationEntity> results = query.getResultList();
|
||||
for (ClientUserSessionAssociationEntity entity : results) {
|
||||
list.add(new UserSessionAdapter(em, realm, entity.getSession()));
|
||||
public List<UserSessionModel> getUserSessions(RealmModel realm, ClientModel client) {
|
||||
List<UserSessionModel> list = new LinkedList<UserSessionModel>();
|
||||
TypedQuery<UserSessionEntity> query = em.createNamedQuery("getUserSessionByClient", UserSessionEntity.class)
|
||||
.setParameter("realmId", realm.getId())
|
||||
.setParameter("clientId", client.getClientId());
|
||||
for (UserSessionEntity entity : query.getResultList()) {
|
||||
list.add(new UserSessionAdapter(session, em, realm, entity));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getActiveUserSessions(String realm, String client) {
|
||||
Query query = em.createNamedQuery("getActiveClientSessions");
|
||||
query.setParameter("clientId", client);
|
||||
Object count = query.getSingleResult();
|
||||
public int getActiveUserSessions(RealmModel realm, ClientModel client) {
|
||||
Object count = em.createNamedQuery("getActiveUserSessionByClient")
|
||||
.setParameter("realmId", realm.getId())
|
||||
.setParameter("clientId", client.getClientId())
|
||||
.getSingleResult();
|
||||
return ((Number)count).intValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUserSession(Session session) {
|
||||
em.remove(((UserSessionAdapter) session).getEntity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUserSessions(String realm, String user) {
|
||||
em.createNamedQuery("removeClientUserSessionByUser").setParameter("userId", user).executeUpdate();
|
||||
em.createNamedQuery("removeUserSessionByUser").setParameter("userId", user).executeUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeExpiredUserSessions(String realm, long refreshTimeout, long sessionTimeout) {
|
||||
TypedQuery<UserSessionEntity> query = em.createNamedQuery("getUserSessionExpired", UserSessionEntity.class)
|
||||
.setParameter("maxTime", sessionTimeout)
|
||||
.setParameter("idleTime", refreshTimeout);
|
||||
List<UserSessionEntity> results = query.getResultList();
|
||||
for (UserSessionEntity entity : results) {
|
||||
public void removeUserSession(RealmModel realm, UserSessionModel session) {
|
||||
UserSessionEntity entity = em.find(UserSessionEntity.class, session.getId());
|
||||
if (entity != null) {
|
||||
em.remove(entity);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUserSessions(String realm) {
|
||||
em.createNamedQuery("removeClientUserSessionByRealm").setParameter("realmId", realm).executeUpdate();
|
||||
em.createNamedQuery("removeRealmUserSessions").setParameter("realmId", realm).executeUpdate();
|
||||
public void removeUserSessions(RealmModel realm, UserModel user) {
|
||||
em.createNamedQuery("removeClientUserSessionByUser")
|
||||
.setParameter("realmId", realm.getId())
|
||||
.setParameter("userId", user.getId())
|
||||
.executeUpdate();
|
||||
em.createNamedQuery("removeUserSessionByUser")
|
||||
.setParameter("realmId", realm.getId())
|
||||
.setParameter("userId", user.getId())
|
||||
.executeUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeycloakTransaction getTransaction() {
|
||||
return new JpaKeycloakTransaction(em);
|
||||
public void removeExpiredUserSessions(RealmModel realm) {
|
||||
int maxTime = Time.currentTime() - realm.getSsoSessionMaxLifespan();
|
||||
int idleTime = Time.currentTime() - realm.getSsoSessionIdleTimeout();
|
||||
|
||||
em.createNamedQuery("removeClientUserSessionByExpired")
|
||||
.setParameter("realmId", realm.getId())
|
||||
.setParameter("maxTime", maxTime)
|
||||
.setParameter("idleTime", idleTime)
|
||||
.executeUpdate();
|
||||
em.createNamedQuery("removeUserSessionByExpired")
|
||||
.setParameter("realmId", realm.getId())
|
||||
.setParameter("maxTime", maxTime)
|
||||
.setParameter("idleTime", idleTime)
|
||||
.executeUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUserSessions(RealmModel realm) {
|
||||
em.createNamedQuery("removeClientUserSessionByRealm").setParameter("realmId", realm.getId()).executeUpdate();
|
||||
em.createNamedQuery("removeUserSessionByRealm").setParameter("realmId", realm.getId()).executeUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRealmRemoved(RealmModel realm) {
|
||||
removeUserSessions(realm);
|
||||
em.createNamedQuery("removeLoginFailuresByRealm").setParameter("realmId", realm.getId()).executeUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClientRemoved(RealmModel realm, ClientModel client) {
|
||||
em.createNamedQuery("removeClientUserSessionByClient").setParameter("realmId", realm.getId()).setParameter("clientId", client.getClientId()).executeUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUserRemoved(RealmModel realm, UserModel user) {
|
||||
removeUserSessions(realm, user);
|
||||
em.createNamedQuery("removeLoginFailuresByUser").setParameter("username", user.getUsername()).executeUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (em.getTransaction().isActive()) em.getTransaction().rollback();
|
||||
if (em.isOpen()) em.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
package org.keycloak.models.sessions.jpa;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.connections.jpa.JpaConnectionProvider;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.UserSessionProvider;
|
||||
import org.keycloak.models.UserSessionProviderFactory;
|
||||
import org.keycloak.util.JpaUtils;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
import javax.persistence.Persistence;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class JpaUserSessionProviderFactory implements UserSessionProviderFactory {
|
||||
|
||||
public static final String ID = "jpa";
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserSessionProvider create(KeycloakSession session) {
|
||||
EntityManager em = session.getProvider(JpaConnectionProvider.class).getEntityManager();
|
||||
return new JpaUserSessionProvider(session, em);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
}
|
|
@ -1,7 +1,10 @@
|
|||
package org.keycloak.models.sessions.jpa;
|
||||
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.models.sessions.Session;
|
||||
import org.keycloak.models.sessions.jpa.entities.ClientUserSessionAssociationEntity;
|
||||
import org.keycloak.models.sessions.jpa.entities.UserSessionEntity;
|
||||
|
||||
|
@ -12,16 +15,18 @@ import java.util.List;
|
|||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class UserSessionAdapter implements Session {
|
||||
public class UserSessionAdapter implements UserSessionModel {
|
||||
|
||||
private String realm;
|
||||
private KeycloakSession session;
|
||||
private RealmModel realm;
|
||||
private UserSessionEntity entity;
|
||||
private EntityManager em;
|
||||
|
||||
public UserSessionAdapter(EntityManager em, String realm, UserSessionEntity entity) {
|
||||
public UserSessionAdapter(KeycloakSession session, EntityManager em, RealmModel realm, UserSessionEntity entity) {
|
||||
this.session = session;
|
||||
this.realm = realm;
|
||||
this.entity = entity;
|
||||
this.em = em;
|
||||
this.realm = realm;
|
||||
}
|
||||
|
||||
public UserSessionEntity getEntity() {
|
||||
|
@ -39,13 +44,13 @@ public class UserSessionAdapter implements Session {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String getUser() {
|
||||
return entity.getUserId();
|
||||
public UserModel getUser() {
|
||||
return realm.getUserById(entity.getUserId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUser(String user) {
|
||||
entity.setUserId(user);
|
||||
public void setUser(UserModel user) {
|
||||
entity.setUserId(user.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -79,29 +84,28 @@ public class UserSessionAdapter implements Session {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void associateClient(String client) {
|
||||
public void associateClient(ClientModel client) {
|
||||
for (ClientUserSessionAssociationEntity ass : entity.getClients()) {
|
||||
if (ass.getClientId().equals(client)) return;
|
||||
if (ass.getClientId().equals(client.getClientId())) return;
|
||||
}
|
||||
|
||||
ClientUserSessionAssociationEntity association = new ClientUserSessionAssociationEntity();
|
||||
association.setClientId(client);
|
||||
association.setClientId(client.getClientId());
|
||||
association.setSession(entity);
|
||||
association.setUserId(entity.getUserId());
|
||||
association.setRealmId(realm);
|
||||
em.persist(association);
|
||||
entity.getClients().add(association);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAssociatedClient(String client) {
|
||||
em.createNamedQuery("removeClientUserSessionByClient").setParameter("clientId", client).executeUpdate();
|
||||
public void removeAssociatedClient(ClientModel client) {
|
||||
em.createNamedQuery("removeClientUserSessionByClient").setParameter("clientId", client.getClientId()).executeUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getClientAssociations() {
|
||||
List<String> clients = new ArrayList<String>();
|
||||
public List<ClientModel> getClientAssociations() {
|
||||
List<ClientModel> clients = new ArrayList<ClientModel>();
|
||||
for (ClientUserSessionAssociationEntity association : entity.getClients()) {
|
||||
clients.add(association.getClientId());
|
||||
clients.add(realm.findClient(association.getClientId()));
|
||||
}
|
||||
return clients;
|
||||
}
|
||||
|
@ -119,4 +123,5 @@ public class UserSessionAdapter implements Session {
|
|||
public int hashCode() {
|
||||
return getId().hashCode();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
package org.keycloak.models.sessions.jpa;
|
||||
|
||||
import org.keycloak.models.sessions.LoginFailure;
|
||||
import org.keycloak.models.UsernameLoginFailureModel;
|
||||
import org.keycloak.models.sessions.jpa.entities.UsernameLoginFailureEntity;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class UsernameLoginFailureAdapter implements LoginFailure
|
||||
public class UsernameLoginFailureAdapter implements UsernameLoginFailureModel
|
||||
{
|
||||
protected UsernameLoginFailureEntity user;
|
||||
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
package org.keycloak.models.sessions.jpa.entities;
|
||||
|
||||
import org.hibernate.annotations.GenericGenerator;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.IdClass;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.NamedQueries;
|
||||
import javax.persistence.NamedQuery;
|
||||
import javax.persistence.Table;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
|
@ -18,37 +17,20 @@ import javax.persistence.Table;
|
|||
@Entity
|
||||
@Table(name = "ClientUserSessionAscEntity")
|
||||
@NamedQueries({
|
||||
@NamedQuery(name = "getAllClientUserSessions", query = "select s from ClientUserSessionAssociationEntity s"),
|
||||
@NamedQuery(name = "getClientUserSessionBySession", query = "select s from ClientUserSessionAssociationEntity s where s.session = :session"),
|
||||
@NamedQuery(name = "getClientUserSessionByClient", query = "select s from ClientUserSessionAssociationEntity s where s.clientId = :clientId"),
|
||||
@NamedQuery(name = "getActiveClientSessions", query = "select COUNT(s) from ClientUserSessionAssociationEntity s where s.clientId = :clientId"),
|
||||
@NamedQuery(name = "removeClientUserSessionByClient", query = "delete from ClientUserSessionAssociationEntity s where s.clientId = :clientId"),
|
||||
@NamedQuery(name = "removeClientUserSessionByUser", query = "delete from ClientUserSessionAssociationEntity s where s.userId = :userId"),
|
||||
@NamedQuery(name = "removeClientUserSessionByRealm", query = "delete from ClientUserSessionAssociationEntity s where s.realmId = :realmId")})
|
||||
@NamedQuery(name = "removeClientUserSessionByRealm", query = "delete from ClientUserSessionAssociationEntity a where a.session IN (select s from UserSessionEntity s where s.realmId = :realmId)"),
|
||||
@NamedQuery(name = "removeClientUserSessionByUser", query = "delete from ClientUserSessionAssociationEntity a where a.session IN (select s from UserSessionEntity s where s.realmId = :realmId and s.userId = :userId)"),
|
||||
@NamedQuery(name = "removeClientUserSessionByClient", query = "delete from ClientUserSessionAssociationEntity a where a.clientId = :clientId and a.session IN (select s from UserSessionEntity s where s.realmId = :realmId)"),
|
||||
@NamedQuery(name = "removeClientUserSessionByExpired", query = "delete from ClientUserSessionAssociationEntity a where a.session IN (select s from UserSessionEntity s where s.realmId = :realmId and (s.started < :maxTime or s.lastSessionRefresh < :idleTime))")
|
||||
})
|
||||
@IdClass(ClientUserSessionAssociationEntity.Key.class)
|
||||
public class ClientUserSessionAssociationEntity {
|
||||
|
||||
@Id
|
||||
@GenericGenerator(name="uuid_generator", strategy="org.keycloak.models.sessions.jpa.utils.JpaIdGenerator")
|
||||
@GeneratedValue(generator = "uuid_generator")
|
||||
private String id;
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
protected UserSessionEntity session;
|
||||
|
||||
// we use ids to avoid select for update contention
|
||||
private String userId;
|
||||
private String realmId;
|
||||
|
||||
@ManyToOne(fetch= FetchType.LAZY)
|
||||
private UserSessionEntity session;
|
||||
|
||||
|
||||
|
||||
private String clientId;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
@Id
|
||||
protected String clientId;
|
||||
|
||||
public UserSessionEntity getSession() {
|
||||
return session;
|
||||
|
@ -66,19 +48,46 @@ public class ClientUserSessionAssociationEntity {
|
|||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
public static class Key implements Serializable {
|
||||
|
||||
private String clientId;
|
||||
private UserSessionEntity session;
|
||||
|
||||
public Key() {
|
||||
}
|
||||
|
||||
public Key(String clientId, UserSessionEntity session) {
|
||||
this.clientId = clientId;
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public UserSessionEntity getSession() {
|
||||
return session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
Key key = (Key) o;
|
||||
|
||||
if (clientId != null ? !clientId.equals(key.clientId) : key.clientId != null) return false;
|
||||
if (session != null ? !session.equals(key.session) : key.session != null) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = clientId != null ? clientId.hashCode() : 0;
|
||||
result = 31 * result + (session != null ? session.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public void setUserId(String userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public String getRealmId() {
|
||||
return realmId;
|
||||
}
|
||||
|
||||
public void setRealmId(String realmId) {
|
||||
this.realmId = realmId;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,32 +18,31 @@ import java.util.Collection;
|
|||
*/
|
||||
@Entity
|
||||
@NamedQueries({
|
||||
@NamedQuery(name = "getUserSessionByUser", query = "select s from UserSessionEntity s where s.userId = :userId"),
|
||||
@NamedQuery(name = "removeRealmUserSessions", query = "delete from UserSessionEntity s where s.realmId = :realmId"),
|
||||
@NamedQuery(name = "removeUserSessionByUser", query = "delete from UserSessionEntity s where s.userId = :userId"),
|
||||
@NamedQuery(name = "getUserSessionExpired", query = "select s from UserSessionEntity s where s.started < :maxTime or s.lastSessionRefresh < :idleTime"),
|
||||
@NamedQuery(name = "removeUserSessionExpired", query = "delete from UserSessionEntity s where s.started < :maxTime or s.lastSessionRefresh < :idleTime")
|
||||
@NamedQuery(name = "getUserSessionByUser", query = "select s from UserSessionEntity s where s.realmId = :realmId and s.userId = :userId"),
|
||||
@NamedQuery(name = "getUserSessionByClient", query = "select s from UserSessionEntity s join s.clients c where s.realmId = :realmId and c.clientId = :clientId"),
|
||||
@NamedQuery(name = "getActiveUserSessionByClient", query = "select count(s) from UserSessionEntity s join s.clients c where s.realmId = :realmId and c.clientId = :clientId"),
|
||||
@NamedQuery(name = "removeUserSessionByRealm", query = "delete from UserSessionEntity s where s.realmId = :realmId"),
|
||||
@NamedQuery(name = "removeUserSessionByUser", query = "delete from UserSessionEntity s where s.realmId = :realmId and s.userId = :userId"),
|
||||
@NamedQuery(name = "removeUserSessionByExpired", query = "delete from UserSessionEntity s where s.realmId = :realmId and (s.started < :maxTime or s.lastSessionRefresh < :idleTime)")
|
||||
})
|
||||
public class UserSessionEntity {
|
||||
|
||||
@Id
|
||||
@GenericGenerator(name="uuid_generator", strategy="org.keycloak.models.sessions.jpa.utils.JpaIdGenerator")
|
||||
@GeneratedValue(generator = "uuid_generator")
|
||||
private String id;
|
||||
protected String id;
|
||||
|
||||
// we use ids to avoid select for update contention
|
||||
private String userId;
|
||||
private String realmId;
|
||||
protected String userId;
|
||||
protected String realmId;
|
||||
|
||||
private String ipAddress;
|
||||
protected String ipAddress;
|
||||
|
||||
private int started;
|
||||
protected int started;
|
||||
|
||||
private int lastSessionRefresh;
|
||||
|
||||
@OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy="session")
|
||||
private Collection<ClientUserSessionAssociationEntity> clients = new ArrayList<ClientUserSessionAssociationEntity>();
|
||||
protected int lastSessionRefresh;
|
||||
|
||||
@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.REMOVE, orphanRemoval = true, mappedBy="session")
|
||||
protected Collection<ClientUserSessionAssociationEntity> clients = new ArrayList<ClientUserSessionAssociationEntity>();
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
|
@ -53,7 +52,6 @@ public class UserSessionEntity {
|
|||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
@ -98,8 +96,4 @@ public class UserSessionEntity {
|
|||
return clients;
|
||||
}
|
||||
|
||||
public void setClients(Collection<ClientUserSessionAssociationEntity> clients) {
|
||||
this.clients = clients;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,9 +3,11 @@ package org.keycloak.models.sessions.jpa.entities;
|
|||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.IdClass;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.NamedQueries;
|
||||
import javax.persistence.NamedQuery;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
|
@ -14,28 +16,23 @@ import javax.persistence.NamedQuery;
|
|||
@Entity
|
||||
@NamedQueries({
|
||||
@NamedQuery(name="getAllFailures", query="select failure from UsernameLoginFailureEntity failure"),
|
||||
@NamedQuery(name = "removeLoginFailuresByRealm", query = "delete from UsernameLoginFailureEntity f where f.realmId = :realmId"),
|
||||
@NamedQuery(name = "removeLoginFailuresByUser", query = "delete from UsernameLoginFailureEntity f where f.realmId = :realmId and f.username = :username")
|
||||
})
|
||||
@IdClass(UsernameLoginFailureEntity.Key.class)
|
||||
public class UsernameLoginFailureEntity {
|
||||
// we manually set the id to be username-realmid
|
||||
// we may have a concurrent creation of the same login failure entry that we want to avoid
|
||||
|
||||
@Id
|
||||
protected String id;
|
||||
protected String username;
|
||||
|
||||
@Id
|
||||
protected String realmId;
|
||||
|
||||
protected int failedLoginNotBefore;
|
||||
protected int numFailures;
|
||||
protected long lastFailure;
|
||||
protected String lastIPFailure;
|
||||
|
||||
protected String realm;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
@ -76,11 +73,36 @@ public class UsernameLoginFailureEntity {
|
|||
this.lastIPFailure = lastIPFailure;
|
||||
}
|
||||
|
||||
public String getRealm() {
|
||||
return realm;
|
||||
public String getRealmId() {
|
||||
return realmId;
|
||||
}
|
||||
|
||||
public void setRealm(String realm) {
|
||||
this.realm = realm;
|
||||
public void setRealmId(String realmId) {
|
||||
this.realmId = realmId;
|
||||
}
|
||||
|
||||
public static class Key implements Serializable {
|
||||
|
||||
private String realmId;
|
||||
|
||||
private String username;
|
||||
|
||||
public Key() {
|
||||
}
|
||||
|
||||
public Key(String realmId, String username) {
|
||||
this.realmId = realmId;
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getRealmId() {
|
||||
return realmId;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
org.keycloak.models.sessions.jpa.JpaSessionProviderFactory
|
||||
org.keycloak.models.sessions.jpa.JpaUserSessionProviderFactory
|
|
@ -24,12 +24,7 @@
|
|||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-model-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-model-tests</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
|
|
@ -15,11 +15,9 @@ import org.keycloak.models.sessions.mem.entities.UsernameLoginFailureKey;
|
|||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.util.Time;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
|
@ -30,7 +28,6 @@ public class MemUserSessionProvider implements UserSessionProvider {
|
|||
private final KeycloakSession session;
|
||||
private final ConcurrentHashMap<UserSessionKey, UserSessionEntity> sessions;
|
||||
private final ConcurrentHashMap<UsernameLoginFailureKey, UsernameLoginFailureEntity> loginFailures;
|
||||
private DummyKeycloakTransaction tx;
|
||||
|
||||
public MemUserSessionProvider(KeycloakSession session, ConcurrentHashMap<UserSessionKey, UserSessionEntity> sessions, ConcurrentHashMap<UsernameLoginFailureKey, UsernameLoginFailureEntity> loginFailures) {
|
||||
this.session = session;
|
||||
|
@ -55,13 +52,13 @@ public class MemUserSessionProvider implements UserSessionProvider {
|
|||
|
||||
sessions.put(new UserSessionKey(realm.getId(), id), entity);
|
||||
|
||||
return new UserSessionAdapter(session, entity);
|
||||
return new UserSessionAdapter(session, realm, entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserSessionModel getUserSession(RealmModel realm, String id) {
|
||||
UserSessionEntity entity = sessions.get(new UserSessionKey(realm.getId(), id));
|
||||
return entity != null ? new UserSessionAdapter(session, entity) : null;
|
||||
return entity != null ? new UserSessionAdapter(session, realm, entity) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -69,18 +66,18 @@ public class MemUserSessionProvider implements UserSessionProvider {
|
|||
List<UserSessionModel> userSessions = new LinkedList<UserSessionModel>();
|
||||
for (UserSessionEntity s : sessions.values()) {
|
||||
if (s.getRealm().equals(realm.getId()) && s.getUser().equals(user.getId())) {
|
||||
userSessions.add(new UserSessionAdapter(session, s));
|
||||
userSessions.add(new UserSessionAdapter(session, realm, s));
|
||||
}
|
||||
}
|
||||
return userSessions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<UserSessionModel> getUserSessions(RealmModel realm, ClientModel client) {
|
||||
Set<UserSessionModel> clientSessions = new HashSet<UserSessionModel>();
|
||||
public List<UserSessionModel> getUserSessions(RealmModel realm, ClientModel client) {
|
||||
List<UserSessionModel> clientSessions = new LinkedList<UserSessionModel>();
|
||||
for (UserSessionEntity s : sessions.values()) {
|
||||
if (s.getRealm().equals(realm.getId()) && s.getClients().contains(client.getClientId())) {
|
||||
clientSessions.add(new UserSessionAdapter(session, s));
|
||||
clientSessions.add(new UserSessionAdapter(session, realm, s));
|
||||
}
|
||||
}
|
||||
return clientSessions;
|
||||
|
@ -194,51 +191,8 @@ public class MemUserSessionProvider implements UserSessionProvider {
|
|||
loginFailures.remove(new UsernameLoginFailureKey(realm.getId(), user.getUsername()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeycloakTransaction getTransaction() {
|
||||
if (tx == null) {
|
||||
tx = new DummyKeycloakTransaction();
|
||||
}
|
||||
return tx;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
public static class DummyKeycloakTransaction implements KeycloakTransaction {
|
||||
|
||||
public boolean rollBackOnly;
|
||||
public boolean active;
|
||||
|
||||
@Override
|
||||
public void begin() {
|
||||
this.active = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commit() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRollbackOnly() {
|
||||
this.rollBackOnly = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getRollbackOnly() {
|
||||
return rollBackOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return active;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||
*/
|
||||
public class MemUserSessionProviderFactory implements UserSessionProviderFactory {
|
||||
|
||||
public static final String ID = "mem";
|
||||
|
||||
private ConcurrentHashMap<UserSessionKey, UserSessionEntity> sessions = new ConcurrentHashMap<UserSessionKey, UserSessionEntity>();
|
||||
|
||||
private ConcurrentHashMap<UsernameLoginFailureKey, UsernameLoginFailureEntity> loginFailures = new ConcurrentHashMap<UsernameLoginFailureKey, UsernameLoginFailureEntity>();
|
||||
|
@ -37,7 +39,7 @@ public class MemUserSessionProviderFactory implements UserSessionProviderFactory
|
|||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "mem";
|
||||
return ID;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,21 +17,16 @@ public class UserSessionAdapter implements UserSessionModel {
|
|||
|
||||
private final KeycloakSession session;
|
||||
|
||||
private final RealmModel realm;
|
||||
|
||||
private final UserSessionEntity entity;
|
||||
|
||||
public UserSessionAdapter(KeycloakSession session, UserSessionEntity entity) {
|
||||
public UserSessionAdapter(KeycloakSession session, RealmModel realm, UserSessionEntity entity) {
|
||||
this.session = session;
|
||||
this.realm = realm;
|
||||
this.entity = entity;
|
||||
}
|
||||
|
||||
public RealmModel getRealm() {
|
||||
return session.model().getRealm(entity.getRealm());
|
||||
}
|
||||
|
||||
public void setRealm(RealmModel realm) {
|
||||
entity.setRealm(realm.getId());
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return entity.getId();
|
||||
}
|
||||
|
@ -41,7 +36,7 @@ public class UserSessionAdapter implements UserSessionModel {
|
|||
}
|
||||
|
||||
public UserModel getUser() {
|
||||
return session.model().getUserById(entity.getUser(), getRealm());
|
||||
return realm.getUserById(entity.getUser());
|
||||
}
|
||||
|
||||
public void setUser(UserModel user) {
|
||||
|
@ -79,13 +74,8 @@ public class UserSessionAdapter implements UserSessionModel {
|
|||
}
|
||||
}
|
||||
|
||||
public List<String> getClientIds() {
|
||||
return entity.getClients();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ClientModel> getClientAssociations() {
|
||||
RealmModel realm = getRealm();
|
||||
List<ClientModel> models = new LinkedList<ClientModel>();
|
||||
for (String clientId : entity.getClients()) {
|
||||
models.add(realm.findClient(clientId));
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
package org.keycloak.models.sessions.mem;
|
||||
|
||||
import org.keycloak.model.test.AbstractUserSessionProviderTest;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.UserSessionProvider;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class MemUserSessionProviderTest extends AbstractUserSessionProviderTest {
|
||||
|
||||
@Override
|
||||
public UserSessionProvider createProvider(KeycloakSession session) {
|
||||
return new MemUserSessionProviderFactory().create(session);
|
||||
}
|
||||
|
||||
}
|
42
model/sessions-mongo/pom.xml
Executable file
42
model/sessions-mongo/pom.xml
Executable file
|
@ -0,0 +1,42 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.0-beta-4-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-model-sessions-mongo</artifactId>
|
||||
<name>Keycloak Model Sessions Mongo</name>
|
||||
<description/>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-model-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-connections-mongo</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mongodb</groupId>
|
||||
<artifactId>mongo-java-driver</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
|
@ -0,0 +1,44 @@
|
|||
package org.keycloak.models.sessions.mongo;
|
||||
|
||||
import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
|
||||
import org.keycloak.connections.mongo.api.MongoStore;
|
||||
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public abstract class AbstractMongoAdapter<T extends MongoIdentifiableEntity> {
|
||||
|
||||
protected final MongoStoreInvocationContext invocationContext;
|
||||
|
||||
public AbstractMongoAdapter(MongoStoreInvocationContext invocationContext) {
|
||||
this.invocationContext = invocationContext;
|
||||
}
|
||||
|
||||
protected abstract T getMongoEntity();
|
||||
|
||||
protected void updateMongoEntity() {
|
||||
getMongoStore().updateEntity(getMongoEntity(), invocationContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) return true;
|
||||
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
AbstractMongoAdapter that = (AbstractMongoAdapter) o;
|
||||
|
||||
if (getMongoEntity() == null && that.getMongoEntity() == null) return true;
|
||||
return getMongoEntity().equals(that.getMongoEntity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getMongoEntity()!=null ? getMongoEntity().hashCode() : super.hashCode();
|
||||
}
|
||||
|
||||
protected MongoStore getMongoStore() {
|
||||
return invocationContext.getMongoStore();
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue