keycloak-uncached

Details

diff --git a/docbook/auth-server-docs/reference/en/en-US/modules/server-installation.xml b/docbook/auth-server-docs/reference/en/en-US/modules/server-installation.xml
index d247c8d..acc75d2 100755
--- a/docbook/auth-server-docs/reference/en/en-US/modules/server-installation.xml
+++ b/docbook/auth-server-docs/reference/en/en-US/modules/server-installation.xml
@@ -186,8 +186,9 @@ bin/add-user-keycloak.[sh|bat] -r master -u <username> -p <password>
                         <term>databaseSchema</term>
                         <listitem>
                             <para>
-                                Specify if schema should be updated or validated. Valid values are "update" and "validate". Value "update is default and means that DB schema will be updated to latest version.
-                                Value "validate" won't touch database schema, but it will fail to start if schema is not updated to latest version.
+                                Specify if schema should be updated or validated. Valid values are <literal>update</literal> and <literal>validate</literal>.
+                                Value <literal>update</literal> is default and means that DB schema will be updated to latest version.
+                                Value <literal>validate</literal> won't touch database schema, but it will fail to start if schema is not updated to latest version.
                             </para>
                         </listitem>
                     </varlistentry>
@@ -304,7 +305,11 @@ bin/add-user-keycloak.[sh|bat] -r master -u <username> -p <password>
 },
 
 "user": {
-    "provider": "${keycloak.user.provider:jpa}"
+    "provider": "jpa"
+},
+
+"userSessionPersister": {
+    "provider": "jpa"
 },
 ]]></programlisting>
 
@@ -325,6 +330,10 @@ bin/add-user-keycloak.[sh|bat] -r master -u <username> -p <password>
 "user": {
     "provider": "mongo"
 },
+
+"userSessionPersister": {
+    "provider": "mongo"
+},
 ]]></programlisting>
 
                 And at the end of the file add the snippet like this where you can configure details about your Mongo database:
@@ -334,7 +343,8 @@ bin/add-user-keycloak.[sh|bat] -r master -u <username> -p <password>
         "host": "127.0.0.1",
         "port": "27017",
         "db": "keycloak",
-        "connectionsPerHost": 100
+        "connectionsPerHost": 100,
+        "databaseSchema": "update"
     }
 }
 ]]></programlisting>
@@ -344,6 +354,14 @@ bin/add-user-keycloak.[sh|bat] -r master -u <username> -p <password>
                 if you want authenticate against your MongoDB. If user and password are not specified, Keycloak will connect
                 unauthenticated to your MongoDB.
             </para>
+            <para>
+                Option <literal>databaseSchema</literal> specify if mongo database "schema" should be updated or validated. Valid values are <literal>update</literal> and <literal>validate</literal>.
+                Value <literal>update</literal> means that DB schema will be updated to latest version.
+                Value <literal>validate</literal> won't touch database schema, but it will fail to start if schema is not updated to latest version.
+            </para>
+            <note>
+                Mongo doesn't have real database schema, but 'schema' in this case means to which Keycloak version the data in the Mongo database corresponds.
+            </note>
             <para>Finally there is set of optional configuration options, which can be used to specify connection-pooling capabilities of Mongo client. Supported int options are:
                 <literal>connectionsPerHost</literal>, <literal>threadsAllowedToBlockForConnectionMultiplier</literal>, <literal>maxWaitTime</literal>, <literal>connectTimeout</literal>
                 <literal>socketTimeout</literal>. Supported boolean options are: <literal>socketKeepAlive</literal>, <literal>autoConnectRetry</literal>.
@@ -360,7 +378,8 @@ bin/add-user-keycloak.[sh|bat] -r master -u <username> -p <password>
 "connectionsMongo": {
     "default": {
         "uri": "mongodb://user:password@127.0.0.1/authentication",
-        "db": "keycloak"
+        "db": "keycloak",
+        "databaseSchema": "update"
     }
 }
 ]]></programlisting>
diff --git a/model/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java b/model/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java
index 70359df..f66c989 100755
--- a/model/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java
+++ b/model/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java
@@ -156,13 +156,24 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
     }
 
     private void update(KeycloakSession session) {
-        MongoUpdaterProvider mongoUpdater = session.getProvider(MongoUpdaterProvider.class);
+        String databaseSchema = config.get("databaseSchema");
 
-        if (mongoUpdater == null) {
-            throw new RuntimeException("Can't update database: Mongo updater provider not found");
-        }
+        if (databaseSchema == null) {
+            throw new RuntimeException("Property 'databaseSchema' needs to be specified in the configuration of mongo connections");
+        } else {
+            MongoUpdaterProvider mongoUpdater = session.getProvider(MongoUpdaterProvider.class);
+            if (mongoUpdater == null) {
+                throw new RuntimeException("Can't update database: Mongo updater provider not found");
+            }
 
-        mongoUpdater.update(session, db);
+            if (databaseSchema.equals("update")) {
+                mongoUpdater.update(session, db);
+            } else if (databaseSchema.equals("validate")) {
+                mongoUpdater.validate(session, db);
+            } else {
+                throw new RuntimeException("Invalid value for databaseSchema: " + databaseSchema);
+            }
+        }
     }
 
 
