diff --git a/pom.xml b/pom.xml index 99a90cd930..c2a58bcd10 100755 --- a/pom.xml +++ b/pom.xml @@ -254,6 +254,11 @@ mongo-java-driver 2.11.2 + + de.flapdoodle.embed + de.flapdoodle.embed.mongo + 1.27 + diff --git a/services/pom.xml b/services/pom.xml index 7c38f45478..8696a5d1e0 100755 --- a/services/pom.xml +++ b/services/pom.xml @@ -152,6 +152,11 @@ org.mongodb mongo-java-driver + + de.flapdoodle.embed + de.flapdoodle.embed.mongo + test + junit junit diff --git a/services/src/main/java/org/keycloak/services/models/nosql/impl/MongoDBImpl.java b/services/src/main/java/org/keycloak/services/models/nosql/impl/MongoDBImpl.java index 6862a0c86e..44a340a21a 100644 --- a/services/src/main/java/org/keycloak/services/models/nosql/impl/MongoDBImpl.java +++ b/services/src/main/java/org/keycloak/services/models/nosql/impl/MongoDBImpl.java @@ -48,7 +48,7 @@ public class MongoDBImpl implements NoSQL { new ConcurrentHashMap, ObjectInfo>(); - public MongoDBImpl(DB database, boolean removeAllObjectsAtStartup, Class[] managedDataTypes) { + public MongoDBImpl(DB database, boolean dropDatabaseOnStartup, Class[] managedDataTypes) { this.database = database; typeConverter = new TypeConverter(); @@ -70,20 +70,9 @@ public class MongoDBImpl implements NoSQL { typeConverter.addDBObjectConverter(new BasicDBObjectConverter(this, typeConverter, type)); } - if (removeAllObjectsAtStartup) { - for (Class type : managedDataTypes) { - ObjectInfo objectInfo = getObjectInfo(type); - String collectionName = objectInfo.getDbCollectionName(); - if (collectionName != null) { - logger.debug("Dropping collection " + collectionName); - - DBCollection dbCollection = this.database.getCollection(collectionName); - dbCollection.drop(); - } else { - logger.debug("Skip removing objects of type " + type + " as it doesn't have it's own collection"); - } - } - logger.info("All objects successfully removed from MongoDB"); + if (dropDatabaseOnStartup) { + this.database.dropDatabase(); + logger.info("Database " + this.database.getName() + " dropped in MongoDB"); } } diff --git a/services/src/main/java/org/keycloak/services/models/nosql/keycloak/adapters/MongoDBSessionFactory.java b/services/src/main/java/org/keycloak/services/models/nosql/keycloak/adapters/MongoDBSessionFactory.java index 114f7b74db..73ba4615b7 100644 --- a/services/src/main/java/org/keycloak/services/models/nosql/keycloak/adapters/MongoDBSessionFactory.java +++ b/services/src/main/java/org/keycloak/services/models/nosql/keycloak/adapters/MongoDBSessionFactory.java @@ -43,14 +43,14 @@ public class MongoDBSessionFactory implements KeycloakSessionFactory { private final MongoClient mongoClient; private final NoSQL mongoDB; - public MongoDBSessionFactory(String host, int port, String dbName, boolean removeAllObjectsAtStartup) { - logger.info(String.format("Going to use MongoDB database. host: %s, port: %d, databaseName: %s, removeAllObjectsAtStartup: %b", host, port, dbName, removeAllObjectsAtStartup)); + public MongoDBSessionFactory(String host, int port, String dbName, boolean dropDatabaseOnStartup) { + logger.info(String.format("Going to use MongoDB database. host: %s, port: %d, databaseName: %s, removeAllObjectsAtStartup: %b", host, port, dbName, dropDatabaseOnStartup)); try { // TODO: authentication support mongoClient = new MongoClient(host, port); DB db = mongoClient.getDB(dbName); - mongoDB = new MongoDBImpl(db, removeAllObjectsAtStartup, MANAGED_DATA_TYPES); + mongoDB = new MongoDBImpl(db, dropDatabaseOnStartup, MANAGED_DATA_TYPES); } catch (UnknownHostException e) { throw new RuntimeException(e); diff --git a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java index dabba4072a..fc82975224 100755 --- a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java +++ b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java @@ -37,6 +37,16 @@ import java.util.Set; * @version $Revision: 1 $ */ public class KeycloakApplication extends Application { + + public static final String SESSION_FACTORY = "keycloak.sessionFactory"; + public static final String SESSION_FACTORY_PICKETLINK = "picketlink"; + public static final String SESSION_FACTORY_MONGO = "mongo"; + public static final String MONGO_HOST = "keycloak.mongodb.host"; + public static final String MONGO_PORT = "keycloak.mongodb.port"; + public static final String MONGO_DB_NAME = "keycloak.mongodb.databaseName"; + public static final String MONGO_DROP_DB_ON_STARTUP = "keycloak.mongodb.dropDatabaseOnStartup"; + + protected Set singletons = new HashSet(); protected Set> classes = new HashSet>(); @@ -62,8 +72,8 @@ public class KeycloakApplication extends Application { } public static KeycloakSessionFactory buildSessionFactory() { - String sessionFactoryType = System.getProperty("keycloak.sessionFactory", "picketlink"); - if ("mongo".equals(sessionFactoryType)) { + String sessionFactoryType = System.getProperty(SESSION_FACTORY, SESSION_FACTORY_PICKETLINK); + if (SESSION_FACTORY_MONGO.equals(sessionFactoryType)) { return buildMongoDBSessionFactory(); } else { return buildPicketlinkSessionFactory(); @@ -76,11 +86,11 @@ public class KeycloakApplication extends Application { } private static KeycloakSessionFactory buildMongoDBSessionFactory() { - String host = System.getProperty("keycloak.mongodb.host", "localhost"); - int port = Integer.parseInt(System.getProperty("keycloak.mongodb.port", "27017")); - String dbName = System.getProperty("keycloak.mongodb.databaseName", "keycloak"); - boolean removeAllObjectsOnStartup = Boolean.parseBoolean(System.getProperty("keycloak.mongodb.removeAllObjectsOnStartup", "true")); - return new MongoDBSessionFactory(host, port, dbName, removeAllObjectsOnStartup); + String host = System.getProperty(MONGO_HOST, "localhost"); + int port = Integer.parseInt(System.getProperty(MONGO_PORT, "27017")); + String dbName = System.getProperty(MONGO_DB_NAME, "keycloak"); + boolean dropDatabaseOnStartup = Boolean.parseBoolean(System.getProperty(MONGO_DROP_DB_ON_STARTUP, "true")); + return new MongoDBSessionFactory(host, port, dbName, dropDatabaseOnStartup); } public KeycloakSessionFactory getFactory() { diff --git a/services/src/test/java/org/keycloak/test/AdapterTest.java b/services/src/test/java/org/keycloak/test/AdapterTest.java index 1bdaa82254..08420363e7 100755 --- a/services/src/test/java/org/keycloak/test/AdapterTest.java +++ b/services/src/test/java/org/keycloak/test/AdapterTest.java @@ -12,6 +12,8 @@ import org.keycloak.services.managers.OAuthClientManager; import org.keycloak.services.managers.RealmManager; import org.keycloak.models.UserModel.RequiredAction; import org.keycloak.services.resources.KeycloakApplication; +import org.keycloak.test.common.AbstractKeycloakTest; +import org.keycloak.test.common.SessionFactoryTestContext; import java.util.HashSet; @@ -24,30 +26,16 @@ import java.util.StringTokenizer; * @version $Revision: 1 $ */ @FixMethodOrder(MethodSorters.NAME_ASCENDING) -public class AdapterTest { - private KeycloakSessionFactory factory; - private KeycloakSession identitySession; - private RealmManager adapter; +public class AdapterTest extends AbstractKeycloakTest { private RealmModel realmModel; - @Before - public void before() throws Exception { - factory = KeycloakApplication.buildSessionFactory(); - identitySession = factory.createSession(); - identitySession.getTransaction().begin(); - adapter = new RealmManager(identitySession); - } - - @After - public void after() throws Exception { - identitySession.getTransaction().commit(); - identitySession.close(); - factory.close(); + public AdapterTest(SessionFactoryTestContext testContext) { + super(testContext); } @Test public void installTest() throws Exception { - new InstallationManager().install(adapter); + new InstallationManager().install(getRealmManager()); } @@ -63,7 +51,7 @@ public class AdapterTest { @Test public void test1CreateRealm() throws Exception { - realmModel = adapter.createRealm("JUGGLER"); + realmModel = getRealmManager().createRealm("JUGGLER"); realmModel.setAccessCodeLifespan(100); realmModel.setAccessCodeLifespanUserAction(600); realmModel.setCookieLoginAllowed(true); @@ -76,7 +64,7 @@ public class AdapterTest { realmModel.addDefaultRole("foo"); System.out.println(realmModel.getId()); - realmModel = adapter.getRealm(realmModel.getId()); + realmModel = getRealmManager().getRealm(realmModel.getId()); Assert.assertNotNull(realmModel); Assert.assertEquals(realmModel.getAccessCodeLifespan(), 100); Assert.assertEquals(600, realmModel.getAccessCodeLifespanUserAction()); diff --git a/services/src/test/java/org/keycloak/test/ImportTest.java b/services/src/test/java/org/keycloak/test/ImportTest.java index d426d4e4d3..33348ed252 100755 --- a/services/src/test/java/org/keycloak/test/ImportTest.java +++ b/services/src/test/java/org/keycloak/test/ImportTest.java @@ -19,6 +19,8 @@ import org.keycloak.models.SocialLinkModel; import org.keycloak.models.UserModel; import org.keycloak.services.resources.KeycloakApplication; import org.keycloak.services.resources.SaasService; +import org.keycloak.test.common.AbstractKeycloakTest; +import org.keycloak.test.common.SessionFactoryTestContext; import java.util.List; import java.util.Set; @@ -28,29 +30,15 @@ import java.util.Set; * @version $Revision: 1 $ */ @FixMethodOrder(MethodSorters.NAME_ASCENDING) -public class ImportTest { - private KeycloakSessionFactory factory; - private KeycloakSession identitySession; - private RealmManager manager; - private RealmModel realmModel; +public class ImportTest extends AbstractKeycloakTest { - @Before - public void before() throws Exception { - factory = KeycloakApplication.buildSessionFactory(); - identitySession = factory.createSession(); - identitySession.getTransaction().begin(); - manager = new RealmManager(identitySession); - } - - @After - public void after() throws Exception { - identitySession.getTransaction().commit(); - identitySession.close(); - factory.close(); + public ImportTest(SessionFactoryTestContext testContext) { + super(testContext); } @Test public void install() throws Exception { + RealmManager manager = getRealmManager(); RealmModel defaultRealm = manager.createRealm(RealmModel.DEFAULT_REALM, RealmModel.DEFAULT_REALM); defaultRealm.setName(RealmModel.DEFAULT_REALM); defaultRealm.setEnabled(true); @@ -93,7 +81,7 @@ public class ImportTest { List resources = realm.getApplications(); Assert.assertEquals(2, resources.size()); - List realms = identitySession.getRealms(admin); + List realms = getIdentitySession().getRealms(admin); Assert.assertEquals(1, realms.size()); // Test scope relationship @@ -129,6 +117,7 @@ public class ImportTest { @Test public void install2() throws Exception { + RealmManager manager = getRealmManager(); RealmModel defaultRealm = manager.createRealm(RealmModel.DEFAULT_REALM, RealmModel.DEFAULT_REALM); defaultRealm.setName(RealmModel.DEFAULT_REALM); defaultRealm.setEnabled(true); diff --git a/services/src/test/java/org/keycloak/test/RealmCreationTest.java b/services/src/test/java/org/keycloak/test/RealmCreationTest.java index a99042f6c5..725a0d1228 100755 --- a/services/src/test/java/org/keycloak/test/RealmCreationTest.java +++ b/services/src/test/java/org/keycloak/test/RealmCreationTest.java @@ -11,6 +11,11 @@ import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.managers.RealmManager; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; +import org.keycloak.services.models.KeycloakSession; +import org.keycloak.services.models.RealmModel; +import org.keycloak.services.resources.KeycloakApplication; +import org.keycloak.test.common.AbstractKeycloakTest; +import org.keycloak.test.common.SessionFactoryTestContext; import javax.ws.rs.NotAuthorizedException; import javax.ws.rs.client.Entity; diff --git a/services/src/test/java/org/keycloak/test/common/AbstractKeycloakTest.java b/services/src/test/java/org/keycloak/test/common/AbstractKeycloakTest.java new file mode 100644 index 0000000000..0e9b692efd --- /dev/null +++ b/services/src/test/java/org/keycloak/test/common/AbstractKeycloakTest.java @@ -0,0 +1,96 @@ +package org.keycloak.test.common; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.keycloak.services.managers.RealmManager; +import org.keycloak.services.models.KeycloakSession; +import org.keycloak.services.models.KeycloakSessionFactory; +import org.keycloak.services.resources.KeycloakApplication; + +/** + * @author Marek Posolda + */ +@RunWith(Parameterized.class) +public abstract class AbstractKeycloakTest { + + protected static final SessionFactoryTestContext[] TEST_CONTEXTS; + + private final SessionFactoryTestContext testContext; + private KeycloakSessionFactory factory; + private KeycloakSession identitySession; + private RealmManager realmManager; + + // STATIC METHODS + + static + { + // TODO: Disable MongoDB by default and enable it just for some specific maven profile (system property)? + TEST_CONTEXTS = new SessionFactoryTestContext[] { + new PicketlinkSessionFactoryTestContext(), + new MongoDBSessionFactoryTestContext() + }; + } + + @Parameterized.Parameters + public static Iterable parameters() { + List params = new ArrayList(); + + for (SessionFactoryTestContext testContext : TEST_CONTEXTS) { + params.add(new Object[] {testContext}); + } + return params; + } + + @BeforeClass + public static void baseBeforeClass() { + for (SessionFactoryTestContext testContext : TEST_CONTEXTS) { + testContext.beforeTestClass(); + } + } + + @AfterClass + public static void baseAfterClass() { + for (SessionFactoryTestContext testContext : TEST_CONTEXTS) { + testContext.afterTestClass(); + } + } + + // NON-STATIC METHODS + + public AbstractKeycloakTest(SessionFactoryTestContext testContext) { + this.testContext = testContext; + } + + @Before + public void before() throws Exception { + testContext.initEnvironment(); + factory = KeycloakApplication.buildSessionFactory(); + identitySession = factory.createSession(); + identitySession.getTransaction().begin(); + realmManager = new RealmManager(identitySession); + } + + @After + public void after() throws Exception { + identitySession.getTransaction().commit(); + identitySession.close(); + factory.close(); + } + + protected RealmManager getRealmManager() { + return realmManager; + } + + protected KeycloakSession getIdentitySession() { + return identitySession; + } + +} diff --git a/services/src/test/java/org/keycloak/test/common/MongoDBSessionFactoryTestContext.java b/services/src/test/java/org/keycloak/test/common/MongoDBSessionFactoryTestContext.java new file mode 100644 index 0000000000..fc723a2137 --- /dev/null +++ b/services/src/test/java/org/keycloak/test/common/MongoDBSessionFactoryTestContext.java @@ -0,0 +1,60 @@ +package org.keycloak.test.common; + +import com.mongodb.DB; +import com.mongodb.MongoClient; +import de.flapdoodle.embed.mongo.MongodExecutable; +import de.flapdoodle.embed.mongo.MongodProcess; +import de.flapdoodle.embed.mongo.MongodStarter; +import de.flapdoodle.embed.mongo.config.MongodConfig; +import de.flapdoodle.embed.mongo.distribution.Version; +import de.flapdoodle.embed.process.runtime.Network; +import org.jboss.resteasy.logging.Logger; +import org.keycloak.services.models.KeycloakSessionFactory; +import org.keycloak.services.resources.KeycloakApplication; + +/** + * @author Marek Posolda + */ +public class MongoDBSessionFactoryTestContext implements SessionFactoryTestContext { + + protected static final Logger logger = Logger.getLogger(MongoDBSessionFactoryTestContext.class); + private static final int PORT = 27777; + + private MongodExecutable mongodExe; + private MongodProcess mongod; + + @Override + public void beforeTestClass() { + logger.info("Bootstrapping MongoDB on localhost, port " + PORT); + try { + mongodExe = MongodStarter.getDefaultInstance().prepare(new MongodConfig(Version.V2_0_5, PORT, Network.localhostIsIPv6())); + mongod = mongodExe.start(); + } catch (Exception e) { + throw new RuntimeException(e); + } + logger.info("MongoDB bootstrapped successfully"); + } + + @Override + public void afterTestClass() { + if (mongodExe != null) { + if (mongod != null) { + mongod.stop(); + } + mongodExe.stop(); + } + logger.info("MongoDB stopped successfully"); + + // Null this, so other tests are not affected + System.setProperty(KeycloakApplication.SESSION_FACTORY, ""); + } + + @Override + public void initEnvironment() { + System.setProperty(KeycloakApplication.SESSION_FACTORY, KeycloakApplication.SESSION_FACTORY_MONGO); + System.setProperty(KeycloakApplication.MONGO_HOST, "localhost"); + System.setProperty(KeycloakApplication.MONGO_PORT, String.valueOf(PORT)); + System.setProperty(KeycloakApplication.MONGO_DB_NAME, "keycloakTest"); + System.setProperty(KeycloakApplication.MONGO_DROP_DB_ON_STARTUP, "true"); + } +} diff --git a/services/src/test/java/org/keycloak/test/common/PicketlinkSessionFactoryTestContext.java b/services/src/test/java/org/keycloak/test/common/PicketlinkSessionFactoryTestContext.java new file mode 100644 index 0000000000..7ffbbf359d --- /dev/null +++ b/services/src/test/java/org/keycloak/test/common/PicketlinkSessionFactoryTestContext.java @@ -0,0 +1,25 @@ +package org.keycloak.test.common; + +import org.keycloak.services.models.KeycloakSessionFactory; +import org.keycloak.services.resources.KeycloakApplication; + +/** + * @author Marek Posolda + */ +public class PicketlinkSessionFactoryTestContext implements SessionFactoryTestContext { + + @Override + public void beforeTestClass() { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void afterTestClass() { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void initEnvironment() { + System.setProperty(KeycloakApplication.SESSION_FACTORY, KeycloakApplication.SESSION_FACTORY_PICKETLINK); + } +} diff --git a/services/src/test/java/org/keycloak/test/common/SessionFactoryTestContext.java b/services/src/test/java/org/keycloak/test/common/SessionFactoryTestContext.java new file mode 100644 index 0000000000..3bbc4bd655 --- /dev/null +++ b/services/src/test/java/org/keycloak/test/common/SessionFactoryTestContext.java @@ -0,0 +1,19 @@ +package org.keycloak.test.common; + +import org.keycloak.services.models.KeycloakSessionFactory; + +/** + * @author Marek Posolda + */ +public interface SessionFactoryTestContext { + + void beforeTestClass(); + + void afterTestClass(); + + /** + * Init system properties (or other configuration) to ensure that KeycloakApplication.buildSessionFactory() will return correct + * instance of KeycloakSessionFactory for our test + */ + void initEnvironment(); +}