Moved model unit tests from services to model/tests . Mongo profile in testsuite

This commit is contained in:
mposolda 2014-02-10 17:36:46 +01:00
parent b3f1032f96
commit b9ff73d61c
65 changed files with 994 additions and 962 deletions

View file

@ -1,4 +1,4 @@
package org.keycloak.services.utils;
package org.keycloak.models.utils;
import java.util.ServiceLoader;

View file

@ -59,6 +59,20 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-tests</artifactId>
<version>${project.version}</version>
<classifier>tests</classifier>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
@ -70,6 +84,22 @@
<target>1.6</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<executions>
<execution>
<id>default-test</id>
<configuration>
<dependenciesToScan>
<dependency>org.keycloak:keycloak-model-tests</dependency>
</dependenciesToScan>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

View file

@ -46,17 +46,23 @@
<artifactId>mongo-java-driver</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-tests</artifactId>
<version>${project.version}</version>
<classifier>tests</classifier>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<keycloak.mongo.host>localhost</keycloak.mongo.host>
<keycloak.mongo.port>27018</keycloak.mongo.port>
<keycloak.mongo.db>keycloak</keycloak.mongo.db>
<keycloak.mongo.clearOnStartup>true</keycloak.mongo.clearOnStartup>
</properties>
<build>
<plugins>
<plugin>
@ -67,6 +73,66 @@
<target>1.6</target>
</configuration>
</plugin>
<!-- Postpone tests to "integration-test" phase, so that we can bootstrap embedded mongo on 27018 before running tests -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<executions>
<execution>
<id>test</id>
<phase>integration-test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<systemPropertyVariables>
<keycloak.mongo.host>${keycloak.mongo.host}</keycloak.mongo.host>
<keycloak.mongo.port>${keycloak.mongo.port}</keycloak.mongo.port>
<keycloak.mongo.db>${keycloak.mongo.db}</keycloak.mongo.db>
<keycloak.mongo.clearOnStartup>${keycloak.mongo.clearOnStartup}</keycloak.mongo.clearOnStartup>
</systemPropertyVariables>
<dependenciesToScan>
<dependency>org.keycloak:keycloak-model-tests</dependency>
</dependenciesToScan>
</configuration>
</execution>
<execution>
<id>default-test</id>
<configuration>
<skip>true</skip>
</configuration>
</execution>
</executions>
</plugin>
<!-- Embedded mongo -->
<plugin>
<groupId>com.github.joelittlejohn.embedmongo</groupId>
<artifactId>embedmongo-maven-plugin</artifactId>
<executions>
<execution>
<id>start-mongodb</id>
<phase>pre-integration-test</phase>
<goals>
<goal>start</goal>
</goals>
<configuration>
<port>${keycloak.mongo.port}</port>
<logging>file</logging>
<logFile>${project.build.directory}/mongodb.log</logFile>
</configuration>
</execution>
<execution>
<id>stop-mongodb</id>
<phase>post-integration-test</phase>
<goals>
<goal>stop</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View file

@ -18,7 +18,7 @@ public class AbstractMongoIdentifiableEntity implements MongoIdentifiableEntity
}
@Override
public void afterRemove(MongoStore mongoStore, MongoStoreInvocationContext invocationContext) {
public void afterRemove(MongoStoreInvocationContext invocationContext) {
// Empty by default
}

View file

@ -17,5 +17,5 @@ public interface MongoIdentifiableEntity extends MongoEntity {
* Lifecycle callback, which is called after removal of this object from Mongo.
* It may be useful for triggering removal of wired objects.
*/
void afterRemove(MongoStore mongoStore, MongoStoreInvocationContext invocationContext);
void afterRemove(MongoStoreInvocationContext invocationContext);
}

View file

@ -11,33 +11,33 @@ import java.util.List;
public interface MongoStore {
/**
* Insert new object
* Insert new entity
*
* @param object to update
* @param entity to insert
*/
void insertObject(MongoIdentifiableEntity object, MongoStoreInvocationContext context);
void insertEntity(MongoIdentifiableEntity entity, MongoStoreInvocationContext context);
/**
* Update existing object
* Update existing entity
*
* @param object to update
* @param entity to update
*/
void updateObject(MongoIdentifiableEntity object, MongoStoreInvocationContext context);
void updateEntity(MongoIdentifiableEntity entity, MongoStoreInvocationContext context);
<T extends MongoIdentifiableEntity> T loadObject(Class<T> type, String oid, MongoStoreInvocationContext context);
<T extends MongoIdentifiableEntity> T loadEntity(Class<T> type, String id, MongoStoreInvocationContext context);
<T extends MongoIdentifiableEntity> T loadSingleObject(Class<T> type, DBObject query, MongoStoreInvocationContext context);
<T extends MongoIdentifiableEntity> T loadSingleEntity(Class<T> type, DBObject query, MongoStoreInvocationContext context);
<T extends MongoIdentifiableEntity> List<T> loadObjects(Class<T> type, DBObject query, MongoStoreInvocationContext context);
<T extends MongoIdentifiableEntity> List<T> loadEntities(Class<T> type, DBObject query, MongoStoreInvocationContext context);
boolean removeObject(MongoIdentifiableEntity object, MongoStoreInvocationContext context);
boolean removeEntity(MongoIdentifiableEntity entity, MongoStoreInvocationContext context);
boolean removeObject(Class<? extends MongoIdentifiableEntity> type, String id, MongoStoreInvocationContext context);
boolean removeEntity(Class<? extends MongoIdentifiableEntity> type, String id, MongoStoreInvocationContext context);
boolean removeObjects(Class<? extends MongoIdentifiableEntity> type, DBObject query, MongoStoreInvocationContext context);
boolean removeEntities(Class<? extends MongoIdentifiableEntity> type, DBObject query, MongoStoreInvocationContext context);
<S> boolean pushItemToList(MongoIdentifiableEntity object, String listPropertyName, S itemToPush, boolean skipIfAlreadyPresent, MongoStoreInvocationContext context);
<S> boolean pushItemToList(MongoIdentifiableEntity entity, String listPropertyName, S itemToPush, boolean skipIfAlreadyPresent, MongoStoreInvocationContext context);
<S> boolean pullItemFromList(MongoIdentifiableEntity object, String listPropertyName, S itemToPull, MongoStoreInvocationContext context);
<S> boolean pullItemFromList(MongoIdentifiableEntity entity, String listPropertyName, S itemToPull, MongoStoreInvocationContext context);
}

View file

@ -1,12 +1,17 @@
package org.keycloak.models.mongo.api.context;
import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
import org.keycloak.models.mongo.api.MongoStore;
/**
* Context, which provides callback methods to be invoked by MongoStore
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public interface MongoStoreInvocationContext {
void addCreatedObject(MongoIdentifiableEntity entity);
void addLoadedObject(MongoIdentifiableEntity entity);
<T extends MongoIdentifiableEntity> T getLoadedObject(Class<T> type, String id);
@ -22,4 +27,6 @@ public interface MongoStoreInvocationContext {
void commit();
void rollback();
MongoStore getMongoStore();
}

View file

@ -2,21 +2,21 @@ package org.keycloak.models.mongo.api.types;
/**
* SPI object to convert object from application type to database type and vice versa. Shouldn't be directly used by application.
* Various converters should be registered in TypeConverter, which is main entry point to be used by application
* Various mappers should be registered in TypeMapper, which is main entry point to be used by application
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public interface Converter<T, S> {
public interface Mapper<T, S> {
/**
* Convert object from one type to expected type
*
* @param converterContext Encapsulates reference to converted object and other things, which might be helpful in conversion
* @param mapperContext Encapsulates reference to converted object and other things, which might be helpful in conversion
* @return converted object
*/
S convertObject(ConverterContext<T> converterContext);
S convertObject(MapperContext<T, S> mapperContext);
Class<? extends T> getConverterObjectType();
Class<? extends T> getTypeOfObjectToConvert();
Class<S> getExpectedReturnType();
}

View file

