keycloak-aplcache
Changes
connections/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java 85(+81 -4)
connections/jpa/src/main/java/org/keycloak/connections/jpa/updater/JpaUpdaterProvider.java 22(+22 -0)
connections/jpa/src/main/java/org/keycloak/connections/jpa/updater/JpaUpdaterProviderFactory.java 9(+9 -0)
connections/jpa-liquibase/pom.xml 74(+74 -0)
connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/LiquibaseJpaUpdaterProvider.java 208(+208 -0)
connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/LiquibaseJpaUpdaterProviderFactory.java 31(+31 -0)
connections/jpa-liquibase/src/main/resources/META-INF/services/org.keycloak.connections.jpa.updater.JpaUpdaterProviderFactory 1(+1 -0)
connections/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java 15(+12 -3)
connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/DefaultMongoUpdaterProvider.java 103(+103 -0)
connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/DefaultMongoUpdaterProviderFactory.java 29(+29 -0)
connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/MongoUpdaterProvider.java 13(+13 -0)
connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/MongoUpdaterProviderFactory.java 9(+9 -0)
connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/MongoUpdaterSpi.java 27(+27 -0)
connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/updates/Update.java 62(+62 -0)
connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/updates/Update1_0_0_Final.java 44(+44 -0)
connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/updates/Update1_1_0_Beta1.java 19(+19 -0)
connections/pom.xml 1(+1 -0)
dependencies/server-all/pom.xml 16(+16 -0)
misc/UpdatingDatabaseSchema.md 79(+79 -0)
model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoApplicationEntity.java 2(+0 -2)
model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoOAuthClientEntity.java 2(+0 -2)
model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoRealmEntity.java 2(+0 -2)
model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/MemUserSessionProvider.java 2(+1 -1)
pom.xml 6(+6 -0)
testsuite/integration/src/test/java/org/keycloak/testsuite/composites/CompositeImportRoleTest.java 25(+11 -14)
testsuite/integration/src/test/java/org/keycloak/testsuite/composites/CompositeRoleTest.java 22(+11 -11)
testsuite/integration/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java 20(+5 -15)
Details
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 4bd1d6e..5a00c74 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<String, Object> properties = new HashMap<String, Object>();
// 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 0000000..9e19f7f
--- /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 <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+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 0000000..29a89f4
--- /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 <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public interface JpaUpdaterProviderFactory extends ProviderFactory<JpaUpdaterProvider> {
+}
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 0000000..e95242a
--- /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 <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+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 d7b47a8..175bcfb 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
connections/jpa-liquibase/pom.xml 74(+74 -0)
diff --git a/connections/jpa-liquibase/pom.xml b/connections/jpa-liquibase/pom.xml
new file mode 100755
index 0000000..5196c42
--- /dev/null
+++ b/connections/jpa-liquibase/pom.xml
@@ -0,0 +1,74 @@
+<?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.1.0-Alpha1-SNAPSHOT</version>
+ <relativePath>../../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>keycloak-connections-jpa-liquibase</artifactId>
+ <name>Keycloak Connections JPA Liquibase Updater</name>
+ <description/>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-connections-jpa</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.liquibase</groupId>
+ <artifactId>liquibase-core</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>org.yaml</groupId>
+ <artifactId>snakeyaml</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.logging</groupId>
+ <artifactId>jboss-logging</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.liquibase</groupId>
+ <artifactId>liquibase-maven-plugin</artifactId>
+<!-- set to ${liquibase.version} once Liquibase 3.2.3 is released (https://liquibase.jira.com/browse/CORE-1987) -->
+ <version>3.1.1</version>
+ <configuration>
+ <changeLogFile>META-INF/jpa-changelog-master.xml</changeLogFile>
+
+ <url>${url}</url>
+ <driver>${driver}</driver>
+ <username>${username}</username>
+ <password>${password}</password>
+
+ <referenceUrl>${referenceUrl}</referenceUrl>
+ <referenceDriver>${driver}</referenceDriver>
+ <referenceUsername>${username}</referenceUsername>
+ <referencePassword>${password}</referencePassword>
+ </configuration>
+ <dependencies>
+ <dependency>
+ <groupId>com.h2database</groupId>
+ <artifactId>h2</artifactId>
+ <version>${h2.version}</version>
+ </dependency>
+ </dependencies>
+ </plugin>
+ </plugins>
+ </build>
+
+ <properties>
+ <username>sa</username>
+ <password></password>
+ <driver>org.h2.Driver</driver>
+ </properties>
+</project>
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 0000000..8d1f6fa
--- /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 <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+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<ChangeSet> 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<RanChangeSet> 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 0000000..d88b787
--- /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 <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+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 0000000..5b5e921
--- /dev/null
+++ b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.0.0.Final.xml
@@ -0,0 +1,376 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.2.xsd">
+ <changeSet author="sthorger@redhat.com" id="1.0.0.Final">
+ <createTable tableName="APPLICATION_DEFAULT_ROLES">
+ <column name="APPLICATION_ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="ROLE_ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ </createTable>
+ <createTable tableName="CLIENT">
+ <column name="DTYPE" type="VARCHAR(31)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="ALLOWED_CLAIMS_MASK" type="BIGINT(19)"/>
+ <column name="ENABLED" type="BOOLEAN(1)"/>
+ <column name="FULL_SCOPE_ALLOWED" type="BOOLEAN(1)"/>
+ <column name="NAME" type="VARCHAR(255)"/>
+ <column name="NOT_BEFORE" type="INT(10)"/>
+ <column name="PUBLIC_CLIENT" type="BOOLEAN(1)"/>
+ <column name="SECRET" type="VARCHAR(255)"/>
+ <column name="BASE_URL" type="VARCHAR(255)"/>
+ <column name="BEARER_ONLY" type="BOOLEAN(1)"/>
+ <column name="MANAGEMENT_URL" type="VARCHAR(255)"/>
+ <column name="SURROGATE_AUTH_REQUIRED" type="BOOLEAN(1)"/>
+ <column name="DIRECT_GRANTS_ONLY" type="BOOLEAN(1)"/>
+ <column name="REALM_ID" type="VARCHAR(36)"/>
+ </createTable>
+ <createTable tableName="CLIENT_SESSION">
+ <column name="ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="ACTION" type="INT(10)"/>
+ <column name="CLIENT_ID" type="VARCHAR(36)"/>
+ <column name="REDIRECT_URI" type="VARCHAR(255)"/>
+ <column name="STATE" type="VARCHAR(255)"/>
+ <column name="TIMESTAMP" type="INT(10)"/>
+ <column name="SESSION_ID" type="VARCHAR(36)"/>
+ </createTable>
+ <createTable tableName="CLIENT_SESSION_ROLE">
+ <column name="ROLE_ID" type="VARCHAR(255)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="CLIENT_SESSION" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ </createTable>
+ <createTable tableName="COMPOSITE_ROLE">
+ <column name="COMPOSITE" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="CHILD_ROLE" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ </createTable>
+ <createTable tableName="CREDENTIAL">
+ <column name="ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="DEVICE" type="VARCHAR(255)"/>
+ <column name="HASH_ITERATIONS" type="INT(10)"/>
+ <column name="SALT" type="BINARY(255)"/>
+ <column name="TYPE" type="VARCHAR(255)"/>
+ <column name="VALUE" type="VARCHAR(255)"/>
+ <column name="USER_ID" type="VARCHAR(36)"/>
+ </createTable>
+ <createTable tableName="EVENT_ENTITY">
+ <column name="ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="CLIENT_ID" type="VARCHAR(255)"/>
+ <column name="DETAILS_JSON" type="VARCHAR(2550)"/>
+ <column name="ERROR" type="VARCHAR(255)"/>
+ <column name="IP_ADDRESS" type="VARCHAR(255)"/>
+ <column name="REALM_ID" type="VARCHAR(255)"/>
+ <column name="SESSION_ID" type="VARCHAR(255)"/>
+ <column name="TIME" type="BIGINT(19)"/>
+ <column name="TYPE" type="VARCHAR(255)"/>
+ <column name="USER_ID" type="VARCHAR(255)"/>
+ </createTable>
+ <createTable tableName="FED_PROVIDERS">
+ <column name="REALM_ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="USERFEDERATIONPROVIDERS_ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ </createTable>
+ <createTable tableName="KEYCLOAK_ROLE">
+ <column name="ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="APP_REALM_CONSTRAINT" type="VARCHAR(36)"/>
+ <column name="APPLICATION_ROLE" type="BOOLEAN(1)"/>
+ <column name="DESCRIPTION" type="VARCHAR(255)"/>
+ <column name="NAME" type="VARCHAR(255)"/>
+ <column name="REALM_ID" type="VARCHAR(255)"/>
+ <column name="APPLICATION" type="VARCHAR(36)"/>
+ <column name="REALM" type="VARCHAR(36)"/>
+ </createTable>
+ <createTable tableName="REALM">
+ <column name="ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="ACCESS_CODE_LIFESPAN" type="INT(10)"/>
+ <column name="USER_ACTION_LIFESPAN" type="INT(10)"/>
+ <column name="ACCESS_TOKEN_LIFESPAN" type="INT(10)"/>
+ <column name="ACCOUNT_THEME" type="VARCHAR(255)"/>
+ <column name="ADMIN_THEME" type="VARCHAR(255)"/>
+ <column name="EMAIL_THEME" type="VARCHAR(255)"/>
+ <column name="ENABLED" type="BOOLEAN(1)"/>
+ <column name="EVENTS_ENABLED" type="BOOLEAN(1)"/>
+ <column name="EVENTS_EXPIRATION" type="BIGINT(19)"/>
+ <column name="LOGIN_THEME" type="VARCHAR(255)"/>
+ <column name="NAME" type="VARCHAR(255)"/>
+ <column name="NOT_BEFORE" type="INT(10)"/>
+ <column name="PASSWORD_CRED_GRANT_ALLOWED" type="BOOLEAN(1)"/>
+ <column name="PASSWORD_POLICY" type="VARCHAR(255)"/>
+ <column name="PRIVATE_KEY" type="VARCHAR(2048)"/>
+ <column name="PUBLIC_KEY" type="VARCHAR(2048)"/>
+ <column name="REGISTRATION_ALLOWED" type="BOOLEAN(1)"/>
+ <column name="REMEMBER_ME" type="BOOLEAN(1)"/>
+ <column name="RESET_PASSWORD_ALLOWED" type="BOOLEAN(1)"/>
+ <column name="SOCIAL" type="BOOLEAN(1)"/>
+ <column name="SSL_REQUIRED" type="VARCHAR(255)"/>
+ <column name="SSO_IDLE_TIMEOUT" type="INT(10)"/>
+ <column name="SSO_MAX_LIFESPAN" type="INT(10)"/>
+ <column name="UPDATE_PROFILE_ON_SOC_LOGIN" type="BOOLEAN(1)"/>
+ <column name="VERIFY_EMAIL" type="BOOLEAN(1)"/>
+ <column name="MASTER_ADMIN_APP" type="VARCHAR(36)"/>
+ </createTable>
+ <createTable tableName="REALM_APPLICATION">
+ <column name="APPLICATION_ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="REALM_ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ </createTable>
+ <createTable tableName="REALM_ATTRIBUTE">
+ <column name="NAME" type="VARCHAR(255)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="VALUE" type="VARCHAR(255)"/>
+ <column name="REALM_ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ </createTable>
+ <createTable tableName="REALM_DEFAULT_ROLES">
+ <column name="REALM_ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="ROLE_ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ </createTable>
+ <createTable tableName="REALM_EVENTS_LISTENERS">
+ <column name="REALM_ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="VALUE" type="VARCHAR(255)"/>
+ </createTable>
+ <createTable tableName="REALM_REQUIRED_CREDENTIAL">
+ <column name="TYPE" type="VARCHAR(255)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="FORM_LABEL" type="VARCHAR(255)"/>
+ <column name="INPUT" type="BOOLEAN(1)"/>
+ <column name="SECRET" type="BOOLEAN(1)"/>
+ <column name="REALM_ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ </createTable>
+ <createTable tableName="REALM_SMTP_CONFIG">
+ <column name="REALM_ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="VALUE" type="VARCHAR(255)"/>
+ <column name="NAME" type="VARCHAR(255)">
+ <constraints nullable="false"/>
+ </column>
+ </createTable>
+ <createTable tableName="REALM_SOCIAL_CONFIG">
+ <column name="REALM_ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="VALUE" type="VARCHAR(255)"/>
+ <column name="NAME" type="VARCHAR(255)">
+ <constraints nullable="false"/>
+ </column>
+ </createTable>
+ <createTable tableName="REDIRECT_URIS">
+ <column name="CLIENT_ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="VALUE" type="VARCHAR(255)"/>
+ </createTable>
+ <createTable tableName="SCOPE_MAPPING">
+ <column name="CLIENT_ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="ROLE_ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ </createTable>
+ <createTable tableName="USERNAME_LOGIN_FAILURE">
+ <column name="REALM_ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="USERNAME" type="VARCHAR(200)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="FAILED_LOGIN_NOT_BEFORE" type="INT(10)"/>
+ <column name="LAST_FAILURE" type="BIGINT(19)"/>
+ <column name="LAST_IP_FAILURE" type="VARCHAR(255)"/>
+ <column name="NUM_FAILURES" type="INT(10)"/>
+ </createTable>
+ <createTable tableName="USER_ATTRIBUTE">
+ <column name="NAME" type="VARCHAR(255)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="VALUE" type="VARCHAR(255)"/>
+ <column name="USER_ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ </createTable>
+ <createTable tableName="USER_ENTITY">
+ <column name="ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="EMAIL" type="VARCHAR(255)"/>
+ <column name="EMAIL_CONSTRAINT" type="VARCHAR(255)"/>
+ <column name="EMAIL_VERIFIED" type="BOOLEAN(1)"/>
+ <column name="ENABLED" type="BOOLEAN(1)"/>
+ <column name="FEDERATION_LINK" type="VARCHAR(255)"/>
+ <column name="FIRST_NAME" type="VARCHAR(255)"/>
+ <column name="LAST_NAME" type="VARCHAR(255)"/>
+ <column name="REALM_ID" type="VARCHAR(255)"/>
+ <column name="TOTP" type="BOOLEAN(1)"/>
+ <column name="USERNAME" type="VARCHAR(255)"/>
+ </createTable>
+ <createTable tableName="USER_FEDERATION_CONFIG">
+ <column name="USER_FEDERATION_PROVIDER_ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="VALUE" type="VARCHAR(255)"/>
+ <column name="NAME" type="VARCHAR(255)">
+ <constraints nullable="false"/>
+ </column>
+ </createTable>
+ <createTable tableName="USER_FEDERATION_PROVIDER">
+ <column name="ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="CHANGED_SYNC_PERIOD" type="INT(10)"/>
+ <column name="DISPLAY_NAME" type="VARCHAR(255)"/>
+ <column name="FULL_SYNC_PERIOD" type="INT(10)"/>
+ <column name="LAST_SYNC" type="INT(10)"/>
+ <column name="PRIORITY" type="INT(10)"/>
+ <column name="PROVIDER_NAME" type="VARCHAR(255)"/>
+ <column name="REALM_ID" type="VARCHAR(36)"/>
+ </createTable>
+ <createTable tableName="USER_REQUIRED_ACTION">
+ <column name="ACTION" type="INT(10)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="USER_ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ </createTable>
+ <createTable tableName="USER_ROLE_MAPPING">
+ <column name="ROLE_ID" type="VARCHAR(255)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="USER_ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ </createTable>
+ <createTable tableName="USER_SESSION">
+ <column name="ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="AUTH_METHOD" type="VARCHAR(255)"/>
+ <column name="IP_ADDRESS" type="VARCHAR(255)"/>
+ <column name="LAST_SESSION_REFRESH" type="INT(10)"/>
+ <column name="LOGIN_USERNAME" type="VARCHAR(255)"/>
+ <column name="REALM_ID" type="VARCHAR(255)"/>
+ <column name="REMEMBER_ME" type="BOOLEAN(1)"/>
+ <column name="STARTED" type="INT(10)"/>
+ <column name="USER_ID" type="VARCHAR(255)"/>
+ </createTable>
+ <createTable tableName="USER_SOCIAL_LINK">
+ <column name="SOCIAL_PROVIDER" type="VARCHAR(255)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="REALM_ID" type="VARCHAR(255)"/>
+ <column name="SOCIAL_USER_ID" type="VARCHAR(255)"/>
+ <column name="SOCIAL_USERNAME" type="VARCHAR(255)"/>
+ <column name="USER_ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ </createTable>
+ <createTable tableName="WEB_ORIGINS">
+ <column name="CLIENT_ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="VALUE" type="VARCHAR(255)"/>
+ </createTable>
+ <addPrimaryKey columnNames="REALM_ID, NAME" constraintName="CONSTRAINT_1" tableName="REALM_SOCIAL_CONFIG"/>
+ <addPrimaryKey columnNames="REALM_ID, USERNAME" constraintName="CONSTRAINT_17" tableName="USERNAME_LOGIN_FAILURE"/>
+ <addPrimaryKey columnNames="ACTION, USER_ID" constraintName="CONSTRAINT_2" tableName="USER_REQUIRED_ACTION"/>
+ <addPrimaryKey columnNames="SOCIAL_PROVIDER, USER_ID" constraintName="CONSTRAINT_3" tableName="USER_SOCIAL_LINK"/>
+ <addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_4" tableName="EVENT_ENTITY"/>
+ <addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_4A" tableName="REALM"/>
+ <addPrimaryKey columnNames="CLIENT_SESSION, ROLE_ID" constraintName="CONSTRAINT_5" tableName="CLIENT_SESSION_ROLE"/>
+ <addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_57" tableName="USER_SESSION"/>
+ <addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_5C" tableName="USER_FEDERATION_PROVIDER"/>
+ <addPrimaryKey columnNames="NAME, USER_ID" constraintName="CONSTRAINT_6" tableName="USER_ATTRIBUTE"/>
+ <addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_7" tableName="CLIENT"/>
+ <addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_8" tableName="CLIENT_SESSION"/>
+ <addPrimaryKey columnNames="CLIENT_ID, ROLE_ID" constraintName="CONSTRAINT_81" tableName="SCOPE_MAPPING"/>
+ <addPrimaryKey columnNames="NAME, REALM_ID" constraintName="CONSTRAINT_9" tableName="REALM_ATTRIBUTE"/>
+ <addPrimaryKey columnNames="REALM_ID, TYPE" constraintName="CONSTRAINT_92" tableName="REALM_REQUIRED_CREDENTIAL"/>
+ <addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_A" tableName="KEYCLOAK_ROLE"/>
+ <addPrimaryKey columnNames="ROLE_ID, USER_ID" constraintName="CONSTRAINT_C" tableName="USER_ROLE_MAPPING"/>
+ <addPrimaryKey columnNames="REALM_ID, NAME" constraintName="CONSTRAINT_E" tableName="REALM_SMTP_CONFIG"/>
+ <addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_F" tableName="CREDENTIAL"/>
+ <addPrimaryKey columnNames="USER_FEDERATION_PROVIDER_ID, NAME" constraintName="CONSTRAINT_F9" tableName="USER_FEDERATION_CONFIG"/>
+ <addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_FB" tableName="USER_ENTITY"/>
+ <addUniqueConstraint columnNames="ROLE_ID" constraintName="UK_8AELWNIBJI49AVXSRTUF6XJOW" deferrable="false" disabled="false" initiallyDeferred="false" tableName="APPLICATION_DEFAULT_ROLES"/>
+ <addUniqueConstraint columnNames="REALM_ID,NAME" constraintName="UK_B71CJLBENV945RB6GCON438AT" deferrable="false" disabled="false" initiallyDeferred="false" tableName="CLIENT"/>
+ <addUniqueConstraint columnNames="USERFEDERATIONPROVIDERS_ID" constraintName="UK_DCCIRJLIPU1478VQC89DID88C" deferrable="false" disabled="false" initiallyDeferred="false" tableName="FED_PROVIDERS"/>
+ <addUniqueConstraint columnNames="REALM_ID,EMAIL_CONSTRAINT" constraintName="UK_DYKN684SL8UP1CRFEI6ECKHD7" deferrable="false" disabled="false" initiallyDeferred="false" tableName="USER_ENTITY"/>
+ <addUniqueConstraint columnNames="ROLE_ID" constraintName="UK_H4WPD7W4HSOOLNI3H0SW7BTJE" deferrable="false" disabled="false" initiallyDeferred="false" tableName="REALM_DEFAULT_ROLES"/>
+ <addUniqueConstraint columnNames="NAME,APP_REALM_CONSTRAINT" constraintName="UK_J3RWUVD56ONTGSUHOGM184WW2" deferrable="false" disabled="false" initiallyDeferred="false" tableName="KEYCLOAK_ROLE"/>
+ <addUniqueConstraint columnNames="REALM_ID" constraintName="UK_L5QGA3RFME47335JY8JXYXH3I" deferrable="false" disabled="false" initiallyDeferred="false" tableName="REALM_APPLICATION"/>
+ <addUniqueConstraint columnNames="NAME" constraintName="UK_ORVSDMLA56612EAEFIQ6WL5OI" deferrable="false" disabled="false" initiallyDeferred="false" tableName="REALM"/>
+ <addUniqueConstraint columnNames="REALM_ID,USERNAME" constraintName="UK_RU8TT6T700S9V50BU18WS5HA6" deferrable="false" disabled="false" initiallyDeferred="false" tableName="USER_ENTITY"/>
+ <addForeignKeyConstraint baseColumnNames="CLIENT_SESSION" baseTableName="CLIENT_SESSION_ROLE" constraintName="FK_11B7SGQW18I532811V7O2DV76" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="CLIENT_SESSION"/>
+ <addForeignKeyConstraint baseColumnNames="CLIENT_ID" baseTableName="REDIRECT_URIS" constraintName="FK_1BURS8PB4OUJ97H5WUPPAHV9F" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="CLIENT"/>
+ <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="USER_FEDERATION_PROVIDER" constraintName="FK_1FJ32F6PTOLW2QY60CD8N01E8" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="REALM"/>
+ <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="FED_PROVIDERS" constraintName="FK_213LYQ09FKXQ8K8NY8DY3737T" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="REALM"/>
+ <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="REALM_REQUIRED_CREDENTIAL" constraintName="FK_5HG65LYBEVAVKQFKI3KPONH9V" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="REALM"/>
+ <addForeignKeyConstraint baseColumnNames="USER_ID" baseTableName="USER_ATTRIBUTE" constraintName="FK_5HRM2VLF9QL5FU043KQEPOVBR" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="USER_ENTITY"/>
+ <addForeignKeyConstraint baseColumnNames="USER_ID" baseTableName="USER_SOCIAL_LINK" constraintName="FK_68CJYS5UWM55UY823Y75XG4OM" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="USER_ENTITY"/>
+ <addForeignKeyConstraint baseColumnNames="USER_ID" baseTableName="USER_REQUIRED_ACTION" constraintName="FK_6QJ3W1JW9CVAFHE19BWSIUVMD" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="USER_ENTITY"/>
+ <addForeignKeyConstraint baseColumnNames="REALM" baseTableName="KEYCLOAK_ROLE" constraintName="FK_6VYQFE4CN4WLQ8R6KT5VDSJ5C" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="REALM"/>
+ <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="REALM_SMTP_CONFIG" constraintName="FK_70EJ8XDXGXD0B9HH6180IRR0O" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="REALM"/>
+ <addForeignKeyConstraint baseColumnNames="APPLICATION_ID" baseTableName="REALM_APPLICATION" constraintName="FK_71S3P0DIUXAWWQQSA528UBY2Q" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="REALM"/>
+ <addForeignKeyConstraint baseColumnNames="ROLE_ID" baseTableName="APPLICATION_DEFAULT_ROLES" constraintName="FK_8AELWNIBJI49AVXSRTUF6XJOW" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="KEYCLOAK_ROLE"/>
+ <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="REALM_ATTRIBUTE" constraintName="FK_8SHXD6L3E9ATQUKACXGPFFPTW" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="REALM"/>
+ <addForeignKeyConstraint baseColumnNames="COMPOSITE" baseTableName="COMPOSITE_ROLE" constraintName="FK_A63WVEKFTU8JO1PNJ81E7MCE2" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="KEYCLOAK_ROLE"/>
+ <addForeignKeyConstraint baseColumnNames="SESSION_ID" baseTableName="CLIENT_SESSION" constraintName="FK_B4AO2VCVAT6UKAU74WBWTFQO1" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="USER_SESSION"/>
+ <addForeignKeyConstraint baseColumnNames="USER_ID" baseTableName="USER_ROLE_MAPPING" constraintName="FK_C4FQV34P1MBYLLOXANG7B1Q3L" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="USER_ENTITY"/>
+ <addForeignKeyConstraint baseColumnNames="USERFEDERATIONPROVIDERS_ID" baseTableName="FED_PROVIDERS" constraintName="FK_DCCIRJLIPU1478VQC89DID88C" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="USER_FEDERATION_PROVIDER"/>
+ <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="REALM_DEFAULT_ROLES" constraintName="FK_EVUDB1PPW84OXFAX2DRS03ICC" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="REALM"/>
+ <addForeignKeyConstraint baseColumnNames="CHILD_ROLE" baseTableName="COMPOSITE_ROLE" constraintName="FK_GR7THLLB9LU8Q4VQA4524JJY8" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="KEYCLOAK_ROLE"/>
+ <addForeignKeyConstraint baseColumnNames="ROLE_ID" baseTableName="REALM_DEFAULT_ROLES" constraintName="FK_H4WPD7W4HSOOLNI3H0SW7BTJE" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="KEYCLOAK_ROLE"/>
+ <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="REALM_EVENTS_LISTENERS" constraintName="FK_H846O4H0W8EPX5NXEV9F5Y69J" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="REALM"/>
+ <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="REALM_APPLICATION" constraintName="FK_L5QGA3RFME47335JY8JXYXH3I" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="CLIENT"/>
+ <addForeignKeyConstraint baseColumnNames="CLIENT_ID" baseTableName="WEB_ORIGINS" constraintName="FK_LOJPHO213XCX4WNKOG82SSRFY" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="CLIENT"/>
+ <addForeignKeyConstraint baseColumnNames="APPLICATION_ID" baseTableName="APPLICATION_DEFAULT_ROLES" constraintName="FK_MAYLTS7KLWQW2H8M2B5JOYTKY" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="CLIENT"/>
+ <addForeignKeyConstraint baseColumnNames="CLIENT_ID" baseTableName="SCOPE_MAPPING" constraintName="FK_OUSE064PLMLR732LXJCN1Q5F1" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="CLIENT"/>
+ <addForeignKeyConstraint baseColumnNames="ROLE_ID" baseTableName="SCOPE_MAPPING" constraintName="FK_P3RH9GRKU11KQFRS4FLTT7RNQ" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="KEYCLOAK_ROLE"/>
+ <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="CLIENT" constraintName="FK_P56CTINXXB9GSK57FO49F9TAC" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="REALM"/>
+ <addForeignKeyConstraint baseColumnNames="USER_ID" baseTableName="CREDENTIAL" constraintName="FK_PFYR0GLASQYL0DEI3KL69R6V0" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="USER_ENTITY"/>
+ <addForeignKeyConstraint baseColumnNames="APPLICATION" baseTableName="KEYCLOAK_ROLE" constraintName="FK_PIMO5LE2C0RAL09FL8CM9WFW9" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="CLIENT"/>
+ <addForeignKeyConstraint baseColumnNames="MASTER_ADMIN_APP" baseTableName="REALM" constraintName="FK_RSAF444KK6QRKMS7N56AIWQ5Y" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="CLIENT"/>
+ <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="REALM_SOCIAL_CONFIG" constraintName="FK_SV5I3C2TI7G0G922FGE683SOV" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="REALM"/>
+ <addForeignKeyConstraint baseColumnNames="USER_FEDERATION_PROVIDER_ID" baseTableName="USER_FEDERATION_CONFIG" constraintName="FK_T13HPU1J94R2EBPEKR39X5EU5" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="USER_FEDERATION_PROVIDER"/>
+ </changeSet>
+</databaseChangeLog>
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 0000000..0b9f0f5
--- /dev/null
+++ b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.1.0.Beta1.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.2.xsd">
+ <changeSet author="sthorger@redhat.com" id="1.1.0.Beta1">
+ <delete tableName="CLIENT_SESSION_ROLE"/>
+ <delete tableName="CLIENT_SESSION"/>
+ <delete tableName="USER_SESSION"/>
+
+ <createTable tableName="CLIENT_ATTRIBUTES">
+ <column name="CLIENT_ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="VALUE" type="VARCHAR(2048)"/>
+ <column name="NAME" type="VARCHAR(255)">
+ <constraints nullable="false"/>
+ </column>
+ </createTable>
+ <createTable tableName="CLIENT_SESSION_NOTE">
+ <column name="NAME" type="VARCHAR(255)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="VALUE" type="VARCHAR(255)"/>
+ <column name="CLIENT_SESSION" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ </createTable>
+ <addColumn tableName="CLIENT_SESSION">
+ <column name="AUTH_METHOD" type="VARCHAR(255)"/>
+ </addColumn>
+ <addColumn tableName="CLIENT">
+ <column name="PROTOCOL" type="VARCHAR(255)"/>
+ </addColumn>
+ <addColumn tableName="CLIENT_SESSION">
+ <column name="REALM_ID" type="VARCHAR(255)"/>
+ </addColumn>
+ <addPrimaryKey columnNames="CLIENT_ID, NAME" constraintName="CONSTRAINT_3C" tableName="CLIENT_ATTRIBUTES"/>
+ <addPrimaryKey columnNames="CLIENT_SESSION, NAME" constraintName="CONSTRAINT_5E" tableName="CLIENT_SESSION_NOTE"/>
+ <addForeignKeyConstraint baseColumnNames="CLIENT_ID" baseTableName="CLIENT_ATTRIBUTES" constraintName="FK3C47C64BEACCA966" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="CLIENT"/>
+ <addForeignKeyConstraint baseColumnNames="CLIENT_SESSION" baseTableName="CLIENT_SESSION_NOTE" constraintName="FK5EDFB00FF51C2736" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="CLIENT_SESSION"/>
+ </changeSet>
+</databaseChangeLog>
\ 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 0000000..010b121
--- /dev/null
+++ b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-master.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.2.xsd">
+ <include file="META-INF/jpa-changelog-1.0.0.Final.xml"/>
+ <include file="META-INF/jpa-changelog-1.1.0.Beta1.xml"/>
+</databaseChangeLog>
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 0000000..cb36ec3
--- /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/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java
index e644c7a..f800dfb 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);
+ }
+ }
+
+ this.mongoStore = new MongoStoreImpl(db, getManagedEntities());
- logger.infof("Initialized mongo model. host: %s, port: %d, db: %s, clearOnStartup: %b", host, port, dbName, clearOnStartup);
+ 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/impl/MongoStoreImpl.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/impl/MongoStoreImpl.java
index 09104ea..85bf25e 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<Class<?>, 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 0000000..1f3f6ae
--- /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 <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+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<String> executed = new LinkedList<String>();
+ 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<Update> updatesToRun = new LinkedList<Update>();
+ 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 0000000..8069850
--- /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 <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+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 0000000..7192f1d
--- /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 <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+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 0000000..981f6d0
--- /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 <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public interface MongoUpdaterProviderFactory extends ProviderFactory<MongoUpdaterProvider> {
+}
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 0000000..da89b4d
--- /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 <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+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 0000000..e8058f0
--- /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 <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+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 0000000..ded25dc
--- /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 <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+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 0000000..89d372f
--- /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 <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+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");
+ }
+
+}
connections/pom.xml 1(+1 -0)
diff --git a/connections/pom.xml b/connections/pom.xml
index a5b3bb2..2d284bb 100755
--- a/connections/pom.xml
+++ b/connections/pom.xml
@@ -14,6 +14,7 @@
<modules>
<module>jpa</module>
+ <module>jpa-liquibase</module>
<module>infinispan</module>
<module>mongo</module>
</modules>
dependencies/server-all/pom.xml 16(+16 -0)
diff --git a/dependencies/server-all/pom.xml b/dependencies/server-all/pom.xml
index a812b08..9c20bd4 100755
--- a/dependencies/server-all/pom.xml
+++ b/dependencies/server-all/pom.xml
@@ -28,6 +28,11 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-connections-jpa-liquibase</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-connections-infinispan</artifactId>
<version>${project.version}</version>
</dependency>
@@ -180,6 +185,17 @@
<groupId>de.idyl</groupId>
<artifactId>winzipaes</artifactId>
</dependency>
+
+ <dependency>
+ <groupId>org.liquibase</groupId>
+ <artifactId>liquibase-core</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>org.yaml</groupId>
+ <artifactId>snakeyaml</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
</dependencies>
</project>
\ No newline at end of file
misc/UpdatingDatabaseSchema.md 79(+79 -0)
diff --git a/misc/UpdatingDatabaseSchema.md b/misc/UpdatingDatabaseSchema.md
new file mode 100644
index 0000000..1dcee57
--- /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-<version>.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<version>` 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 9000d37..7534e66 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 <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
@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 bf07f91..ab01359 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 <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
@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 c1a4267..2784352 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 <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
@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 be034da..cf0cfed 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 <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
@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 a0c16bf..c9f317e 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 <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
@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 b43f827..53cf7f3 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);
}
pom.xml 6(+6 -0)
diff --git a/pom.xml b/pom.xml
index 73523c0..763bdcb 100755
--- a/pom.xml
+++ b/pom.xml
@@ -44,6 +44,7 @@
<selenium.version>2.35.0</selenium.version>
<javax.mail.version>1.4.5</javax.mail.version>
<infinispan.version>6.0.2.Final</infinispan.version>
+ <liquibase.version>3.2.2</liquibase.version>
<!-- maven-compiler-plugin -->
<maven.compiler.target>1.6</maven.compiler.target>
@@ -449,6 +450,11 @@
<artifactId>infinispan-core</artifactId>
<version>${infinispan.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.liquibase</groupId>
+ <artifactId>liquibase-core</artifactId>
+ <version>${liquibase.version}</version>
+ </dependency>
</dependencies>
</dependencyManagement>
diff --git a/testsuite/integration/src/main/resources/log4j.properties b/testsuite/integration/src/main/resources/log4j.properties
index 315b7ca..778d39a 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/main/resources/META-INF/keycloak-server.json b/testsuite/integration/src/main/resources/META-INF/keycloak-server.json
index 6e90fa1..728631d 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/test/java/org/keycloak/testsuite/account/AccountTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java
index 5aa52c3..4cfaaa3 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 32ba03c..47d1ff5 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 6aaf068..b661d5a 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 c4bf603..5261da6 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 7b616f3..06e793f 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 8f3f76f..5870eb0 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,