diff --git a/model/mongo/src/main/java/org/keycloak/connections/mongo/updater/impl/DefaultMongoUpdaterProvider.java b/model/mongo/src/main/java/org/keycloak/connections/mongo/updater/impl/DefaultMongoUpdaterProvider.java
index 889281c..aa4130d 100755
--- a/model/mongo/src/main/java/org/keycloak/connections/mongo/updater/impl/DefaultMongoUpdaterProvider.java
+++ b/model/mongo/src/main/java/org/keycloak/connections/mongo/updater/impl/DefaultMongoUpdaterProvider.java
@@ -65,38 +65,19 @@ public class DefaultMongoUpdaterProvider implements MongoUpdaterProvider {
         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);
-                }
-            }
+            List<String> executed = getExecuted(db, changeLogExists, changeLog);
+            List<Update> updatesToRun = getUpdatesToRun(executed);
 
             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());
+                        log.debugv("Updating database from {0} to {1}", executed.get(executed.size() - 1), updatesToRun.get(updatesToRun.size() - 1).getId());
                     } else {
-                        log.debugv("Updating database");
+                        log.info("Updating database");
                     }
                 }
 
@@ -121,10 +102,66 @@ public class DefaultMongoUpdaterProvider implements MongoUpdaterProvider {
         }
     }
 
+
+    @Override
+    public void validate(KeycloakSession session, DB db) {
+        log.debug("Validating database");
+
+        boolean changeLogExists = db.collectionExists(CHANGE_LOG_COLLECTION);
+        DBCollection changeLog = db.getCollection(CHANGE_LOG_COLLECTION);
+
+        List<String> executed = getExecuted(db, changeLogExists, changeLog);
+        List<Update> updatesToRun = getUpdatesToRun(executed);
+
+        if (!updatesToRun.isEmpty()) {
+            String errorMessage = String.format("Failed to validate Mongo database schema. Schema needs updating database from %s to %s. Please change databaseSchema to 'update'",
+                    executed.get(executed.size() - 1), updatesToRun.get(updatesToRun.size() - 1).getId());
+            throw new RuntimeException(errorMessage);
+        } else {
+            log.debug("Validation passed. Database is up to date");
+        }
+    }
+
+
+    private List<String> getExecuted(DB db, boolean changeLogExists, DBCollection changeLog) {
+        boolean realmExists = db.collectionExists("realms");
+
+        List<String> executed = new LinkedList<>();
+        if (!changeLogExists && realmExists) {
+            Update1_0_0_Final u = new Update1_0_0_Final();
+            executed.add(u.getId());
+            createLog(changeLog, u, 1);
+        } else if (changeLogExists) {
+            DBCursor cursor = changeLog.find().sort(new BasicDBObject("orderExecuted", 1));
+            while (cursor.hasNext()) {
+                executed.add((String) cursor.next().get("_id"));
+            }
+        }
+        return executed;
+    }
+
+
+    private List<Update> getUpdatesToRun(List<String> executed) {
+        try {
+            List<Update> updatesToRun = new LinkedList<>();
+            for (Class<? extends Update> updateClass : updates) {
+                Update u = updateClass.newInstance();
+                if (!executed.contains(u.getId())) {
+                    updatesToRun.add(u);
+                }
+            }
+            return updatesToRun;
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+
     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/model/mongo/src/main/java/org/keycloak/connections/mongo/updater/MongoUpdaterProvider.java b/model/mongo/src/main/java/org/keycloak/connections/mongo/updater/MongoUpdaterProvider.java
index b5000ec..e7224c9 100644
--- a/model/mongo/src/main/java/org/keycloak/connections/mongo/updater/MongoUpdaterProvider.java
+++ b/model/mongo/src/main/java/org/keycloak/connections/mongo/updater/MongoUpdaterProvider.java
@@ -26,6 +26,8 @@ import org.keycloak.provider.Provider;
  */
 public interface MongoUpdaterProvider extends Provider {
 
-    public void update(KeycloakSession session, DB db);
+    void update(KeycloakSession session, DB db);
+
+    void validate(KeycloakSession session, DB db);
 
 }
diff --git a/testsuite/integration/src/test/resources/META-INF/keycloak-server.json b/testsuite/integration/src/test/resources/META-INF/keycloak-server.json
index 443f3e1..ec76904 100755
--- a/testsuite/integration/src/test/resources/META-INF/keycloak-server.json
+++ b/testsuite/integration/src/test/resources/META-INF/keycloak-server.json
@@ -73,6 +73,7 @@
             "host": "${keycloak.connectionsMongo.host:127.0.0.1}",
             "port": "${keycloak.connectionsMongo.port:27017}",
             "db": "${keycloak.connectionsMongo.db:keycloak}",
+            "databaseSchema": "${keycloak.connectionsMongo.databaseSchema:update}",
             "connectionsPerHost": "${keycloak.connectionsMongo.connectionsPerHost:100}"
         }
     },
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json b/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json
index c20c075..9208927 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json
@@ -94,6 +94,7 @@
             "host": "${keycloak.connectionsMongo.host:127.0.0.1}",
             "port": "${keycloak.connectionsMongo.port:27017}",
             "db": "${keycloak.connectionsMongo.db:keycloak}",
+            "databaseSchema": "${keycloak.connectionsMongo.databaseSchema:update}",
             "connectionsPerHost": "${keycloak.connectionsMongo.connectionsPerHost:100}"
         }
     },