@ -5,18 +5,18 @@ import java.util.List;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class ConverterContext<T> {
public class MapperContext<T, S> {
// object to convert
private final T objectToConvert;
// expected return type, which could be useful information in some converters, so they are able to dynamically instantiate types
private final Class<?> expectedReturnType;
private final Class<? extends S> expectedReturnType;
// in case that expected return type is generic type (like "List<String>"), then genericTypes could contain list of expected generic arguments
private final List<Class<?>> genericTypes;
public ConverterContext(T objectToConvert, Class<?> expectedReturnType, List<Class<?>> genericTypes) {
public MapperContext(T objectToConvert, Class<? extends S> expectedReturnType, List<Class<?>> genericTypes) {
this.objectToConvert = objectToConvert;
this.expectedReturnType = expectedReturnType;
this.genericTypes = genericTypes;
@ -26,7 +26,7 @@ public class ConverterContext<T> {
return objectToConvert;
}
public Class<?> getExpectedReturnType() {
public Class<? extends S> getExpectedReturnType() {
return expectedReturnType;
}

View file

@ -0,0 +1,111 @@
package org.keycloak.models.mongo.api.types;
import java.util.HashMap;
import java.util.Map;
/**
* Registry of mappers, which allow to convert application object to database objects. MapperRegistry is main entry point to be used by application.
* Application can create instance of MapperRegistry and then register required Mapper objects.
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class MapperRegistry {
// TODO: Thread-safety support (maybe...)
// Mappers of Application objects to DB objects
private Map<Class<?>, Mapper<?, ?>> appObjectMappers = new HashMap<Class<?>, Mapper<?, ?>>();
// Mappers of DB objects to Application objects
private Map<Class<?>, Map<Class<?>, Mapper<?, ?>>> dbObjectMappers = new HashMap<Class<?>, Map<Class<?>, Mapper<?,?>>>();
/**
* Add mapper for converting application objects to DB objects
*
* @param mapper
*/
public void addAppObjectMapper(Mapper<?, ?> mapper) {
appObjectMappers.put(mapper.getTypeOfObjectToConvert(), mapper);
}
/**
* Add mapper for converting DB objects to application objects
*
* @param mapper
*/
public void addDBObjectMapper(Mapper<?, ?> mapper) {
Class<?> dbObjectType = mapper.getTypeOfObjectToConvert();
Class<?> appObjectType = mapper.getExpectedReturnType();
Map<Class<?>, Mapper<?, ?>> appObjects = dbObjectMappers.get(dbObjectType);
if (appObjects == null) {
appObjects = new HashMap<Class<?>, Mapper<?, ?>>();
dbObjectMappers.put(dbObjectType, appObjects);
}
appObjects.put(appObjectType, mapper);
}
public <S> S convertDBObjectToApplicationObject(MapperContext<Object, S> context) {
Object dbObject = context.getObjectToConvert();
Class<?> expectedApplicationObjectType = context.getExpectedReturnType();
Class<?> dbObjectType = dbObject.getClass();
Mapper<Object, S> mapper;
Map<Class<?>, Mapper<?, ?>> appObjects = dbObjectMappers.get(dbObjectType);
if (appObjects == null) {
throw new IllegalArgumentException("Not found any mappers for type " + dbObjectType);
} else {
if (appObjects.size() == 1) {
mapper = (Mapper<Object, S>)appObjects.values().iterator().next();
} else {
// Try to find converter for requested application type
mapper = (Mapper<Object, S>)getAppConverterForType(context.getExpectedReturnType(), appObjects);
}
}
if (mapper == null) {
throw new IllegalArgumentException("Can't found mapper for type " + dbObjectType + " and expectedApplicationType " + expectedApplicationObjectType);
}
return mapper.convertObject(context);
}
public <S> S convertApplicationObjectToDBObject(Object applicationObject, Class<S> expectedDBObjectType) {
Class<?> appObjectType = applicationObject.getClass();
Mapper<Object, S> mapper = (Mapper<Object, S>)getAppConverterForType(appObjectType, appObjectMappers);
if (mapper == null) {
throw new IllegalArgumentException("Can't found converter for type " + appObjectType + " in registered appObjectMappers");
}
if (!expectedDBObjectType.isAssignableFrom(mapper.getExpectedReturnType())) {
throw new IllegalArgumentException("Converter " + mapper + " has return type " + mapper.getExpectedReturnType() +
" but we need type " + expectedDBObjectType);
}
return mapper.convertObject(new MapperContext(applicationObject, expectedDBObjectType, null));
}
// Try to find converter for given type or all it's supertypes
private static Mapper<Object, ?> getAppConverterForType(Class<?> appObjectType, Map<Class<?>, Mapper<?, ?>> appObjectConverters) {
Mapper<Object, ?> mapper = (Mapper<Object, ?>)appObjectConverters.get(appObjectType);
if (mapper != null) {
return mapper;
} else {
Class<?>[] interfaces = appObjectType.getInterfaces();
for (Class<?> interface1 : interfaces) {
mapper = getAppConverterForType(interface1, appObjectConverters);
if (mapper != null) {
return mapper;
}
}
Class<?> superType = appObjectType.getSuperclass();
if (superType != null) {
return getAppConverterForType(superType, appObjectConverters);
} else {
return null;
}
}
}
}

View file

@ -1,115 +0,0 @@
package org.keycloak.models.mongo.api.types;
import java.util.HashMap;
import java.util.Map;
/**
* Registry of converters, which allow to convert application object to database objects. TypeConverter is main entry point to be used by application.
* Application can create instance of TypeConverter and then register required Converter objects.
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class TypeConverter {
// TODO: Thread-safety support (maybe...)
// Converters of Application objects to DB objects
private Map<Class<?>, Converter<?, ?>> appObjectConverters = new HashMap<Class<?>, Converter<?, ?>>();
// Converters of DB objects to Application objects
private Map<Class<?>, Map<Class<?>, Converter<?, ?>>> dbObjectConverters = new HashMap<Class<?>, Map<Class<?>, Converter<?,?>>>();
/**
* Add converter for converting application objects to DB objects
*
* @param converter
*/
public void addAppObjectConverter(Converter<?, ?> converter) {
appObjectConverters.put(converter.getConverterObjectType(), converter);
}
/**
* Add converter for converting DB objects to application objects
*
* @param converter
*/
public void addDBObjectConverter(Converter<?, ?> converter) {
Class<?> dbObjectType = converter.getConverterObjectType();
Class<?> appObjectType = converter.getExpectedReturnType();
Map<Class<?>, Converter<?, ?>> appObjects = dbObjectConverters.get(dbObjectType);
if (appObjects == null) {
appObjects = new HashMap<Class<?>, Converter<?, ?>>();
dbObjectConverters.put(dbObjectType, appObjects);
}
appObjects.put(appObjectType, converter);
}
public Object convertDBObjectToApplicationObject(ConverterContext<Object> context) {
Object dbObject = context.getObjectToConvert();
Class<?> expectedApplicationObjectType = context.getExpectedReturnType();
Class<?> dbObjectType = dbObject.getClass();
Converter<Object, Object> converter;
Map<Class<?>, Converter<?, ?>> appObjects = dbObjectConverters.get(dbObjectType);
if (appObjects == null) {
throw new IllegalArgumentException("Not found any converters for type " + dbObjectType);
} else {
if (appObjects.size() == 1) {
converter = (Converter<Object, Object>)appObjects.values().iterator().next();
} else {
// Try to find converter for requested application type
converter = (Converter<Object, Object>)getAppConverterForType(context.getExpectedReturnType(), appObjects);
}
}
if (converter == null) {
throw new IllegalArgumentException("Can't found converter for type " + dbObjectType + " and expectedApplicationType " + expectedApplicationObjectType);
}
/*if (!expectedApplicationObjectType.isAssignableFrom(converter.getExpectedReturnType())) {
throw new IllegalArgumentException("Converter " + converter + " has return type " + converter.getExpectedReturnType() +
" but we need type " + expectedApplicationObjectType);
} */
return converter.convertObject(context);
}
public <S> S convertApplicationObjectToDBObject(Object applicationObject, Class<S> expectedDBObjectType) {
Class<?> appObjectType = applicationObject.getClass();
Converter<Object, S> converter = (Converter<Object, S>)getAppConverterForType(appObjectType, appObjectConverters);
if (converter == null) {
throw new IllegalArgumentException("Can't found converter for type " + appObjectType + " in registered appObjectConverters");
}
if (!expectedDBObjectType.isAssignableFrom(converter.getExpectedReturnType())) {
throw new IllegalArgumentException("Converter " + converter + " has return type " + converter.getExpectedReturnType() +
" but we need type " + expectedDBObjectType);
}
return converter.convertObject(new ConverterContext(applicationObject, expectedDBObjectType, null));
}
// Try to find converter for given type or all it's supertypes
private static Converter<Object, ?> getAppConverterForType(Class<?> appObjectType, Map<Class<?>, Converter<?, ?>> appObjectConverters) {
Converter<Object, ?> converter = (Converter<Object, ?>)appObjectConverters.get(appObjectType);
if (converter != null) {
return converter;
} else {
Class<?>[] interfaces = appObjectType.getInterfaces();
for (Class<?> interface1 : interfaces) {
converter = getAppConverterForType(interface1, appObjectConverters);
if (converter != null) {
return converter;
}
}
Class<?> superType = appObjectType.getSuperclass();
if (superType != null) {
return getAppConverterForType(superType, appObjectConverters);
} else {
return null;
}
}
}
}

View file

@ -12,7 +12,7 @@ import java.util.Map;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class ObjectInfo {
public class EntityInfo {
private final Class<? extends MongoEntity> objectClass;
@ -20,7 +20,7 @@ public class ObjectInfo {
private final Map<String, Property<Object>> properties;
public ObjectInfo(Class<? extends MongoEntity> objectClass, String dbCollectionName, List<Property<Object>> properties) {
public EntityInfo(Class<? extends MongoEntity> objectClass, String dbCollectionName, List<Property<Object>> properties) {
this.objectClass = objectClass;
this.dbCollectionName = dbCollectionName;

View file

@ -15,18 +15,18 @@ import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
import org.keycloak.models.mongo.api.MongoStore;
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
import org.keycloak.models.mongo.api.context.MongoTask;
import org.keycloak.models.mongo.api.types.Converter;
import org.keycloak.models.mongo.api.types.ConverterContext;
import org.keycloak.models.mongo.api.types.TypeConverter;
import org.keycloak.models.mongo.impl.types.BasicDBListConverter;
import org.keycloak.models.mongo.impl.types.BasicDBObjectConverter;
import org.keycloak.models.mongo.impl.types.BasicDBObjectToMapConverter;
import org.keycloak.models.mongo.impl.types.EnumToStringConverter;
import org.keycloak.models.mongo.impl.types.ListConverter;
import org.keycloak.models.mongo.impl.types.MapConverter;
import org.keycloak.models.mongo.impl.types.MongoEntityConverter;
import org.keycloak.models.mongo.impl.types.SimpleConverter;
import org.keycloak.models.mongo.impl.types.StringToEnumConverter;
import org.keycloak.models.mongo.api.types.Mapper;
import org.keycloak.models.mongo.api.types.MapperContext;
import org.keycloak.models.mongo.api.types.MapperRegistry;
import org.keycloak.models.mongo.impl.types.BasicDBListMapper;
import org.keycloak.models.mongo.impl.types.BasicDBObjectMapper;
import org.keycloak.models.mongo.impl.types.BasicDBObjectToMapMapper;
import org.keycloak.models.mongo.impl.types.EnumToStringMapper;
import org.keycloak.models.mongo.impl.types.ListMapper;
import org.keycloak.models.mongo.impl.types.MapMapper;
import org.keycloak.models.mongo.impl.types.MongoEntityMapper;
import org.keycloak.models.mongo.impl.types.SimpleMapper;
import org.keycloak.models.mongo.impl.types.StringToEnumMapper;
import org.picketlink.common.properties.Property;
import org.picketlink.common.properties.query.AnnotatedPropertyCriteria;
import org.picketlink.common.properties.query.PropertyQueries;
@ -49,39 +49,39 @@ public class MongoStoreImpl implements MongoStore {
private final DB database;
private static final Logger logger = Logger.getLogger(MongoStoreImpl.class);
private final TypeConverter typeConverter;
private ConcurrentMap<Class<? extends MongoEntity>, ObjectInfo> objectInfoCache =
new ConcurrentHashMap<Class<? extends MongoEntity>, ObjectInfo>();
private final MapperRegistry mapperRegistry;
private ConcurrentMap<Class<? extends MongoEntity>, EntityInfo> entityInfoCache =
new ConcurrentHashMap<Class<? extends MongoEntity>, EntityInfo>();
public MongoStoreImpl(DB database, boolean clearCollectionsOnStartup, Class<? extends MongoEntity>[] managedEntityTypes) {
this.database = database;
typeConverter = new TypeConverter();
mapperRegistry = new MapperRegistry();
for (Class<?> simpleConverterClass : SIMPLE_TYPES) {
SimpleConverter converter = new SimpleConverter(simpleConverterClass);
typeConverter.addAppObjectConverter(converter);
typeConverter.addDBObjectConverter(converter);
SimpleMapper converter = new SimpleMapper(simpleConverterClass);
mapperRegistry.addAppObjectMapper(converter);
mapperRegistry.addDBObjectMapper(converter);
}
// Specific converter for ArrayList is added just for performance purposes to avoid recursive converter lookup (most of list impl will be ArrayList)
typeConverter.addAppObjectConverter(new ListConverter(typeConverter, ArrayList.class));
typeConverter.addAppObjectConverter(new ListConverter(typeConverter, List.class));
typeConverter.addDBObjectConverter(new BasicDBListConverter(typeConverter));
mapperRegistry.addAppObjectMapper(new ListMapper(mapperRegistry, ArrayList.class));
mapperRegistry.addAppObjectMapper(new ListMapper(mapperRegistry, List.class));
mapperRegistry.addDBObjectMapper(new BasicDBListMapper(mapperRegistry));
typeConverter.addAppObjectConverter(new MapConverter(HashMap.class));
typeConverter.addAppObjectConverter(new MapConverter(Map.class));
typeConverter.addDBObjectConverter(new BasicDBObjectToMapConverter());
mapperRegistry.addAppObjectMapper(new MapMapper(HashMap.class));
mapperRegistry.addAppObjectMapper(new MapMapper(Map.class));
mapperRegistry.addDBObjectMapper(new BasicDBObjectToMapMapper());
// Enum converters
typeConverter.addAppObjectConverter(new EnumToStringConverter());
typeConverter.addDBObjectConverter(new StringToEnumConverter());
mapperRegistry.addAppObjectMapper(new EnumToStringMapper());
mapperRegistry.addDBObjectMapper(new StringToEnumMapper());
for (Class<? extends MongoEntity> type : managedEntityTypes) {
getObjectInfo(type);
typeConverter.addAppObjectConverter(new MongoEntityConverter(this, typeConverter, type));
typeConverter.addDBObjectConverter(new BasicDBObjectConverter(this, typeConverter, type));
getEntityInfo(type);
mapperRegistry.addAppObjectMapper(new MongoEntityMapper(this, mapperRegistry, type));
mapperRegistry.addDBObjectMapper(new BasicDBObjectMapper(this, mapperRegistry, type));
}
if (clearCollectionsOnStartup) {
@ -107,18 +107,18 @@ public class MongoStoreImpl implements MongoStore {
}
@Override
public void insertObject(MongoIdentifiableEntity object, MongoStoreInvocationContext context) {
Class<? extends MongoEntity> clazz = object.getClass();
public void insertEntity(MongoIdentifiableEntity entity, MongoStoreInvocationContext context) {
Class<? extends MongoEntity> clazz = entity.getClass();
// Find annotations for ID, for all the properties and for the name of the collection.
ObjectInfo objectInfo = getObjectInfo(clazz);
EntityInfo entityInfo = getEntityInfo(clazz);
// Create instance of BasicDBObject and add all declared properties to it (properties with null value probably should be skipped)
BasicDBObject dbObject = typeConverter.convertApplicationObjectToDBObject(object, BasicDBObject.class);
BasicDBObject dbObject = mapperRegistry.convertApplicationObjectToDBObject(entity, BasicDBObject.class);
DBCollection dbCollection = database.getCollection(objectInfo.getDbCollectionName());
DBCollection dbCollection = database.getCollection(entityInfo.getDbCollectionName());
String currentId = object.getId();
String currentId = entity.getId();
// Inserting object, which already has oid property set. So we need to set "_id"
if (currentId != null) {
@ -129,28 +129,28 @@ public class MongoStoreImpl implements MongoStore {
// Add id to value of given object
if (currentId == null) {
object.setId(dbObject.getString("_id"));
entity.setId(dbObject.getString("_id"));
}
// Treat object as if it is read (It is already submited to transaction)
context.addLoadedObject(object);
context.addLoadedObject(entity);
}
@Override
public void updateObject(final MongoIdentifiableEntity object, MongoStoreInvocationContext context) {
public void updateEntity(final MongoIdentifiableEntity entity, MongoStoreInvocationContext context) {
MongoTask fullUpdateTask = new MongoTask() {
@Override
public void execute() {
Class<? extends MongoEntity> clazz = object.getClass();
ObjectInfo objectInfo = getObjectInfo(clazz);
BasicDBObject dbObject = typeConverter.convertApplicationObjectToDBObject(object, BasicDBObject.class);
DBCollection dbCollection = database.getCollection(objectInfo.getDbCollectionName());
Class<? extends MongoEntity> clazz = entity.getClass();
EntityInfo entityInfo = getEntityInfo(clazz);
BasicDBObject dbObject = mapperRegistry.convertApplicationObjectToDBObject(entity, BasicDBObject.class);
DBCollection dbCollection = database.getCollection(entityInfo.getDbCollectionName());
String currentId = object.getId();
String currentId = entity.getId();
if (currentId == null) {
throw new IllegalStateException("Can't update object without id: " + object);
throw new IllegalStateException("Can't update entity without id: " + entity);
} else {
BasicDBObject query = new BasicDBObject("_id", getObjectId(currentId));
dbCollection.update(query, dbObject);
@ -164,12 +164,12 @@ public class MongoStoreImpl implements MongoStore {
};
// update is just added to context and postponed
context.addUpdateTask(object, fullUpdateTask);
context.addUpdateTask(entity, fullUpdateTask);
}
@Override
public <T extends MongoIdentifiableEntity> T loadObject(Class<T> type, String id, MongoStoreInvocationContext context) {
public <T extends MongoIdentifiableEntity> T loadEntity(Class<T> type, String id, MongoStoreInvocationContext context) {
// First look if we already read the object with this oid and type during this transaction. If yes, use it instead of DB lookup
T cached = context.getLoadedObject(type, id);
if (cached != null) return cached;
@ -181,8 +181,8 @@ public class MongoStoreImpl implements MongoStore {
if (dbObject == null) return null;
ConverterContext<Object> converterContext = new ConverterContext<Object>(dbObject, type, null);
T converted = (T)typeConverter.convertDBObjectToApplicationObject(converterContext);
MapperContext<Object, T> mapperContext = new MapperContext<Object, T>(dbObject, type, null);
T converted = mapperRegistry.convertDBObjectToApplicationObject(mapperContext);
// Now add it to loaded objects
context.addLoadedObject(converted);
@ -192,8 +192,8 @@ public class MongoStoreImpl implements MongoStore {
@Override
public <T extends MongoIdentifiableEntity> T loadSingleObject(Class<T> type, DBObject query, MongoStoreInvocationContext context) {
List<T> result = loadObjects(type, query, context);
public <T extends MongoIdentifiableEntity> T loadSingleEntity(Class<T> type, DBObject query, MongoStoreInvocationContext context) {
List<T> result = loadEntities(type, query, context);
if (result.size() > 1) {
throw new IllegalStateException("There are " + result.size() + " results for type=" + type + ", query=" + query + ". We expect just one");
} else if (result.size() == 1) {
@ -206,7 +206,7 @@ public class MongoStoreImpl implements MongoStore {
@Override
public <T extends MongoIdentifiableEntity> List<T> loadObjects(Class<T> type, DBObject query, MongoStoreInvocationContext context) {
public <T extends MongoIdentifiableEntity> List<T> loadEntities(Class<T> type, DBObject query, MongoStoreInvocationContext context) {
// First we should execute all pending tasks before searching DB
context.beforeDBSearch(type);
@ -218,21 +218,21 @@ public class MongoStoreImpl implements MongoStore {
@Override
public boolean removeObject(MongoIdentifiableEntity object, MongoStoreInvocationContext context) {
return removeObject(object.getClass(), object.getId(), context);
public boolean removeEntity(MongoIdentifiableEntity entity, MongoStoreInvocationContext context) {
return removeEntity(entity.getClass(), entity.getId(), context);
}
@Override
public boolean removeObject(Class<? extends MongoIdentifiableEntity> type, String id, MongoStoreInvocationContext context) {
MongoIdentifiableEntity found = loadObject(type, id, context);
public boolean removeEntity(Class<? extends MongoIdentifiableEntity> type, String id, MongoStoreInvocationContext context) {
MongoIdentifiableEntity found = loadEntity(type, id, context);
if (found == null) {
return false;
} else {
DBCollection dbCollection = getDBCollectionForType(type);
BasicDBObject dbQuery = new BasicDBObject("_id", getObjectId(id));
dbCollection.remove(dbQuery);
logger.info("Object of type: " + type + ", id: " + id + " removed from MongoDB.");
logger.info("Entity of type: " + type + ", id: " + id + " removed from MongoDB.");
context.addRemovedObject(found);
return true;
@ -241,14 +241,14 @@ public class MongoStoreImpl implements MongoStore {
@Override
public boolean removeObjects(Class<? extends MongoIdentifiableEntity> type, DBObject query, MongoStoreInvocationContext context) {
List<? extends MongoIdentifiableEntity> foundObjects = loadObjects(type, query, context);
public boolean removeEntities(Class<? extends MongoIdentifiableEntity> type, DBObject query, MongoStoreInvocationContext context) {
List<? extends MongoIdentifiableEntity> foundObjects = loadEntities(type, query, context);
if (foundObjects.size() == 0) {
return false;
} else {
DBCollection dbCollection = getDBCollectionForType(type);
dbCollection.remove(query);
logger.info("Removed " + foundObjects.size() + " objects of type: " + type + ", query: " + query);
logger.info("Removed " + foundObjects.size() + " entities of type: " + type + ", query: " + query);
for (MongoIdentifiableEntity found : foundObjects) {
context.addRemovedObject(found);;
@ -258,20 +258,20 @@ public class MongoStoreImpl implements MongoStore {
}
@Override
public <S> boolean pushItemToList(final MongoIdentifiableEntity object, final String listPropertyName, S itemToPush, boolean skipIfAlreadyPresent, MongoStoreInvocationContext context) {
final Class<? extends MongoEntity> type = object.getClass();
ObjectInfo objectInfo = getObjectInfo(type);
public <S> boolean pushItemToList(final MongoIdentifiableEntity entity, final String listPropertyName, S itemToPush, boolean skipIfAlreadyPresent, MongoStoreInvocationContext context) {
final Class<? extends MongoEntity> type = entity.getClass();
EntityInfo entityInfo = getEntityInfo(type);
// Add item to list directly in this object
Property<Object> listProperty = objectInfo.getPropertyByName(listPropertyName);
Property<Object> listProperty = entityInfo.getPropertyByName(listPropertyName);
if (listProperty == null) {
throw new IllegalArgumentException("Property " + listPropertyName + " doesn't exist on object " + object);
throw new IllegalArgumentException("Property " + listPropertyName + " doesn't exist on object " + entity);
}
List<S> list = (List<S>)listProperty.getValue(object);
List<S> list = (List<S>)listProperty.getValue(entity);
if (list == null) {
list = new ArrayList<S>();
listProperty.setValue(object, list);
listProperty.setValue(entity, list);
}
// Skip if item is already in list
@ -284,14 +284,14 @@ public class MongoStoreImpl implements MongoStore {
// Add update of list to pending tasks
final List<S> listt = list;
context.addUpdateTask(object, new MongoTask() {
context.addUpdateTask(entity, new MongoTask() {
@Override
public void execute() {
// Now DB update of new list with usage of $set
BasicDBList dbList = typeConverter.convertApplicationObjectToDBObject(listt, BasicDBList.class);
BasicDBList dbList = mapperRegistry.convertApplicationObjectToDBObject(listt, BasicDBList.class);
BasicDBObject query = new BasicDBObject("_id", getObjectId(object.getId()));
BasicDBObject query = new BasicDBObject("_id", getObjectId(entity.getId()));
BasicDBObject listObject = new BasicDBObject(listPropertyName, dbList);
BasicDBObject setCommand = new BasicDBObject("$set", listObject);
getDBCollectionForType(type).update(query, setCommand);
@ -308,16 +308,16 @@ public class MongoStoreImpl implements MongoStore {
@Override
public <S> boolean pullItemFromList(final MongoIdentifiableEntity object, final String listPropertyName, final S itemToPull, MongoStoreInvocationContext context) {
final Class<? extends MongoEntity> type = object.getClass();
ObjectInfo objectInfo = getObjectInfo(type);
public <S> boolean pullItemFromList(final MongoIdentifiableEntity entity, final String listPropertyName, final S itemToPull, MongoStoreInvocationContext context) {
final Class<? extends MongoEntity> type = entity.getClass();
EntityInfo entityInfo = getEntityInfo(type);
// Remove item from list directly in this object
Property<Object> listProperty = objectInfo.getPropertyByName(listPropertyName);
Property<Object> listProperty = entityInfo.getPropertyByName(listPropertyName);
if (listProperty == null) {
throw new IllegalArgumentException("Property " + listPropertyName + " doesn't exist on object " + object);
throw new IllegalArgumentException("Property " + listPropertyName + " doesn't exist on object " + entity);
}
List<S> list = (List<S>)listProperty.getValue(object);
List<S> list = (List<S>)listProperty.getValue(entity);
// If list is null, we skip both object and DB update
if (list == null || !list.contains(itemToPull)) {
@ -328,13 +328,13 @@ public class MongoStoreImpl implements MongoStore {
list.remove(itemToPull);
// Add update of list to pending tasks
context.addUpdateTask(object, new MongoTask() {
context.addUpdateTask(entity, new MongoTask() {
@Override
public void execute() {
// Pull item from DB
Object dbItemToPull = typeConverter.convertApplicationObjectToDBObject(itemToPull, Object.class);
BasicDBObject query = new BasicDBObject("_id", getObjectId(object.getId()));
Object dbItemToPull = mapperRegistry.convertApplicationObjectToDBObject(itemToPull, Object.class);
BasicDBObject query = new BasicDBObject("_id", getObjectId(entity.getId()));
BasicDBObject pullObject = new BasicDBObject(listPropertyName, dbItemToPull);
BasicDBObject pullCommand = new BasicDBObject("$pull", pullObject);
getDBCollectionForType(type).update(query, pullCommand);
@ -351,31 +351,31 @@ public class MongoStoreImpl implements MongoStore {
}
// Possibility to add user-defined converters
public void addAppObjectConverter(Converter<?, ?> converter) {
typeConverter.addAppObjectConverter(converter);
public void addAppObjectConverter(Mapper<?, ?> mapper) {
mapperRegistry.addAppObjectMapper(mapper);
}
public void addDBObjectConverter(Converter<?, ?> converter) {
typeConverter.addDBObjectConverter(converter);
public void addDBObjectConverter(Mapper<?, ?> mapper) {
mapperRegistry.addDBObjectMapper(mapper);
}
public ObjectInfo getObjectInfo(Class<? extends MongoEntity> objectClass) {
ObjectInfo objectInfo = objectInfoCache.get(objectClass);
if (objectInfo == null) {
public EntityInfo getEntityInfo(Class<? extends MongoEntity> objectClass) {
EntityInfo entityInfo = entityInfoCache.get(objectClass);
if (entityInfo == null) {
List<Property<Object>> properties = PropertyQueries.createQuery(objectClass).addCriteria(new AnnotatedPropertyCriteria(MongoField.class)).getResultList();
MongoCollection classAnnotation = objectClass.getAnnotation(MongoCollection.class);
String dbCollectionName = classAnnotation==null ? null : classAnnotation.collectionName();
objectInfo = new ObjectInfo(objectClass, dbCollectionName, properties);
entityInfo = new EntityInfo(objectClass, dbCollectionName, properties);
ObjectInfo existing = objectInfoCache.putIfAbsent(objectClass, objectInfo);
EntityInfo existing = entityInfoCache.putIfAbsent(objectClass, entityInfo);
if (existing != null) {
objectInfo = existing;
entityInfo = existing;
}
}
return objectInfo;
return entityInfo;
}
protected <T extends MongoIdentifiableEntity> List<T> convertCursor(Class<T> type, DBCursor cursor, MongoStoreInvocationContext context) {
@ -389,8 +389,8 @@ public class MongoStoreImpl implements MongoStore {
if (object == null) {
// So convert and use fresh instance from DB
ConverterContext<Object> converterContext = new ConverterContext<Object>(dbObject, type, null);
object = (T)typeConverter.convertDBObjectToApplicationObject(converterContext);
MapperContext<Object, T> mapperContext = new MapperContext<Object, T>(dbObject, type, null);
object = mapperRegistry.convertDBObjectToApplicationObject(mapperContext);
context.addLoadedObject(object);
}
@ -404,9 +404,9 @@ public class MongoStoreImpl implements MongoStore {
}
protected DBCollection getDBCollectionForType(Class<? extends MongoEntity> type) {
ObjectInfo objectInfo = getObjectInfo(type);
String dbCollectionName = objectInfo.getDbCollectionName();
return dbCollectionName==null ? null : database.getCollection(objectInfo.getDbCollectionName());
EntityInfo entityInfo = getEntityInfo(type);
String dbCollectionName = entityInfo.getDbCollectionName();
return dbCollectionName==null ? null : database.getCollection(entityInfo.getDbCollectionName());
}
// We allow ObjectId to be both "ObjectId" or "String".

View file

@ -13,10 +13,14 @@ import org.keycloak.models.mongo.api.context.MongoTask;
*/
public class SimpleMongoStoreInvocationContext implements MongoStoreInvocationContext {
private final MongoStore store;
private final MongoStore mongoStore;
public SimpleMongoStoreInvocationContext(MongoStore store) {
this.store = store;
public SimpleMongoStoreInvocationContext(MongoStore mongoStore) {
this.mongoStore = mongoStore;
}
@Override
public void addCreatedObject(MongoIdentifiableEntity entity) {
}
@Override
@ -35,7 +39,7 @@ public class SimpleMongoStoreInvocationContext implements MongoStoreInvocationCo
@Override
public void addRemovedObject(MongoIdentifiableEntity entityToRemove) {
entityToRemove.afterRemove(store, this);
entityToRemove.afterRemove(this);
}
@Override
@ -53,4 +57,9 @@ public class SimpleMongoStoreInvocationContext implements MongoStoreInvocationCo
@Override
public void rollback() {
}
@Override
public MongoStore getMongoStore() {
return mongoStore;
}
}

View file

@ -2,6 +2,7 @@ package org.keycloak.models.mongo.impl.context;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
@ -31,6 +32,12 @@ public class TransactionMongoStoreInvocationContext implements MongoStoreInvocat
this.mongoStore = mongoStore;
}
@Override
public void addCreatedObject(MongoIdentifiableEntity entity) {
// For now just add it to list of loaded objects
addLoadedObject(entity);
}
@Override
public void addLoadedObject(MongoIdentifiableEntity entity) {
loadedObjects.put(entity.getId(), entity);
@ -49,7 +56,7 @@ public class TransactionMongoStoreInvocationContext implements MongoStoreInvocat
Set<MongoTask> currentObjectTasks = pendingUpdateTasks.get(entityToUpdate);
if (currentObjectTasks == null) {
currentObjectTasks = new HashSet<MongoTask>();
currentObjectTasks = new LinkedHashSet<MongoTask>();
pendingUpdateTasks.put(entityToUpdate, currentObjectTasks);
} else {
// if task is full update, then remove all other tasks as we need to do full update of object anyway
@ -74,7 +81,7 @@ public class TransactionMongoStoreInvocationContext implements MongoStoreInvocat
pendingUpdateTasks.remove(entityToRemove);
loadedObjects.remove(entityToRemove.getId());
entityToRemove.afterRemove(mongoStore, this);
entityToRemove.afterRemove(this);
}
@Override
@ -107,8 +114,6 @@ public class TransactionMongoStoreInvocationContext implements MongoStoreInvocat
@Override
public void commit() {
loadedObjects.clear();
// Now execute all pending update tasks
for (Set<MongoTask> mongoTasks : pendingUpdateTasks.values()) {
for (MongoTask currentTask : mongoTasks) {
@ -117,13 +122,19 @@ public class TransactionMongoStoreInvocationContext implements MongoStoreInvocat
}
// And clear it
loadedObjects.clear();
pendingUpdateTasks.clear();
}
@Override
public void rollback() {
// Just clear the map without executions of tasks
// Just clear the map without executions of tasks TODO: Attempt to do complete rollback (removal of created objects, restoring of removed objects, rollback of updates)
loadedObjects.clear();
pendingUpdateTasks.clear();
}
@Override
public MongoStore getMongoStore() {
return mongoStore;
}
}

View file

@ -1,44 +0,0 @@
package org.keycloak.models.mongo.impl.types;
import com.mongodb.BasicDBList;
import org.keycloak.models.mongo.api.types.Converter;
import org.keycloak.models.mongo.api.types.ConverterContext;
import org.keycloak.models.mongo.api.types.TypeConverter;
import java.util.ArrayList;
import java.util.List;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class BasicDBListConverter implements Converter<BasicDBList, List> {
private final TypeConverter typeConverter;
public BasicDBListConverter(TypeConverter typeConverter) {
this.typeConverter = typeConverter;
}
@Override
public List convertObject(ConverterContext<BasicDBList> context) {
BasicDBList dbList = context.getObjectToConvert();
ArrayList<Object> appObjects = new ArrayList<Object>();
Class<?> expectedListElementType = context.getGenericTypes().get(0);
for (Object dbObject : dbList) {
ConverterContext<Object> newContext = new ConverterContext<Object>(dbObject, expectedListElementType, null);
appObjects.add(typeConverter.convertDBObjectToApplicationObject(newContext));
}
return appObjects;
}
@Override
public Class<? extends BasicDBList> getConverterObjectType() {
return BasicDBList.class;
}
@Override
public Class<List> getExpectedReturnType() {
return List.class;
}
}

View file

@ -0,0 +1,44 @@
package org.keycloak.models.mongo.impl.types;
import com.mongodb.BasicDBList;
import org.keycloak.models.mongo.api.types.Mapper;
import org.keycloak.models.mongo.api.types.MapperContext;
import org.keycloak.models.mongo.api.types.MapperRegistry;
import java.util.ArrayList;
import java.util.List;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class BasicDBListMapper implements Mapper<BasicDBList, List> {
private final MapperRegistry mapperRegistry;
public BasicDBListMapper(MapperRegistry mapperRegistry) {
this.mapperRegistry = mapperRegistry;
}
@Override
public List convertObject(MapperContext<BasicDBList, List> context) {
BasicDBList dbList = context.getObjectToConvert();
ArrayList<Object> appObjects = new ArrayList<Object>();
Class<?> expectedListElementType = context.getGenericTypes().get(0);
for (Object dbObject : dbList) {
MapperContext<Object, Object> newContext = new MapperContext<Object, Object>(dbObject, expectedListElementType, null);
appObjects.add(mapperRegistry.convertDBObjectToApplicationObject(newContext));
}
return appObjects;
}
@Override
public Class<? extends BasicDBList> getTypeOfObjectToConvert() {
return BasicDBList.class;
}
@Override
public Class<List> getExpectedReturnType() {
return List.class;
}
}

View file

@ -9,39 +9,39 @@ import com.mongodb.BasicDBObject;
import org.jboss.logging.Logger;
import org.keycloak.models.mongo.api.MongoEntity;
import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
import org.keycloak.models.mongo.api.types.Converter;
import org.keycloak.models.mongo.api.types.ConverterContext;
import org.keycloak.models.mongo.api.types.TypeConverter;
import org.keycloak.models.mongo.api.types.Mapper;
import org.keycloak.models.mongo.api.types.MapperContext;
import org.keycloak.models.mongo.api.types.MapperRegistry;
import org.keycloak.models.mongo.impl.MongoStoreImpl;
import org.keycloak.models.mongo.impl.ObjectInfo;
import org.keycloak.models.mongo.impl.EntityInfo;
import org.picketlink.common.properties.Property;
import org.picketlink.common.reflection.Types;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class BasicDBObjectConverter<S extends MongoEntity> implements Converter<BasicDBObject, S> {
public class BasicDBObjectMapper<S extends MongoEntity> implements Mapper<BasicDBObject, S> {
private static final Logger logger = Logger.getLogger(BasicDBObjectConverter.class);
private static final Logger logger = Logger.getLogger(BasicDBObjectMapper.class);
private final MongoStoreImpl mongoStoreImpl;
private final TypeConverter typeConverter;
private final MapperRegistry mapperRegistry;
private final Class<S> expectedObjectType;
public BasicDBObjectConverter(MongoStoreImpl mongoStoreImpl, TypeConverter typeConverter, Class<S> expectedObjectType) {
public BasicDBObjectMapper(MongoStoreImpl mongoStoreImpl, MapperRegistry mapperRegistry, Class<S> expectedObjectType) {
this.mongoStoreImpl = mongoStoreImpl;
this.typeConverter = typeConverter;
this.mapperRegistry = mapperRegistry;
this.expectedObjectType = expectedObjectType;
}
@Override
public S convertObject(ConverterContext<BasicDBObject> context) {
public S convertObject(MapperContext<BasicDBObject, S> context) {
BasicDBObject dbObject = context.getObjectToConvert();
if (dbObject == null) {
return null;
}
ObjectInfo objectInfo = mongoStoreImpl.getObjectInfo(expectedObjectType);
EntityInfo entityInfo = mongoStoreImpl.getEntityInfo(expectedObjectType);
S object;
try {
@ -60,7 +60,7 @@ public class BasicDBObjectConverter<S extends MongoEntity> implements Converter<
((MongoIdentifiableEntity)object).setId(value.toString());
}
} else if ((property = objectInfo.getPropertyByName(key)) != null) {
} else if ((property = entityInfo.getPropertyByName(key)) != null) {
// It's declared property with @DBField annotation
setPropertyValue(object, value, property);
@ -79,7 +79,7 @@ public class BasicDBObjectConverter<S extends MongoEntity> implements Converter<
return;
}
ConverterContext<Object> context;
MapperContext<Object, ?> context;
Type type = property.getBaseType();
@ -94,15 +94,15 @@ public class BasicDBObjectConverter<S extends MongoEntity> implements Converter<
}
Class<?> expectedReturnType = (Class<?>)parameterized.getRawType();
context = new ConverterContext<Object>(valueFromDB, expectedReturnType, genericTypes);
context = new MapperContext<Object, Object>(valueFromDB, expectedReturnType, genericTypes);
} else {
Class<?> expectedReturnType = (Class<?>)type;
// handle primitives
expectedReturnType = Types.boxedClass(expectedReturnType);
context = new ConverterContext<Object>(valueFromDB, expectedReturnType, null);
context = new MapperContext<Object, Object>(valueFromDB, expectedReturnType, null);
}
Object appObject = typeConverter.convertDBObjectToApplicationObject(context);
Object appObject = mapperRegistry.convertDBObjectToApplicationObject(context);
if (Types.boxedClass(property.getJavaClass()).isAssignableFrom(appObject.getClass())) {
property.setValue(object, appObject);
@ -113,7 +113,7 @@ public class BasicDBObjectConverter<S extends MongoEntity> implements Converter<
}
@Override
public Class<? extends BasicDBObject> getConverterObjectType() {
public Class<? extends BasicDBObject> getTypeOfObjectToConvert() {
return BasicDBObject.class;
}

View file

@ -4,18 +4,18 @@ import java.util.HashMap;
import java.util.Map;
import com.mongodb.BasicDBObject;
import org.keycloak.models.mongo.api.types.Converter;
import org.keycloak.models.mongo.api.types.ConverterContext;
import org.keycloak.models.mongo.api.types.Mapper;
import org.keycloak.models.mongo.api.types.MapperContext;
/**
* For now, we support just convert to Map<String, String>
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class BasicDBObjectToMapConverter implements Converter<BasicDBObject, Map> {
public class BasicDBObjectToMapMapper implements Mapper<BasicDBObject, Map> {
@Override
public Map convertObject(ConverterContext<BasicDBObject> context) {
public Map convertObject(MapperContext<BasicDBObject, Map> context) {
BasicDBObject objectToConvert = context.getObjectToConvert();
HashMap<String, String> result = new HashMap<String, String>();
@ -23,8 +23,8 @@ public class BasicDBObjectToMapConverter implements Converter<BasicDBObject, Map
String key = entry.getKey();
String value = (String)entry.getValue();
if (key.contains(MapConverter.DOT_PLACEHOLDER)) {
key = key.replaceAll(MapConverter.DOT_PLACEHOLDER, ".");
if (key.contains(MapMapper.DOT_PLACEHOLDER)) {
key = key.replaceAll(MapMapper.DOT_PLACEHOLDER, ".");
}
result.put(key, value);
@ -33,7 +33,7 @@ public class BasicDBObjectToMapConverter implements Converter<BasicDBObject, Map
}
@Override
public Class<? extends BasicDBObject> getConverterObjectType() {
public Class<? extends BasicDBObject> getTypeOfObjectToConvert() {
return BasicDBObject.class;
}

View file

@ -1,23 +1,23 @@
package org.keycloak.models.mongo.impl.types;
import org.keycloak.models.mongo.api.types.Converter;
import org.keycloak.models.mongo.api.types.ConverterContext;
import org.keycloak.models.mongo.api.types.Mapper;
import org.keycloak.models.mongo.api.types.MapperContext;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class EnumToStringConverter implements Converter<Enum, String> {
public class EnumToStringMapper implements Mapper<Enum, String> {
// It will be saved in form of "org.keycloak.Gender#MALE" so it's possible to parse enumType out of it
@Override
public String convertObject(ConverterContext<Enum> context) {
public String convertObject(MapperContext<Enum, String> context) {
Enum objectToConvert = context.getObjectToConvert();
return objectToConvert.toString();
}
@Override
public Class<? extends Enum> getConverterObjectType() {
public Class<? extends Enum> getTypeOfObjectToConvert() {
return Enum.class;
}

View file

@ -1,46 +0,0 @@
package org.keycloak.models.mongo.impl.types;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import org.keycloak.models.mongo.api.types.Converter;
import org.keycloak.models.mongo.api.types.ConverterContext;
import org.keycloak.models.mongo.api.types.TypeConverter;
import java.util.List;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class ListConverter<T extends List> implements Converter<T, BasicDBList> {
private final TypeConverter typeConverter;
private final Class<T> listType;
public ListConverter(TypeConverter typeConverter, Class<T> listType) {
this.typeConverter = typeConverter;
this.listType = listType;
}
@Override
public BasicDBList convertObject(ConverterContext<T> context) {
T appObjectsList = context.getObjectToConvert();
BasicDBList dbObjects = new BasicDBList();
for (Object appObject : appObjectsList) {
Object dbObject = typeConverter.convertApplicationObjectToDBObject(appObject, Object.class);
dbObjects.add(dbObject);
}
return dbObjects;
}
@Override
public Class<? extends T> getConverterObjectType() {
return listType;
}
@Override
public Class<BasicDBList> getExpectedReturnType() {
return BasicDBList.class;
}
}

View file

@ -0,0 +1,45 @@
package org.keycloak.models.mongo.impl.types;
import com.mongodb.BasicDBList;
import org.keycloak.models.mongo.api.types.Mapper;
import org.keycloak.models.mongo.api.types.MapperContext;
import org.keycloak.models.mongo.api.types.MapperRegistry;
import java.util.List;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class ListMapper<T extends List> implements Mapper<T, BasicDBList> {
private final MapperRegistry mapperRegistry;
private final Class<T> listType;
public ListMapper(MapperRegistry mapperRegistry, Class<T> listType) {
this.mapperRegistry = mapperRegistry;
this.listType = listType;
}
@Override
public BasicDBList convertObject(MapperContext<T, BasicDBList> context) {
T appObjectsList = context.getObjectToConvert();
BasicDBList dbObjects = new BasicDBList();
for (Object appObject : appObjectsList) {
Object dbObject = mapperRegistry.convertApplicationObjectToDBObject(appObject, Object.class);
dbObjects.add(dbObject);
}
return dbObjects;
}
@Override
public Class<? extends T> getTypeOfObjectToConvert() {
return listType;
}
@Override
public Class<BasicDBList> getExpectedReturnType() {
return BasicDBList.class;
}
}

View file

@ -4,25 +4,27 @@ import java.util.Map;
import java.util.Set;
import com.mongodb.BasicDBObject;
import org.keycloak.models.mongo.api.types.Converter;
import org.keycloak.models.mongo.api.types.ConverterContext;
import org.keycloak.models.mongo.api.types.Mapper;
import org.keycloak.models.mongo.api.types.MapperContext;
/**
* For now, we support just convert from Map<String, String>
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class MapConverter<T extends Map> implements Converter<T, BasicDBObject> {
public class MapMapper<T extends Map> implements Mapper<T, BasicDBObject> {
// Just some dummy way of encoding . character as it's not allowed by mongo in key fields
static final String DOT_PLACEHOLDER = "###";
private final Class<T> mapType;
public MapConverter(Class<T> mapType) {
public MapMapper(Class<T> mapType) {
this.mapType = mapType;
}
@Override
public BasicDBObject convertObject(ConverterContext<T> context) {
public BasicDBObject convertObject(MapperContext<T, BasicDBObject> context) {
T objectToConvert = context.getObjectToConvert();
BasicDBObject dbObject = new BasicDBObject();
@ -41,7 +43,7 @@ public class MapConverter<T extends Map> implements Converter<T, BasicDBObject>
}
@Override
public Class<? extends T> getConverterObjectType() {
public Class<? extends T> getTypeOfObjectToConvert() {
return mapType;
}

View file

@ -2,11 +2,11 @@ package org.keycloak.models.mongo.impl.types;
import com.mongodb.BasicDBObject;
import org.keycloak.models.mongo.api.MongoEntity;
import org.keycloak.models.mongo.api.types.Converter;
import org.keycloak.models.mongo.api.types.ConverterContext;
import org.keycloak.models.mongo.api.types.TypeConverter;
import org.keycloak.models.mongo.api.types.Mapper;
import org.keycloak.models.mongo.api.types.MapperContext;
import org.keycloak.models.mongo.api.types.MapperRegistry;
import org.keycloak.models.mongo.impl.MongoStoreImpl;
import org.keycloak.models.mongo.impl.ObjectInfo;
import org.keycloak.models.mongo.impl.EntityInfo;
import org.picketlink.common.properties.Property;
import java.util.Collection;
@ -14,32 +14,32 @@ import java.util.Collection;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class MongoEntityConverter<T extends MongoEntity> implements Converter<T, BasicDBObject> {
public class MongoEntityMapper<T extends MongoEntity> implements Mapper<T, BasicDBObject> {
private final MongoStoreImpl mongoStoreImpl;
private final TypeConverter typeConverter;
private final Class<T> expectedNoSQLObjectType;
private final MapperRegistry mapperRegistry;
private final Class<T> expectedMongoEntityType;
public MongoEntityConverter(MongoStoreImpl mongoStoreImpl, TypeConverter typeConverter, Class<T> expectedNoSQLObjectType) {
public MongoEntityMapper(MongoStoreImpl mongoStoreImpl, MapperRegistry mapperRegistry, Class<T> expectedMongoEntityType) {
this.mongoStoreImpl = mongoStoreImpl;
this.typeConverter = typeConverter;
this.expectedNoSQLObjectType = expectedNoSQLObjectType;
this.mapperRegistry = mapperRegistry;
this.expectedMongoEntityType = expectedMongoEntityType;
}
@Override
public BasicDBObject convertObject(ConverterContext<T> context) {
public BasicDBObject convertObject(MapperContext<T, BasicDBObject> context) {
T applicationObject = context.getObjectToConvert();
ObjectInfo objectInfo = mongoStoreImpl.getObjectInfo(applicationObject.getClass());
EntityInfo entityInfo = mongoStoreImpl.getEntityInfo(applicationObject.getClass());
// Create instance of BasicDBObject and add all declared properties to it (properties with null value probably should be skipped)
BasicDBObject dbObject = new BasicDBObject();
Collection<Property<Object>> props = objectInfo.getProperties();
Collection<Property<Object>> props = entityInfo.getProperties();
for (Property<Object> property : props) {
String propName = property.getName();
Object propValue = property.getValue(applicationObject);
Object dbValue = propValue == null ? null : typeConverter.convertApplicationObjectToDBObject(propValue, Object.class);
Object dbValue = propValue == null ? null : mapperRegistry.convertApplicationObjectToDBObject(propValue, Object.class);
dbObject.put(propName, dbValue);
}
@ -47,8 +47,8 @@ public class MongoEntityConverter<T extends MongoEntity> implements Converter<T,
}
@Override
public Class<? extends T> getConverterObjectType() {
return expectedNoSQLObjectType;
public Class<? extends T> getTypeOfObjectToConvert() {
return expectedMongoEntityType;
}
@Override

View file

@ -1,29 +1,29 @@
package org.keycloak.models.mongo.impl.types;
import org.keycloak.models.mongo.api.types.Converter;
import org.keycloak.models.mongo.api.types.ConverterContext;
import org.keycloak.models.mongo.api.types.Mapper;
import org.keycloak.models.mongo.api.types.MapperContext;
/**
* Just returns input
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class SimpleConverter<T> implements Converter<T, T> {
public class SimpleMapper<T> implements Mapper<T, T> {
private final Class<T> expectedType;
public SimpleConverter(Class<T> expectedType) {
public SimpleMapper(Class<T> expectedType) {
this.expectedType = expectedType;
}
@Override
public T convertObject(ConverterContext<T> context) {
public T convertObject(MapperContext<T, T> context) {
T objectToConvert = context.getObjectToConvert();
return objectToConvert;
}
@Override
public Class<? extends T> getConverterObjectType() {
public Class<? extends T> getTypeOfObjectToConvert() {
return expectedType;
}

View file

@ -1,28 +0,0 @@
package org.keycloak.models.mongo.impl.types;
import org.keycloak.models.mongo.api.types.Converter;
import org.keycloak.models.mongo.api.types.ConverterContext;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class StringToEnumConverter implements Converter<String, Enum> {
@Override
public Enum convertObject(ConverterContext<String> context) {
String enumValue = context.getObjectToConvert();
Class<? extends Enum> clazz = (Class<? extends Enum>)context.getExpectedReturnType();
return Enum.valueOf(clazz, enumValue);
}
@Override
public Class<? extends String> getConverterObjectType() {
return String.class;
}
@Override
public Class<Enum> getExpectedReturnType() {
return Enum.class;
}
}

View file

@ -0,0 +1,28 @@
package org.keycloak.models.mongo.impl.types;
import org.keycloak.models.mongo.api.types.Mapper;
import org.keycloak.models.mongo.api.types.MapperContext;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class StringToEnumMapper implements Mapper<String, Enum> {
@Override
public Enum convertObject(MapperContext<String, Enum> context) {
String enumValue = context.getObjectToConvert();
Class<? extends Enum> clazz = context.getExpectedReturnType();
return Enum.valueOf(clazz, enumValue);
}
@Override
public Class<? extends String> getTypeOfObjectToConvert() {
return String.class;
}
@Override
public Class<Enum> getExpectedReturnType() {
return Enum.class;
}
}

View file

@ -9,11 +9,9 @@ import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
*/
public abstract class AbstractAdapter {
protected MongoStore mongoStore;
protected MongoStoreInvocationContext invocationContext;
public AbstractAdapter(MongoStore mongoStore, MongoStoreInvocationContext invocationContext) {
this.mongoStore = mongoStore;
public AbstractAdapter(MongoStoreInvocationContext invocationContext) {
this.invocationContext = invocationContext;
}
@ -35,4 +33,8 @@ public abstract class AbstractAdapter {
public int hashCode() {
return getMongoEntity()!=null ? getMongoEntity().hashCode() : super.hashCode();
}
protected MongoStore getMongoStore() {
return invocationContext.getMongoStore();
}
}

View file

@ -6,7 +6,6 @@ import org.keycloak.models.ApplicationModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.mongo.api.AbstractMongoIdentifiableEntity;
import org.keycloak.models.mongo.api.MongoStore;
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
import org.keycloak.models.mongo.keycloak.entities.ApplicationEntity;
import org.keycloak.models.mongo.keycloak.entities.RoleEntity;
@ -26,30 +25,30 @@ public class ApplicationAdapter extends AbstractAdapter implements ApplicationMo
private final ApplicationEntity application;
private UserAdapter resourceUser;
public ApplicationAdapter(ApplicationEntity applicationEntity, MongoStore mongoStore, MongoStoreInvocationContext invContext) {
this(applicationEntity, null, mongoStore, invContext);
public ApplicationAdapter(ApplicationEntity applicationEntity, MongoStoreInvocationContext invContext) {
this(applicationEntity, null, invContext);
}
public ApplicationAdapter(ApplicationEntity applicationEntity, UserAdapter resourceUser, MongoStore mongoStore, MongoStoreInvocationContext invContext) {
super(mongoStore, invContext);
public ApplicationAdapter(ApplicationEntity applicationEntity, UserAdapter resourceUser, MongoStoreInvocationContext invContext) {
super(invContext);
this.application = applicationEntity;
this.resourceUser = resourceUser;
}
@Override
public void updateApplication() {
mongoStore.updateObject(application, invocationContext);
getMongoStore().updateEntity(application, invocationContext);
}
@Override
public UserAdapter getApplicationUser() {
// This is not thread-safe. Assumption is that ApplicationAdapter instance is per-client object
if (resourceUser == null) {
UserEntity userEntity = mongoStore.loadObject(UserEntity.class, application.getResourceUserId(), invocationContext);
UserEntity userEntity = getMongoStore().loadEntity(UserEntity.class, application.getResourceUserId(), invocationContext);
if (userEntity == null) {
throw new IllegalStateException("User " + application.getResourceUserId() + " not found");
}
resourceUser = new UserAdapter(userEntity, mongoStore, invocationContext);
resourceUser = new UserAdapter(userEntity, invocationContext);
}
return resourceUser;
@ -116,21 +115,21 @@ public class ApplicationAdapter extends AbstractAdapter implements ApplicationMo
.and("name").is(name)
.and("applicationId").is(getId())
.get();
RoleEntity role = mongoStore.loadSingleObject(RoleEntity.class, query, invocationContext);
RoleEntity role = getMongoStore().loadSingleEntity(RoleEntity.class, query, invocationContext);
if (role == null) {
return null;
} else {
return new RoleAdapter(role, this, mongoStore, invocationContext);
return new RoleAdapter(role, invocationContext);
}
}
@Override
public RoleModel getRoleById(String id) {
RoleEntity role = mongoStore.loadObject(RoleEntity.class, id, invocationContext);
RoleEntity role = getMongoStore().loadEntity(RoleEntity.class, id, invocationContext);
if (role == null) {
return null;
} else {
return new RoleAdapter(role, this, mongoStore, invocationContext);
return new RoleAdapter(role, this, invocationContext);
}
}
@ -145,13 +144,13 @@ public class ApplicationAdapter extends AbstractAdapter implements ApplicationMo
roleEntity.setName(name);
roleEntity.setApplicationId(getId());
mongoStore.insertObject(roleEntity, invocationContext);
return new RoleAdapter(roleEntity, this, mongoStore, invocationContext);
getMongoStore().insertEntity(roleEntity, invocationContext);
return new RoleAdapter(roleEntity, this, invocationContext);
}
@Override
public boolean removeRoleById(String id) {
return mongoStore.removeObject(RoleEntity.class ,id, invocationContext);
return getMongoStore().removeEntity(RoleEntity.class, id, invocationContext);
}
@Override
@ -159,11 +158,11 @@ public class ApplicationAdapter extends AbstractAdapter implements ApplicationMo
DBObject query = new QueryBuilder()
.and("applicationId").is(getId())
.get();
List<RoleEntity> roles = mongoStore.loadObjects(RoleEntity.class, query, invocationContext);
List<RoleEntity> roles = getMongoStore().loadEntities(RoleEntity.class, query, invocationContext);
Set<RoleModel> result = new HashSet<RoleModel>();
for (RoleEntity role : roles) {
result.add(new RoleAdapter(role, this, mongoStore, invocationContext));
result.add(new RoleAdapter(role, this, invocationContext));
}
return result;
@ -172,11 +171,11 @@ public class ApplicationAdapter extends AbstractAdapter implements ApplicationMo
@Override
public Set<RoleModel> getApplicationRoleMappings(UserModel user) {
Set<RoleModel> result = new HashSet<RoleModel>();
List<RoleEntity> roles = MongoModelUtils.getAllRolesOfUser(user, mongoStore, invocationContext);
List<RoleEntity> roles = MongoModelUtils.getAllRolesOfUser(user, invocationContext);
for (RoleEntity role : roles) {
if (getId().equals(role.getApplicationId())) {
result.add(new RoleAdapter(role, this, mongoStore, invocationContext));
result.add(new RoleAdapter(role, this, invocationContext));
}
}
return result;
@ -185,17 +184,17 @@ public class ApplicationAdapter extends AbstractAdapter implements ApplicationMo
@Override
public void addScope(RoleModel role) {
UserAdapter appUser = getApplicationUser();
mongoStore.pushItemToList(appUser.getUser(), "scopeIds", role.getId(), true, invocationContext);
getMongoStore().pushItemToList(appUser.getUser(), "scopeIds", role.getId(), true, invocationContext);
}
@Override
public Set<RoleModel> getApplicationScopeMappings(UserModel user) {
Set<RoleModel> result = new HashSet<RoleModel>();
List<RoleEntity> roles = MongoModelUtils.getAllScopesOfUser(user, mongoStore, invocationContext);
List<RoleEntity> roles = MongoModelUtils.getAllScopesOfUser(user, invocationContext);
for (RoleEntity role : roles) {
if (getId().equals(role.getApplicationId())) {
result.add(new RoleAdapter(role, this, mongoStore, invocationContext));
result.add(new RoleAdapter(role, this, invocationContext));
}
}
return result;
@ -213,7 +212,7 @@ public class ApplicationAdapter extends AbstractAdapter implements ApplicationMo
addRole(name);
}
mongoStore.pushItemToList(application, "defaultRoles", name, true, invocationContext);
getMongoStore().pushItemToList(application, "defaultRoles", name, true, invocationContext);
}
@Override

View file

@ -9,7 +9,6 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.mongo.api.MongoStore;
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
import org.keycloak.models.mongo.impl.context.SimpleMongoStoreInvocationContext;
import org.keycloak.models.mongo.impl.context.TransactionMongoStoreInvocationContext;
import org.keycloak.models.mongo.keycloak.entities.RealmEntity;
import org.keycloak.models.utils.KeycloakModelUtils;
@ -24,10 +23,8 @@ public class MongoKeycloakSession implements KeycloakSession {
private final MongoStoreInvocationContext invocationContext;
private final MongoKeycloakTransaction transaction;
private final MongoStore mongoStore;
public MongoKeycloakSession(MongoStore mongoStore) {
this.mongoStore = mongoStore;
// this.invocationContext = new SimpleMongoStoreInvocationContext(mongoStore);
this.invocationContext = new TransactionMongoStoreInvocationContext(mongoStore);
this.transaction = new MongoKeycloakTransaction(invocationContext);
@ -58,25 +55,25 @@ public class MongoKeycloakSession implements KeycloakSession {
newRealm.setId(id);
newRealm.setName(name);
mongoStore.insertObject(newRealm, invocationContext);
getMongoStore().insertEntity(newRealm, invocationContext);
return new RealmAdapter(newRealm, mongoStore, invocationContext);
return new RealmAdapter(newRealm, invocationContext);
}
@Override
public RealmModel getRealm(String id) {
RealmEntity realmEntity = mongoStore.loadObject(RealmEntity.class, id, invocationContext);
return realmEntity != null ? new RealmAdapter(realmEntity, mongoStore, invocationContext) : null;
RealmEntity realmEntity = getMongoStore().loadEntity(RealmEntity.class, id, invocationContext);
return realmEntity != null ? new RealmAdapter(realmEntity, invocationContext) : null;
}
@Override
public List<RealmModel> getRealms(UserModel admin) {
DBObject query = new BasicDBObject();
List<RealmEntity> realms = mongoStore.loadObjects(RealmEntity.class, query, invocationContext);
List<RealmEntity> realms = getMongoStore().loadEntities(RealmEntity.class, query, invocationContext);
List<RealmModel> results = new ArrayList<RealmModel>();
for (RealmEntity realmEntity : realms) {
results.add(new RealmAdapter(realmEntity, mongoStore, invocationContext));
results.add(new RealmAdapter(realmEntity, invocationContext));
}
return results;
}
@ -86,14 +83,18 @@ public class MongoKeycloakSession implements KeycloakSession {
DBObject query = new QueryBuilder()
.and("name").is(name)
.get();
RealmEntity realm = mongoStore.loadSingleObject(RealmEntity.class, query, invocationContext);
RealmEntity realm = getMongoStore().loadSingleEntity(RealmEntity.class, query, invocationContext);
if (realm == null) return null;
return new RealmAdapter(realm, mongoStore, invocationContext);
return new RealmAdapter(realm, invocationContext);
}
@Override
public boolean removeRealm(String id) {
return mongoStore.removeObject(RealmEntity.class, id, invocationContext);
return getMongoStore().removeEntity(RealmEntity.class, id, invocationContext);
}
protected MongoStore getMongoStore() {
return invocationContext.getMongoStore();
}
}

View file

@ -16,7 +16,6 @@ import org.keycloak.models.mongo.keycloak.entities.RequiredCredentialEntity;
import org.keycloak.models.mongo.keycloak.entities.RoleEntity;
import org.keycloak.models.mongo.keycloak.entities.SocialLinkEntity;
import org.keycloak.models.mongo.keycloak.entities.UserEntity;
import org.keycloak.models.mongo.utils.EmbeddedMongo;
import org.keycloak.models.mongo.utils.MongoConfiguration;
import java.net.UnknownHostException;
@ -40,20 +39,12 @@ public class MongoKeycloakSessionFactory implements KeycloakSessionFactory {
OAuthClientEntity.class
};
private final EmbeddedMongo embeddedMongo;
private final MongoClient mongoClient;
private final MongoStore mongoStore;
public MongoKeycloakSessionFactory(MongoConfiguration config) {
logger.info(String.format("Configuring MongoStore with: " + config));
if (config.isStartEmbedded()) {
embeddedMongo = new EmbeddedMongo();
embeddedMongo.startEmbeddedMongo(config.getPort());
} else {
embeddedMongo = null;
}
try {
// TODO: authentication support
mongoClient = new MongoClient(config.getHost(), config.getPort());
@ -75,9 +66,5 @@ public class MongoKeycloakSessionFactory implements KeycloakSessionFactory {
public void close() {
logger.info("Closing MongoDB client");
mongoClient.close();
if (embeddedMongo != null) {
embeddedMongo.stopEmbeddedMongo();
}
}
}

View file

@ -3,7 +3,6 @@ package org.keycloak.models.mongo.keycloak.adapters;
import org.keycloak.models.OAuthClientModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.mongo.api.AbstractMongoIdentifiableEntity;
import org.keycloak.models.mongo.api.MongoStore;
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
import org.keycloak.models.mongo.keycloak.entities.OAuthClientEntity;
import org.keycloak.models.mongo.keycloak.entities.UserEntity;
@ -16,14 +15,14 @@ public class OAuthClientAdapter extends AbstractAdapter implements OAuthClientMo
private final OAuthClientEntity delegate;
private UserAdapter oauthAgent;
public OAuthClientAdapter(OAuthClientEntity oauthClientEntity, UserAdapter oauthAgent, MongoStore mongoStore, MongoStoreInvocationContext invContext) {
super(mongoStore, invContext);
public OAuthClientAdapter(OAuthClientEntity oauthClientEntity, UserAdapter oauthAgent, MongoStoreInvocationContext invContext) {
super(invContext);
this.delegate = oauthClientEntity;
this.oauthAgent = oauthAgent;
}
public OAuthClientAdapter(OAuthClientEntity oauthClientEntity, MongoStore mongoStore, MongoStoreInvocationContext invContext) {
this(oauthClientEntity, null, mongoStore, invContext);
public OAuthClientAdapter(OAuthClientEntity oauthClientEntity, MongoStoreInvocationContext invContext) {
this(oauthClientEntity, null, invContext);
}
@Override
@ -35,8 +34,8 @@ public class OAuthClientAdapter extends AbstractAdapter implements OAuthClientMo
public UserModel getOAuthAgent() {
// This is not thread-safe. Assumption is that OAuthClientAdapter instance is per-client object
if (oauthAgent == null) {
UserEntity user = mongoStore.loadObject(UserEntity.class, delegate.getOauthAgentId(), invocationContext);
oauthAgent = user!=null ? new UserAdapter(user, mongoStore, invocationContext) : null;
UserEntity user = getMongoStore().loadEntity(UserEntity.class, delegate.getOauthAgentId(), invocationContext);
oauthAgent = user!=null ? new UserAdapter(user, invocationContext) : null;
}
return oauthAgent;
}

View file

@ -13,7 +13,6 @@ import org.keycloak.models.SocialLinkModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.mongo.api.AbstractMongoIdentifiableEntity;
import org.keycloak.models.mongo.api.MongoStore;
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
import org.keycloak.models.mongo.keycloak.entities.ApplicationEntity;
import org.keycloak.models.mongo.keycloak.entities.CredentialEntity;
@ -54,8 +53,8 @@ public class RealmAdapter extends AbstractAdapter implements RealmModel {
private volatile transient PasswordPolicy passwordPolicy;
public RealmAdapter(RealmEntity realmEntity, MongoStore mongoStore, MongoStoreInvocationContext invocationContext) {
super(mongoStore, invocationContext);
public RealmAdapter(RealmEntity realmEntity, MongoStoreInvocationContext invocationContext) {
super(invocationContext);
this.realm = realmEntity;
}
@ -280,12 +279,12 @@ public class RealmAdapter extends AbstractAdapter implements RealmModel {
.and("loginName").is(name)
.and("realmId").is(getId())
.get();
UserEntity user = mongoStore.loadSingleObject(UserEntity.class, query, invocationContext);
UserEntity user = getMongoStore().loadSingleEntity(UserEntity.class, query, invocationContext);
if (user == null) {
return null;
} else {
return new UserAdapter(user, mongoStore, invocationContext);
return new UserAdapter(user, invocationContext);
}
}
@ -295,12 +294,12 @@ public class RealmAdapter extends AbstractAdapter implements RealmModel {
.and("email").is(email)
.and("realmId").is(getId())
.get();
UserEntity user = mongoStore.loadSingleObject(UserEntity.class, query, invocationContext);
UserEntity user = getMongoStore().loadSingleEntity(UserEntity.class, query, invocationContext);
if (user == null) {
return null;
} else {
return new UserAdapter(user, mongoStore, invocationContext);
return new UserAdapter(user, invocationContext);
}
}
@ -332,8 +331,8 @@ public class RealmAdapter extends AbstractAdapter implements RealmModel {
userEntity.setEnabled(true);
userEntity.setRealmId(getId());
mongoStore.insertObject(userEntity, invocationContext);
return new UserAdapter(userEntity, mongoStore, invocationContext);
getMongoStore().insertEntity(userEntity, invocationContext);
return new UserAdapter(userEntity, invocationContext);
}
@Override
@ -342,7 +341,7 @@ public class RealmAdapter extends AbstractAdapter implements RealmModel {
.and("loginName").is(name)
.and("realmId").is(getId())
.get();
return mongoStore.removeObjects(UserEntity.class, query, invocationContext);
return getMongoStore().removeEntities(UserEntity.class, query, invocationContext);
}
@Override
@ -351,11 +350,11 @@ public class RealmAdapter extends AbstractAdapter implements RealmModel {
.and("name").is(name)
.and("realmId").is(getId())
.get();
RoleEntity role = mongoStore.loadSingleObject(RoleEntity.class, query, invocationContext);
RoleEntity role = getMongoStore().loadSingleEntity(RoleEntity.class, query, invocationContext);
if (role == null) {
return null;
} else {
return new RoleAdapter(role, this, mongoStore, invocationContext);
return new RoleAdapter(role, this, invocationContext);
}
}
@ -372,13 +371,13 @@ public class RealmAdapter extends AbstractAdapter implements RealmModel {
roleEntity.setName(name);
roleEntity.setRealmId(getId());
mongoStore.insertObject(roleEntity, invocationContext);
return new RoleAdapter(roleEntity, this, mongoStore, invocationContext);
getMongoStore().insertEntity(roleEntity, invocationContext);
return new RoleAdapter(roleEntity, this, invocationContext);
}
@Override
public boolean removeRoleById(String id) {
return mongoStore.removeObject(RoleEntity.class ,id, invocationContext);
return getMongoStore().removeEntity(RoleEntity.class, id, invocationContext);
}
@Override
@ -386,13 +385,13 @@ public class RealmAdapter extends AbstractAdapter implements RealmModel {
DBObject query = new QueryBuilder()
.and("realmId").is(getId())
.get();
List<RoleEntity> roles = mongoStore.loadObjects(RoleEntity.class, query, invocationContext);
List<RoleEntity> roles = getMongoStore().loadEntities(RoleEntity.class, query, invocationContext);
Set<RoleModel> result = new HashSet<RoleModel>();
if (roles == null) return result;
for (RoleEntity role : roles) {
result.add(new RoleAdapter(role, this, mongoStore, invocationContext));
result.add(new RoleAdapter(role, this, invocationContext));
}
return result;
@ -400,11 +399,11 @@ public class RealmAdapter extends AbstractAdapter implements RealmModel {
@Override
public RoleModel getRoleById(String id) {
RoleEntity role = mongoStore.loadObject(RoleEntity.class, id, invocationContext);
RoleEntity role = getMongoStore().loadEntity(RoleEntity.class, id, invocationContext);
if (role == null) {
return null;
} else {
return new RoleAdapter(role, this, mongoStore, invocationContext);
return new RoleAdapter(role, this, invocationContext);
}
}
@ -420,7 +419,7 @@ public class RealmAdapter extends AbstractAdapter implements RealmModel {
addRole(name);
}
mongoStore.pushItemToList(realm, "defaultRoles", name, true, invocationContext);
getMongoStore().pushItemToList(realm, "defaultRoles", name, true, invocationContext);
}
@Override
@ -441,14 +440,14 @@ public class RealmAdapter extends AbstractAdapter implements RealmModel {
@Override
public ApplicationModel getApplicationById(String id) {
ApplicationEntity appData = mongoStore.loadObject(ApplicationEntity.class, id, invocationContext);
ApplicationEntity appData = getMongoStore().loadEntity(ApplicationEntity.class, id, invocationContext);
// Check if application belongs to this realm
if (appData == null || !getId().equals(appData.getRealmId())) {
return null;
}
return new ApplicationAdapter(appData, mongoStore, invocationContext);
return new ApplicationAdapter(appData, invocationContext);
}
@Override
@ -457,8 +456,8 @@ public class RealmAdapter extends AbstractAdapter implements RealmModel {
.and("realmId").is(getId())
.and("name").is(name)
.get();
ApplicationEntity appEntity = mongoStore.loadSingleObject(ApplicationEntity.class, query, invocationContext);
return appEntity==null ? null : new ApplicationAdapter(appEntity, mongoStore, invocationContext);
ApplicationEntity appEntity = getMongoStore().loadSingleEntity(ApplicationEntity.class, query, invocationContext);
return appEntity==null ? null : new ApplicationAdapter(appEntity, invocationContext);
}
@Override
@ -475,11 +474,11 @@ public class RealmAdapter extends AbstractAdapter implements RealmModel {
DBObject query = new QueryBuilder()
.and("realmId").is(getId())
.get();
List<ApplicationEntity> appDatas = mongoStore.loadObjects(ApplicationEntity.class, query, invocationContext);
List<ApplicationEntity> appDatas = getMongoStore().loadEntities(ApplicationEntity.class, query, invocationContext);
List<ApplicationModel> result = new ArrayList<ApplicationModel>();
for (ApplicationEntity appData : appDatas) {
result.add(new ApplicationAdapter(appData, mongoStore, invocationContext));
result.add(new ApplicationAdapter(appData, invocationContext));
}
return result;
}
@ -493,14 +492,14 @@ public class RealmAdapter extends AbstractAdapter implements RealmModel {
appData.setRealmId(getId());
appData.setEnabled(true);
appData.setResourceUserId(resourceUser.getUser().getId());
mongoStore.insertObject(appData, invocationContext);
getMongoStore().insertEntity(appData, invocationContext);
return new ApplicationAdapter(appData, resourceUser, mongoStore, invocationContext);
return new ApplicationAdapter(appData, resourceUser, invocationContext);
}
@Override
public boolean removeApplication(String id) {
return mongoStore.removeObject(ApplicationEntity.class, id, invocationContext);
return getMongoStore().removeEntity(ApplicationEntity.class, id, invocationContext);
}
@Override
@ -517,20 +516,20 @@ public class RealmAdapter extends AbstractAdapter implements RealmModel {
@Override
public void grantRole(UserModel user, RoleModel role) {
UserEntity userEntity = ((UserAdapter)user).getUser();
mongoStore.pushItemToList(userEntity, "roleIds", role.getId(), true, invocationContext);
getMongoStore().pushItemToList(userEntity, "roleIds", role.getId(), true, invocationContext);
}
@Override
public Set<RoleModel> getRoleMappings(UserModel user) {
Set<RoleModel> result = new HashSet<RoleModel>();
List<RoleEntity> roles = MongoModelUtils.getAllRolesOfUser(user, mongoStore, invocationContext);
List<RoleEntity> roles = MongoModelUtils.getAllRolesOfUser(user, invocationContext);
for (RoleEntity role : roles) {
if (getId().equals(role.getRealmId())) {
result.add(new RoleAdapter(role, this, mongoStore, invocationContext));
result.add(new RoleAdapter(role, this, invocationContext));
} else {
// Likely applicationRole, but we don't have this application yet
result.add(new RoleAdapter(role, mongoStore, invocationContext));
result.add(new RoleAdapter(role, invocationContext));
}
}
return result;
@ -555,20 +554,20 @@ public class RealmAdapter extends AbstractAdapter implements RealmModel {
@Override
public void deleteRoleMapping(UserModel user, RoleModel role) {
UserEntity userEntity = ((UserAdapter)user).getUser();
mongoStore.pullItemFromList(userEntity, "roleIds", role.getId(), invocationContext);
getMongoStore().pullItemFromList(userEntity, "roleIds", role.getId(), invocationContext);
}
@Override
public Set<RoleModel> getScopeMappings(UserModel user) {
Set<RoleModel> result = new HashSet<RoleModel>();
List<RoleEntity> roles = MongoModelUtils.getAllScopesOfUser(user, mongoStore, invocationContext);
List<RoleEntity> roles = MongoModelUtils.getAllScopesOfUser(user, invocationContext);
for (RoleEntity role : roles) {
if (getId().equals(role.getRealmId())) {
result.add(new RoleAdapter(role, this, mongoStore, invocationContext));
result.add(new RoleAdapter(role, this, invocationContext));
} else {
// Likely applicationRole, but we don't have this application yet
result.add(new RoleAdapter(role, mongoStore, invocationContext));
result.add(new RoleAdapter(role, invocationContext));
}
}
return result;
@ -593,13 +592,13 @@ public class RealmAdapter extends AbstractAdapter implements RealmModel {
@Override
public void addScopeMapping(UserModel agent, RoleModel role) {
UserEntity userEntity = ((UserAdapter)agent).getUser();
mongoStore.pushItemToList(userEntity, "scopeIds", role.getId(), true, invocationContext);
getMongoStore().pushItemToList(userEntity, "scopeIds", role.getId(), true, invocationContext);
}
@Override
public void deleteScopeMapping(UserModel user, RoleModel role) {
UserEntity userEntity = ((UserAdapter)user).getUser();
mongoStore.pullItemFromList(userEntity, "scopeIds", role.getId(), invocationContext);
getMongoStore().pullItemFromList(userEntity, "scopeIds", role.getId(), invocationContext);
}
@Override
@ -610,14 +609,14 @@ public class RealmAdapter extends AbstractAdapter implements RealmModel {
oauthClient.setOauthAgentId(oauthAgent.getUser().getId());
oauthClient.setRealmId(getId());
oauthClient.setName(name);
mongoStore.insertObject(oauthClient, invocationContext);
getMongoStore().insertEntity(oauthClient, invocationContext);
return new OAuthClientAdapter(oauthClient, oauthAgent, mongoStore, invocationContext);
return new OAuthClientAdapter(oauthClient, oauthAgent, invocationContext);
}
@Override
public boolean removeOAuthClient(String id) {
return mongoStore.removeObject(OAuthClientEntity.class, id, invocationContext);
return getMongoStore().removeEntity(OAuthClientEntity.class, id, invocationContext);
}
@Override
@ -628,15 +627,15 @@ public class RealmAdapter extends AbstractAdapter implements RealmModel {
.and("realmId").is(getId())
.and("oauthAgentId").is(user.getUser().getId())
.get();
OAuthClientEntity oauthClient = mongoStore.loadSingleObject(OAuthClientEntity.class, query, invocationContext);
return oauthClient == null ? null : new OAuthClientAdapter(oauthClient, user, mongoStore, invocationContext);
OAuthClientEntity oauthClient = getMongoStore().loadSingleEntity(OAuthClientEntity.class, query, invocationContext);
return oauthClient == null ? null : new OAuthClientAdapter(oauthClient, user, invocationContext);
}
@Override
public OAuthClientModel getOAuthClientById(String id) {
OAuthClientEntity clientEntity = mongoStore.loadObject(OAuthClientEntity.class, id, invocationContext);
OAuthClientEntity clientEntity = getMongoStore().loadEntity(OAuthClientEntity.class, id, invocationContext);
if (clientEntity == null) return null;
return new OAuthClientAdapter(clientEntity, mongoStore, invocationContext);
return new OAuthClientAdapter(clientEntity, invocationContext);
}
@Override
@ -644,10 +643,10 @@ public class RealmAdapter extends AbstractAdapter implements RealmModel {
DBObject query = new QueryBuilder()
.and("realmId").is(getId())
.get();
List<OAuthClientEntity> results = mongoStore.loadObjects(OAuthClientEntity.class, query, invocationContext);
List<OAuthClientEntity> results = getMongoStore().loadEntities(OAuthClientEntity.class, query, invocationContext);
List<OAuthClientModel> list = new ArrayList<OAuthClientModel>();
for (OAuthClientEntity data : results) {
list.add(new OAuthClientAdapter(data, mongoStore, invocationContext));
list.add(new OAuthClientAdapter(data, invocationContext));
}
return list;
}
@ -795,7 +794,7 @@ public class RealmAdapter extends AbstractAdapter implements RealmModel {
}
credentialEntity.setDevice(cred.getDevice());
mongoStore.updateObject(userEntity, invocationContext);
getMongoStore().updateEntity(userEntity, invocationContext);
}
@Override
@ -805,8 +804,8 @@ public class RealmAdapter extends AbstractAdapter implements RealmModel {
.and("socialLinks.socialUsername").is(socialLink.getSocialUsername())
.and("realmId").is(getId())
.get();
UserEntity userEntity = mongoStore.loadSingleObject(UserEntity.class, query, invocationContext);
return userEntity==null ? null : new UserAdapter(userEntity, mongoStore, invocationContext);
UserEntity userEntity = getMongoStore().loadSingleEntity(UserEntity.class, query, invocationContext);
return userEntity==null ? null : new UserAdapter(userEntity, invocationContext);
}
@Override
@ -833,7 +832,7 @@ public class RealmAdapter extends AbstractAdapter implements RealmModel {
socialLinkEntity.setSocialProvider(socialLink.getSocialProvider());
socialLinkEntity.setSocialUsername(socialLink.getSocialUsername());
mongoStore.pushItemToList(userEntity, "socialLinks", socialLinkEntity, true, invocationContext);
getMongoStore().pushItemToList(userEntity, "socialLinks", socialLinkEntity, true, invocationContext);
}
@Override
@ -843,11 +842,11 @@ public class RealmAdapter extends AbstractAdapter implements RealmModel {
socialLinkEntity.setSocialUsername(socialLink.getSocialUsername());
UserEntity userEntity = ((UserAdapter)user).getUser();
mongoStore.pullItemFromList(userEntity, "socialLinks", socialLinkEntity, invocationContext);
getMongoStore().pullItemFromList(userEntity, "socialLinks", socialLinkEntity, invocationContext);
}
protected void updateRealm() {
mongoStore.updateObject(realm, invocationContext);
getMongoStore().updateEntity(realm, invocationContext);
}
protected RequiredCredentialModel initRequiredCredentialModel(String type) {
@ -863,7 +862,7 @@ public class RealmAdapter extends AbstractAdapter implements RealmModel {
DBObject query = new QueryBuilder()
.and("realmId").is(getId())
.get();
List<UserEntity> users = mongoStore.loadObjects(UserEntity.class, query, invocationContext);
List<UserEntity> users = getMongoStore().loadEntities(UserEntity.class, query, invocationContext);
return convertUserEntities(users);
}
@ -903,7 +902,7 @@ public class RealmAdapter extends AbstractAdapter implements RealmModel {
).get()
);
List<UserEntity> users = mongoStore.loadObjects(UserEntity.class, builder.get(), invocationContext);
List<UserEntity> users = getMongoStore().loadEntities(UserEntity.class, builder.get(), invocationContext);
return convertUserEntities(users);
}
@ -925,14 +924,14 @@ public class RealmAdapter extends AbstractAdapter implements RealmModel {
queryBuilder.and(UserModel.EMAIL).regex(Pattern.compile("(?i:" + entry.getValue() + "$)"));
}
}
List<UserEntity> users = mongoStore.loadObjects(UserEntity.class, queryBuilder.get(), invocationContext);
List<UserEntity> users = getMongoStore().loadEntities(UserEntity.class, queryBuilder.get(), invocationContext);
return convertUserEntities(users);
}
protected List<UserModel> convertUserEntities(List<UserEntity> userEntities) {
List<UserModel> userModels = new ArrayList<UserModel>();
for (UserEntity user : userEntities) {
userModels.add(new UserAdapter(user, mongoStore, invocationContext));
userModels.add(new UserAdapter(user, invocationContext));
}
return userModels;
}

View file

@ -10,7 +10,6 @@ import com.mongodb.QueryBuilder;
import org.keycloak.models.RoleContainerModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.mongo.api.AbstractMongoIdentifiableEntity;
import org.keycloak.models.mongo.api.MongoStore;
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
import org.keycloak.models.mongo.keycloak.entities.ApplicationEntity;
import org.keycloak.models.mongo.keycloak.entities.RealmEntity;
@ -28,15 +27,14 @@ public class RoleAdapter extends AbstractAdapter implements RoleModel {
private final RoleEntity role;
private RoleContainerModel roleContainer;
public RoleAdapter(RoleEntity roleEntity, MongoStore mongoStore, MongoStoreInvocationContext invContext) {
this(roleEntity, null, mongoStore, invContext);
public RoleAdapter(RoleEntity roleEntity, MongoStoreInvocationContext invContext) {
this(roleEntity, null, invContext);
}
public RoleAdapter(RoleEntity roleEntity, RoleContainerModel roleContainer, MongoStore mongoStore, MongoStoreInvocationContext invContext) {
super(mongoStore, invContext);
public RoleAdapter(RoleEntity roleEntity, RoleContainerModel roleContainer, MongoStoreInvocationContext invContext) {
super(invContext);
this.role = roleEntity;
this.roleContainer = roleContainer;
this.mongoStore = mongoStore;
}
@Override
@ -72,17 +70,17 @@ public class RoleAdapter extends AbstractAdapter implements RoleModel {
}
protected void updateRole() {
mongoStore.updateObject(role, invocationContext);
getMongoStore().updateEntity(role, invocationContext);
}
@Override
public void addCompositeRole(RoleModel childRole) {
mongoStore.pushItemToList(role, "compositeRoleIds", childRole.getId(), true, invocationContext);
getMongoStore().pushItemToList(role, "compositeRoleIds", childRole.getId(), true, invocationContext);
}
@Override
public void removeCompositeRole(RoleModel childRole) {
mongoStore.pullItemFromList(role, "compositeRoleIds", childRole.getId(), invocationContext);
getMongoStore().pullItemFromList(role, "compositeRoleIds", childRole.getId(), invocationContext);
}
@Override
@ -94,11 +92,11 @@ public class RoleAdapter extends AbstractAdapter implements RoleModel {
DBObject query = new QueryBuilder()
.and("_id").in(MongoModelUtils.convertStringsToObjectIds(role.getCompositeRoleIds()))
.get();
List<RoleEntity> childRoles = mongoStore.loadObjects(RoleEntity.class, query, invocationContext);
List<RoleEntity> childRoles = getMongoStore().loadEntities(RoleEntity.class, query, invocationContext);
Set<RoleModel> set = new HashSet<RoleModel>();
for (RoleEntity childRole : childRoles) {
set.add(new RoleAdapter(childRole, mongoStore, invocationContext));
set.add(new RoleAdapter(childRole, invocationContext));
}
return set;
}
@ -108,17 +106,17 @@ public class RoleAdapter extends AbstractAdapter implements RoleModel {
if (roleContainer == null) {
// Compute it
if (role.getRealmId() != null) {
RealmEntity realm = mongoStore.loadObject(RealmEntity.class, role.getRealmId(), invocationContext);
RealmEntity realm = getMongoStore().loadEntity(RealmEntity.class, role.getRealmId(), invocationContext);
if (realm == null) {
throw new IllegalStateException("Realm with id: " + role.getRealmId() + " doesn't exists");
}
roleContainer = new RealmAdapter(realm, mongoStore, invocationContext);
roleContainer = new RealmAdapter(realm, invocationContext);
} else if (role.getApplicationId() != null) {
ApplicationEntity appEntity = mongoStore.loadObject(ApplicationEntity.class, role.getApplicationId(), invocationContext);
ApplicationEntity appEntity = getMongoStore().loadEntity(ApplicationEntity.class, role.getApplicationId(), invocationContext);
if (appEntity == null) {
throw new IllegalStateException("Application with id: " + role.getApplicationId() + " doesn't exists");
}
roleContainer = new ApplicationAdapter(appEntity, mongoStore, invocationContext);
roleContainer = new ApplicationAdapter(appEntity, invocationContext);
} else {
throw new IllegalStateException("Both realmId and applicationId are null for role: " + this);
}

View file

@ -2,7 +2,6 @@ package org.keycloak.models.mongo.keycloak.adapters;
import org.keycloak.models.UserModel;
import org.keycloak.models.mongo.api.AbstractMongoIdentifiableEntity;
import org.keycloak.models.mongo.api.MongoStore;
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
import org.keycloak.models.mongo.keycloak.entities.UserEntity;
@ -23,8 +22,8 @@ public class UserAdapter extends AbstractAdapter implements UserModel {
private final UserEntity user;
public UserAdapter(UserEntity userEntity, MongoStore mongoStore, MongoStoreInvocationContext invContext) {
super(mongoStore, invContext);
public UserAdapter(UserEntity userEntity, MongoStoreInvocationContext invContext) {
super(invContext);
this.user = userEntity;
}
@ -139,12 +138,12 @@ public class UserAdapter extends AbstractAdapter implements UserModel {
@Override
public void addWebOrigin(String webOrigin) {
mongoStore.pushItemToList(user, "webOrigins", webOrigin, true, invocationContext);
getMongoStore().pushItemToList(user, "webOrigins", webOrigin, true, invocationContext);
}
@Override
public void removeWebOrigin(String webOrigin) {
mongoStore.pullItemFromList(user, "webOrigins", webOrigin, invocationContext);
getMongoStore().pullItemFromList(user, "webOrigins", webOrigin, invocationContext);
}
@Override
@ -166,12 +165,12 @@ public class UserAdapter extends AbstractAdapter implements UserModel {
@Override
public void addRedirectUri(String redirectUri) {
mongoStore.pushItemToList(user, "redirectUris", redirectUri, true, invocationContext);
getMongoStore().pushItemToList(user, "redirectUris", redirectUri, true, invocationContext);
}
@Override
public void removeRedirectUri(String redirectUri) {
mongoStore.pullItemFromList(user, "redirectUris", redirectUri, invocationContext);
getMongoStore().pullItemFromList(user, "redirectUris", redirectUri, invocationContext);
}
@Override
@ -185,12 +184,12 @@ public class UserAdapter extends AbstractAdapter implements UserModel {
@Override
public void addRequiredAction(RequiredAction action) {
mongoStore.pushItemToList(user, "requiredActions", action, true, invocationContext);
getMongoStore().pushItemToList(user, "requiredActions", action, true, invocationContext);
}
@Override
public void removeRequiredAction(RequiredAction action) {
mongoStore.pullItemFromList(user, "requiredActions", action, invocationContext);
getMongoStore().pullItemFromList(user, "requiredActions", action, invocationContext);
}
@Override
@ -205,7 +204,7 @@ public class UserAdapter extends AbstractAdapter implements UserModel {
}
protected void updateUser() {
mongoStore.updateObject(user, invocationContext);
getMongoStore().updateEntity(user, invocationContext);
}
@Override

View file

@ -9,7 +9,6 @@ import org.keycloak.models.mongo.api.AbstractMongoIdentifiableEntity;
import org.keycloak.models.mongo.api.MongoCollection;
import org.keycloak.models.mongo.api.MongoEntity;
import org.keycloak.models.mongo.api.MongoField;
import org.keycloak.models.mongo.api.MongoStore;
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
/**
@ -103,14 +102,14 @@ public class ApplicationEntity extends AbstractMongoIdentifiableEntity implement
}
@Override
public void afterRemove(MongoStore mongoStore, MongoStoreInvocationContext invContext) {
public void afterRemove(MongoStoreInvocationContext context) {
// Remove resourceUser of this application
mongoStore.removeObject(UserEntity.class, resourceUserId, invContext);
context.getMongoStore().removeEntity(UserEntity.class, resourceUserId, context);
// Remove all roles, which belongs to this application
DBObject query = new QueryBuilder()
.and("applicationId").is(getId())
.get();
mongoStore.removeObjects(RoleEntity.class, query, invContext);
context.getMongoStore().removeEntities(RoleEntity.class, query, context);
}
}

View file

@ -4,7 +4,6 @@ import org.keycloak.models.mongo.api.AbstractMongoIdentifiableEntity;
import org.keycloak.models.mongo.api.MongoCollection;
import org.keycloak.models.mongo.api.MongoEntity;
import org.keycloak.models.mongo.api.MongoField;
import org.keycloak.models.mongo.api.MongoStore;
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
/**
@ -46,8 +45,8 @@ public class OAuthClientEntity extends AbstractMongoIdentifiableEntity implement
}
@Override
public void afterRemove(MongoStore mongoStore, MongoStoreInvocationContext invContext) {
public void afterRemove(MongoStoreInvocationContext context) {
// Remove user of this oauthClient
mongoStore.removeObject(UserEntity.class, oauthAgentId, invContext);
context.getMongoStore().removeEntity(UserEntity.class, oauthAgentId, context);
}
}

View file

@ -6,7 +6,6 @@ import org.keycloak.models.mongo.api.AbstractMongoIdentifiableEntity;
import org.keycloak.models.mongo.api.MongoCollection;
import org.keycloak.models.mongo.api.MongoEntity;
import org.keycloak.models.mongo.api.MongoField;
import org.keycloak.models.mongo.api.MongoStore;
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
import java.util.ArrayList;
@ -249,18 +248,18 @@ public class RealmEntity extends AbstractMongoIdentifiableEntity implements Mong
}
@Override
public void afterRemove(MongoStore mongoStore, MongoStoreInvocationContext invContext) {
public void afterRemove(MongoStoreInvocationContext context) {
DBObject query = new QueryBuilder()
.and("realmId").is(getId())
.get();
// Remove all users of this realm
mongoStore.removeObjects(UserEntity.class, query, invContext);
context.getMongoStore().removeEntities(UserEntity.class, query, context);
// Remove all roles of this realm
mongoStore.removeObjects(RoleEntity.class, query, invContext);
context.getMongoStore().removeEntities(RoleEntity.class, query, context);
// Remove all applications of this realm
mongoStore.removeObjects(ApplicationEntity.class, query, invContext);
context.getMongoStore().removeEntities(ApplicationEntity.class, query, context);
}
}

View file

@ -74,13 +74,15 @@ public class RoleEntity extends AbstractMongoIdentifiableEntity implements Mongo
}
@Override
public void afterRemove(MongoStore mongoStore, MongoStoreInvocationContext invContext) {
public void afterRemove(MongoStoreInvocationContext invContext) {
MongoStore mongoStore = invContext.getMongoStore();
// Remove this role from all users, which has it
DBObject query = new QueryBuilder()
.and("roleIds").is(getId())
.get();
List<UserEntity> users = mongoStore.loadObjects(UserEntity.class, query, invContext);
List<UserEntity> users = mongoStore.loadEntities(UserEntity.class, query, invContext);
for (UserEntity user : users) {
logger.info("Removing role " + getName() + " from user " + user.getLoginName());
mongoStore.pullItemFromList(user, "roleIds", getId(), invContext);
@ -91,7 +93,7 @@ public class RoleEntity extends AbstractMongoIdentifiableEntity implements Mongo
.and("scopeIds").is(getId())
.get();
users = mongoStore.loadObjects(UserEntity.class, query, invContext);
users = mongoStore.loadEntities(UserEntity.class, query, invContext);
for (UserEntity user : users) {
logger.info("Removing scope " + getName() + " from user " + user.getLoginName());
mongoStore.pullItemFromList(user, "scopeIds", getId(), invContext);
@ -99,7 +101,7 @@ public class RoleEntity extends AbstractMongoIdentifiableEntity implements Mongo
// Remove defaultRoles from realm
if (realmId != null) {
RealmEntity realmEntity = mongoStore.loadObject(RealmEntity.class, realmId, invContext);
RealmEntity realmEntity = mongoStore.loadEntity(RealmEntity.class, realmId, invContext);
// Realm might be already removed at this point
if (realmEntity != null) {
@ -109,7 +111,7 @@ public class RoleEntity extends AbstractMongoIdentifiableEntity implements Mongo
// Remove defaultRoles from application
if (applicationId != null) {
ApplicationEntity appEntity = mongoStore.loadObject(ApplicationEntity.class, applicationId, invContext);
ApplicationEntity appEntity = mongoStore.loadEntity(ApplicationEntity.class, applicationId, invContext);
// Application might be already removed at this point
if (appEntity != null) {
@ -121,7 +123,7 @@ public class RoleEntity extends AbstractMongoIdentifiableEntity implements Mongo
query = new QueryBuilder()
.and("compositeRoleIds").is(getId())
.get();
List<RoleEntity> parentRoles = mongoStore.loadObjects(RoleEntity.class, query, invContext);
List<RoleEntity> parentRoles = mongoStore.loadEntities(RoleEntity.class, query, invContext);
for (RoleEntity role : parentRoles) {
mongoStore.pullItemFromList(role, "compositeRoleIds", getId(), invContext);
}

View file

@ -1,50 +0,0 @@
package org.keycloak.models.mongo.utils;
import java.io.IOException;
import de.flapdoodle.embed.mongo.MongodExecutable;
import de.flapdoodle.embed.mongo.MongodProcess;
import de.flapdoodle.embed.mongo.MongodStarter;
import de.flapdoodle.embed.mongo.config.IMongodConfig;
import de.flapdoodle.embed.mongo.config.MongodConfigBuilder;
import de.flapdoodle.embed.mongo.config.Net;
import de.flapdoodle.embed.mongo.distribution.Version;
import de.flapdoodle.embed.process.runtime.Network;
import org.jboss.logging.Logger;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class EmbeddedMongo {
protected static final Logger logger = Logger.getLogger(EmbeddedMongo.class);
private MongodExecutable mongodExe;
private MongodProcess mongod;
public void startEmbeddedMongo(int port) {
logger.info("Going to start embedded Mongo on port=" + port);
try {
IMongodConfig mongodConfig = new MongodConfigBuilder()
.version(Version.Main.PRODUCTION)
.net(new Net(port, Network.localhostIsIPv6()))
.build();
mongodExe = MongodStarter.getDefaultInstance().prepare(mongodConfig);
mongod = mongodExe.start();
} catch (IOException e) {
logger.warn("Couldn't start Embedded Mongo on port " + port + ". Maybe it's already started? Cause: " + e.getClass() + " " + e.getMessage());
throw new RuntimeException(e);
}
}
public void stopEmbeddedMongo() {
if (mongodExe != null) {
if (mongod != null) {
logger.info("Going to stop embedded MongoDB.");
mongod.stop();
}
mongodExe.stop();
}
}
}

View file

@ -12,14 +12,12 @@ public class MongoConfiguration {
private final String dbName;
private final boolean clearCollectionsOnStartup;
private final boolean startEmbedded;
public MongoConfiguration(String host, int port, String dbName, boolean clearCollectionsOnStartup, boolean startEmbedded) {
public MongoConfiguration(String host, int port, String dbName, boolean clearCollectionsOnStartup) {
this.host = host;
this.port = port;
this.dbName = dbName;
this.clearCollectionsOnStartup = clearCollectionsOnStartup;
this.startEmbedded = startEmbedded;
}
public String getHost() {
@ -38,13 +36,9 @@ public class MongoConfiguration {
return clearCollectionsOnStartup;
}
public boolean isStartEmbedded() {
return startEmbedded;
}
@Override
public String toString() {
return String.format("MongoConfiguration: host: %s, port: %d, dbName: %s, clearCollectionsOnStartup: %b, startEmbedded: %b",
host, port, dbName, clearCollectionsOnStartup, startEmbedded);
return String.format("MongoConfiguration: host: %s, port: %d, dbName: %s, clearCollectionsOnStartup: %b",
host, port, dbName, clearCollectionsOnStartup);
}
}

View file

@ -9,7 +9,6 @@ import com.mongodb.DBObject;
import com.mongodb.QueryBuilder;
import org.bson.types.ObjectId;
import org.keycloak.models.UserModel;
import org.keycloak.models.mongo.api.MongoStore;
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
import org.keycloak.models.mongo.keycloak.adapters.UserAdapter;
import org.keycloak.models.mongo.keycloak.entities.RoleEntity;
@ -29,7 +28,7 @@ public class MongoModelUtils {
}
// Get everything including both application and realm roles
public static List<RoleEntity> getAllRolesOfUser(UserModel user, MongoStore mongoStore, MongoStoreInvocationContext invContext) {
public static List<RoleEntity> getAllRolesOfUser(UserModel user, MongoStoreInvocationContext invContext) {
UserEntity userEntity = ((UserAdapter)user).getUser();
List<String> roleIds = userEntity.getRoleIds();
@ -40,11 +39,11 @@ public class MongoModelUtils {
DBObject query = new QueryBuilder()
.and("_id").in(convertStringsToObjectIds(roleIds))
.get();
return mongoStore.loadObjects(RoleEntity.class, query, invContext);
return invContext.getMongoStore().loadEntities(RoleEntity.class, query, invContext);
}
// Get everything including both application and realm scopes
public static List<RoleEntity> getAllScopesOfUser(UserModel user, MongoStore mongoStore, MongoStoreInvocationContext invContext) {
public static List<RoleEntity> getAllScopesOfUser(UserModel user, MongoStoreInvocationContext invContext) {
UserEntity userEntity = ((UserAdapter)user).getUser();
List<String> scopeIds = userEntity.getScopeIds();
@ -55,6 +54,6 @@ public class MongoModelUtils {
DBObject query = new QueryBuilder()
.and("_id").in(convertStringsToObjectIds(scopeIds))
.get();
return mongoStore.loadObjects(RoleEntity.class, query, invContext);
return invContext.getMongoStore().loadEntities(RoleEntity.class, query, invContext);
}
}

View file

@ -8,39 +8,39 @@ public class SystemPropertiesConfigurationProvider {
private static final String MONGO_HOST = "keycloak.mongo.host";
private static final String MONGO_PORT = "keycloak.mongo.port";
private static final String MONGO_DB_NAME = "keycloak.mongo.db";
private static final String MONGO_CLEAR_COLLECTIONS_ON_STARTUP = "keycloak.mongo.clearCollectionsOnStartup";
private static final String MONGO_START_EMBEDDED = "keycloak.mongo.startEmbedded";
private static final String MONGO_CLEAR_ON_STARTUP = "keycloak.mongo.clearOnStartup";
// Property names from Liveoak . Those are used as fallback in case that original value is not available
private static final String MONGO_HOST_2 = "mongo.host";
private static final String MONGO_PORT_2 = "mongo.port";
private static final String MONGO_DB_NAME_2 = "mongo.db";
private static final String MONGO_CLEAR_ON_STARTUP_2 = "mongo.clearCollectionsOnStartup";
// Port where MongoDB instance is normally started on linux. This port should be used if we're not starting embedded instance
private static final int MONGO_DEFAULT_PORT = 27017;
// Port where embedded MongoDB instance will be started. Same port will be used by KeycloakApplication then
public static final int MONGO_DEFAULT_PORT_EMBEDDED = 27018;
private static final String MONGO_DEFAULT_PORT = "27017";
public static String getMongoHost() {
return System.getProperty(MONGO_HOST, "localhost");
return getSystemPropertyWithFallback(MONGO_HOST, MONGO_HOST_2, "localhost");
}
public static int getMongoPort() {
String portProp = System.getProperty(MONGO_PORT);
if (portProp != null) {
return Integer.parseInt(portProp);
} else {
// Default port is 27017 in case of non-embedded, and 27018 in case of embedded
return isStartEmbedded() ? MONGO_DEFAULT_PORT_EMBEDDED : MONGO_DEFAULT_PORT;
}
String portProp = getSystemPropertyWithFallback(MONGO_PORT, MONGO_PORT_2, MONGO_DEFAULT_PORT);
return Integer.parseInt(portProp);
}
public static String getMongoDbName() {
return System.getProperty(MONGO_DB_NAME, "keycloak");
return getSystemPropertyWithFallback(MONGO_DB_NAME, MONGO_DB_NAME_2, "keycloak");
}
public static boolean isClearCollectionsOnStartup() {
return Boolean.parseBoolean(System.getProperty(MONGO_CLEAR_COLLECTIONS_ON_STARTUP, "true"));
String property = getSystemPropertyWithFallback(MONGO_CLEAR_ON_STARTUP, MONGO_CLEAR_ON_STARTUP_2, "false");
return "true".equalsIgnoreCase(property);
}
public static boolean isStartEmbedded() {
return Boolean.parseBoolean(System.getProperty(MONGO_START_EMBEDDED, "false"));
// Check if property propName1 (like "keycloak.mongo.host" is available and if not, then fallback to property "mongo.host" )
private static String getSystemPropertyWithFallback(String propName1, String propName2, String defaultValue) {
String propValue1 = System.getProperty(propName1);
return propValue1!=null ? propValue1 : System.getProperty(propName2, defaultValue);
}
// Create configuration based on system properties
@ -49,8 +49,7 @@ public class SystemPropertiesConfigurationProvider {
getMongoHost(),
getMongoPort(),
getMongoDbName(),
isClearCollectionsOnStartup(),
isStartEmbedded()
isClearCollectionsOnStartup()
);
}
}

View file

@ -12,8 +12,8 @@ import org.keycloak.models.mongo.api.MongoEntity;
import org.keycloak.models.mongo.api.MongoStore;
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
import org.keycloak.models.mongo.impl.MongoStoreImpl;
import org.keycloak.models.mongo.impl.context.SimpleMongoStoreInvocationContext;
import org.keycloak.models.mongo.impl.context.TransactionMongoStoreInvocationContext;
import org.keycloak.models.mongo.utils.SystemPropertiesConfigurationProvider;
import java.net.UnknownHostException;
import java.util.ArrayList;
@ -37,7 +37,7 @@ public class MongoDBModelTest {
public void before() throws Exception {
try {
// TODO: authentication support
mongoClient = new MongoClient("localhost", 27017);
mongoClient = new MongoClient("localhost", SystemPropertiesConfigurationProvider.getMongoPort());
DB db = mongoClient.getDB("keycloakTest");
mongoStore = new MongoStoreImpl(db, true, MANAGED_DATA_TYPES);
@ -62,7 +62,7 @@ public class MongoDBModelTest {
john.setAge(25);
john.setGender(Person.Gender.MALE);
mongoStore.insertObject(john, context);
mongoStore.insertEntity(john, context);
// Add another user
Person mary = new Person();
@ -83,12 +83,12 @@ public class MongoDBModelTest {
mary.setGender(Person.Gender.FEMALE);
mary.setGenders(asList(Person.Gender.FEMALE));
mongoStore.insertObject(mary, context);
mongoStore.insertEntity(mary, context);
Assert.assertEquals(2, mongoStore.loadObjects(Person.class, new QueryBuilder().get(), context).size());
Assert.assertEquals(2, mongoStore.loadEntities(Person.class, new QueryBuilder().get(), context).size());
DBObject query = new QueryBuilder().and("addresses.flatNumbers").is("flat1").get();
List<Person> persons = mongoStore.loadObjects(Person.class, query, context);
List<Person> persons = mongoStore.loadEntities(Person.class, query, context);
Assert.assertEquals(1, persons.size());
mary = persons.get(0);
Assert.assertEquals(mary.getFirstName(), "mary");
@ -105,7 +105,7 @@ public class MongoDBModelTest {
addr3.setStreet("Broadway");
mongoStore.pushItemToList(mary, "addresses", addr3, true, context);
mary = mongoStore.loadObject(Person.class, mary.getId(), context);
mary = mongoStore.loadEntity(Person.class, mary.getId(), context);
Assert.assertEquals(3, mary.getKids().size());
Assert.assertTrue(mary.getKids().contains("Pauline"));
Assert.assertFalse(mary.getKids().contains("Paul"));
@ -121,16 +121,16 @@ public class MongoDBModelTest {
mary.addAttribute("attr1", "value1");
mary.addAttribute("attr2", "value2");
mary.addAttribute("attr.some3", "value3");
mongoStore.updateObject(mary, context);
mongoStore.updateEntity(mary, context);
mary = mongoStore.loadObject(Person.class, mary.getId(), context);
mary = mongoStore.loadEntity(Person.class, mary.getId(), context);
Assert.assertEquals(3, mary.getAttributes().size());
mary.removeAttribute("attr2");
mary.removeAttribute("nonExisting");
mongoStore.updateObject(mary, context);
mongoStore.updateEntity(mary, context);
mary = mongoStore.loadObject(Person.class, mary.getId(), context);
mary = mongoStore.loadEntity(Person.class, mary.getId(), context);
Assert.assertEquals(2, mary.getAttributes().size());
Assert.assertEquals("value1", mary.getAttributes().get("attr1"));
Assert.assertEquals("value3", mary.getAttributes().get("attr.some3"));

View file

@ -38,5 +38,6 @@
<!-- <module>picketlink</module> -->
<module>jpa</module>
<module>mongo</module>
<module>tests</module>
</modules>
</project>

69
model/tests/pom.xml Normal file
View file

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.0-alpha-2-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-model-tests</artifactId>
<name>Keycloak Model Tests</name>
<description/>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-services</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<id>package-tests-jar</id>
<phase>package</phase>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
</plugins>
</build>
</project>

View file

@ -0,0 +1,55 @@
package org.keycloak.model.test;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import org.jboss.resteasy.logging.Logger;
import org.junit.After;
import org.junit.Before;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.resources.KeycloakApplication;
import org.keycloak.util.JsonSerialization;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class AbstractModelTest {
private final Logger log = Logger.getLogger(getClass());
protected KeycloakSessionFactory factory;
protected KeycloakSession identitySession;
protected RealmManager realmManager;
@Before
public void before() throws Exception {
factory = KeycloakApplication.createSessionFactory();
identitySession = factory.createSession();
identitySession.getTransaction().begin();
realmManager = new RealmManager(identitySession);
}
@After
public void after() throws Exception {
identitySession.getTransaction().commit();
identitySession.close();
factory.close();
}
public static RealmRepresentation loadJson(String path) throws IOException {
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(path);
ByteArrayOutputStream os = new ByteArrayOutputStream();
int c;
while ((c = is.read()) != -1) {
os.write(c);
}
byte[] bytes = os.toByteArray();
System.out.println(new String(bytes));
return JsonSerialization.readValue(bytes, RealmRepresentation.class);
}
}

View file

@ -1,4 +1,4 @@
package org.keycloak.test;
package org.keycloak.model.test;
import org.junit.Assert;
import org.junit.FixMethodOrder;
@ -23,36 +23,21 @@ import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class AdapterTest extends AbstractKeycloakTest {
public class AdapterTest extends AbstractModelTest {
private RealmModel realmModel;
public AdapterTest(String providerId) {
super(providerId);
}
@Test
public void installTest() throws Exception {
new ApplianceBootstrap().bootstrap(identitySession);
}
@Test
public void testMe() {
String hello = "Bill Burke";
StringTokenizer tokenizer = new StringTokenizer(hello, " ");
while (tokenizer.hasMoreTokens()) {
System.out.println("token: " + tokenizer.nextToken());
}
}
@Test
public void test1CreateRealm() throws Exception {
realmModel = realmManager.createRealm("JUGGLER");

View file

@ -1,4 +1,4 @@
package org.keycloak.test;
package org.keycloak.model.test;
import org.junit.Assert;
import org.junit.Before;
@ -16,15 +16,11 @@ import java.util.List;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class ApplicationModelTest extends AbstractKeycloakTest {
public class ApplicationModelTest extends AbstractModelTest {
private ApplicationModel application;
private RealmModel realm;
private ApplicationManager appManager;
public ApplicationModelTest(String providerId) {
super(providerId);
}
@Before
public void before() throws Exception {
super.before();

View file

@ -1,4 +1,4 @@
package org.keycloak.services.managers;
package org.keycloak.model.test;
import org.junit.Assert;
import org.junit.Before;
@ -9,14 +9,14 @@ import org.keycloak.models.UserModel;
import org.keycloak.models.UserModel.RequiredAction;
import org.keycloak.models.utils.TimeBasedOTP;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.AuthenticationManager.AuthenticationStatus;
import org.keycloak.test.AbstractKeycloakTest;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap;
import java.util.UUID;
public class AuthenticationManagerTest extends AbstractKeycloakTest {
public class AuthenticationManagerTest extends AbstractModelTest {
private AuthenticationManager am;
private MultivaluedMap<String, String> formData;
@ -24,9 +24,6 @@ public class AuthenticationManagerTest extends AbstractKeycloakTest {
private RealmModel realm;
private UserModel user;
public AuthenticationManagerTest(String providerId) {
super(providerId);
}
@Test
public void authForm() {
AuthenticationStatus status = am.authenticateForm(realm, user, formData);

View file

@ -1,4 +1,4 @@
package org.keycloak.test;
package org.keycloak.model.test;
import java.util.HashSet;
import java.util.Set;
@ -16,17 +16,13 @@ import org.keycloak.services.managers.RealmManager;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class CompositeRolesModelTest extends AbstractKeycloakTest {
public CompositeRolesModelTest(String providerId) {
super(providerId);
}
public class CompositeRolesModelTest extends AbstractModelTest {
@Before
public void before() throws Exception {
super.before();
RealmManager manager = realmManager;
RealmRepresentation rep = AbstractKeycloakServerTest.loadJson("testcomposites.json");
RealmRepresentation rep = AbstractModelTest.loadJson("testcomposites.json");
RealmModel realm = manager.createRealm("Test", rep.getRealm());
manager.importRealm(rep, realm);
}
@ -34,13 +30,42 @@ public class CompositeRolesModelTest extends AbstractKeycloakTest {
@Test
public void testAppComposites() {
Set<RoleModel> requestedRoles = getRequestedRoles("APP_COMPOSITE_APPLICATION", "APP_COMPOSITE_USER");
Assert.assertEquals(2, requestedRoles.size());
assertContains("APP_ROLE_APPLICATION", "APP_ROLE_1", requestedRoles);
assertContains("realm", "REALM_ROLE_1", requestedRoles);
}
RoleModel expectedRole1 = getRole("APP_ROLE_APPLICATION", "APP_ROLE_1");
RoleModel expectedRole2 = getRole("realm", "REALM_ROLE_1");
@Test
public void testRealmAppComposites() {
Set<RoleModel> requestedRoles = getRequestedRoles("APP_COMPOSITE_APPLICATION", "REALM_APP_COMPOSITE_USER");
assertContains(requestedRoles, expectedRole1);
assertContains(requestedRoles, expectedRole2);
Assert.assertEquals(1, requestedRoles.size());
assertContains("APP_ROLE_APPLICATION", "APP_ROLE_1", requestedRoles);
}
@Test
public void testRealmOnlyWithUserCompositeAppComposite() throws Exception {
Set<RoleModel> requestedRoles = getRequestedRoles("REALM_COMPOSITE_1_APPLICATION", "REALM_COMPOSITE_1_USER");
Assert.assertEquals(1, requestedRoles.size());
assertContains("realm", "REALM_COMPOSITE_1", requestedRoles);
}
@Test
public void testRealmOnlyWithUserCompositeAppRole() throws Exception {
Set<RoleModel> requestedRoles = getRequestedRoles("REALM_ROLE_1_APPLICATION", "REALM_COMPOSITE_1_USER");
Assert.assertEquals(1, requestedRoles.size());
assertContains("realm", "REALM_ROLE_1", requestedRoles);
}
@Test
public void testRealmOnlyWithUserRoleAppComposite() throws Exception {
Set<RoleModel> requestedRoles = getRequestedRoles("REALM_COMPOSITE_1_APPLICATION", "REALM_ROLE_1_USER");
Assert.assertEquals(1, requestedRoles.size());
assertContains("realm", "REALM_ROLE_1", requestedRoles);
}
// TODO: more tests...
@ -92,7 +117,9 @@ public class CompositeRolesModelTest extends AbstractKeycloakTest {
}
}
private void assertContains(Set<RoleModel> requestedRoles, RoleModel expectedRole) {
private void assertContains(String appName, String roleName, Set<RoleModel> requestedRoles) {
RoleModel expectedRole = getRole(appName, roleName);
Assert.assertTrue(requestedRoles.contains(expectedRole));
// Check if requestedRole has correct role container

View file

@ -1,4 +1,4 @@
package org.keycloak.test;
package org.keycloak.model.test;
import org.junit.Assert;
import org.junit.FixMethodOrder;
@ -23,16 +23,12 @@ import java.util.Set;
* @version $Revision: 1 $
*/
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class ImportTest extends AbstractKeycloakTest {
public ImportTest(String providerId) {
super(providerId);
}
public class ImportTest extends AbstractModelTest {
@Test
public void install() throws Exception {
RealmManager manager = realmManager;
RealmRepresentation rep = AbstractKeycloakServerTest.loadJson("testrealm.json");
RealmRepresentation rep = AbstractModelTest.loadJson("testrealm.json");
RealmModel realm = manager.createRealm("demo", rep.getRealm());
manager.importRealm(rep, realm);
@ -148,7 +144,7 @@ public class ImportTest extends AbstractKeycloakTest {
@Test
public void install2() throws Exception {
RealmManager manager = realmManager;
RealmRepresentation rep = AbstractKeycloakServerTest.loadJson("testrealm-demo.json");
RealmRepresentation rep = AbstractModelTest.loadJson("testrealm-demo.json");
RealmModel realm = manager.createRealm("demo", rep.getRealm());
manager.importRealm(rep, realm);

View file

@ -1,4 +1,4 @@
package org.keycloak.test;
package org.keycloak.model.test;
import org.junit.Assert;
import org.junit.Test;
@ -12,11 +12,7 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
public class ModelTest extends AbstractKeycloakTest {
public ModelTest(String providerId) {
super(providerId);
}
public class ModelTest extends AbstractModelTest {
@Test
public void importExportRealm() {

View file

@ -1,4 +1,4 @@
package org.keycloak.test;
package org.keycloak.model.test;
import org.junit.Assert;
import org.junit.Test;
@ -14,11 +14,7 @@ import java.util.List;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class UserModelTest extends AbstractKeycloakTest {
public UserModelTest(String providerId) {
super(providerId);
}
public class UserModelTest extends AbstractModelTest {
@Test
public void persistUser() {

12
pom.xml
View file

@ -313,11 +313,6 @@
<artifactId>mongo-java-driver</artifactId>
<version>2.11.2</version>
</dependency>
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<version>1.40</version>
</dependency>
<dependency>
<groupId>org.apache.jmeter</groupId>
<artifactId>ApacheJMeter_java</artifactId>
@ -395,7 +390,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<!-- <version>2.5</version> -->
<version>2.16</version>
<configuration>
<forkMode>once</forkMode>
<argLine>-Xms512m -Xmx512m</argLine>
@ -479,6 +474,11 @@
<artifactId>exec-maven-plugin</artifactId>
<version>1.2.1</version>
</plugin>
<plugin>
<groupId>com.github.joelittlejohn.embedmongo</groupId>
<artifactId>embedmongo-maven-plugin</artifactId>
<version>0.1.10</version>
</plugin>
</plugins>
</pluginManagement>

View file

@ -56,41 +56,6 @@
<artifactId>keycloak-jaxrs-oauth-client</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-jpa</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<!--
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-picketlink</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
-->
<!-- TODO: remove -->
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-mongo</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.picketlink</groupId>
<artifactId>picketlink-common</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-social-core</artifactId>
@ -141,21 +106,6 @@
<artifactId>resteasy-multipart-provider</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-undertow</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.undertow</groupId>
<artifactId>undertow-servlet</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.undertow</groupId>
<artifactId>undertow-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
@ -185,21 +135,6 @@
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.0-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.icegreen</groupId>
<artifactId>greenmail</artifactId>

View file

@ -8,7 +8,7 @@ import org.keycloak.services.managers.ApplianceBootstrap;
import org.keycloak.services.managers.SocialRequestManager;
import org.keycloak.services.managers.TokenManager;
import org.keycloak.services.resources.admin.AdminService;
import org.keycloak.services.utils.ModelProviderUtils;
import org.keycloak.models.utils.ModelProviderUtils;
import javax.servlet.ServletContext;
import javax.ws.rs.core.Application;

View file

@ -1,70 +0,0 @@
package org.keycloak.test;
import io.undertow.servlet.Servlets;
import io.undertow.servlet.api.DeploymentInfo;
import io.undertow.servlet.api.FilterInfo;
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
import org.jboss.resteasy.plugins.server.undertow.UndertowJaxrsServer;
import org.jboss.resteasy.spi.ResteasyDeployment;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.keycloak.SkeletonKeyContextResolver;
import org.keycloak.util.JsonSerialization;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.services.filters.KeycloakSessionServletFilter;
import org.keycloak.services.resources.KeycloakApplication;
import javax.servlet.DispatcherType;
import javax.ws.rs.client.Client;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class AbstractKeycloakServerTest {
public static UndertowJaxrsServer server;
public static ResteasyDeployment deployment;
public static Client client;
public static KeycloakApplication application;
@BeforeClass
public static void undertowSetup() throws Exception {
deployment = new ResteasyDeployment();
deployment.setApplicationClass(KeycloakApplication.class.getName());
server = new UndertowJaxrsServer().start();
DeploymentInfo di = server.undertowDeployment(deployment);
di.setClassLoader(AbstractKeycloakServerTest.class.getClassLoader());
di.setContextPath("/");
di.setDeploymentName("Keycloak");
FilterInfo filter = Servlets.filter("SessionFilter", KeycloakSessionServletFilter.class);
di.addFilter(filter);
di.addFilterUrlMapping("SessionFilter", "/*", DispatcherType.REQUEST);
server.deploy(di);
application = (KeycloakApplication) deployment.getApplication();
client = new ResteasyClientBuilder().connectionPoolSize(10).build();
client.register(SkeletonKeyContextResolver.class);
}
@AfterClass
public static void undertowShutdown() throws Exception {
server.stop();
}
public static RealmRepresentation loadJson(String path) throws IOException {
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(path);
ByteArrayOutputStream os = new ByteArrayOutputStream();
int c;
while ((c = is.read()) != -1) {
os.write(c);
}
byte[] bytes = os.toByteArray();
System.out.println(new String(bytes));
return JsonSerialization.readValue(bytes, RealmRepresentation.class);
}
}

View file

@ -1,74 +0,0 @@
package org.keycloak.test;
import org.jboss.resteasy.logging.Logger;
import org.junit.After;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.ModelProvider;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.resources.KeycloakApplication;
import org.keycloak.services.utils.ModelProviderUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
@RunWith(Parameterized.class)
public abstract class AbstractKeycloakTest {
private static final Logger log = Logger.getLogger(AbstractKeycloakTest.class);
protected KeycloakSessionFactory factory;
protected KeycloakSession identitySession;
protected RealmManager realmManager;
@Parameterized.Parameters
public static Iterable<Object[]> parameters() {
Iterable<ModelProvider> modelProviders;
// We will run tests with all registered models if -Dkeycloak.model=all . Otherwise just with configured provider
String configuredProvider = System.getProperty(ModelProviderUtils.MODEL_PROVIDER);
if ("all".equalsIgnoreCase(configuredProvider)) {
modelProviders = ModelProviderUtils.getRegisteredProviders();
} else {
ModelProvider provider = ModelProviderUtils.getConfiguredModelProvider();
modelProviders = Arrays.asList(provider);
}
log.debug("Will use model providers: " + modelProviders);
List<Object[]> params = new ArrayList<Object[]>();
for (ModelProvider provider : modelProviders) {
params.add(new Object[] { provider.getId() });
}
return params;
}
public AbstractKeycloakTest(String providerId) {
System.setProperty(ModelProviderUtils.MODEL_PROVIDER, providerId);
}
@Before
public void before() throws Exception {
factory = KeycloakApplication.createSessionFactory();
identitySession = factory.createSession();
identitySession.getTransaction().begin();
realmManager = new RealmManager(identitySession);
}
@After
public void after() throws Exception {
identitySession.getTransaction().commit();
identitySession.close();
factory.close();
}
}

View file

@ -8,6 +8,13 @@ The testsuite uses Sellenium. By default it uses the HtmlUnit WebDriver, but can
To run the tests with Firefox add `-Dbrowser=firefox` or for Chrome add `-Dbrowser=chrome`
Mongo
-----
The testsuite is executed with JPA model implementation with data saved in H2 database by default. To run testsuite with Mongo model, just add property `-Dkeycloak.model=mongo` when executing it.
Note that this will automatically run embedded Mongo database on localhost/27018 and it will stop it after whole testsuite is finished.
So you don't need to have Mongo installed on your laptop to run mongo execution tests.
Test utils
==========
@ -41,6 +48,16 @@ For example to use the example themes run the server with:
**NOTE:** If `keycloak.theme.dir` is specified the default themes (base, rcue and keycloak) are loaded from the classpath
### Run server with Mongo model
To start a Keycloak server with identity model data persisted in Mongo database instead of default JPA/H2 you can run:
mvn exec:java -Pkeycloak-server -Dkeycloak.model=mongo
By default it's using database `keycloak` on localhost/27017 and it uses already existing data from this DB (no cleanup of existing data during bootstrap). Assumption is that you already have DB running on localhost/27017 . Use system properties to configure things differently:
mvn exec:java -Pkeycloak-server -Dkeycloak.model=mongo -Dkeycloak.mongo.host=localhost -Dkeycloak.mongo.port=27017 -Dkeycloak.mongo.db=keycloak -Dkeycloak.mongo.clearCollectionsOnStartup=false
TOTP codes
----------

View file

@ -350,11 +350,72 @@
<properties>
<keycloak.mongo.host>localhost</keycloak.mongo.host>
<keycloak.mongo.port>27017</keycloak.mongo.port>
<keycloak.mongo.port>27018</keycloak.mongo.port>
<keycloak.mongo.db>keycloak</keycloak.mongo.db>
<keycloak.mongo.clearCollectionsOnStartup>true</keycloak.mongo.clearCollectionsOnStartup>
<keycloak.mongo.clearOnStartup>true</keycloak.mongo.clearOnStartup>
</properties>
<build>
<plugins>
<!-- Postpone tests to "integration-test" phase, so that we can bootstrap embedded mongo on 27018 before running tests -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<executions>
<execution>
<id>test</id>
<phase>integration-test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<systemPropertyVariables>
<keycloak.mongo.host>${keycloak.mongo.host}</keycloak.mongo.host>
<keycloak.mongo.port>${keycloak.mongo.port}</keycloak.mongo.port>
<keycloak.mongo.db>${keycloak.mongo.db}</keycloak.mongo.db>
<keycloak.mongo.clearOnStartup>${keycloak.mongo.clearOnStartup}</keycloak.mongo.clearOnStartup>
</systemPropertyVariables>
</configuration>
</execution>
<execution>
<id>default-test</id>
<configuration>
<skip>true</skip>
</configuration>
</execution>
</executions>
</plugin>
<!-- Embedded mongo -->
<plugin>
<groupId>com.github.joelittlejohn.embedmongo</groupId>
<artifactId>embedmongo-maven-plugin</artifactId>
<executions>
<execution>
<id>start-mongodb</id>
<phase>pre-integration-test</phase>
<goals>
<goal>start</goal>
</goals>
<configuration>
<port>${keycloak.mongo.port}</port>
<logging>file</logging>
<logFile>${project.build.directory}/mongodb.log</logFile>
</configuration>
</execution>
<execution>
<id>stop-mongodb</id>
<phase>post-integration-test</phase>
<goals>
<goal>stop</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>