diff --git a/connections/jpa-liquibase/pom.xml b/connections/jpa-liquibase/pom.xml
new file mode 100755
index 0000000000..5196c428ad
--- /dev/null
+++ b/connections/jpa-liquibase/pom.xml
@@ -0,0 +1,74 @@
+
+
+
+ keycloak-parent
+ org.keycloak
+ 1.1.0-Alpha1-SNAPSHOT
+ ../../pom.xml
+
+ 4.0.0
+
+ keycloak-connections-jpa-liquibase
+ Keycloak Connections JPA Liquibase Updater
+
+
+
+
+ org.keycloak
+ keycloak-connections-jpa
+ ${project.version}
+
+
+ org.liquibase
+ liquibase-core
+
+
+ org.yaml
+ snakeyaml
+
+
+
+
+ org.jboss.logging
+ jboss-logging
+
+
+
+
+
+
+ org.liquibase
+ liquibase-maven-plugin
+
+ 3.1.1
+
+ META-INF/jpa-changelog-master.xml
+
+ ${url}
+ ${driver}
+ ${username}
+ ${password}
+
+ ${referenceUrl}
+ ${driver}
+ ${username}
+ ${password}
+
+
+
+ com.h2database
+ h2
+ ${h2.version}
+
+
+
+
+
+
+
+ sa
+
+ org.h2.Driver
+
+
diff --git a/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/LiquibaseJpaUpdaterProvider.java b/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/LiquibaseJpaUpdaterProvider.java
new file mode 100644
index 0000000000..8d1f6fad4a
--- /dev/null
+++ b/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/LiquibaseJpaUpdaterProvider.java
@@ -0,0 +1,208 @@
+package org.keycloak.connections.jpa.updater.liquibase;
+
+import liquibase.Contexts;
+import liquibase.Liquibase;
+import liquibase.changelog.ChangeSet;
+import liquibase.changelog.DatabaseChangeLog;
+import liquibase.changelog.RanChangeSet;
+import liquibase.database.Database;
+import liquibase.database.DatabaseFactory;
+import liquibase.database.core.DB2Database;
+import liquibase.database.core.DerbyDatabase;
+import liquibase.database.core.FirebirdDatabase;
+import liquibase.database.core.H2Database;
+import liquibase.database.core.HsqlDatabase;
+import liquibase.database.core.InformixDatabase;
+import liquibase.database.core.MSSQLDatabase;
+import liquibase.database.core.MySQLDatabase;
+import liquibase.database.core.OracleDatabase;
+import liquibase.database.core.PostgresDatabase;
+import liquibase.database.core.SQLiteDatabase;
+import liquibase.database.core.SybaseASADatabase;
+import liquibase.database.core.SybaseDatabase;
+import liquibase.database.jvm.JdbcConnection;
+import liquibase.exception.LiquibaseException;
+import liquibase.logging.LogFactory;
+import liquibase.logging.LogLevel;
+import liquibase.resource.ClassLoaderResourceAccessor;
+import liquibase.servicelocator.ServiceLocator;
+import org.jboss.logging.Logger;
+import org.keycloak.connections.jpa.updater.JpaUpdaterProvider;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.List;
+
+/**
+ * @author Stian Thorgersen
+ */
+public class LiquibaseJpaUpdaterProvider implements JpaUpdaterProvider {
+
+ private static final Logger logger = Logger.getLogger(LiquibaseJpaUpdaterProvider.class);
+
+ private static final String CHANGELOG = "META-INF/jpa-changelog-master.xml";
+
+ @Override
+ public String getCurrentVersionSql() {
+ return "SELECT ID from DATABASECHANGELOG ORDER BY DATEEXECUTED DESC LIMIT 1";
+ }
+
+ @Override
+ public void update(Connection connection) {
+ logger.debug("Starting database update");
+
+ try {
+ Liquibase liquibase = getLiquibase(connection);
+
+ List changeSets = liquibase.listUnrunChangeSets((Contexts) null);
+ if (!changeSets.isEmpty()) {
+ if (changeSets.get(0).getId().equals(FIRST_VERSION)) {
+ Statement statement = connection.createStatement();
+ try {
+ statement.executeQuery("SELECT id FROM REALM");
+
+ logger.infov("Updating database from {0} to {1}", FIRST_VERSION, changeSets.get(changeSets.size() - 1).getId());
+ liquibase.markNextChangeSetRan((Contexts) null);
+ } catch (SQLException e) {
+ logger.info("Initializing database schema");
+ }
+ } else {
+ if (logger.isDebugEnabled()) {
+ List ranChangeSets = liquibase.getDatabase().getRanChangeSetList();
+ logger.debugv("Updating database from {0} to {1}", ranChangeSets.get(ranChangeSets.size() - 1).getId(), changeSets.get(changeSets.size() - 1).getId());
+ } else {
+ logger.infov("Updating database");
+ }
+ }
+
+ liquibase.update((Contexts) null);
+ }
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to update database", e);
+ }
+ logger.debug("Completed database update");
+ }
+
+ @Override
+ public void validate(Connection connection) {
+ try {
+ Liquibase liquibase = getLiquibase(connection);
+
+ liquibase.validate();
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to validate database", e);
+ }
+ }
+
+ private Liquibase getLiquibase(Connection connection) throws Exception {
+ if (!System.getProperties().containsKey("liquibase.scan.packages")) {
+ System.setProperty("liquibase.scan.packages", "liquibase.change,liquibase.changelog,liquibase.database,liquibase.parser,liquibase.precondition,liquibase.datatype,liquibase.serializer,liquibase.sqlgenerator,liquibase.executor,liquibase.snapshot,liquibase.logging,liquibase.diff,liquibase.structure,liquibase.structurecompare,liquibase.lockservice");
+ }
+ LogFactory.setInstance(new LogWrapper());
+ Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(connection));
+ return new Liquibase(CHANGELOG, new ClassLoaderResourceAccessor(getClass().getClassLoader()), database);
+ }
+
+ @Override
+ public void close() {
+ }
+
+ private static class LogWrapper extends LogFactory {
+
+ private liquibase.logging.Logger logger = new liquibase.logging.Logger() {
+ @Override
+ public void setName(String name) {
+ }
+
+ @Override
+ public void setLogLevel(String level) {
+ }
+
+ @Override
+ public void setLogLevel(LogLevel level) {
+ }
+
+ @Override
+ public void setLogLevel(String logLevel, String logFile) {
+ }
+
+ @Override
+ public void severe(String message) {
+ LiquibaseJpaUpdaterProvider.logger.error(message);
+ }
+
+ @Override
+ public void severe(String message, Throwable e) {
+ LiquibaseJpaUpdaterProvider.logger.error(message, e);
+ }
+
+ @Override
+ public void warning(String message) {
+ LiquibaseJpaUpdaterProvider.logger.warn(message);
+ }
+
+ @Override
+ public void warning(String message, Throwable e) {
+ LiquibaseJpaUpdaterProvider.logger.warn(message, e);
+ }
+
+ @Override
+ public void info(String message) {
+ LiquibaseJpaUpdaterProvider.logger.debug(message);
+ }
+
+ @Override
+ public void info(String message, Throwable e) {
+ LiquibaseJpaUpdaterProvider.logger.debug(message, e);
+ }
+
+ @Override
+ public void debug(String message) {
+ LiquibaseJpaUpdaterProvider.logger.trace(message);
+ }
+
+ @Override
+ public LogLevel getLogLevel() {
+ if (LiquibaseJpaUpdaterProvider.logger.isTraceEnabled()) {
+ return LogLevel.DEBUG;
+ } else if (LiquibaseJpaUpdaterProvider.logger.isDebugEnabled()) {
+ return LogLevel.INFO;
+ } else {
+ return LogLevel.WARNING;
+ }
+ }
+
+ @Override
+ public void debug(String message, Throwable e) {
+ LiquibaseJpaUpdaterProvider.logger.trace(message, e);
+ }
+
+ @Override
+ public void setChangeLog(DatabaseChangeLog databaseChangeLog) {
+ }
+
+ @Override
+ public void setChangeSet(ChangeSet changeSet) {
+ }
+
+ @Override
+ public int getPriority() {
+ return 0;
+ }
+ };
+
+ @Override
+ public liquibase.logging.Logger getLog(String name) {
+ return logger;
+ }
+
+ @Override
+ public liquibase.logging.Logger getLog() {
+ return logger;
+ }
+
+ }
+
+}
diff --git a/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/LiquibaseJpaUpdaterProviderFactory.java b/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/LiquibaseJpaUpdaterProviderFactory.java
new file mode 100644
index 0000000000..d88b787df1
--- /dev/null
+++ b/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/LiquibaseJpaUpdaterProviderFactory.java
@@ -0,0 +1,31 @@
+package org.keycloak.connections.jpa.updater.liquibase;
+
+import org.keycloak.Config;
+import org.keycloak.connections.jpa.updater.JpaUpdaterProvider;
+import org.keycloak.connections.jpa.updater.JpaUpdaterProviderFactory;
+import org.keycloak.models.KeycloakSession;
+
+/**
+ * @author Stian Thorgersen
+ */
+public class LiquibaseJpaUpdaterProviderFactory implements JpaUpdaterProviderFactory {
+
+ @Override
+ public JpaUpdaterProvider create(KeycloakSession session) {
+ return new LiquibaseJpaUpdaterProvider();
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+ }
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public String getId() {
+ return "liquibase";
+ }
+
+}
diff --git a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.0.0.Final.xml b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.0.0.Final.xml
new file mode 100644
index 0000000000..5b5e921e26
--- /dev/null
+++ b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.0.0.Final.xml
@@ -0,0 +1,376 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.1.0.Beta1.xml b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.1.0.Beta1.xml
new file mode 100644
index 0000000000..0b9f0f527f
--- /dev/null
+++ b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.1.0.Beta1.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-master.xml b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-master.xml
new file mode 100644
index 0000000000..010b121b68
--- /dev/null
+++ b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-master.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/connections/jpa-liquibase/src/main/resources/META-INF/services/org.keycloak.connections.jpa.updater.JpaUpdaterProviderFactory b/connections/jpa-liquibase/src/main/resources/META-INF/services/org.keycloak.connections.jpa.updater.JpaUpdaterProviderFactory
new file mode 100644
index 0000000000..cb36ec330d
--- /dev/null
+++ b/connections/jpa-liquibase/src/main/resources/META-INF/services/org.keycloak.connections.jpa.updater.JpaUpdaterProviderFactory
@@ -0,0 +1 @@
+org.keycloak.connections.jpa.updater.liquibase.LiquibaseJpaUpdaterProviderFactory
\ No newline at end of file
diff --git a/connections/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java b/connections/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java
index 4bd1d6ee09..5a00c7403e 100755
--- a/connections/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java
+++ b/connections/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java
@@ -1,12 +1,20 @@
package org.keycloak.connections.jpa;
import org.hibernate.ejb.AvailableSettings;
+import org.jboss.logging.Logger;
import org.keycloak.Config;
+import org.keycloak.connections.jpa.updater.JpaUpdaterProvider;
import org.keycloak.models.KeycloakSession;
+import javax.naming.InitialContext;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
+import javax.sql.DataSource;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
@@ -15,13 +23,15 @@ import java.util.Map;
*/
public class DefaultJpaConnectionProviderFactory implements JpaConnectionProviderFactory {
+ private static final Logger logger = Logger.getLogger(DefaultJpaConnectionProviderFactory.class);
+
private volatile EntityManagerFactory emf;
private Config.Scope config;
@Override
public JpaConnectionProvider create(KeycloakSession session) {
- lazyInit();
+ lazyInit(session);
EntityManager em = emf.createEntityManager();
em = PersistenceExceptionConverter.create(em);
@@ -46,11 +56,17 @@ public class DefaultJpaConnectionProviderFactory implements JpaConnectionProvide
this.config = config;
}
- private void lazyInit() {
+ private void lazyInit(KeycloakSession session) {
if (emf == null) {
synchronized (this) {
if (emf == null) {
+ logger.debug("Initializing JPA connections");
+
+ Connection connection = null;
+
String unitName = config.get("unitName");
+ String databaseSchema = config.get("databaseSchema");
+
Map properties = new HashMap();
// Only load config from keycloak-server.json if unitName is not specified
@@ -83,19 +99,80 @@ public class DefaultJpaConnectionProviderFactory implements JpaConnectionProvide
properties.put("hibernate.dialect", driverDialect);
}
- String databaseSchema = config.get("databaseSchema", "validate");
if (databaseSchema != null) {
- properties.put("hibernate.hbm2ddl.auto", databaseSchema);
+ if (databaseSchema.equals("development-update")) {
+ properties.put("hibernate.hbm2ddl.auto", "update");
+ databaseSchema = null;
+ } else if (databaseSchema.equals("development-validate")) {
+ properties.put("hibernate.hbm2ddl.auto", "validate");
+ databaseSchema = null;
+ }
}
properties.put("hibernate.show_sql", config.getBoolean("showSql", false));
properties.put("hibernate.format_sql", config.getBoolean("formatSql", true));
}
+ if (databaseSchema != null) {
+ logger.trace("Updating database");
+
+ JpaUpdaterProvider updater = session.getProvider(JpaUpdaterProvider.class);
+ connection = getConnection();
+
+ if (databaseSchema.equals("update")) {
+ String currentVersion = null;
+ try {
+ ResultSet resultSet = connection.createStatement().executeQuery(updater.getCurrentVersionSql());
+ if (resultSet.next()) {
+ currentVersion = resultSet.getString(1);
+ }
+ } catch (SQLException e) {
+ }
+
+ if (currentVersion == null || !JpaUpdaterProvider.LAST_VERSION.equals(currentVersion)) {
+ updater.update(connection);
+ } else {
+ logger.debug("Database is up to date");
+ }
+ } else if (databaseSchema.equals("validate")) {
+ updater.validate(connection);
+ } else {
+ throw new RuntimeException("Invalid value for databaseSchema: " + databaseSchema);
+ }
+
+ logger.trace("Database update completed");
+ }
+
+ logger.trace("Creating EntityManagerFactory");
emf = Persistence.createEntityManagerFactory(unitName, properties);
+ logger.trace("EntityManagerFactory created");
+
+ // Close after creating EntityManagerFactory to prevent in-mem databases from closing
+ if (connection != null) {
+ try {
+ connection.close();
+ } catch (SQLException e) {
+ logger.warn(e);
+ }
+ }
}
}
}
}
+ private Connection getConnection() {
+ try {
+ String dataSourceLookup = config.get("dataSource");
+ if (dataSourceLookup != null) {
+ DataSource dataSource = (DataSource) new InitialContext().lookup(dataSourceLookup);
+ return dataSource.getConnection();
+ } else {
+ Class.forName(config.get("driver"));
+ return DriverManager.getConnection(config.get("url"), config.get("user"), config.get("password"));
+ }
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to connect to database", e);
+ }
+ }
+
}
diff --git a/connections/jpa/src/main/java/org/keycloak/connections/jpa/updater/JpaUpdaterProvider.java b/connections/jpa/src/main/java/org/keycloak/connections/jpa/updater/JpaUpdaterProvider.java
new file mode 100644
index 0000000000..9e19f7f3a7
--- /dev/null
+++ b/connections/jpa/src/main/java/org/keycloak/connections/jpa/updater/JpaUpdaterProvider.java
@@ -0,0 +1,22 @@
+package org.keycloak.connections.jpa.updater;
+
+import org.keycloak.provider.Provider;
+
+import java.sql.Connection;
+
+/**
+ * @author Stian Thorgersen
+ */
+public interface JpaUpdaterProvider extends Provider {
+
+ public String FIRST_VERSION = "1.0.0.Final";
+
+ public String LAST_VERSION = "1.1.0.Beta1";
+
+ public String getCurrentVersionSql();
+
+ public void update(Connection connection);
+
+ public void validate(Connection connection);
+
+}
diff --git a/connections/jpa/src/main/java/org/keycloak/connections/jpa/updater/JpaUpdaterProviderFactory.java b/connections/jpa/src/main/java/org/keycloak/connections/jpa/updater/JpaUpdaterProviderFactory.java
new file mode 100644
index 0000000000..29a89f40e8
--- /dev/null
+++ b/connections/jpa/src/main/java/org/keycloak/connections/jpa/updater/JpaUpdaterProviderFactory.java
@@ -0,0 +1,9 @@
+package org.keycloak.connections.jpa.updater;
+
+import org.keycloak.provider.ProviderFactory;
+
+/**
+ * @author Stian Thorgersen
+ */
+public interface JpaUpdaterProviderFactory extends ProviderFactory {
+}
diff --git a/connections/jpa/src/main/java/org/keycloak/connections/jpa/updater/JpaUpdaterSpi.java b/connections/jpa/src/main/java/org/keycloak/connections/jpa/updater/JpaUpdaterSpi.java
new file mode 100644
index 0000000000..e95242aac9
--- /dev/null
+++ b/connections/jpa/src/main/java/org/keycloak/connections/jpa/updater/JpaUpdaterSpi.java
@@ -0,0 +1,27 @@
+package org.keycloak.connections.jpa.updater;
+
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+
+/**
+ * @author Stian Thorgersen
+ */
+public class JpaUpdaterSpi implements Spi {
+
+ @Override
+ public String getName() {
+ return "connectionsJpaUpdater";
+ }
+
+ @Override
+ public Class extends Provider> getProviderClass() {
+ return JpaUpdaterProvider.class;
+ }
+
+ @Override
+ public Class extends ProviderFactory> getProviderFactoryClass() {
+ return JpaUpdaterProviderFactory.class;
+ }
+
+}
diff --git a/connections/jpa/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/connections/jpa/src/main/resources/META-INF/services/org.keycloak.provider.Spi
index d7b47a8319..175bcfbf10 100644
--- a/connections/jpa/src/main/resources/META-INF/services/org.keycloak.provider.Spi
+++ b/connections/jpa/src/main/resources/META-INF/services/org.keycloak.provider.Spi
@@ -1 +1,2 @@
-org.keycloak.connections.jpa.JpaConnectionSpi
\ No newline at end of file
+org.keycloak.connections.jpa.JpaConnectionSpi
+org.keycloak.connections.jpa.updater.JpaUpdaterSpi
\ No newline at end of file
diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java
index e644c7acaf..f800dfbed3 100644
--- a/connections/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java
+++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java
@@ -9,6 +9,7 @@ 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.connections.mongo.updater.DefaultMongoUpdaterProvider;
import org.keycloak.models.KeycloakSession;
import java.util.Collections;
@@ -64,7 +65,6 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
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");
@@ -77,9 +77,18 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
this.db = client.getDB(dbName);
- this.mongoStore = new MongoStoreImpl(db, clearOnStartup, getManagedEntities());
+ String databaseSchema = config.get("databaseSchema");
+ if (databaseSchema != null) {
+ if (databaseSchema.equals("update")) {
+ new DefaultMongoUpdaterProvider().update(db);
+ } else {
+ throw new RuntimeException("Invalid value for databaseSchema: " + databaseSchema);
+ }
+ }
- logger.infof("Initialized mongo model. host: %s, port: %d, db: %s, clearOnStartup: %b", host, port, dbName, clearOnStartup);
+ this.mongoStore = new MongoStoreImpl(db, getManagedEntities());
+
+ logger.debugv("Initialized mongo model. host: %s, port: %d, db: %s", host, port, dbName);
} catch (Exception e) {
throw new RuntimeException(e);
}
diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/api/MongoIndex.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/api/MongoIndex.java
deleted file mode 100644
index 7b8a0f5448..0000000000
--- a/connections/mongo/src/main/java/org/keycloak/connections/mongo/api/MongoIndex.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package org.keycloak.connections.mongo.api;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.Inherited;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-import static java.lang.annotation.ElementType.TYPE;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-/**
- * @author Stian Thorgersen
- */
-@Target({TYPE})
-@Documented
-@Retention(RUNTIME)
-@Inherited
-public @interface MongoIndex {
-
- String[] fields();
-
- boolean unique() default false;
-
- boolean sparse() default false;
-
-}
diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/api/MongoIndexes.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/api/MongoIndexes.java
deleted file mode 100644
index 7f0a3f5f99..0000000000
--- a/connections/mongo/src/main/java/org/keycloak/connections/mongo/api/MongoIndexes.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package org.keycloak.connections.mongo.api;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.Inherited;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-import static java.lang.annotation.ElementType.TYPE;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-/**
- * @author Stian Thorgersen
- */
-@Target({TYPE})
-@Documented
-@Retention(RUNTIME)
-@Inherited
-public @interface MongoIndexes {
-
- MongoIndex[] value();
-
-}
diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/impl/MongoStoreImpl.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/impl/MongoStoreImpl.java
index 09104ea7a8..85bf25e3d6 100755
--- a/connections/mongo/src/main/java/org/keycloak/connections/mongo/impl/MongoStoreImpl.java
+++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/impl/MongoStoreImpl.java
@@ -11,8 +11,6 @@ import org.jboss.logging.Logger;
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;
@@ -57,7 +55,7 @@ public class MongoStoreImpl implements MongoStore {
new ConcurrentHashMap, EntityInfo>();
- public MongoStoreImpl(DB database, boolean clearCollectionsOnStartup, Class>[] managedEntityTypes) {
+ public MongoStoreImpl(DB database, Class>[] managedEntityTypes) {
this.database = database;
mapperRegistry = new MapperRegistry();
@@ -86,13 +84,6 @@ public class MongoStoreImpl implements MongoStore {
mapperRegistry.addAppObjectMapper(new MongoEntityMapper(this, mapperRegistry, type));
mapperRegistry.addDBObjectMapper(new BasicDBObjectMapper(this, mapperRegistry, type));
}
-
- if (clearCollectionsOnStartup) {
- // dropDatabase();
- clearManagedCollections(managedEntityTypes);
- }
-
- initManagedCollections(managedEntityTypes);
}
protected void dropDatabase() {
@@ -100,63 +91,6 @@ public class MongoStoreImpl implements MongoStore {
logger.info("Database " + this.database.getName() + " dropped in MongoDB");
}
- // Don't drop database, but just clear all data in managed collections (useful for export/import or during development)
- protected void clearManagedCollections(Class>[] managedEntityTypes) {
- for (Class> clazz : managedEntityTypes) {
- DBCollection dbCollection = getDBCollectionForType(clazz);
- if (dbCollection != null) {
- dbCollection.remove(new BasicDBObject());
- logger.debug("Collection " + dbCollection.getName() + " cleared from " + this.database.getName());
- }
- }
- }
-
- protected void initManagedCollections(Class>[] managedEntityTypes) {
- for (Class> clazz : managedEntityTypes) {
- EntityInfo entityInfo = getEntityInfo(clazz);
- String dbCollectionName = entityInfo.getDbCollectionName();
- if (dbCollectionName != null && !database.collectionExists(dbCollectionName)) {
- DBCollection dbCollection = database.getCollection(dbCollectionName);
-
- logger.debug("Created collection " + dbCollection.getName() + " in " + this.database.getName());
-
- MongoIndex index = clazz.getAnnotation(MongoIndex.class);
- if (index != null) {
- createIndex(dbCollection, index);
- }
-
- MongoIndexes indexes = clazz.getAnnotation(MongoIndexes.class);
- if (indexes != null) {
- for (MongoIndex i : indexes.value()) {
- createIndex(dbCollection, i);
- }
- }
- }
- }
- }
-
- protected void createIndex(DBCollection dbCollection, MongoIndex index) {
- BasicDBObject fields = new BasicDBObject();
- for (String f : index.fields()) {
- fields.put(f, 1);
- }
-
- boolean unique = index.unique();
- boolean sparse = index.sparse();
-
- BasicDBObject options = new BasicDBObject();
- if (unique) {
- options.put("unique", unique);
- }
- if (sparse) {
- options.put("sparse", sparse);
- }
-
- dbCollection.ensureIndex(fields, options);
-
- logger.debug("Created index " + fields + "(options: " + options + ") on " + dbCollection.getName() + " in " + this.database.getName());
- }
-
@Override
public void insertEntity(MongoIdentifiableEntity entity, MongoStoreInvocationContext context) {
Class extends MongoEntity> clazz = entity.getClass();
diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/DefaultMongoUpdaterProvider.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/DefaultMongoUpdaterProvider.java
new file mode 100644
index 0000000000..1f3f6ae198
--- /dev/null
+++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/DefaultMongoUpdaterProvider.java
@@ -0,0 +1,103 @@
+package org.keycloak.connections.mongo.updater;
+
+import com.mongodb.BasicDBObject;
+import com.mongodb.DB;
+import com.mongodb.DBCollection;
+import com.mongodb.DBCursor;
+import com.mongodb.DBObject;
+import org.jboss.logging.Logger;
+import org.keycloak.connections.mongo.updater.updates.Update;
+import org.keycloak.connections.mongo.updater.updates.Update1_0_0_Final;
+import org.keycloak.connections.mongo.updater.updates.Update1_1_0_Beta1;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author Stian Thorgersen
+ */
+public class DefaultMongoUpdaterProvider implements MongoUpdaterProvider {
+
+ public static final Logger log = Logger.getLogger(DefaultMongoUpdaterProvider.class);
+
+ public static final String CHANGE_LOG_COLLECTION = "databaseChangeLog";
+
+ private Class extends Update>[] updates = new Class[]{
+ Update1_0_0_Final.class,
+ Update1_1_0_Beta1.class
+ };
+
+ @Override
+ public void update(DB db) {
+ log.debug("Starting database update");
+ try {
+ boolean changeLogExists = db.collectionExists(CHANGE_LOG_COLLECTION);
+ boolean realmExists = db.collectionExists("realms");
+
+ DBCollection changeLog = db.getCollection(CHANGE_LOG_COLLECTION);
+
+ List executed = new LinkedList();
+ if (!changeLogExists && realmExists) {
+ Update1_0_0_Final u = new Update1_0_0_Final();
+ executed.add(u.getId());
+ createLog(changeLog, u, 1);
+ } else if (changeLogExists) {
+ DBCursor cursor = changeLog.find().sort(new BasicDBObject("orderExecuted", 1));
+ while (cursor.hasNext()) {
+ executed.add((String) cursor.next().get("_id"));
+ }
+ }
+
+ List updatesToRun = new LinkedList();
+ for (Class extends Update> updateClass : updates) {
+ Update u = updateClass.newInstance();
+ if (!executed.contains(u.getId())) {
+ updatesToRun.add(u);
+ }
+ }
+
+ if (!updatesToRun.isEmpty()) {
+ if (executed.isEmpty()) {
+ log.info("Initializing database schema");
+ } else {
+ if (log.isDebugEnabled()) {
+ log.infov("Updating database from {0} to {1}", executed.get(executed.size() - 1), updatesToRun.get(updatesToRun.size() - 1).getId());
+ } else {
+ log.debugv("Updating database");
+ }
+ }
+
+ int order = executed.size();
+ for (Update u : updatesToRun) {
+ log.debugv("Executing updates for {0}", u.getId());
+
+ u.setLog(log);
+ u.setDb(db);
+ u.update();
+
+ createLog(changeLog, u, ++order);
+
+ log.debugv("Completed updates for {0}", u.getId());
+ }
+ }
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to update database", e);
+ }
+ log.debug("Completed database update");
+ }
+
+ private void createLog(DBCollection changeLog, Update update, int orderExecuted) {
+ changeLog.insert(new BasicDBObject("_id", update.getId()).append("dateExecuted", new Date()).append("orderExecuted", orderExecuted));
+ }
+
+ @Override
+ public void close() {
+ }
+
+}
diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/DefaultMongoUpdaterProviderFactory.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/DefaultMongoUpdaterProviderFactory.java
new file mode 100644
index 0000000000..80698505b0
--- /dev/null
+++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/DefaultMongoUpdaterProviderFactory.java
@@ -0,0 +1,29 @@
+package org.keycloak.connections.mongo.updater;
+
+import org.keycloak.Config;
+import org.keycloak.models.KeycloakSession;
+
+/**
+ * @author Stian Thorgersen
+ */
+public class DefaultMongoUpdaterProviderFactory implements MongoUpdaterProviderFactory {
+
+ @Override
+ public MongoUpdaterProvider create(KeycloakSession session) {
+ return new DefaultMongoUpdaterProvider();
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+ }
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public String getId() {
+ return "default";
+ }
+
+}
diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/MongoUpdaterProvider.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/MongoUpdaterProvider.java
new file mode 100644
index 0000000000..7192f1d10d
--- /dev/null
+++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/MongoUpdaterProvider.java
@@ -0,0 +1,13 @@
+package org.keycloak.connections.mongo.updater;
+
+import com.mongodb.DB;
+import org.keycloak.provider.Provider;
+
+/**
+ * @author Stian Thorgersen
+ */
+public interface MongoUpdaterProvider extends Provider {
+
+ public void update(DB db);
+
+}
diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/MongoUpdaterProviderFactory.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/MongoUpdaterProviderFactory.java
new file mode 100644
index 0000000000..981f6d0ad9
--- /dev/null
+++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/MongoUpdaterProviderFactory.java
@@ -0,0 +1,9 @@
+package org.keycloak.connections.mongo.updater;
+
+import org.keycloak.provider.ProviderFactory;
+
+/**
+ * @author Stian Thorgersen
+ */
+public interface MongoUpdaterProviderFactory extends ProviderFactory {
+}
diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/MongoUpdaterSpi.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/MongoUpdaterSpi.java
new file mode 100644
index 0000000000..da89b4d8f8
--- /dev/null
+++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/MongoUpdaterSpi.java
@@ -0,0 +1,27 @@
+package org.keycloak.connections.mongo.updater;
+
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+
+/**
+ * @author Stian Thorgersen
+ */
+public class MongoUpdaterSpi implements Spi {
+
+ @Override
+ public String getName() {
+ return "connectionsMongoUpdater";
+ }
+
+ @Override
+ public Class extends Provider> getProviderClass() {
+ return MongoUpdaterProvider.class;
+ }
+
+ @Override
+ public Class extends ProviderFactory> getProviderFactoryClass() {
+ return MongoUpdaterProviderFactory.class;
+ }
+
+}
diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/updates/Update.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/updates/Update.java
new file mode 100644
index 0000000000..e8058f02f4
--- /dev/null
+++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/updates/Update.java
@@ -0,0 +1,62 @@
+package org.keycloak.connections.mongo.updater.updates;
+
+import com.mongodb.BasicDBObject;
+import com.mongodb.DB;
+import com.mongodb.DBCollection;
+import org.jboss.logging.Logger;
+
+import java.util.Arrays;
+
+/**
+ * @author Stian Thorgersen
+ */
+public abstract class Update {
+
+ protected DB db;
+
+ protected Logger log;
+
+ public abstract String getId();
+
+ public abstract void update() throws ClassNotFoundException;
+
+ protected DBCollection createCollection(String name) {
+ if (db.collectionExists(name)) {
+ throw new RuntimeException("Failed to create collection {0}: collection already exists");
+ }
+
+ DBCollection col = db.getCollection(name);
+ log.debugv("Created collection {0}", name);
+ return col;
+ }
+
+ protected void ensureIndex(String name, String field, boolean unique, boolean sparse) {
+ ensureIndex(name, new String[]{field}, unique, sparse);
+ }
+
+ protected void ensureIndex(String name, String[] fields, boolean unique, boolean sparse) {
+ DBCollection col = db.getCollection(name);
+
+ BasicDBObject o = new BasicDBObject();
+ for (String f : fields) {
+ o.append(f, 1);
+ }
+
+ col.ensureIndex(o, new BasicDBObject("unique", unique).append("sparse", sparse));
+ log.debugv("Created index {0}, fields={1}, unique={2}, sparse={3}", name, Arrays.toString(fields), unique, sparse);
+ }
+
+ protected void deleteEntries(String collection) {
+ db.getCollection(collection).remove(new BasicDBObject());
+ log.debugv("Deleted entries from {0}", collection);
+ }
+
+ public void setLog(Logger log) {
+ this.log = log;
+ }
+
+ public void setDb(DB db) {
+ this.db = db;
+ }
+
+}
diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/updates/Update1_0_0_Final.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/updates/Update1_0_0_Final.java
new file mode 100644
index 0000000000..ded25dc74e
--- /dev/null
+++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/updates/Update1_0_0_Final.java
@@ -0,0 +1,44 @@
+package org.keycloak.connections.mongo.updater.updates;
+
+import com.mongodb.BasicDBObject;
+import com.mongodb.DBCollection;
+import org.keycloak.connections.mongo.updater.DefaultMongoUpdaterProvider;
+
+/**
+ * @author Stian Thorgersen
+ */
+public class Update1_0_0_Final extends Update {
+
+ @Override
+ public String getId() {
+ return "1.0.0.Final";
+ }
+
+ @Override
+ public void update() throws ClassNotFoundException {
+ DBCollection realmsCollection = db.getCollection("realms");
+ realmsCollection.ensureIndex(new BasicDBObject("name", 1), new BasicDBObject("unique", true));
+
+ DefaultMongoUpdaterProvider.log.debugv("Created collection {0}", "realms");
+
+ createCollection("users");
+ ensureIndex("users", new String[] { "realmId", "username"}, true, false);
+ ensureIndex("users", "emailIndex", true, true);
+
+ createCollection("roles");
+ ensureIndex("roles", "nameIndex", true, false);
+
+ createCollection("applications");
+ ensureIndex("applications", new String[]{"realmId", "name"}, true, false);
+
+ createCollection("oauthClients");
+ ensureIndex("oauthClients", new String[] { "realmId", "name"}, true, false);
+
+ createCollection("userFailures");
+
+ createCollection("sessions");
+
+ createCollection("clientSessions");
+ }
+
+}
diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/updates/Update1_1_0_Beta1.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/updates/Update1_1_0_Beta1.java
new file mode 100644
index 0000000000..89d372f4be
--- /dev/null
+++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/updates/Update1_1_0_Beta1.java
@@ -0,0 +1,19 @@
+package org.keycloak.connections.mongo.updater.updates;
+
+/**
+ * @author Stian Thorgersen
+ */
+public class Update1_1_0_Beta1 extends Update {
+
+ @Override
+ public String getId() {
+ return "1.1.0.Beta1";
+ }
+
+ @Override
+ public void update() {
+ deleteEntries("clientSessions");
+ deleteEntries("sessions");
+ }
+
+}
diff --git a/connections/pom.xml b/connections/pom.xml
index a5b3bb2d6d..2d284bb5f2 100755
--- a/connections/pom.xml
+++ b/connections/pom.xml
@@ -14,6 +14,7 @@
jpa
+ jpa-liquibase
infinispan
mongo
diff --git a/dependencies/server-all/pom.xml b/dependencies/server-all/pom.xml
index a812b08f12..9c20bd459d 100755
--- a/dependencies/server-all/pom.xml
+++ b/dependencies/server-all/pom.xml
@@ -26,6 +26,11 @@
keycloak-connections-jpa
${project.version}
+
+ org.keycloak
+ keycloak-connections-jpa-liquibase
+ ${project.version}
+
org.keycloak
keycloak-connections-infinispan
@@ -180,6 +185,17 @@
de.idyl
winzipaes
+
+
+ org.liquibase
+ liquibase-core
+
+
+ org.yaml
+ snakeyaml
+
+
+
\ No newline at end of file
diff --git a/misc/UpdatingDatabaseSchema.md b/misc/UpdatingDatabaseSchema.md
new file mode 100644
index 0000000000..1dcee578df
--- /dev/null
+++ b/misc/UpdatingDatabaseSchema.md
@@ -0,0 +1,79 @@
+Updating Database Schema
+========================
+
+Keycloak supports automatically migrating the database to a new version. This is done by applying one or more change-sets
+to the existing database. This means if you need to do any changes to database schemas for JPA or Mongo you need to create
+a change-set that can transform the schema as well as any existing data.
+
+This includes changes to:
+
+* Realm entities
+* User entities
+* User session entities
+* Event entities
+
+
+Creating a JPA change-set
+-------------------------
+
+We use Liquibase to support updating the database. The change-sets are located in
+`connections/jpa-liquibase/src/main/resources/META-INF`. There's a separate file for each release that requires database
+changes.
+
+To manually create a change-set add a new file in the above location with the name `jpa-changelog-.xml`. This file
+should contain a single `change-set` with `id` equal to the next version to be released and `author` set to your email
+address. Then look at Liquibase documentation on how to write this file. Add a reference to this file in `jpa-changelog-master.xml`.
+The file should have a single change-set and the id of the change-set should be the next version to be released.
+
+You also need to update `org.keycloak.connections.jpa.updater.JpaUpdaterProvider#LAST_VERSION`. This
+is used by Keycloak to quickly determine if the database is up to date or not.
+
+You can also have Liquibase and Hibernate create one for you. To do this follow these steps:
+
+1. Delete existing databases
+ `rm keycloak*h2.db`
+2. Create a database of the old format:
+ `mvn -f connections/jpa-liquibase/pom.xml liquibase:update -Durl=jdbc:h2:keycloak`
+3. Make a copy of the database:
+ `cp keycloak.h2.db keycloak-old.h2.db`
+3. Run KeycloakServer to make Hibernate update the schema:
+ `mvn -f testsuite/integration exec:java -Pkeycloak-server -Dkeycloak.connectionsJpa.url='jdbc:h2:keycloak' -Dkeycloak.connectionsJpa.databaseSchema='development-update'`
+4. Wait until server is completely started, then stop it
+5. View the difference:
+ `mvn -f connections/jpa-liquibase/pom.xml liquibase:diff -Durl=jdbc:h2:keycloak-old -DreferenceUrl=jdbc:h2:keycloak`
+6. Create a change-set file:
+ `mvn -f connections/jpa-liquibase/pom.xml liquibase:diff -Durl=jdbc:h2:keycloak-old -DreferenceUrl=jdbc:h2:keycloak -Dliquibase.diffChangeLogFile=changelog.xml`
+
+This will generate the file `changelog.xml`. Once it's generated edit the file and combine all `change-sets` into
+a single `change-set` and change the `id` to the next version to be released and `author` to your email address. Then
+follow the steps above to copy it to the correct location and update `jpa-changelog-master.xml`. You have to manually
+add entries to the `change-set` to update existing data if required.
+
+When you have update the change-set Hibernate can validate the schema for you. First run:
+
+ rm -rf keycloak*h2.db
+ mvn -f testsuite/integration exec:java -Pkeycloak-server -Dkeycloak.connectionsJpa.url='jdbc:h2:keycloak' -Dkeycloak.connectionsJpa.databaseSchema='update'
+
+Once the server has started fully, stop it and run:
+
+ mvn -f testsuite/integration exec:java -Pkeycloak-server -Dkeycloak.connectionsJpa.url='jdbc:h2:keycloak' -Dkeycloak.connectionsJpa.databaseSchema='development-validate'
+
+
+Creating a Mongo change-set
+---------------------------
+
+As Mongo is schema-less it's significantly easier to create a change-set. You only need to create/delete collections as
+needed, as well as update any indexes. You will also need to update existing data if required.
+
+Mongo change-sets are written in Java and are located in the `connections/mongo` module, to add a new change-set create
+a new class that implements `org.keycloak.connections.mongo.updater.updates.Update` the name of the class should be
+`Update` with `.` replaced with `_`.
+
+You also need to add a reference to this file in `org.keycloak.connections.mongo.updater.DefaultMongoUpdaterProvider`.
+It should be added last to the `DefaultMongoUpdaterProvider#updates` array.
+
+
+Testing database migration
+--------------------------
+
+Get the database from an old version of Keycloak that includes the demo applications. Start the server with this and test it.
\ No newline at end of file
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoApplicationEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoApplicationEntity.java
index 9000d37ca4..7534e66fb0 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoApplicationEntity.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoApplicationEntity.java
@@ -4,7 +4,6 @@ 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.ApplicationEntity;
@@ -12,7 +11,6 @@ import org.keycloak.models.entities.ApplicationEntity;
* @author Marek Posolda
*/
@MongoCollection(collectionName = "applications")
-@MongoIndex(fields = { "realmId", "name" }, unique = true)
public class MongoApplicationEntity extends ApplicationEntity implements MongoIdentifiableEntity {
@Override
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoOAuthClientEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoOAuthClientEntity.java
index bf07f91a20..ab01359cd5 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoOAuthClientEntity.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoOAuthClientEntity.java
@@ -4,7 +4,6 @@ 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;
@@ -12,7 +11,6 @@ import org.keycloak.models.entities.OAuthClientEntity;
* @author Marek Posolda
*/
@MongoCollection(collectionName = "oauthClients")
-@MongoIndex(fields = { "realmId", "name" }, unique = true)
public class MongoOAuthClientEntity extends OAuthClientEntity implements MongoIdentifiableEntity {
@Override
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoRealmEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoRealmEntity.java
index c1a4267337..278435200c 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoRealmEntity.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoRealmEntity.java
@@ -4,7 +4,6 @@ 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.RealmEntity;
@@ -12,7 +11,6 @@ import org.keycloak.models.entities.RealmEntity;
* @author Marek Posolda
*/
@MongoCollection(collectionName = "realms")
-@MongoIndex(fields = { "name" }, unique = true)
public class MongoRealmEntity extends RealmEntity implements MongoIdentifiableEntity {
@Override
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoRoleEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoRoleEntity.java
index be034da440..cf0cfed248 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoRoleEntity.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoRoleEntity.java
@@ -6,7 +6,6 @@ import org.jboss.logging.Logger;
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 org.keycloak.models.entities.RoleEntity;
@@ -17,7 +16,6 @@ import java.util.List;
* @author Marek Posolda
*/
@MongoCollection(collectionName = "roles")
-@MongoIndex(fields = "nameIndex", unique = true)
public class MongoRoleEntity extends RoleEntity implements MongoIdentifiableEntity {
private static final Logger logger = Logger.getLogger(MongoRoleEntity.class);
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoUserEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoUserEntity.java
index a0c16bf9e7..c9f317e50d 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoUserEntity.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoUserEntity.java
@@ -2,8 +2,6 @@ package org.keycloak.models.mongo.keycloak.entities;
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;
@@ -11,13 +9,8 @@ import org.keycloak.models.entities.UserEntity;
* @author Marek Posolda
*/
@MongoCollection(collectionName = "users")
-@MongoIndexes({
- @MongoIndex(fields = { "realmId", "username" }, unique = true),
- @MongoIndex(fields = { "emailIndex" }, unique = true, sparse = true),
-})
public class MongoUserEntity extends UserEntity implements MongoIdentifiableEntity {
-
public String getEmailIndex() {
return getEmail() != null ? getRealmId() + "//" + getEmail() : null;
}
diff --git a/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/MemUserSessionProvider.java b/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/MemUserSessionProvider.java
index b43f827607..53cf7f34e9 100755
--- a/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/MemUserSessionProvider.java
+++ b/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/MemUserSessionProvider.java
@@ -253,7 +253,7 @@ public class MemUserSessionProvider implements UserSessionProvider {
@Override
public void onClientRemoved(RealmModel realm, ClientModel client) {
for (ClientSessionEntity e : clientSessions.values()) {
- if (e.getSession().getRealm().equals(realm.getId()) && e.getClientId().equals(client.getId())) {
+ if (e.getRealmId().equals(realm.getId()) && e.getClientId().equals(client.getId())) {
clientSessions.remove(e.getId());
e.getSession().removeClientSession(e);
}
diff --git a/pom.xml b/pom.xml
index 73523c09df..763bdcb731 100755
--- a/pom.xml
+++ b/pom.xml
@@ -44,6 +44,7 @@
2.35.0
1.4.5
6.0.2.Final
+ 3.2.2
1.6
@@ -449,6 +450,11 @@
infinispan-core
${infinispan.version}
+
+ org.liquibase
+ liquibase-core
+ ${liquibase.version}
+
diff --git a/testsuite/integration/src/main/resources/META-INF/keycloak-server.json b/testsuite/integration/src/main/resources/META-INF/keycloak-server.json
index 6e90fa12df..728631d7a9 100755
--- a/testsuite/integration/src/main/resources/META-INF/keycloak-server.json
+++ b/testsuite/integration/src/main/resources/META-INF/keycloak-server.json
@@ -67,7 +67,7 @@
"driverDialect": "${keycloak.connectionsJpa.driverDialect:}",
"user": "${keycloak.connectionsJpa.user:sa}",
"password": "${keycloak.connectionsJpa.password:}",
- "databaseSchema": "${keycloak.connectionsJpa.databaseSchema:create-drop}",
+ "databaseSchema": "${keycloak.connectionsJpa.databaseSchema:update}",
"showSql": "${keycloak.connectionsJpa.showSql:false}",
"formatSql": "${keycloak.connectionsJpa.formatSql:true}"
}
@@ -78,7 +78,7 @@
"host": "${keycloak.connectionsMongo.host:127.0.0.1}",
"port": "${keycloak.connectionsMongo.port:27017}",
"db": "${keycloak.connectionsMongo.db:keycloak}",
- "clearOnStartup": "${keycloak.connectionsMongo.clearOnStartup:false}"
+ "databaseSchema": "${keycloak.connectionsMongo.databaseSchema:update}"
}
}
}
\ No newline at end of file
diff --git a/testsuite/integration/src/main/resources/log4j.properties b/testsuite/integration/src/main/resources/log4j.properties
index 315b7ca2f6..778d39ac55 100755
--- a/testsuite/integration/src/main/resources/log4j.properties
+++ b/testsuite/integration/src/main/resources/log4j.properties
@@ -6,6 +6,15 @@ log4j.appender.stdout.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p [%c] %m%n
log4j.logger.org.keycloak=info
+# Enable to view loaded SPI and Providers
+# log4j.logger.org.keycloak.services.DefaultKeycloakSessionFactory=debug
+
+# Enable to view database updates
+# log4j.logger.org.keycloak.connections.jpa.updater.liquibase.LiquibaseJpaUpdaterProvider=debug
+# log4j.logger.org.keycloak.connections.mongo.updater.DefaultMongoUpdaterProvider=debug
+
+# log4j.logger.org.keycloak.connections.jpa.DefaultJpaConnectionProviderFactory=debug
+
log4j.logger.org.xnio=off
log4j.logger.org.hibernate=off
log4j.logger.org.jboss.resteasy=warn
\ No newline at end of file
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java
index 5aa52c3c86..4cfaaa3586 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java
@@ -157,15 +157,11 @@ public class AccountTest {
});
}
- @Test
- @Ignore
- public void runit() throws Exception {
- Thread.sleep(10000000);
-
- }
-
-
-
+// @Test
+// @Ignore
+// public void runit() throws Exception {
+// Thread.sleep(10000000);
+// }
@Test
public void returnToAppFromQueryParam() {
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/composites/CompositeImportRoleTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/composites/CompositeImportRoleTest.java
index 32ba03cfcd..47d1ff5204 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/composites/CompositeImportRoleTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/composites/CompositeImportRoleTest.java
@@ -54,7 +54,7 @@ public class CompositeImportRoleTest {
@Override
protected void configure(KeycloakSession session, RealmManager manager, RealmModel adminRealm) {
RealmRepresentation representation = KeycloakServer.loadJson(getClass().getResourceAsStream("/testcomposite.json"), RealmRepresentation.class);
- representation.setId("Test");
+ representation.setId("test");
RealmModel realm = manager.importRealm(representation);
realmPublicKey = realm.getPublicKey();
@@ -78,7 +78,7 @@ public class CompositeImportRoleTest {
@Test
public void testAppCompositeUser() throws Exception {
- oauth.realm("Test");
+ oauth.realm("test");
oauth.realmPublicKey(realmPublicKey);
oauth.clientId("APP_COMPOSITE_APPLICATION");
oauth.doLogin("APP_COMPOSITE_USER", "password");
@@ -92,7 +92,7 @@ public class CompositeImportRoleTest {
AccessToken token = oauth.verifyToken(response.getAccessToken());
- Assert.assertEquals(keycloakRule.getUser("Test", "APP_COMPOSITE_USER").getId(), token.getSubject());
+ Assert.assertEquals(keycloakRule.getUser("test", "APP_COMPOSITE_USER").getId(), token.getSubject());
Assert.assertEquals(1, token.getResourceAccess("APP_ROLE_APPLICATION").getRoles().size());
Assert.assertEquals(1, token.getRealmAccess().getRoles().size());
@@ -103,7 +103,7 @@ public class CompositeImportRoleTest {
@Test
public void testRealmAppCompositeUser() throws Exception {
- oauth.realm("Test");
+ oauth.realm("test");
oauth.realmPublicKey(realmPublicKey);
oauth.clientId("APP_ROLE_APPLICATION");
oauth.doLogin("REALM_APP_COMPOSITE_USER", "password");
@@ -117,7 +117,7 @@ public class CompositeImportRoleTest {
AccessToken token = oauth.verifyToken(response.getAccessToken());
- Assert.assertEquals(keycloakRule.getUser("Test", "REALM_APP_COMPOSITE_USER").getId(), token.getSubject());
+ Assert.assertEquals(keycloakRule.getUser("test", "REALM_APP_COMPOSITE_USER").getId(), token.getSubject());
Assert.assertEquals(1, token.getResourceAccess("APP_ROLE_APPLICATION").getRoles().size());
Assert.assertTrue(token.getResourceAccess("APP_ROLE_APPLICATION").isUserInRole("APP_ROLE_1"));
@@ -127,7 +127,7 @@ public class CompositeImportRoleTest {
@Test
public void testRealmOnlyWithUserCompositeAppComposite() throws Exception {
- oauth.realm("Test");
+ oauth.realm("test");
oauth.realmPublicKey(realmPublicKey);
oauth.clientId("REALM_COMPOSITE_1_APPLICATION");
oauth.doLogin("REALM_COMPOSITE_1_USER", "password");
@@ -141,7 +141,7 @@ public class CompositeImportRoleTest {
AccessToken token = oauth.verifyToken(response.getAccessToken());
- Assert.assertEquals(keycloakRule.getUser("Test", "REALM_COMPOSITE_1_USER").getId(), token.getSubject());
+ Assert.assertEquals(keycloakRule.getUser("test", "REALM_COMPOSITE_1_USER").getId(), token.getSubject());
Assert.assertEquals(2, token.getRealmAccess().getRoles().size());
Assert.assertTrue(token.getRealmAccess().isUserInRole("REALM_COMPOSITE_1"));
@@ -150,7 +150,7 @@ public class CompositeImportRoleTest {
@Test
public void testRealmOnlyWithUserCompositeAppRole() throws Exception {
- oauth.realm("Test");
+ oauth.realm("test");
oauth.realmPublicKey(realmPublicKey);
oauth.clientId("REALM_ROLE_1_APPLICATION");
oauth.doLogin("REALM_COMPOSITE_1_USER", "password");
@@ -164,7 +164,7 @@ public class CompositeImportRoleTest {
AccessToken token = oauth.verifyToken(response.getAccessToken());
- Assert.assertEquals(keycloakRule.getUser("Test", "REALM_COMPOSITE_1_USER").getId(), token.getSubject());
+ Assert.assertEquals(keycloakRule.getUser("test", "REALM_COMPOSITE_1_USER").getId(), token.getSubject());
Assert.assertEquals(1, token.getRealmAccess().getRoles().size());
Assert.assertTrue(token.getRealmAccess().isUserInRole("REALM_ROLE_1"));
@@ -172,7 +172,7 @@ public class CompositeImportRoleTest {
@Test
public void testRealmOnlyWithUserRoleAppComposite() throws Exception {
- oauth.realm("Test");
+ oauth.realm("test");
oauth.realmPublicKey(realmPublicKey);
oauth.clientId("REALM_COMPOSITE_1_APPLICATION");
oauth.doLogin("REALM_ROLE_1_USER", "password");
@@ -186,13 +186,10 @@ public class CompositeImportRoleTest {
AccessToken token = oauth.verifyToken(response.getAccessToken());
- Assert.assertEquals(keycloakRule.getUser("Test", "REALM_ROLE_1_USER").getId(), token.getSubject());
+ Assert.assertEquals(keycloakRule.getUser("test", "REALM_ROLE_1_USER").getId(), token.getSubject());
Assert.assertEquals(1, token.getRealmAccess().getRoles().size());
Assert.assertTrue(token.getRealmAccess().isUserInRole("REALM_ROLE_1"));
}
-
-
-
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/composites/CompositeRoleTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/composites/CompositeRoleTest.java
index 6aaf0682af..b661d5a08b 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/composites/CompositeRoleTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/composites/CompositeRoleTest.java
@@ -58,7 +58,7 @@ public class CompositeRoleTest {
public static AbstractKeycloakRule keycloakRule = new AbstractKeycloakRule(){
@Override
protected void configure(KeycloakSession session, RealmManager manager, RealmModel adminRealm) {
- RealmModel realm = manager.createRealm("Test");
+ RealmModel realm = manager.createRealm("test");
KeycloakModelUtils.generateRealmKeys(realm);
realmPublicKey = realm.getPublicKey();
realm.setSsoSessionIdleTimeout(3000);
@@ -166,7 +166,7 @@ public class CompositeRoleTest {
@Test
public void testAppCompositeUser() throws Exception {
- oauth.realm("Test");
+ oauth.realm("test");
oauth.realmPublicKey(realmPublicKey);
oauth.clientId("APP_COMPOSITE_APPLICATION");
oauth.doLogin("APP_COMPOSITE_USER", "password");
@@ -180,7 +180,7 @@ public class CompositeRoleTest {
AccessToken token = oauth.verifyToken(response.getAccessToken());
- Assert.assertEquals(keycloakRule.getUser("Test", "APP_COMPOSITE_USER").getId(), token.getSubject());
+ Assert.assertEquals(keycloakRule.getUser("test", "APP_COMPOSITE_USER").getId(), token.getSubject());
Assert.assertEquals(1, token.getResourceAccess("APP_ROLE_APPLICATION").getRoles().size());
Assert.assertEquals(1, token.getRealmAccess().getRoles().size());
@@ -194,7 +194,7 @@ public class CompositeRoleTest {
@Test
public void testRealmAppCompositeUser() throws Exception {
- oauth.realm("Test");
+ oauth.realm("test");
oauth.realmPublicKey(realmPublicKey);
oauth.clientId("APP_ROLE_APPLICATION");
oauth.doLogin("REALM_APP_COMPOSITE_USER", "password");
@@ -208,7 +208,7 @@ public class CompositeRoleTest {
AccessToken token = oauth.verifyToken(response.getAccessToken());
- Assert.assertEquals(keycloakRule.getUser("Test", "REALM_APP_COMPOSITE_USER").getId(), token.getSubject());
+ Assert.assertEquals(keycloakRule.getUser("test", "REALM_APP_COMPOSITE_USER").getId(), token.getSubject());
Assert.assertEquals(1, token.getResourceAccess("APP_ROLE_APPLICATION").getRoles().size());
Assert.assertTrue(token.getResourceAccess("APP_ROLE_APPLICATION").isUserInRole("APP_ROLE_1"));
@@ -219,7 +219,7 @@ public class CompositeRoleTest {
@Test
public void testRealmOnlyWithUserCompositeAppComposite() throws Exception {
- oauth.realm("Test");
+ oauth.realm("test");
oauth.realmPublicKey(realmPublicKey);
oauth.clientId("REALM_COMPOSITE_1_APPLICATION");
oauth.doLogin("REALM_COMPOSITE_1_USER", "password");
@@ -233,7 +233,7 @@ public class CompositeRoleTest {
AccessToken token = oauth.verifyToken(response.getAccessToken());
- Assert.assertEquals(keycloakRule.getUser("Test", "REALM_COMPOSITE_1_USER").getId(), token.getSubject());
+ Assert.assertEquals(keycloakRule.getUser("test", "REALM_COMPOSITE_1_USER").getId(), token.getSubject());
Assert.assertEquals(2, token.getRealmAccess().getRoles().size());
Assert.assertTrue(token.getRealmAccess().isUserInRole("REALM_COMPOSITE_1"));
@@ -245,7 +245,7 @@ public class CompositeRoleTest {
@Test
public void testRealmOnlyWithUserCompositeAppRole() throws Exception {
- oauth.realm("Test");
+ oauth.realm("test");
oauth.realmPublicKey(realmPublicKey);
oauth.clientId("REALM_ROLE_1_APPLICATION");
oauth.doLogin("REALM_COMPOSITE_1_USER", "password");
@@ -259,7 +259,7 @@ public class CompositeRoleTest {
AccessToken token = oauth.verifyToken(response.getAccessToken());
- Assert.assertEquals(keycloakRule.getUser("Test", "REALM_COMPOSITE_1_USER").getId(), token.getSubject());
+ Assert.assertEquals(keycloakRule.getUser("test", "REALM_COMPOSITE_1_USER").getId(), token.getSubject());
Assert.assertEquals(1, token.getRealmAccess().getRoles().size());
Assert.assertTrue(token.getRealmAccess().isUserInRole("REALM_ROLE_1"));
@@ -270,7 +270,7 @@ public class CompositeRoleTest {
@Test
public void testRealmOnlyWithUserRoleAppComposite() throws Exception {
- oauth.realm("Test");
+ oauth.realm("test");
oauth.realmPublicKey(realmPublicKey);
oauth.clientId("REALM_COMPOSITE_1_APPLICATION");
oauth.doLogin("REALM_ROLE_1_USER", "password");
@@ -284,7 +284,7 @@ public class CompositeRoleTest {
AccessToken token = oauth.verifyToken(response.getAccessToken());
- Assert.assertEquals(keycloakRule.getUser("Test", "REALM_ROLE_1_USER").getId(), token.getSubject());
+ Assert.assertEquals(keycloakRule.getUser("test", "REALM_ROLE_1_USER").getId(), token.getSubject());
Assert.assertEquals(1, token.getRealmAccess().getRoles().size());
Assert.assertTrue(token.getRealmAccess().isUserInRole("REALM_ROLE_1"));
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java
index c4bf603c8b..5261da63e4 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java
@@ -64,7 +64,7 @@ public class ExportImportTest {
propsHelper.pushProperty(JPA_CONNECTION_URL, "jdbc:h2:file:" + dbDir + ";DB_CLOSE_DELAY=-1");
connectionURLSet = true;
}
- propsHelper.pushProperty(JPA_DB_SCHEMA, "create");
+ propsHelper.pushProperty(JPA_DB_SCHEMA, "update");
}
@Override
@@ -98,7 +98,6 @@ public class ExportImportTest {
addUser(manager.getSession().users(), appRealm, "user1", "password");
addUser(manager.getSession().users(), appRealm, "user2", "password");
addUser(manager.getSession().users(), appRealm, "user3", "password");
- addUser(manager.getSession().users(), adminstrationRealm, "admin2", "admin2");
// Import "test-realm" realm
try {
@@ -129,6 +128,10 @@ public class ExportImportTest {
systemProps.remove(propToRemove);
}
}
+
+ protected String[] getTestRealms() {
+ return new String[]{"test", "demo", "test-realm"};
+ }
};
@ClassRule
@@ -222,10 +225,6 @@ public class ExportImportTest {
new RealmManager(session).removeRealm(realmProvider.getRealmByName("test"));
Assert.assertEquals(2, realmProvider.getRealms().size());
- RealmModel master = realmProvider.getRealmByName(Config.getAdminRealm());
- UserModel admin2 = session.users().getUserByUsername("admin2", master);
- session.users().removeUser(master, admin2);
- assertNotAuthenticated(userProvider, realmProvider, Config.getAdminRealm(), "admin2", "admin2");
assertNotAuthenticated(userProvider, realmProvider, "test", "test-user@localhost", "password");
assertNotAuthenticated(userProvider, realmProvider, "test", "user1", "password");
assertNotAuthenticated(userProvider, realmProvider, "test", "user2", "password");
@@ -247,7 +246,6 @@ public class ExportImportTest {
UserProvider userProvider = session.users();
Assert.assertEquals(3, model.getRealms().size());
- assertAuthenticated(userProvider, model, Config.getAdminRealm(), "admin2", "admin2");
assertAuthenticated(userProvider, model, "test", "test-user@localhost", "password");
assertAuthenticated(userProvider, model, "test", "user1", "password");
assertAuthenticated(userProvider, model, "test", "user2", "password");
@@ -275,11 +273,6 @@ public class ExportImportTest {
new RealmManager(session).removeRealm(realmProvider.getRealmByName("test"));
Assert.assertEquals(2, realmProvider.getRealms().size());
- RealmModel master = realmProvider.getRealmByName(Config.getAdminRealm());
- UserModel admin2 = session.users().getUserByUsername("admin2", master);
- session.users().removeUser(master, admin2);
-
- assertNotAuthenticated(userProvider, realmProvider, Config.getAdminRealm(), "admin2", "admin2");
assertNotAuthenticated(userProvider, realmProvider, "test", "test-user@localhost", "password");
assertNotAuthenticated(userProvider, realmProvider, "test", "user1", "password");
assertNotAuthenticated(userProvider, realmProvider, "test", "user2", "password");
@@ -301,13 +294,10 @@ public class ExportImportTest {
UserProvider userProvider = session.users();
Assert.assertEquals(3, realmProvider.getRealms().size());
- assertNotAuthenticated(userProvider, realmProvider, Config.getAdminRealm(), "admin2", "admin2");
assertAuthenticated(userProvider, realmProvider, "test", "test-user@localhost", "password");
assertAuthenticated(userProvider, realmProvider, "test", "user1", "password");
assertAuthenticated(userProvider, realmProvider, "test", "user2", "password");
assertAuthenticated(userProvider, realmProvider, "test", "user3", "password");
-
- addUser(userProvider, realmProvider.getRealmByName(Config.getAdminRealm()), "admin2", "admin2");
} finally {
keycloakRule.stopSession(session, true);
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java
index 7b616f332d..06e793f92a 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java
@@ -30,12 +30,15 @@ import java.net.Socket;
* @version $Revision: 1 $
*/
public abstract class AbstractKeycloakRule extends ExternalResource {
+
protected KeycloakServer server;
protected void before() throws Throwable {
server = new KeycloakServer();
server.start();
+ removeTestRealms();
+
setupKeycloak();
}
@@ -123,6 +126,7 @@ public abstract class AbstractKeycloakRule extends ExternalResource {
deploymentInfo.addServlet(servlet);
return deploymentInfo;
}
+
public void deployApplication(String name, String contextPath, Class extends Servlet> servletClass, String adapterConfigPath, String role) {
deployApplication(name, contextPath, servletClass, adapterConfigPath, role, true);
@@ -147,9 +151,27 @@ public abstract class AbstractKeycloakRule extends ExternalResource {
@Override
protected void after() {
+ removeTestRealms();
stopServer();
}
+ protected void removeTestRealms() {
+ KeycloakSession session = server.getSessionFactory().create();
+ try {
+ session.getTransaction().begin();
+ RealmManager realmManager = new RealmManager(session);
+ for (String realmName : getTestRealms()) {
+ RealmModel realm = realmManager.getRealmByName(realmName);
+ if (realm != null) {
+ realmManager.removeRealm(realm);
+ }
+ }
+ session.getTransaction().commit();
+ } finally {
+ session.close();
+ }
+ }
+
public RealmRepresentation loadJson(String path) throws IOException {
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(path);
ByteArrayOutputStream os = new ByteArrayOutputStream();
@@ -169,7 +191,7 @@ public abstract class AbstractKeycloakRule extends ExternalResource {
public void stopSession(KeycloakSession session, boolean commit) {
KeycloakTransaction transaction = session.getTransaction();
- if (commit) {
+ if (commit && !transaction.getRollbackOnly()) {
transaction.commit();
} else {
transaction.rollback();
@@ -208,4 +230,9 @@ public abstract class AbstractKeycloakRule extends ExternalResource {
Thread.currentThread().interrupt();
}
}
+
+ protected String[] getTestRealms() {
+ return new String[]{"test", "demo"};
+ }
+
}
diff --git a/testsuite/integration/src/test/resources/testcomposite.json b/testsuite/integration/src/test/resources/testcomposite.json
index 8f3f76fd95..5870eb0b29 100755
--- a/testsuite/integration/src/test/resources/testcomposite.json
+++ b/testsuite/integration/src/test/resources/testcomposite.json
@@ -1,6 +1,6 @@
{
- "id": "Test",
- "realm": "Test",
+ "id": "test",
+ "realm": "test",
"enabled": true,
"accessTokenLifespan": 600,
"accessCodeLifespan": 600,