keycloak-memoizeit
Changes
model/api/pom.xml 11(+11 -0)
model/mongo/pom.xml 9(+2 -7)
model/mongo/src/main/java/org/keycloak/models/mongo/api/AbstractAttributedNoSQLObject.java 37(+0 -37)
model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/BasicDBListConverter.java 51(+11 -40)
model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/BasicDBObjectConverter.java 70(+46 -24)
model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/BasicDBObjectToMapConverter.java 44(+44 -0)
model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/MongoEntityConverter.java 30(+11 -19)
model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ApplicationAdapter.java 242(+95 -147)
model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoDBSessionFactory.java 70(+0 -70)
model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoKeycloakSession.java 62(+34 -28)
model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoKeycloakSessionFactory.java 83(+83 -0)
model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoKeycloakTransaction.java 2(+1 -1)
model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/OAuthClientAdapter.java 25(+12 -13)
model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java 853(+476 -377)
model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RoleAdapter.java 138(+124 -14)
model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/credentials/PasswordCredentialHandler.java 154(+0 -154)
model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/credentials/TOTPCredentialHandler.java 135(+0 -135)
model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/data/credentials/OTPData.java 66(+0 -66)
model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/data/credentials/PasswordData.java 66(+0 -66)
model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/ApplicationEntity.java 62(+39 -23)
model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/CredentialEntity.java 51(+51 -0)
model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/OAuthClientEntity.java 38(+19 -19)
model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/RealmEntity.java 170(+103 -67)
model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/RequiredCredentialEntity.java 55(+8 -47)
model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/SocialLinkEntity.java 20(+10 -10)
model/mongo/src/main/java/org/keycloak/models/mongo/MongoDBSessionFactoryTestContext.java 57(+0 -57)
model/mongo/src/main/java/org/keycloak/models/mongo/utils/SystemPropertiesConfigurationProvider.java 56(+56 -0)
model/pom.xml 2(+1 -1)
pom.xml 4(+2 -2)
services/pom.xml 18(+16 -2)
Details
model/api/pom.xml 11(+11 -0)
diff --git a/model/api/pom.xml b/model/api/pom.xml
index 2c64294..a5260d4 100755
--- a/model/api/pom.xml
+++ b/model/api/pom.xml
@@ -18,6 +18,17 @@
<artifactId>base64</artifactId>
</dependency>
<dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk16</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-core</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
diff --git a/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java b/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
new file mode 100644
index 0000000..4582a03
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
@@ -0,0 +1,86 @@
+package org.keycloak.models.utils;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.security.Key;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.bouncycastle.openssl.PEMWriter;
+import org.keycloak.models.RoleModel;
+import org.keycloak.util.PemUtils;
+
+/**
+ * Set of helper methods, which are useful in various model implementations.
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public final class KeycloakModelUtils {
+
+ private KeycloakModelUtils() {
+ }
+
+ private static AtomicLong counter = new AtomicLong(1);
+
+ public static String generateId() {
+ return counter.getAndIncrement() + "-" + System.currentTimeMillis();
+ }
+
+ public static PublicKey getPublicKey(String publicKeyPem) {
+ if (publicKeyPem != null) {
+ try {
+ return PemUtils.decodePublicKey(publicKeyPem);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ } else {
+ return null;
+ }
+ }
+
+ public static PrivateKey getPrivateKey(String privateKeyPem) {
+ if (privateKeyPem != null) {
+ try {
+ return PemUtils.decodePrivateKey(privateKeyPem);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return null;
+ }
+
+ public static String getPemFromKey(Key key) {
+ StringWriter writer = new StringWriter();
+ PEMWriter pemWriter = new PEMWriter(writer);
+ try {
+ pemWriter.writeObject(key);
+ pemWriter.flush();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ String s = writer.toString();
+ return PemUtils.removeBeginEnd(s);
+ }
+
+ /**
+ * Deep search if given role is descendant of composite role
+ *
+ * @param role role to check
+ * @param composite composite role
+ * @param visited set of already visited roles (used for recursion)
+ * @return true if "role" is descendant of "composite"
+ */
+ public static boolean searchFor(RoleModel role, RoleModel composite, Set<RoleModel> visited) {
+ if (visited.contains(composite)) return false;
+ visited.add(composite);
+ Set<RoleModel> composites = composite.getComposites();
+ if (composites.contains(role)) return true;
+ for (RoleModel contained : composites) {
+ if (!contained.isComposite()) continue;
+ if (searchFor(role, contained, visited)) return true;
+ }
+ return false;
+ }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaKeycloakSession.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaKeycloakSession.java
index 7b7db28..a072c75 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaKeycloakSession.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaKeycloakSession.java
@@ -2,7 +2,7 @@ package org.keycloak.models.jpa;
import org.keycloak.models.*;
import org.keycloak.models.jpa.entities.*;
-import org.keycloak.models.utils.KeycloakSessionUtils;
+import org.keycloak.models.utils.KeycloakModelUtils;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
@@ -28,7 +28,7 @@ public class JpaKeycloakSession implements KeycloakSession {
@Override
public RealmModel createRealm(String name) {
- return createRealm(KeycloakSessionUtils.generateId(), name);
+ return createRealm(KeycloakModelUtils.generateId(), name);
}
@Override
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
index 965cbbe..467953c 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
@@ -13,6 +13,7 @@ import org.keycloak.models.jpa.entities.SocialLinkEntity;
import org.keycloak.models.jpa.entities.UserEntity;
import org.keycloak.models.jpa.entities.UserRoleMappingEntity;
import org.keycloak.models.jpa.entities.UserScopeMappingEntity;
+import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.models.utils.Pbkdf2PasswordEncoder;
import org.keycloak.util.PemUtils;
import org.keycloak.models.ApplicationModel;
@@ -187,59 +188,29 @@ public class RealmAdapter implements RealmModel {
@Override
public PublicKey getPublicKey() {
if (publicKey != null) return publicKey;
- String pem = getPublicKeyPem();
- if (pem != null) {
- try {
- publicKey = PemUtils.decodePublicKey(pem);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
+ publicKey = KeycloakModelUtils.getPublicKey(getPublicKeyPem());
return publicKey;
}
@Override
public void setPublicKey(PublicKey publicKey) {
this.publicKey = publicKey;
- StringWriter writer = new StringWriter();
- PEMWriter pemWriter = new PEMWriter(writer);
- try {
- pemWriter.writeObject(publicKey);
- pemWriter.flush();
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- String s = writer.toString();
- setPublicKeyPem(PemUtils.removeBeginEnd(s));
+ String publicKeyPem = KeycloakModelUtils.getPemFromKey(publicKey);
+ setPublicKeyPem(publicKeyPem);
}
@Override
public PrivateKey getPrivateKey() {
if (privateKey != null) return privateKey;
- String pem = getPrivateKeyPem();
- if (pem != null) {
- try {
- privateKey = PemUtils.decodePrivateKey(pem);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
+ privateKey = KeycloakModelUtils.getPrivateKey(getPrivateKeyPem());
return privateKey;
}
@Override
public void setPrivateKey(PrivateKey privateKey) {
this.privateKey = privateKey;
- StringWriter writer = new StringWriter();
- PEMWriter pemWriter = new PEMWriter(writer);
- try {
- pemWriter.writeObject(privateKey);
- pemWriter.flush();
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- String s = writer.toString();
- setPrivateKeyPem(PemUtils.removeBeginEnd(s));
+ String privateKeyPem = KeycloakModelUtils.getPemFromKey(privateKey);
+ setPrivateKeyPem(privateKeyPem);
}
protected RequiredCredentialModel initRequiredCredentialModel(String type) {
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RoleAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RoleAdapter.java
index 2c9a8c9..e418441 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/RoleAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RoleAdapter.java
@@ -6,6 +6,7 @@ import org.keycloak.models.RoleModel;
import org.keycloak.models.jpa.entities.ApplicationRoleEntity;
import org.keycloak.models.jpa.entities.RealmRoleEntity;
import org.keycloak.models.jpa.entities.RoleEntity;
+import org.keycloak.models.utils.KeycloakModelUtils;
import javax.persistence.EntityManager;
import java.util.HashSet;
@@ -94,27 +95,13 @@ public class RoleAdapter implements RoleModel {
return set;
}
- public static boolean searchFor(RoleModel role, RoleModel composite, Set<RoleModel> visited) {
- if (visited.contains(composite)) return false;
- visited.add(composite);
- Set<RoleModel> composites = composite.getComposites();
- if (composites.contains(role)) return true;
- for (RoleModel contained : composites) {
- if (!contained.isComposite()) continue;
- if (searchFor(role, contained, visited)) return true;
- }
- return false;
- }
-
-
-
@Override
public boolean hasRole(RoleModel role) {
if (this.equals(role)) return true;
if (!isComposite()) return false;
Set<RoleModel> visited = new HashSet<RoleModel>();
- return searchFor(role, this, visited);
+ return KeycloakModelUtils.searchFor(role, this, visited);
}
@Override
model/mongo/pom.xml 9(+2 -7)
diff --git a/model/mongo/pom.xml b/model/mongo/pom.xml
index 537112a..d1b5952 100755
--- a/model/mongo/pom.xml
+++ b/model/mongo/pom.xml
@@ -5,7 +5,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
- <version>1.0-alpha-1</version>
+ <version>1.0-alpha-2-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -42,11 +42,6 @@
<scope>provided</scope>
</dependency>
<dependency>
- <groupId>org.picketlink</groupId>
- <artifactId>picketlink-idm-api</artifactId>
- <scope>provided</scope>
- </dependency>
- <dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<scope>provided</scope>
@@ -54,7 +49,7 @@
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
- <scope>test</scope>
+ <scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/api/MongoStore.java b/model/mongo/src/main/java/org/keycloak/models/mongo/api/MongoStore.java
new file mode 100755
index 0000000..89e4bb7
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/api/MongoStore.java
@@ -0,0 +1,43 @@
+package org.keycloak.models.mongo.api;
+
+import com.mongodb.DBObject;
+
+import java.util.List;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public interface MongoStore {
+
+ /**
+ * Insert new object
+ *
+ * @param object to update
+ */
+ void insertObject(MongoEntity object);
+
+ /**
+ * Update existing object
+ *
+ * @param object to update
+ */
+ void updateObject(MongoEntity object);
+
+
+ <T extends MongoEntity> T loadObject(Class<T> type, String oid);
+
+ <T extends MongoEntity> T loadSingleObject(Class<T> type, DBObject query);
+
+ <T extends MongoEntity> List<T> loadObjects(Class<T> type, DBObject query);
+
+ // Object must have filled oid
+ boolean removeObject(MongoEntity object);
+
+ boolean removeObject(Class<? extends MongoEntity> type, String oid);
+
+ boolean removeObjects(Class<? extends MongoEntity> type, DBObject query);
+
+ <S> boolean pushItemToList(MongoEntity object, String listPropertyName, S itemToPush, boolean skipIfAlreadyPresent);
+
+ <S> void pullItemFromList(MongoEntity object, String listPropertyName, S itemToPull);
+}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/api/types/Converter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/api/types/Converter.java
index a6b6c86..9c963c1 100644
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/api/types/Converter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/api/types/Converter.java
@@ -8,7 +8,13 @@ package org.keycloak.models.mongo.api.types;
*/
public interface Converter<T, S> {
- S convertObject(T objectToConvert);
+ /**
+ * Convert object from one type to expected type
+ *
+ * @param converterContext Encapsulates reference to converted object and other things, which might be helpful in conversion
+ * @return converted object
+ */
+ S convertObject(ConverterContext<T> converterContext);
Class<? extends T> getConverterObjectType();
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/api/types/ConverterContext.java b/model/mongo/src/main/java/org/keycloak/models/mongo/api/types/ConverterContext.java
new file mode 100644
index 0000000..aecb8c0
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/api/types/ConverterContext.java
@@ -0,0 +1,36 @@
+package org.keycloak.models.mongo.api.types;
+
+import java.util.List;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class ConverterContext<T> {
+
+ // 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;
+
+ // 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) {
+ this.objectToConvert = objectToConvert;
+ this.expectedReturnType = expectedReturnType;
+ this.genericTypes = genericTypes;
+ }
+
+ public T getObjectToConvert() {
+ return objectToConvert;
+ }
+
+ public Class<?> getExpectedReturnType() {
+ return expectedReturnType;
+ }
+
+ public List<Class<?>> getGenericTypes() {
+ return genericTypes;
+ }
+}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/api/types/TypeConverter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/api/types/TypeConverter.java
index e097930..05ad4d1 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/api/types/TypeConverter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/api/types/TypeConverter.java
@@ -46,19 +46,22 @@ public class TypeConverter {
}
- public <S> S convertDBObjectToApplicationObject(Object dbObject, Class<S> expectedApplicationObjectType) {
+ public Object convertDBObjectToApplicationObject(ConverterContext<Object> context) {
+ Object dbObject = context.getObjectToConvert();
+ Class<?> expectedApplicationObjectType = context.getExpectedReturnType();
+
Class<?> dbObjectType = dbObject.getClass();
- Converter<Object, S> converter;
+ 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, S>)appObjects.values().iterator().next();
+ converter = (Converter<Object, Object>)appObjects.values().iterator().next();
} else {
// Try to find converter for requested application type
- converter = (Converter<Object, S>)getAppConverterForType(expectedApplicationObjectType, appObjects);
+ converter = (Converter<Object, Object>)getAppConverterForType(context.getExpectedReturnType(), appObjects);
}
}
@@ -70,7 +73,7 @@ public class TypeConverter {
" but we need type " + expectedApplicationObjectType);
} */
- return converter.convertObject(dbObject);
+ return converter.convertObject(context);
}
@@ -84,7 +87,7 @@ public class TypeConverter {
throw new IllegalArgumentException("Converter " + converter + " has return type " + converter.getExpectedReturnType() +
" but we need type " + expectedDBObjectType);
}
- return converter.convertObject(applicationObject);
+ return converter.convertObject(new ConverterContext(applicationObject, expectedDBObjectType, null));
}
// Try to find converter for given type or all it's supertypes
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/impl/ObjectInfo.java b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/ObjectInfo.java
index b511626..0662ad6 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/impl/ObjectInfo.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/ObjectInfo.java
@@ -1,6 +1,6 @@
package org.keycloak.models.mongo.impl;
-import org.keycloak.models.mongo.api.NoSQLObject;
+import org.keycloak.models.mongo.api.MongoEntity;
import org.picketlink.common.properties.Property;
import java.util.Collection;
@@ -14,7 +14,7 @@ import java.util.Map;
*/
public class ObjectInfo {
- private final Class<? extends NoSQLObject> objectClass;
+ private final Class<? extends MongoEntity> objectClass;
private final String dbCollectionName;
@@ -22,7 +22,7 @@ public class ObjectInfo {
private final Map<String, Property<Object>> properties;
- public ObjectInfo(Class<? extends NoSQLObject> objectClass, String dbCollectionName, Property<String> oidProperty, List<Property<Object>> properties) {
+ public ObjectInfo(Class<? extends MongoEntity> objectClass, String dbCollectionName, Property<String> oidProperty, List<Property<Object>> properties) {
this.objectClass = objectClass;
this.dbCollectionName = dbCollectionName;
this.oidProperty = oidProperty;
@@ -34,7 +34,7 @@ public class ObjectInfo {
this.properties = Collections.unmodifiableMap(props);
}
- public Class<? extends NoSQLObject> getObjectClass() {
+ public Class<? extends MongoEntity> getObjectClass() {
return objectClass;
}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/BasicDBListConverter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/BasicDBListConverter.java
index 04824ba..544e1f8 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/BasicDBListConverter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/BasicDBListConverter.java
@@ -1,16 +1,17 @@
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.ArrayList;
+import java.util.List;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
-public class BasicDBListConverter implements Converter<BasicDBList, ArrayList> {
+public class BasicDBListConverter implements Converter<BasicDBList, List> {
private final TypeConverter typeConverter;
@@ -19,16 +20,14 @@ public class BasicDBListConverter implements Converter<BasicDBList, ArrayList> {
}
@Override
- public ArrayList convertObject(BasicDBList dbList) {
+ public List convertObject(ConverterContext<BasicDBList> context) {
+ BasicDBList dbList = context.getObjectToConvert();
ArrayList<Object> appObjects = new ArrayList<Object>();
- Class<?> expectedListElementType = null;
- for (Object dbObject : dbList) {
-
- if (expectedListElementType == null) {
- expectedListElementType = findExpectedListElementType(dbObject);
- }
+ Class<?> expectedListElementType = context.getGenericTypes().get(0);
- appObjects.add(typeConverter.convertDBObjectToApplicationObject(dbObject, expectedListElementType));
+ for (Object dbObject : dbList) {
+ ConverterContext<Object> newContext = new ConverterContext<Object>(dbObject, expectedListElementType, null);
+ appObjects.add(typeConverter.convertDBObjectToApplicationObject(newContext));
}
return appObjects;
}
@@ -39,35 +38,7 @@ public class BasicDBListConverter implements Converter<BasicDBList, ArrayList> {
}
@Override
- public Class<ArrayList> getExpectedReturnType() {
- return ArrayList.class;
- }
-
- private Class<?> findExpectedListElementType(Object dbObject) {
- if (dbObject instanceof BasicDBObject) {
- BasicDBObject basicDBObject = (BasicDBObject) dbObject;
- String type = (String)basicDBObject.get(ListConverter.OBJECT_TYPE);
- if (type == null) {
- throw new IllegalStateException("Not found OBJECT_TYPE key inside object " + dbObject);
- }
- basicDBObject.remove(ListConverter.OBJECT_TYPE);
-
- try {
- return Class.forName(type);
- } catch (ClassNotFoundException cnfe) {
- throw new RuntimeException(cnfe);
- }
- } else {
- // Special case (if we have String like "org.keycloak.Gender###MALE" we expect that substring before ### is className
- if (String.class.equals(dbObject.getClass())) {
- String dbObjString = (String)dbObject;
- if (dbObjString.contains(ClassCache.SPLIT)) {
- String className = dbObjString.substring(0, dbObjString.indexOf(ClassCache.SPLIT));
- return ClassCache.getInstance().getOrLoadClass(className);
- }
- }
-
- return dbObject.getClass();
- }
+ public Class<List> getExpectedReturnType() {
+ return List.class;
}
}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/BasicDBObjectConverter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/BasicDBObjectConverter.java
index a423652..62d0a82 100644
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/BasicDBObjectConverter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/BasicDBObjectConverter.java
@@ -1,12 +1,17 @@
package org.keycloak.models.mongo.impl.types;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.List;
+
import com.mongodb.BasicDBObject;
import org.jboss.logging.Logger;
-import org.keycloak.models.mongo.api.AttributedNoSQLObject;
-import org.keycloak.models.mongo.api.NoSQLObject;
+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.impl.MongoDBImpl;
+import org.keycloak.models.mongo.impl.MongoStoreImpl;
import org.keycloak.models.mongo.impl.ObjectInfo;
import org.picketlink.common.properties.Property;
import org.picketlink.common.reflection.Types;
@@ -14,31 +19,32 @@ import org.picketlink.common.reflection.Types;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
-public class BasicDBObjectConverter<S extends NoSQLObject> implements Converter<BasicDBObject, S> {
+public class BasicDBObjectConverter<S extends MongoEntity> implements Converter<BasicDBObject, S> {
private static final Logger logger = Logger.getLogger(BasicDBObjectConverter.class);
- private final MongoDBImpl mongoDBImpl;
+ private final MongoStoreImpl mongoStoreImpl;
private final TypeConverter typeConverter;
- private final Class<S> expectedNoSQLObjectType;
+ private final Class<S> expectedObjectType;
- public BasicDBObjectConverter(MongoDBImpl mongoDBImpl, TypeConverter typeConverter, Class<S> expectedNoSQLObjectType) {
- this.mongoDBImpl = mongoDBImpl;
+ public BasicDBObjectConverter(MongoStoreImpl mongoStoreImpl, TypeConverter typeConverter, Class<S> expectedObjectType) {
+ this.mongoStoreImpl = mongoStoreImpl;
this.typeConverter = typeConverter;
- this.expectedNoSQLObjectType = expectedNoSQLObjectType;
+ this.expectedObjectType = expectedObjectType;
}
@Override
- public S convertObject(BasicDBObject dbObject) {
+ public S convertObject(ConverterContext<BasicDBObject> context) {
+ BasicDBObject dbObject = context.getObjectToConvert();
if (dbObject == null) {
return null;
}
- ObjectInfo objectInfo = mongoDBImpl.getObjectInfo(expectedNoSQLObjectType);
+ ObjectInfo objectInfo = mongoStoreImpl.getObjectInfo(expectedObjectType);
S object;
try {
- object = expectedNoSQLObjectType.newInstance();
+ object = expectedObjectType.newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
@@ -58,34 +64,50 @@ public class BasicDBObjectConverter<S extends NoSQLObject> implements Converter<
// It's declared property with @DBField annotation
setPropertyValue(object, value, property);
- } else if (object instanceof AttributedNoSQLObject) {
- // It's attributed object and property is not declared, so we will call setAttribute
- ((AttributedNoSQLObject)object).setAttribute(key, value.toString());
-
} else {
// Show warning if it's unknown
- logger.warn("Property with key " + key + " not known for type " + expectedNoSQLObjectType);
+ logger.warn("Property with key " + key + " not known for type " + expectedObjectType);
}
}
return object;
}
- private void setPropertyValue(NoSQLObject object, Object valueFromDB, Property property) {
+ private void setPropertyValue(MongoEntity object, Object valueFromDB, Property property) {
if (valueFromDB == null) {
property.setValue(object, null);
return;
}
- Class<?> expectedReturnType = property.getJavaClass();
- // handle primitives
- expectedReturnType = Types.boxedClass(expectedReturnType);
+ ConverterContext<Object> context;
+
+ Type type = property.getBaseType();
+
+ // This can be the case when we have parameterized type (like "List<String>")
+ if (type instanceof ParameterizedType) {
+ ParameterizedType parameterized = (ParameterizedType) type;
+ Type[] genericTypeArguments = parameterized.getActualTypeArguments();
+
+ List<Class<?>> genericTypes = new ArrayList<Class<?>>();
+ for (Type genericType : genericTypeArguments) {
+ genericTypes.add((Class<?>)genericType);
+ }
+
+ Class<?> expectedReturnType = (Class<?>)parameterized.getRawType();
+ context = new ConverterContext<Object>(valueFromDB, expectedReturnType, genericTypes);
+ } else {
+ Class<?> expectedReturnType = (Class<?>)type;
+ // handle primitives
+ expectedReturnType = Types.boxedClass(expectedReturnType);
+ context = new ConverterContext<Object>(valueFromDB, expectedReturnType, null);
+ }
+
+ Object appObject = typeConverter.convertDBObjectToApplicationObject(context);
- Object appObject = typeConverter.convertDBObjectToApplicationObject(valueFromDB, expectedReturnType);
if (Types.boxedClass(property.getJavaClass()).isAssignableFrom(appObject.getClass())) {
property.setValue(object, appObject);
} else {
- throw new IllegalStateException("Converted object " + appObject + " is not of type " + expectedReturnType +
+ throw new IllegalStateException("Converted object " + appObject + " is not of type " + context.getExpectedReturnType() +
". So can't be assigned as property " + property.getName() + " of " + object.getClass());
}
}
@@ -97,6 +119,6 @@ public class BasicDBObjectConverter<S extends NoSQLObject> implements Converter<
@Override
public Class<S> getExpectedReturnType() {
- return expectedNoSQLObjectType;
+ return expectedObjectType;
}
}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/BasicDBObjectToMapConverter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/BasicDBObjectToMapConverter.java
new file mode 100644
index 0000000..4314ade
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/BasicDBObjectToMapConverter.java
@@ -0,0 +1,44 @@
+package org.keycloak.models.mongo.impl.types;
+
+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;
+
+/**
+ * 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> {
+
+ @Override
+ public Map convertObject(ConverterContext<BasicDBObject> context) {
+ BasicDBObject objectToConvert = context.getObjectToConvert();
+
+ HashMap<String, String> result = new HashMap<String, String>();
+ for (Map.Entry<String, Object> entry : objectToConvert.entrySet()) {
+ String key = entry.getKey();
+ String value = (String)entry.getValue();
+
+ if (key.contains(MapConverter.DOT_PLACEHOLDER)) {
+ key = key.replaceAll(MapConverter.DOT_PLACEHOLDER, ".");
+ }
+
+ result.put(key, value);
+ }
+ return result;
+ }
+
+ @Override
+ public Class<? extends BasicDBObject> getConverterObjectType() {
+ return BasicDBObject.class;
+ }
+
+ @Override
+ public Class<Map> getExpectedReturnType() {
+ return Map.class;
+ }
+}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/EnumToStringConverter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/EnumToStringConverter.java
index 2a800df..ffb758f 100644
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/EnumToStringConverter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/EnumToStringConverter.java
@@ -1,6 +1,7 @@
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>
@@ -9,9 +10,10 @@ public class EnumToStringConverter implements Converter<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(Enum objectToConvert) {
- String className = objectToConvert.getClass().getName();
- return className + ClassCache.SPLIT + objectToConvert.toString();
+ public String convertObject(ConverterContext<Enum> context) {
+ Enum objectToConvert = context.getObjectToConvert();
+
+ return objectToConvert.toString();
}
@Override
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/ListConverter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/ListConverter.java
index 49fb627..739ff9a 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/ListConverter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/ListConverter.java
@@ -3,6 +3,7 @@ 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;
@@ -12,9 +13,6 @@ import java.util.List;
*/
public class ListConverter<T extends List> implements Converter<T, BasicDBList> {
- // Key for ObjectType field, which points to actual Java type of element objects inside list
- static final String OBJECT_TYPE = "OBJECT_TYPE";
-
private final TypeConverter typeConverter;
private final Class<T> listType;
@@ -24,17 +22,13 @@ public class ListConverter<T extends List> implements Converter<T, BasicDBList>
}
@Override
- public BasicDBList convertObject(T appObjectsList) {
+ 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);
- // We need to add OBJECT_TYPE key to object, so we can retrieve correct Java type of object during load of this list
- if (dbObject instanceof BasicDBObject) {
- BasicDBObject basicDBObject = (BasicDBObject)dbObject;
- basicDBObject.put(OBJECT_TYPE, appObject.getClass().getName());
- }
-
dbObjects.add(dbObject);
}
return dbObjects;
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/MapConverter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/MapConverter.java
new file mode 100644
index 0000000..0ac8051
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/MapConverter.java
@@ -0,0 +1,52 @@
+package org.keycloak.models.mongo.impl.types;
+
+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;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class MapConverter<T extends Map> implements Converter<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) {
+ this.mapType = mapType;
+ }
+
+ @Override
+ public BasicDBObject convertObject(ConverterContext<T> context) {
+ T objectToConvert = context.getObjectToConvert();
+
+ BasicDBObject dbObject = new BasicDBObject();
+ Set<Map.Entry> entries = objectToConvert.entrySet();
+ for (Map.Entry entry : entries) {
+ String key = (String)entry.getKey();
+ String value = (String)entry.getValue();
+
+ if (key.contains(".")) {
+ key = key.replaceAll("\\.", DOT_PLACEHOLDER);
+ }
+
+ dbObject.put(key, value);
+ }
+ return dbObject;
+ }
+
+ @Override
+ public Class<? extends T> getConverterObjectType() {
+ return mapType;
+ }
+
+ @Override
+ public Class<BasicDBObject> getExpectedReturnType() {
+ return BasicDBObject.class;
+ }
+}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/SimpleConverter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/SimpleConverter.java
index 5ba1de5..eca7c1a 100644
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/SimpleConverter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/SimpleConverter.java
@@ -1,8 +1,11 @@
package org.keycloak.models.mongo.impl.types;
import org.keycloak.models.mongo.api.types.Converter;
+import org.keycloak.models.mongo.api.types.ConverterContext;
/**
+ * Just returns input
+ *
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class SimpleConverter<T> implements Converter<T, T> {
@@ -14,7 +17,8 @@ public class SimpleConverter<T> implements Converter<T, T> {
}
@Override
- public T convertObject(T objectToConvert) {
+ public T convertObject(ConverterContext<T> context) {
+ T objectToConvert = context.getObjectToConvert();
return objectToConvert;
}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/StringToEnumConverter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/StringToEnumConverter.java
index 0c948ec..5bb84a8 100644
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/StringToEnumConverter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/StringToEnumConverter.java
@@ -1,6 +1,7 @@
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>
@@ -8,15 +9,10 @@ import org.keycloak.models.mongo.api.types.Converter;
public class StringToEnumConverter implements Converter<String, Enum> {
@Override
- public Enum convertObject(String objectToConvert) {
- int index = objectToConvert.indexOf(ClassCache.SPLIT);
- if (index == -1) {
- throw new IllegalStateException("Can't convert enum type with value " + objectToConvert);
- }
+ public Enum convertObject(ConverterContext<String> context) {
+ String enumValue = context.getObjectToConvert();
- String className = objectToConvert.substring(0, index);
- String enumValue = objectToConvert.substring(index + 3);
- Class<? extends Enum> clazz = (Class<? extends Enum>)ClassCache.getInstance().getOrLoadClass(className);
+ Class<? extends Enum> clazz = (Class<? extends Enum>)context.getExpectedReturnType();
return Enum.valueOf(clazz, enumValue);
}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ApplicationAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ApplicationAdapter.java
index c1a4dd2..835d18d 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ApplicationAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ApplicationAdapter.java
@@ -1,13 +1,15 @@
package org.keycloak.models.mongo.keycloak.adapters;
+import com.mongodb.DBObject;
+import com.mongodb.QueryBuilder;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
-import org.keycloak.models.mongo.api.NoSQL;
-import org.keycloak.models.mongo.api.query.NoSQLQuery;
-import org.keycloak.models.mongo.keycloak.data.ApplicationData;
-import org.keycloak.models.mongo.keycloak.data.RoleData;
-import org.keycloak.models.mongo.keycloak.data.UserData;
+import org.keycloak.models.mongo.api.MongoStore;
+import org.keycloak.models.mongo.keycloak.entities.ApplicationEntity;
+import org.keycloak.models.mongo.keycloak.entities.RoleEntity;
+import org.keycloak.models.mongo.keycloak.entities.UserEntity;
+import org.keycloak.models.mongo.utils.MongoModelUtils;
import java.util.ArrayList;
import java.util.HashSet;
@@ -19,29 +21,38 @@ import java.util.Set;
*/
public class ApplicationAdapter implements ApplicationModel {
- private final ApplicationData application;
- private final NoSQL noSQL;
+ private final ApplicationEntity application;
+ private final MongoStore mongoStore;
- private UserData resourceUser;
+ private UserAdapter resourceUser;
- public ApplicationAdapter(ApplicationData applicationData, NoSQL noSQL) {
- this.application = applicationData;
- this.noSQL = noSQL;
+ public ApplicationAdapter(ApplicationEntity applicationEntity, MongoStore mongoStore) {
+ this(applicationEntity, null, mongoStore);
+ }
+
+ public ApplicationAdapter(ApplicationEntity applicationEntity, UserAdapter resourceUser, MongoStore mongoStore) {
+ this.application = applicationEntity;
+ this.resourceUser = resourceUser;
+ this.mongoStore = mongoStore;
}
@Override
public void updateApplication() {
- noSQL.saveObject(application);
+ mongoStore.updateObject(application);
}
@Override
- public UserModel getApplicationUser() {
+ public UserAdapter getApplicationUser() {
// This is not thread-safe. Assumption is that ApplicationAdapter instance is per-client object
if (resourceUser == null) {
- resourceUser = noSQL.loadObject(UserData.class, application.getResourceUserId());
+ UserEntity userEntity = mongoStore.loadObject(UserEntity.class, application.getResourceUserId());
+ if (userEntity == null) {
+ throw new IllegalStateException("User " + application.getResourceUserId() + " not found");
+ }
+ resourceUser = new UserAdapter(userEntity, mongoStore);
}
- return resourceUser != null ? new UserAdapter(resourceUser, noSQL) : null;
+ return resourceUser;
}
@Override
@@ -101,199 +112,136 @@ public class ApplicationAdapter implements ApplicationModel {
@Override
public RoleAdapter getRole(String name) {
- NoSQLQuery query = noSQL.createQueryBuilder()
- .andCondition("name", name)
- .andCondition("applicationId", getId())
- .build();
- RoleData role = noSQL.loadSingleObject(RoleData.class, query);
+ DBObject query = new QueryBuilder()
+ .and("name").is(name)
+ .and("applicationId").is(getId())
+ .get();
+ RoleEntity role = mongoStore.loadSingleObject(RoleEntity.class, query);
if (role == null) {
return null;
} else {
- return new RoleAdapter(role, noSQL);
+ return new RoleAdapter(role, this, mongoStore);
}
}
@Override
public RoleModel getRoleById(String id) {
- RoleData role = noSQL.loadObject(RoleData.class, id);
+ RoleEntity role = mongoStore.loadObject(RoleEntity.class, id);
if (role == null) {
return null;
} else {
- return new RoleAdapter(role, noSQL);
+ return new RoleAdapter(role, this, mongoStore);
}
}
@Override
- public void grantRole(UserModel user, RoleModel role) {
- UserData userData = ((UserAdapter)user).getUser();
- noSQL.pushItemToList(userData, "roleIds", role.getId());
- }
+ public RoleAdapter addRole(String name) {
+ RoleAdapter existing = getRole(name);
+ if (existing != null) {
+ return existing;
+ }
- @Override
- public boolean hasRole(UserModel user, String role) {
- RoleModel roleModel = getRole(role);
- return hasRole(user, roleModel);
- }
+ RoleEntity roleEntity = new RoleEntity();
+ roleEntity.setName(name);
+ roleEntity.setApplicationId(getId());
- @Override
- public boolean hasRole(UserModel user, RoleModel role) {
- UserData userData = ((UserAdapter)user).getUser();
-
- List<String> roleIds = userData.getRoleIds();
- String roleId = role.getId();
- if (roleIds != null) {
- for (String currentId : roleIds) {
- if (roleId.equals(currentId)) {
- return true;
- }
- }
- }
- return false;
+ mongoStore.insertObject(roleEntity);
+ return new RoleAdapter(roleEntity, this, mongoStore);
}
@Override
- public RoleAdapter addRole(String name) {
- if (getRole(name) != null) {
- throw new IllegalArgumentException("Role " + name + " already exists");
- }
-
- RoleData roleData = new RoleData();
- roleData.setName(name);
- roleData.setApplicationId(getId());
-
- noSQL.saveObject(roleData);
- return new RoleAdapter(roleData, noSQL);
+ public boolean removeRoleById(String id) {
+ return mongoStore.removeObject(RoleEntity.class ,id);
}
@Override
- public List<RoleModel> getRoles() {
- NoSQLQuery query = noSQL.createQueryBuilder()
- .andCondition("applicationId", getId())
- .build();
- List<RoleData> roles = noSQL.loadObjects(RoleData.class, query);
-
- List<RoleModel> result = new ArrayList<RoleModel>();
- for (RoleData role : roles) {
- result.add(new RoleAdapter(role, noSQL));
+ public Set<RoleModel> getRoles() {
+ DBObject query = new QueryBuilder()
+ .and("applicationId").is(getId())
+ .get();
+ List<RoleEntity> roles = mongoStore.loadObjects(RoleEntity.class, query);
+
+ Set<RoleModel> result = new HashSet<RoleModel>();
+ for (RoleEntity role : roles) {
+ result.add(new RoleAdapter(role, this, mongoStore));
}
return result;
}
- // Static so that it can be used from RealmAdapter as well
- static List<RoleData> getAllRolesOfUser(UserModel user, NoSQL noSQL) {
- UserData userData = ((UserAdapter)user).getUser();
- List<String> roleIds = userData.getRoleIds();
-
- NoSQLQuery query = noSQL.createQueryBuilder()
- .inCondition("_id", roleIds)
- .build();
- return noSQL.loadObjects(RoleData.class, query);
- }
-
@Override
- public Set<String> getRoleMappingValues(UserModel user) {
- Set<String> result = new HashSet<String>();
- List<RoleData> roles = getAllRolesOfUser(user, noSQL);
- // TODO: Maybe improve as currently we need to obtain all roles and then filter programmatically...
- for (RoleData role : roles) {
+ public Set<RoleModel> getApplicationRoleMappings(UserModel user) {
+ Set<RoleModel> result = new HashSet<RoleModel>();
+ List<RoleEntity> roles = MongoModelUtils.getAllRolesOfUser(user, mongoStore);
+
+ for (RoleEntity role : roles) {
if (getId().equals(role.getApplicationId())) {
- result.add(role.getName());
+ result.add(new RoleAdapter(role, this, mongoStore));
}
}
return result;
}
@Override
- public List<RoleModel> getRoleMappings(UserModel user) {
- List<RoleModel> result = new ArrayList<RoleModel>();
- List<RoleData> roles = getAllRolesOfUser(user, noSQL);
- // TODO: Maybe improve as currently we need to obtain all roles and then filter programmatically...
- for (RoleData role : roles) {
+ public void addScope(RoleModel role) {
+ UserAdapter appUser = getApplicationUser();
+ mongoStore.pushItemToList(appUser.getUser(), "scopeIds", role.getId(), true);
+ }
+
+ @Override
+ public Set<RoleModel> getApplicationScopeMappings(UserModel user) {
+ Set<RoleModel> result = new HashSet<RoleModel>();
+ List<RoleEntity> roles = MongoModelUtils.getAllScopesOfUser(user, mongoStore);
+
+ for (RoleEntity role : roles) {
if (getId().equals(role.getApplicationId())) {
- result.add(new RoleAdapter(role, noSQL));
+ result.add(new RoleAdapter(role, this, mongoStore));
}
}
return result;
}
@Override
- public void deleteRoleMapping(UserModel user, RoleModel role) {
- UserData userData = ((UserAdapter)user).getUser();
- noSQL.pullItemFromList(userData, "roleIds", role.getId());
+ public List<String> getDefaultRoles() {
+ return application.getDefaultRoles();
}
@Override
- public void addScopeMapping(UserModel agent, String roleName) {
- RoleAdapter role = getRole(roleName);
+ public void addDefaultRole(String name) {
+ RoleModel role = getRole(name);
if (role == null) {
- throw new RuntimeException("Role not found");
+ addRole(name);
}
- addScopeMapping(agent, role);
- }
-
- @Override
- public void addScopeMapping(UserModel agent, RoleModel role) {
- UserData userData = ((UserAdapter)agent).getUser();
- noSQL.pushItemToList(userData, "scopeIds", role.getId());
- }
-
- @Override
- public void deleteScopeMapping(UserModel user, RoleModel role) {
- UserData userData = ((UserAdapter)user).getUser();
- noSQL.pullItemFromList(userData, "scopeIds", role.getId());
- }
-
- // Static so that it can be used from RealmAdapter as well
- static List<RoleData> getAllScopesOfUser(UserModel user, NoSQL noSQL) {
- UserData userData = ((UserAdapter)user).getUser();
- List<String> roleIds = userData.getScopeIds();
-
- NoSQLQuery query = noSQL.createQueryBuilder()
- .inCondition("_id", roleIds)
- .build();
- return noSQL.loadObjects(RoleData.class, query);
+ mongoStore.pushItemToList(application, "defaultRoles", name, true);
}
@Override
- public Set<String> getScopeMappingValues(UserModel agent) {
- Set<String> result = new HashSet<String>();
- List<RoleData> roles = getAllScopesOfUser(agent, noSQL);
- // TODO: Maybe improve as currently we need to obtain all roles and then filter programmatically...
- for (RoleData role : roles) {
- if (getId().equals(role.getApplicationId())) {
- result.add(role.getName());
+ public void updateDefaultRoles(String[] defaultRoles) {
+ List<String> roleNames = new ArrayList<String>();
+ for (String roleName : defaultRoles) {
+ RoleModel role = getRole(roleName);
+ if (role == null) {
+ addRole(roleName);
}
- }
- return result;
- }
- @Override
- public List<RoleModel> getScopeMappings(UserModel agent) {
- List<RoleModel> result = new ArrayList<RoleModel>();
- List<RoleData> roles = getAllScopesOfUser(agent, noSQL);
- // TODO: Maybe improve as currently we need to obtain all roles and then filter programmatically...
- for (RoleData role : roles) {
- if (getId().equals(role.getApplicationId())) {
- result.add(new RoleAdapter(role, noSQL));
- }
+ roleNames.add(roleName);
}
- return result;
- }
- @Override
- public List<String> getDefaultRoles() {
- return null; //To change body of implemented methods use File | Settings | File Templates.
+ application.setDefaultRoles(roleNames);
}
@Override
- public void addDefaultRole(String name) {
- //To change body of implemented methods use File | Settings | File Templates.
+ public boolean equals(Object o) {
+ if (o == null) return false;
+ if (o == this) return true;
+ if (!(o instanceof ApplicationAdapter)) return false;
+ ApplicationAdapter app = (ApplicationAdapter)o;
+ return app.getId().equals(getId());
}
@Override
- public void updateDefaultRoles(String[] defaultRoles) {
- //To change body of implemented methods use File | Settings | File Templates.
+ public int hashCode() {
+ return getId().hashCode();
}
}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoKeycloakSessionFactory.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoKeycloakSessionFactory.java
new file mode 100755
index 0000000..6c2af86
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoKeycloakSessionFactory.java
@@ -0,0 +1,83 @@
+package org.keycloak.models.mongo.keycloak.adapters;
+
+import com.mongodb.DB;
+import com.mongodb.MongoClient;
+import org.jboss.logging.Logger;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.mongo.api.MongoEntity;
+import org.keycloak.models.mongo.api.MongoStore;
+import org.keycloak.models.mongo.impl.MongoStoreImpl;
+import org.keycloak.models.mongo.keycloak.entities.ApplicationEntity;
+import org.keycloak.models.mongo.keycloak.entities.CredentialEntity;
+import org.keycloak.models.mongo.keycloak.entities.OAuthClientEntity;
+import org.keycloak.models.mongo.keycloak.entities.RealmEntity;
+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;
+
+/**
+ * KeycloakSessionFactory implementation based on MongoDB
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class MongoKeycloakSessionFactory implements KeycloakSessionFactory {
+ protected static final Logger logger = Logger.getLogger(MongoKeycloakSessionFactory.class);
+
+ private static final Class<? extends MongoEntity>[] MANAGED_ENTITY_TYPES = (Class<? extends MongoEntity>[])new Class<?>[] {
+ RealmEntity.class,
+ UserEntity.class,
+ RoleEntity.class,
+ RequiredCredentialEntity.class,
+ CredentialEntity.class,
+ SocialLinkEntity.class,
+ ApplicationEntity.class,
+ 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());
+
+ DB db = mongoClient.getDB(config.getDbName());
+ mongoStore = new MongoStoreImpl(db, config.isClearCollectionsOnStartup(), MANAGED_ENTITY_TYPES);
+
+ } catch (UnknownHostException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public KeycloakSession createSession() {
+ return new MongoKeycloakSession(mongoStore);
+ }
+
+ @Override
+ public void close() {
+ logger.info("Closing MongoDB client");
+ mongoClient.close();
+
+ if (embeddedMongo != null) {
+ embeddedMongo.stopEmbeddedMongo();
+ }
+ }
+}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/OAuthClientAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/OAuthClientAdapter.java
index d522db9..e4b866d 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/OAuthClientAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/OAuthClientAdapter.java
@@ -2,28 +2,27 @@ package org.keycloak.models.mongo.keycloak.adapters;
import org.keycloak.models.OAuthClientModel;
import org.keycloak.models.UserModel;
-import org.keycloak.models.mongo.api.NoSQL;
-import org.keycloak.models.mongo.keycloak.data.OAuthClientData;
-import org.keycloak.models.mongo.keycloak.data.UserData;
+import org.keycloak.models.mongo.api.MongoStore;
+import org.keycloak.models.mongo.keycloak.entities.OAuthClientEntity;
+import org.keycloak.models.mongo.keycloak.entities.UserEntity;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class OAuthClientAdapter implements OAuthClientModel {
- private final OAuthClientData delegate;
+ private final OAuthClientEntity delegate;
private UserAdapter oauthAgent;
- private final NoSQL noSQL;
+ private final MongoStore mongoStore;
- public OAuthClientAdapter(OAuthClientData oauthClientData, UserAdapter oauthAgent, NoSQL noSQL) {
- this.delegate = oauthClientData;
+ public OAuthClientAdapter(OAuthClientEntity oauthClientEntity, UserAdapter oauthAgent, MongoStore mongoStore) {
+ this.delegate = oauthClientEntity;
this.oauthAgent = oauthAgent;
- this.noSQL = noSQL;
+ this.mongoStore = mongoStore;
}
- public OAuthClientAdapter(OAuthClientData oauthClientData, NoSQL noSQL) {
- this.delegate = oauthClientData;
- this.noSQL = noSQL;
+ public OAuthClientAdapter(OAuthClientEntity oauthClientEntity, MongoStore mongoStore) {
+ this(oauthClientEntity, null, mongoStore);
}
@Override
@@ -35,8 +34,8 @@ public class OAuthClientAdapter implements OAuthClientModel {
public UserModel getOAuthAgent() {
// This is not thread-safe. Assumption is that OAuthClientAdapter instance is per-client object
if (oauthAgent == null) {
- UserData user = noSQL.loadObject(UserData.class, delegate.getOauthAgentId());
- oauthAgent = user!=null ? new UserAdapter(user, noSQL) : null;
+ UserEntity user = mongoStore.loadObject(UserEntity.class, delegate.getOauthAgentId());
+ oauthAgent = user!=null ? new UserAdapter(user, mongoStore) : null;
}
return oauthAgent;
}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
index 7d4aa72..48ab577 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
@@ -1,63 +1,60 @@
package org.keycloak.models.mongo.keycloak.adapters;
-import org.bouncycastle.openssl.PEMWriter;
-import org.keycloak.PemUtils;
+import com.mongodb.DBObject;
+import com.mongodb.QueryBuilder;
+import org.jboss.logging.Logger;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.OAuthClientModel;
+import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RequiredCredentialModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.SocialLinkModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
-import org.keycloak.models.mongo.api.NoSQL;
-import org.keycloak.models.mongo.api.query.NoSQLQuery;
-import org.keycloak.models.mongo.api.query.NoSQLQueryBuilder;
-import org.keycloak.models.mongo.keycloak.credentials.PasswordCredentialHandler;
-import org.keycloak.models.mongo.keycloak.credentials.TOTPCredentialHandler;
-import org.keycloak.models.mongo.keycloak.data.ApplicationData;
-import org.keycloak.models.mongo.keycloak.data.OAuthClientData;
-import org.keycloak.models.mongo.keycloak.data.RealmData;
-import org.keycloak.models.mongo.keycloak.data.RequiredCredentialData;
-import org.keycloak.models.mongo.keycloak.data.RoleData;
-import org.keycloak.models.mongo.keycloak.data.SocialLinkData;
-import org.keycloak.models.mongo.keycloak.data.UserData;
-import org.keycloak.representations.idm.CredentialRepresentation;
-import org.picketlink.idm.credential.Credentials;
-
-import java.io.IOException;
-import java.io.StringWriter;
+import org.keycloak.models.mongo.api.MongoStore;
+import org.keycloak.models.mongo.keycloak.entities.ApplicationEntity;
+import org.keycloak.models.mongo.keycloak.entities.CredentialEntity;
+import org.keycloak.models.mongo.keycloak.entities.OAuthClientEntity;
+import org.keycloak.models.mongo.keycloak.entities.RealmEntity;
+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.MongoModelUtils;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.models.utils.Pbkdf2PasswordEncoder;
+import org.keycloak.models.utils.TimeBasedOTP;
+
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.regex.Pattern;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class RealmAdapter implements RealmModel {
- private final RealmData realm;
- private final NoSQL noSQL;
+ private static final Logger logger = Logger.getLogger(RealmAdapter.class);
+
+ private final RealmEntity realm;
+ private final MongoStore mongoStore;
protected volatile transient PublicKey publicKey;
protected volatile transient PrivateKey privateKey;
- // TODO: likely shouldn't be static. And ATM, just empty map is passed -> It's not possible to configure stuff like PasswordEncoder etc.
- private static PasswordCredentialHandler passwordCredentialHandler = new PasswordCredentialHandler(new HashMap<String, Object>());
- private static TOTPCredentialHandler totpCredentialHandler = new TOTPCredentialHandler(new HashMap<String, Object>());
-
- public RealmAdapter(RealmData realmData, NoSQL noSQL) {
- this.realm = realmData;
- this.noSQL = noSQL;
- }
+ private volatile transient PasswordPolicy passwordPolicy;
- protected String getOid() {
- return realm.getOid();
+ public RealmAdapter(RealmEntity realmEntity, MongoStore mongoStore) {
+ this.realm = realmEntity;
+ this.mongoStore = mongoStore;
}
@Override
@@ -88,79 +85,83 @@ public class RealmAdapter implements RealmModel {
}
@Override
- public boolean isSocial() {
- return realm.isSocial();
+ public boolean isSslNotRequired() {
+ return realm.isSslNotRequired();
}
@Override
- public void setSocial(boolean social) {
- realm.setSocial(social);
+ public void setSslNotRequired(boolean sslNotRequired) {
+ realm.setSslNotRequired(sslNotRequired);
updateRealm();
}
@Override
- public boolean isAutomaticRegistrationAfterSocialLogin() {
- return realm.isAutomaticRegistrationAfterSocialLogin();
+ public boolean isRegistrationAllowed() {
+ return realm.isRegistrationAllowed();
}
@Override
- public void setAutomaticRegistrationAfterSocialLogin(boolean automaticRegistrationAfterSocialLogin) {
- realm.setAutomaticRegistrationAfterSocialLogin(automaticRegistrationAfterSocialLogin);
+ public void setRegistrationAllowed(boolean registrationAllowed) {
+ realm.setRegistrationAllowed(registrationAllowed);
updateRealm();
}
@Override
- public boolean isSslNotRequired() {
- return realm.isSslNotRequired();
+ public boolean isVerifyEmail() {
+ return realm.isVerifyEmail();
}
@Override
- public void setSslNotRequired(boolean sslNotRequired) {
- realm.setSslNotRequired(sslNotRequired);
+ public void setVerifyEmail(boolean verifyEmail) {
+ realm.setVerifyEmail(verifyEmail);
updateRealm();
}
@Override
- public boolean isCookieLoginAllowed() {
- return realm.isCookieLoginAllowed();
+ public boolean isResetPasswordAllowed() {
+ return realm.isResetPasswordAllowed();
}
@Override
- public void setCookieLoginAllowed(boolean cookieLoginAllowed) {
- realm.setCookieLoginAllowed(cookieLoginAllowed);
+ public void setResetPasswordAllowed(boolean resetPassword) {
+ realm.setResetPasswordAllowed(resetPassword);
updateRealm();
}
@Override
- public boolean isRegistrationAllowed() {
- return realm.isRegistrationAllowed();
+ public boolean isSocial() {
+ return realm.isSocial();
}
@Override
- public void setRegistrationAllowed(boolean registrationAllowed) {
- realm.setRegistrationAllowed(registrationAllowed);
+ public void setSocial(boolean social) {
+ realm.setSocial(social);
updateRealm();
}
@Override
- public boolean isVerifyEmail() {
- return realm.isVerifyEmail();
+ public boolean isUpdateProfileOnInitialSocialLogin() {
+ return realm.isUpdateProfileOnInitialSocialLogin();
}
@Override
- public void setVerifyEmail(boolean verifyEmail) {
- realm.setVerifyEmail(verifyEmail);
+ public void setUpdateProfileOnInitialSocialLogin(boolean updateProfileOnInitialSocialLogin) {
+ realm.setUpdateProfileOnInitialSocialLogin(updateProfileOnInitialSocialLogin);
updateRealm();
}
@Override
- public boolean isResetPasswordAllowed() {
- return realm.isResetPasswordAllowed();
+ public PasswordPolicy getPasswordPolicy() {
+ if (passwordPolicy == null) {
+ passwordPolicy = new PasswordPolicy(realm.getPasswordPolicy());
+ }
+ return passwordPolicy;
}
@Override
- public void setResetPasswordAllowed(boolean resetPassword) {
- realm.setResetPasswordAllowed(resetPassword);
+ public void setPasswordPolicy(PasswordPolicy policy) {
+ this.passwordPolicy = policy;
+ realm.setPasswordPolicy(policy.toString());
updateRealm();
}
@@ -224,200 +225,220 @@ public class RealmAdapter implements RealmModel {
@Override
public PublicKey getPublicKey() {
if (publicKey != null) return publicKey;
- String pem = getPublicKeyPem();
- if (pem != null) {
- try {
- publicKey = PemUtils.decodePublicKey(pem);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
+ publicKey = KeycloakModelUtils.getPublicKey(getPublicKeyPem());
return publicKey;
}
@Override
public void setPublicKey(PublicKey publicKey) {
this.publicKey = publicKey;
- StringWriter writer = new StringWriter();
- PEMWriter pemWriter = new PEMWriter(writer);
- try {
- pemWriter.writeObject(publicKey);
- pemWriter.flush();
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- String s = writer.toString();
- setPublicKeyPem(PemUtils.removeBeginEnd(s));
+ String publicKeyPem = KeycloakModelUtils.getPemFromKey(publicKey);
+ setPublicKeyPem(publicKeyPem);
}
@Override
public PrivateKey getPrivateKey() {
if (privateKey != null) return privateKey;
- String pem = getPrivateKeyPem();
- if (pem != null) {
- try {
- privateKey = PemUtils.decodePrivateKey(pem);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
+ privateKey = KeycloakModelUtils.getPrivateKey(getPrivateKeyPem());
return privateKey;
}
@Override
public void setPrivateKey(PrivateKey privateKey) {
this.privateKey = privateKey;
- StringWriter writer = new StringWriter();
- PEMWriter pemWriter = new PEMWriter(writer);
- try {
- pemWriter.writeObject(privateKey);
- pemWriter.flush();
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- String s = writer.toString();
- setPrivateKeyPem(PemUtils.removeBeginEnd(s));
+ String privateKeyPem = KeycloakModelUtils.getPemFromKey(privateKey);
+ setPrivateKeyPem(privateKeyPem);
}
@Override
public UserAdapter getUser(String name) {
- NoSQLQuery query = noSQL.createQueryBuilder()
- .andCondition("loginName", name)
- .andCondition("realmId", getOid())
- .build();
- UserData user = noSQL.loadSingleObject(UserData.class, query);
+ DBObject query = new QueryBuilder()
+ .and("loginName").is(name)
+ .and("realmId").is(getId())
+ .get();
+ UserEntity user = mongoStore.loadSingleObject(UserEntity.class, query);
+
+ if (user == null) {
+ return null;
+ } else {
+ return new UserAdapter(user, mongoStore);
+ }
+ }
+
+ @Override
+ public UserModel getUserByEmail(String email) {
+ DBObject query = new QueryBuilder()
+ .and("email").is(email)
+ .and("realmId").is(getId())
+ .get();
+ UserEntity user = mongoStore.loadSingleObject(UserEntity.class, query);
if (user == null) {
return null;
} else {
- return new UserAdapter(user, noSQL);
+ return new UserAdapter(user, mongoStore);
}
}
@Override
public UserAdapter addUser(String username) {
+ UserAdapter userModel = addUserEntity(username);
+
+ for (String r : getDefaultRoles()) {
+ grantRole(userModel, getRole(r));
+ }
+
+ for (ApplicationModel application : getApplications()) {
+ for (String r : application.getDefaultRoles()) {
+ grantRole(userModel, application.getRole(r));
+ }
+ }
+
+ return userModel;
+ }
+
+ // Add just user entity without defaultRoles
+ protected UserAdapter addUserEntity(String username) {
if (getUser(username) != null) {
throw new IllegalArgumentException("User " + username + " already exists");
}
- UserData userData = new UserData();
- userData.setLoginName(username);
- userData.setEnabled(true);
- userData.setRealmId(getOid());
+ UserEntity userEntity = new UserEntity();
+ userEntity.setLoginName(username);
+ userEntity.setEnabled(true);
+ userEntity.setRealmId(getId());
- noSQL.saveObject(userData);
- return new UserAdapter(userData, noSQL);
+ mongoStore.insertObject(userEntity);
+ return new UserAdapter(userEntity, mongoStore);
}
- // This method doesn't exists on interface actually
- public void removeUser(String name) {
- NoSQLQuery query = noSQL.createQueryBuilder()
- .andCondition("loginName", name)
- .andCondition("realmId", getOid())
- .build();
- noSQL.removeObjects(UserData.class, query);
+ @Override
+ public boolean removeUser(String name) {
+ DBObject query = new QueryBuilder()
+ .and("loginName").is(name)
+ .and("realmId").is(getId())
+ .get();
+ return mongoStore.removeObjects(UserEntity.class, query);
}
@Override
public RoleAdapter getRole(String name) {
- NoSQLQuery query = noSQL.createQueryBuilder()
- .andCondition("name", name)
- .andCondition("realmId", getOid())
- .build();
- RoleData role = noSQL.loadSingleObject(RoleData.class, query);
+ DBObject query = new QueryBuilder()
+ .and("name").is(name)
+ .and("realmId").is(getId())
+ .get();
+ RoleEntity role = mongoStore.loadSingleObject(RoleEntity.class, query);
if (role == null) {
return null;
} else {
- return new RoleAdapter(role, noSQL);
+ return new RoleAdapter(role, this, mongoStore);
}
}
@Override
public RoleModel addRole(String name) {
- if (getRole(name) != null) {
- throw new IllegalArgumentException("Role " + name + " already exists");
+ RoleAdapter role = getRole(name);
+ if (role != null) {
+ // Compatibility with JPA model
+ return role;
+ // throw new IllegalArgumentException("Role " + name + " already exists");
}
- RoleData roleData = new RoleData();
- roleData.setName(name);
- roleData.setRealmId(getOid());
+ RoleEntity roleEntity = new RoleEntity();
+ roleEntity.setName(name);
+ roleEntity.setRealmId(getId());
+
+ mongoStore.insertObject(roleEntity);
+ return new RoleAdapter(roleEntity, this, mongoStore);
+ }
- noSQL.saveObject(roleData);
- return new RoleAdapter(roleData, noSQL);
+ @Override
+ public boolean removeRoleById(String id) {
+ return mongoStore.removeObject(RoleEntity.class ,id);
}
@Override
- public List<RoleModel> getRoles() {
- NoSQLQuery query = noSQL.createQueryBuilder()
- .andCondition("realmId", getOid())
- .build();
- List<RoleData> roles = noSQL.loadObjects(RoleData.class, query);
+ public Set<RoleModel> getRoles() {
+ DBObject query = new QueryBuilder()
+ .and("realmId").is(getId())
+ .get();
+ List<RoleEntity> roles = mongoStore.loadObjects(RoleEntity.class, query);
+
+ Set<RoleModel> result = new HashSet<RoleModel>();
- List<RoleModel> result = new ArrayList<RoleModel>();
- for (RoleData role : roles) {
- result.add(new RoleAdapter(role, noSQL));
+ if (roles == null) return result;
+ for (RoleEntity role : roles) {
+ result.add(new RoleAdapter(role, this, mongoStore));
}
return result;
}
@Override
- public List<RoleModel> getDefaultRoles() {
- List<String> defaultRoles = realm.getDefaultRoles();
-
- NoSQLQuery query = noSQL.createQueryBuilder()
- .inCondition("_id", defaultRoles)
- .build();
- List<RoleData> defaultRolesData = noSQL.loadObjects(RoleData.class, query);
-
- List<RoleModel> defaultRoleModels = new ArrayList<RoleModel>();
- for (RoleData roleData : defaultRolesData) {
- defaultRoleModels.add(new RoleAdapter(roleData, noSQL));
+ public RoleModel getRoleById(String id) {
+ RoleEntity role = mongoStore.loadObject(RoleEntity.class, id);
+ if (role == null) {
+ return null;
+ } else {
+ return new RoleAdapter(role, this, mongoStore);
}
- return defaultRoleModels;
+ }
+
+ @Override
+ public List<String> getDefaultRoles() {
+ return realm.getDefaultRoles();
}
@Override
public void addDefaultRole(String name) {
RoleModel role = getRole(name);
if (role == null) {
- role = addRole(name);
+ addRole(name);
}
- noSQL.pushItemToList(realm, "defaultRoles", role.getId());
+ mongoStore.pushItemToList(realm, "defaultRoles", name, true);
}
@Override
public void updateDefaultRoles(String[] defaultRoles) {
- // defaultRoles is array with names of roles. So we need to convert to array of ids
- List<String> roleIds = new ArrayList<String>();
+ List<String> roleNames = new ArrayList<String>();
for (String roleName : defaultRoles) {
RoleModel role = getRole(roleName);
if (role == null) {
- role = addRole(roleName);
+ addRole(roleName);
}
- roleIds.add(role.getId());
+ roleNames.add(roleName);
}
- realm.setDefaultRoles(roleIds);
+ realm.setDefaultRoles(roleNames);
updateRealm();
}
@Override
public ApplicationModel getApplicationById(String id) {
- ApplicationData appData = noSQL.loadObject(ApplicationData.class, id);
+ ApplicationEntity appData = mongoStore.loadObject(ApplicationEntity.class, id);
// Check if application belongs to this realm
- if (appData == null || !getOid().equals(appData.getRealmId())) {
+ if (appData == null || !getId().equals(appData.getRealmId())) {
return null;
}
- ApplicationModel model = new ApplicationAdapter(appData, noSQL);
+ ApplicationModel model = new ApplicationAdapter(appData, mongoStore);
return model;
}
@Override
+ public ApplicationModel getApplicationByName(String name) {
+ DBObject query = new QueryBuilder()
+ .and("realmId").is(getId())
+ .and("name").is(name)
+ .get();
+ ApplicationEntity appEntity = mongoStore.loadSingleObject(ApplicationEntity.class, query);
+ return appEntity==null ? null : new ApplicationAdapter(appEntity, mongoStore);
+ }
+
+ @Override
public Map<String, ApplicationModel> getApplicationNameMap() {
Map<String, ApplicationModel> resourceMap = new HashMap<String, ApplicationModel>();
for (ApplicationModel resource : getApplications()) {
@@ -428,356 +449,364 @@ public class RealmAdapter implements RealmModel {
@Override
public List<ApplicationModel> getApplications() {
- NoSQLQuery query = noSQL.createQueryBuilder()
- .andCondition("realmId", getOid())
- .build();
- List<ApplicationData> appDatas = noSQL.loadObjects(ApplicationData.class, query);
+ DBObject query = new QueryBuilder()
+ .and("realmId").is(getId())
+ .get();
+ List<ApplicationEntity> appDatas = mongoStore.loadObjects(ApplicationEntity.class, query);
List<ApplicationModel> result = new ArrayList<ApplicationModel>();
- for (ApplicationData appData : appDatas) {
- result.add(new ApplicationAdapter(appData, noSQL));
+ for (ApplicationEntity appData : appDatas) {
+ result.add(new ApplicationAdapter(appData, mongoStore));
}
return result;
}
@Override
public ApplicationModel addApplication(String name) {
- UserAdapter resourceUser = addUser(name);
+ UserAdapter resourceUser = addUserEntity(name);
- ApplicationData appData = new ApplicationData();
+ ApplicationEntity appData = new ApplicationEntity();
appData.setName(name);
- appData.setRealmId(getOid());
+ appData.setRealmId(getId());
+ appData.setEnabled(true);
appData.setResourceUserId(resourceUser.getUser().getId());
- noSQL.saveObject(appData);
+ mongoStore.insertObject(appData);
- ApplicationModel resource = new ApplicationAdapter(appData, noSQL);
+ ApplicationModel resource = new ApplicationAdapter(appData, resourceUser, mongoStore);
return resource;
}
@Override
+ public boolean removeApplication(String id) {
+ return mongoStore.removeObject(ApplicationEntity.class, id);
+ }
+
+ @Override
public boolean hasRole(UserModel user, RoleModel role) {
- UserData userData = ((UserAdapter)user).getUser();
-
- List<String> roleIds = userData.getRoleIds();
- String roleId = role.getId();
- if (roleIds != null) {
- for (String currentId : roleIds) {
- if (roleId.equals(currentId)) {
- return true;
- }
- }
+ Set<RoleModel> roles = getRoleMappings(user);
+ if (roles.contains(role)) return true;
+
+ for (RoleModel mapping : roles) {
+ if (mapping.hasRole(role)) return true;
}
return false;
}
@Override
public void grantRole(UserModel user, RoleModel role) {
- UserData userData = ((UserAdapter)user).getUser();
- noSQL.pushItemToList(userData, "roleIds", role.getId());
+ UserEntity userEntity = ((UserAdapter)user).getUser();
+ mongoStore.pushItemToList(userEntity, "roleIds", role.getId(), true);
}
@Override
- public List<RoleModel> getRoleMappings(UserModel user) {
- List<RoleModel> result = new ArrayList<RoleModel>();
- List<RoleData> roles = ApplicationAdapter.getAllRolesOfUser(user, noSQL);
- // TODO: Maybe improve as currently we need to obtain all roles and then filter programmatically...
- for (RoleData role : roles) {
- if (getOid().equals(role.getRealmId())) {
- result.add(new RoleAdapter(role, noSQL));
+ public Set<RoleModel> getRoleMappings(UserModel user) {
+ Set<RoleModel> result = new HashSet<RoleModel>();
+ List<RoleEntity> roles = MongoModelUtils.getAllRolesOfUser(user, mongoStore);
+
+ for (RoleEntity role : roles) {
+ if (getId().equals(role.getRealmId())) {
+ result.add(new RoleAdapter(role, this, mongoStore));
+ } else {
+ // Likely applicationRole, but we don't have this application yet
+ result.add(new RoleAdapter(role, mongoStore));
}
}
return result;
}
@Override
- public Set<String> getRoleMappingValues(UserModel user) {
- Set<String> result = new HashSet<String>();
- List<RoleData> roles = ApplicationAdapter.getAllRolesOfUser(user, noSQL);
- // TODO: Maybe improve as currently we need to obtain all roles and then filter programmatically...
- for (RoleData role : roles) {
- if (getOid().equals(role.getRealmId())) {
- result.add(role.getName());
+ public Set<RoleModel> getRealmRoleMappings(UserModel user) {
+ Set<RoleModel> allRoles = getRoleMappings(user);
+
+ // Filter to retrieve just realm roles TODO: Maybe improve to avoid filter programmatically... Maybe have separate fields for realmRoles and appRoles on user?
+ Set<RoleModel> realmRoles = new HashSet<RoleModel>();
+ for (RoleModel role : allRoles) {
+ RoleEntity roleEntity = ((RoleAdapter)role).getRole();
+
+ if (getId().equals(roleEntity.getRealmId())) {
+ realmRoles.add(role);
}
}
- return result;
+ return realmRoles;
}
@Override
public void deleteRoleMapping(UserModel user, RoleModel role) {
- UserData userData = ((UserAdapter)user).getUser();
- noSQL.pullItemFromList(userData, "roleIds", role.getId());
+ UserEntity userEntity = ((UserAdapter)user).getUser();
+ mongoStore.pullItemFromList(userEntity, "roleIds", role.getId());
}
@Override
- public void addScopeMapping(UserModel agent, String roleName) {
- RoleAdapter role = getRole(roleName);
- if (role == null) {
- throw new RuntimeException("Role not found");
+ public Set<RoleModel> getScopeMappings(UserModel user) {
+ Set<RoleModel> result = new HashSet<RoleModel>();
+ List<RoleEntity> roles = MongoModelUtils.getAllScopesOfUser(user, mongoStore);
+
+ for (RoleEntity role : roles) {
+ if (getId().equals(role.getRealmId())) {
+ result.add(new RoleAdapter(role, this, mongoStore));
+ } else {
+ // Likely applicationRole, but we don't have this application yet
+ result.add(new RoleAdapter(role, mongoStore));
+ }
}
+ return result;
+ }
+
+ @Override
+ public Set<RoleModel> getRealmScopeMappings(UserModel user) {
+ Set<RoleModel> allScopes = getScopeMappings(user);
- addScopeMapping(agent, role);
+ // Filter to retrieve just realm roles TODO: Maybe improve to avoid filter programmatically... Maybe have separate fields for realmRoles and appRoles on user?
+ Set<RoleModel> realmRoles = new HashSet<RoleModel>();
+ for (RoleModel role : allScopes) {
+ RoleEntity roleEntity = ((RoleAdapter)role).getRole();
+
+ if (getId().equals(roleEntity.getRealmId())) {
+ realmRoles.add(role);
+ }
+ }
+ return realmRoles;
}
@Override
public void addScopeMapping(UserModel agent, RoleModel role) {
- UserData userData = ((UserAdapter)agent).getUser();
- noSQL.pushItemToList(userData, "scopeIds", role.getId());
+ UserEntity userEntity = ((UserAdapter)agent).getUser();
+ mongoStore.pushItemToList(userEntity, "scopeIds", role.getId(), true);
}
@Override
public void deleteScopeMapping(UserModel user, RoleModel role) {
- UserData userData = ((UserAdapter)user).getUser();
- noSQL.pullItemFromList(userData, "scopeIds", role.getId());
+ UserEntity userEntity = ((UserAdapter)user).getUser();
+ mongoStore.pullItemFromList(userEntity, "scopeIds", role.getId());
}
@Override
public OAuthClientModel addOAuthClient(String name) {
- UserAdapter oauthAgent = addUser(name);
+ UserAdapter oauthAgent = addUserEntity(name);
- OAuthClientData oauthClient = new OAuthClientData();
+ OAuthClientEntity oauthClient = new OAuthClientEntity();
oauthClient.setOauthAgentId(oauthAgent.getUser().getId());
- oauthClient.setRealmId(getOid());
- noSQL.saveObject(oauthClient);
+ oauthClient.setRealmId(getId());
+ oauthClient.setName(name);
+ mongoStore.insertObject(oauthClient);
- return new OAuthClientAdapter(oauthClient, oauthAgent, noSQL);
- }
-
- @Override
- public OAuthClientModel getOAuthClient(String name) {
- UserAdapter user = getUser(name);
- if (user == null) return null;
- NoSQLQuery query = noSQL.createQueryBuilder()
- .andCondition("realmId", getOid())
- .andCondition("oauthAgentId", user.getUser().getId())
- .build();
- OAuthClientData oauthClient = noSQL.loadSingleObject(OAuthClientData.class, query);
- return oauthClient == null ? null : new OAuthClientAdapter(oauthClient, user, noSQL);
+ return new OAuthClientAdapter(oauthClient, oauthAgent, mongoStore);
}
@Override
- public List<OAuthClientModel> getOAuthClients() {
- NoSQLQuery query = noSQL.createQueryBuilder()
- .andCondition("realmId", getOid())
- .build();
- List<OAuthClientData> results = noSQL.loadObjects(OAuthClientData.class, query);
- List<OAuthClientModel> list = new ArrayList<OAuthClientModel>();
- for (OAuthClientData data : results) {
- list.add(new OAuthClientAdapter(data, noSQL));
- }
- return list;
+ public boolean removeOAuthClient(String id) {
+ return mongoStore.removeObject(OAuthClientEntity.class, id);
}
@Override
- public List<RoleModel> getScopeMappings(UserModel agent) {
- List<RoleModel> result = new ArrayList<RoleModel>();
- List<RoleData> roles = ApplicationAdapter.getAllScopesOfUser(agent, noSQL);
- // TODO: Maybe improve as currently we need to obtain all roles and then filter programmatically...
- for (RoleData role : roles) {
- if (getOid().equals(role.getRealmId())) {
- result.add(new RoleAdapter(role, noSQL));
- }
- }
- return result;
+ public OAuthClientModel getOAuthClient(String name) {
+ UserAdapter user = getUser(name);
+ if (user == null) return null;
+ DBObject query = new QueryBuilder()
+ .and("realmId").is(getId())
+ .and("oauthAgentId").is(user.getUser().getId())
+ .get();
+ OAuthClientEntity oauthClient = mongoStore.loadSingleObject(OAuthClientEntity.class, query);
+ return oauthClient == null ? null : new OAuthClientAdapter(oauthClient, user, mongoStore);
}
@Override
- public Set<String> getScopeMappingValues(UserModel agent) {
- Set<String> result = new HashSet<String>();
- List<RoleData> roles = ApplicationAdapter.getAllScopesOfUser(agent, noSQL);
- // TODO: Maybe improve as currently we need to obtain all roles and then filter programmatically...
- for (RoleData role : roles) {
- if (getOid().equals(role.getRealmId())) {
- result.add(role.getName());
- }
- }
- return result;
+ public OAuthClientModel getOAuthClientById(String id) {
+ OAuthClientEntity clientEntity = mongoStore.loadObject(OAuthClientEntity.class, id);
+ if (clientEntity == null) return null;
+ return new OAuthClientAdapter(clientEntity, mongoStore);
}
@Override
- public RoleModel getRoleById(String id) {
- RoleData role = noSQL.loadObject(RoleData.class, id);
- if (role == null) {
- return null;
- } else {
- return new RoleAdapter(role, noSQL);
+ public List<OAuthClientModel> getOAuthClients() {
+ DBObject query = new QueryBuilder()
+ .and("realmId").is(getId())
+ .get();
+ List<OAuthClientEntity> results = mongoStore.loadObjects(OAuthClientEntity.class, query);
+ List<OAuthClientModel> list = new ArrayList<OAuthClientModel>();
+ for (OAuthClientEntity data : results) {
+ list.add(new OAuthClientAdapter(data, mongoStore));
}
+ return list;
}
@Override
- public boolean hasRole(UserModel user, String role) {
- RoleModel roleModel = getRole(role);
- return hasRole(user, roleModel);
- }
-
- @Override
- public void addRequiredCredential(String cred) {
- RequiredCredentialModel credentialModel = initRequiredCredentialModel(cred);
- addRequiredCredential(credentialModel, RequiredCredentialData.CLIENT_TYPE_USER);
+ public void addRequiredCredential(String type) {
+ RequiredCredentialModel credentialModel = initRequiredCredentialModel(type);
+ addRequiredCredential(credentialModel, realm.getRequiredCredentials());
}
@Override
public void addRequiredResourceCredential(String type) {
RequiredCredentialModel credentialModel = initRequiredCredentialModel(type);
- addRequiredCredential(credentialModel, RequiredCredentialData.CLIENT_TYPE_RESOURCE);
+ addRequiredCredential(credentialModel, realm.getRequiredApplicationCredentials());
}
@Override
public void addRequiredOAuthClientCredential(String type) {
RequiredCredentialModel credentialModel = initRequiredCredentialModel(type);
- addRequiredCredential(credentialModel, RequiredCredentialData.CLIENT_TYPE_OAUTH_RESOURCE);
+ addRequiredCredential(credentialModel, realm.getRequiredOAuthClientCredentials());
}
- protected void addRequiredCredential(RequiredCredentialModel credentialModel, int clientType) {
- RequiredCredentialData credData = new RequiredCredentialData();
- credData.setType(credentialModel.getType());
- credData.setFormLabel(credentialModel.getFormLabel());
- credData.setInput(credentialModel.isInput());
- credData.setSecret(credentialModel.isSecret());
+ protected void addRequiredCredential(RequiredCredentialModel credentialModel, List<RequiredCredentialEntity> persistentCollection) {
+ RequiredCredentialEntity credEntity = new RequiredCredentialEntity();
+ credEntity.setType(credentialModel.getType());
+ credEntity.setFormLabel(credentialModel.getFormLabel());
+ credEntity.setInput(credentialModel.isInput());
+ credEntity.setSecret(credentialModel.isSecret());
- credData.setRealmId(getOid());
- credData.setClientType(clientType);
+ persistentCollection.add(credEntity);
- noSQL.saveObject(credData);
+ updateRealm();
}
@Override
public void updateRequiredCredentials(Set<String> creds) {
- List<RequiredCredentialData> credsData = getRequiredCredentialsData(RequiredCredentialData.CLIENT_TYPE_USER);
- updateRequiredCredentials(creds, credsData);
+ updateRequiredCredentials(creds, realm.getRequiredCredentials());
}
@Override
public void updateRequiredApplicationCredentials(Set<String> creds) {
- List<RequiredCredentialData> credsData = getRequiredCredentialsData(RequiredCredentialData.CLIENT_TYPE_RESOURCE);
- updateRequiredCredentials(creds, credsData);
+ updateRequiredCredentials(creds, realm.getRequiredApplicationCredentials());
}
@Override
public void updateRequiredOAuthClientCredentials(Set<String> creds) {
- List<RequiredCredentialData> credsData = getRequiredCredentialsData(RequiredCredentialData.CLIENT_TYPE_OAUTH_RESOURCE);
- updateRequiredCredentials(creds, credsData);
+ updateRequiredCredentials(creds, realm.getRequiredOAuthClientCredentials());
}
- protected void updateRequiredCredentials(Set<String> creds, List<RequiredCredentialData> credsData) {
+ protected void updateRequiredCredentials(Set<String> creds, List<RequiredCredentialEntity> credsEntities) {
Set<String> already = new HashSet<String>();
- for (RequiredCredentialData data : credsData) {
- if (!creds.contains(data.getType())) {
- noSQL.removeObject(data);
+ Set<RequiredCredentialEntity> toRemove = new HashSet<RequiredCredentialEntity>();
+ for (RequiredCredentialEntity entity : credsEntities) {
+ if (!creds.contains(entity.getType())) {
+ toRemove.add(entity);
} else {
- already.add(data.getType());
+ already.add(entity.getType());
}
}
+ for (RequiredCredentialEntity entity : toRemove) {
+ creds.remove(entity);
+ }
for (String cred : creds) {
- // TODO
- System.out.println("updating cred: " + cred);
- // logger.info("updating cred: " + cred);
+ logger.info("updating cred: " + cred);
if (!already.contains(cred)) {
- addRequiredCredential(cred);
+ RequiredCredentialModel credentialModel = initRequiredCredentialModel(cred);
+ addRequiredCredential(credentialModel, credsEntities);
}
}
}
@Override
public List<RequiredCredentialModel> getRequiredCredentials() {
- return getRequiredCredentials(RequiredCredentialData.CLIENT_TYPE_USER);
+ return convertRequiredCredentialEntities(realm.getRequiredCredentials());
}
@Override
public List<RequiredCredentialModel> getRequiredApplicationCredentials() {
- return getRequiredCredentials(RequiredCredentialData.CLIENT_TYPE_RESOURCE);
+ return convertRequiredCredentialEntities(realm.getRequiredApplicationCredentials());
}
@Override
public List<RequiredCredentialModel> getRequiredOAuthClientCredentials() {
- return getRequiredCredentials(RequiredCredentialData.CLIENT_TYPE_OAUTH_RESOURCE);
+ return convertRequiredCredentialEntities(realm.getRequiredOAuthClientCredentials());
}
- protected List<RequiredCredentialModel> getRequiredCredentials(int credentialType) {
- List<RequiredCredentialData> credsData = getRequiredCredentialsData(credentialType);
+ protected List<RequiredCredentialModel> convertRequiredCredentialEntities(Collection<RequiredCredentialEntity> credEntities) {
List<RequiredCredentialModel> result = new ArrayList<RequiredCredentialModel>();
- for (RequiredCredentialData data : credsData) {
+ for (RequiredCredentialEntity entity : credEntities) {
RequiredCredentialModel model = new RequiredCredentialModel();
- model.setFormLabel(data.getFormLabel());
- model.setInput(data.isInput());
- model.setSecret(data.isSecret());
- model.setType(data.getType());
+ model.setFormLabel(entity.getFormLabel());
+ model.setInput(entity.isInput());
+ model.setSecret(entity.isSecret());
+ model.setType(entity.getType());
result.add(model);
}
return result;
}
- protected List<RequiredCredentialData> getRequiredCredentialsData(int credentialType) {
- NoSQLQuery query = noSQL.createQueryBuilder()
- .andCondition("realmId", getOid())
- .andCondition("clientType", credentialType)
- .build();
- return noSQL.loadObjects(RequiredCredentialData.class, query);
- }
-
@Override
public boolean validatePassword(UserModel user, String password) {
- Credentials.Status status = passwordCredentialHandler.validate(noSQL, ((UserAdapter)user).getUser(), password);
- return status == Credentials.Status.VALID;
+ for (CredentialEntity cred : ((UserAdapter)user).getUser().getCredentials()) {
+ if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
+ return new Pbkdf2PasswordEncoder(cred.getSalt()).verify(password, cred.getValue());
+ }
+ }
+ return false;
}
@Override
public boolean validateTOTP(UserModel user, String password, String token) {
- Credentials.Status status = totpCredentialHandler.validate(noSQL, ((UserAdapter)user).getUser(), password, token, null);
- return status == Credentials.Status.VALID;
+ if (!validatePassword(user, password)) return false;
+ for (CredentialEntity cred : ((UserAdapter)user).getUser().getCredentials()) {
+ if (cred.getType().equals(UserCredentialModel.TOTP)) {
+ return new TimeBasedOTP().validate(token, cred.getValue().getBytes());
+ }
+ }
+ return false;
}
@Override
public void updateCredential(UserModel user, UserCredentialModel cred) {
- if (cred.getType().equals(CredentialRepresentation.PASSWORD)) {
- passwordCredentialHandler.update(noSQL, ((UserAdapter)user).getUser(), cred.getValue(), null, null);
- } else if (cred.getType().equals(CredentialRepresentation.TOTP)) {
- totpCredentialHandler.update(noSQL, ((UserAdapter)user).getUser(), cred.getValue(), cred.getDevice(), null, null);
- } else if (cred.getType().equals(CredentialRepresentation.CLIENT_CERT)) {
- // TODO
-// X509Certificate cert = null;
-// try {
-// cert = org.keycloak.PemUtils.decodeCertificate(cred.getValue());
-// } catch (Exception e) {
-// throw new RuntimeException(e);
-// }
-// X509CertificateCredentials creds = new X509CertificateCredentials(cert);
-// idm.updateCredential(((UserAdapter)user).getUser(), creds);
+ CredentialEntity credentialEntity = null;
+ UserEntity userEntity = ((UserAdapter) user).getUser();
+ for (CredentialEntity entity : userEntity.getCredentials()) {
+ if (entity.getType().equals(cred.getType())) {
+ credentialEntity = entity;
+ }
}
+
+ if (credentialEntity == null) {
+ credentialEntity = new CredentialEntity();
+ credentialEntity.setType(cred.getType());
+ credentialEntity.setDevice(cred.getDevice());
+ userEntity.getCredentials().add(credentialEntity);
+ }
+ if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
+ byte[] salt = Pbkdf2PasswordEncoder.getSalt();
+ credentialEntity.setValue(new Pbkdf2PasswordEncoder(salt).encode(cred.getValue()));
+ credentialEntity.setSalt(salt);
+ } else {
+ credentialEntity.setValue(cred.getValue());
+ }
+ credentialEntity.setDevice(cred.getDevice());
+
+ mongoStore.updateObject(userEntity);
}
@Override
public UserModel getUserBySocialLink(SocialLinkModel socialLink) {
- NoSQLQuery query = noSQL.createQueryBuilder()
- .andCondition("socialProvider", socialLink.getSocialProvider())
- .andCondition("socialUsername", socialLink.getSocialUsername())
- .andCondition("realmId", getOid())
- .build();
- SocialLinkData socialLinkData = noSQL.loadSingleObject(SocialLinkData.class, query);
-
- if (socialLinkData == null) {
+ DBObject query = new QueryBuilder()
+ .and("socialProvider").is(socialLink.getSocialProvider())
+ .and("socialUsername").is(socialLink.getSocialUsername())
+ .and("realmId").is(getId())
+ .get();
+ SocialLinkEntity socialLinkEntity = mongoStore.loadSingleObject(SocialLinkEntity.class, query);
+
+ if (socialLinkEntity == null) {
return null;
} else {
- UserData userData = noSQL.loadObject(UserData.class, socialLinkData.getUserId());
- // TODO: Add some checking if userData exists and programmatically remove binding if it doesn't? (There are more similar places where this should be handled)
- return new UserAdapter(userData, noSQL);
+ UserEntity userEntity = mongoStore.loadObject(UserEntity.class, socialLinkEntity.getUserId());
+ // TODO: Programmatically remove binding if userEntity doesn't exists? (There are more similar places where this should be handled)
+ return userEntity==null ? null : new UserAdapter(userEntity, mongoStore);
}
}
@Override
public Set<SocialLinkModel> getSocialLinks(UserModel user) {
- UserData userData = ((UserAdapter)user).getUser();
- String userId = userData.getId();
+ UserEntity userEntity = ((UserAdapter)user).getUser();
+ String userId = userEntity.getId();
- NoSQLQuery query = noSQL.createQueryBuilder()
- .andCondition("userId", userId)
- .build();
- List<SocialLinkData> dbSocialLinks = noSQL.loadObjects(SocialLinkData.class, query);
+ DBObject query = new QueryBuilder()
+ .and("userId").is(userId)
+ .get();
+ List<SocialLinkEntity> dbSocialLinks = mongoStore.loadObjects(SocialLinkEntity.class, query);
Set<SocialLinkModel> result = new HashSet<SocialLinkModel>();
- for (SocialLinkData socialLinkData : dbSocialLinks) {
- SocialLinkModel model = new SocialLinkModel(socialLinkData.getSocialProvider(), socialLinkData.getSocialUsername());
+ for (SocialLinkEntity socialLinkEntity : dbSocialLinks) {
+ SocialLinkModel model = new SocialLinkModel(socialLinkEntity.getSocialProvider(), socialLinkEntity.getSocialUsername());
result.add(model);
}
return result;
@@ -785,30 +814,30 @@ public class RealmAdapter implements RealmModel {
@Override
public void addSocialLink(UserModel user, SocialLinkModel socialLink) {
- UserData userData = ((UserAdapter)user).getUser();
- SocialLinkData socialLinkData = new SocialLinkData();
- socialLinkData.setSocialProvider(socialLink.getSocialProvider());
- socialLinkData.setSocialUsername(socialLink.getSocialUsername());
- socialLinkData.setUserId(userData.getId());
- socialLinkData.setRealmId(getOid());
+ UserEntity userEntity = ((UserAdapter)user).getUser();
+ SocialLinkEntity socialLinkEntity = new SocialLinkEntity();
+ socialLinkEntity.setSocialProvider(socialLink.getSocialProvider());
+ socialLinkEntity.setSocialUsername(socialLink.getSocialUsername());
+ socialLinkEntity.setUserId(userEntity.getId());
+ socialLinkEntity.setRealmId(getId());
- noSQL.saveObject(socialLinkData);
+ mongoStore.insertObject(socialLinkEntity);
}
@Override
public void removeSocialLink(UserModel user, SocialLinkModel socialLink) {
- UserData userData = ((UserAdapter)user).getUser();
- String userId = userData.getId();
- NoSQLQuery query = noSQL.createQueryBuilder()
- .andCondition("socialProvider", socialLink.getSocialProvider())
- .andCondition("socialUsername", socialLink.getSocialUsername())
- .andCondition("userId", userId)
- .build();
- noSQL.removeObjects(SocialLinkData.class, query);
+ UserEntity userEntity = ((UserAdapter)user).getUser();
+ String userId = userEntity.getId();
+ DBObject query = new QueryBuilder()
+ .and("socialProvider").is(socialLink.getSocialProvider())
+ .and("socialUsername").is(socialLink.getSocialUsername())
+ .and("userId").is(userId)
+ .get();
+ mongoStore.removeObjects(SocialLinkEntity.class, query);
}
protected void updateRealm() {
- noSQL.saveObject(realm);
+ mongoStore.updateObject(realm);
}
protected RequiredCredentialModel initRequiredCredentialModel(String type) {
@@ -820,46 +849,116 @@ public class RealmAdapter implements RealmModel {
}
@Override
+ public List<UserModel> getUsers() {
+ DBObject query = new QueryBuilder()
+ .and("realmId").is(getId())
+ .get();
+ List<UserEntity> users = mongoStore.loadObjects(UserEntity.class, query);
+ return convertUserEntities(users);
+ }
+
+ @Override
+ public List<UserModel> searchForUser(String search) {
+ search = search.trim();
+ Pattern caseInsensitivePattern = Pattern.compile("(?i:" + search + ")");
+
+ QueryBuilder nameBuilder;
+ int spaceInd = search.lastIndexOf(" ");
+
+ // Case when we have search string like "ohn Bow". Then firstName must end with "ohn" AND lastName must start with "bow" (everything case-insensitive)
+ if (spaceInd != -1) {
+ String firstName = search.substring(0, spaceInd);
+ String lastName = search.substring(spaceInd + 1);
+ Pattern firstNamePattern = Pattern.compile("(?i:" + firstName + "$)");
+ Pattern lastNamePattern = Pattern.compile("(?i:^" + lastName + ")");
+ nameBuilder = new QueryBuilder().and(
+ new QueryBuilder().put("firstName").regex(firstNamePattern).get(),
+ new QueryBuilder().put("lastName").regex(lastNamePattern).get()
+ );
+ } else {
+ // Case when we have search without spaces like "foo". The firstName OR lastName could be "foo" (everything case-insensitive)
+ nameBuilder = new QueryBuilder().or(
+ new QueryBuilder().put("firstName").regex(caseInsensitivePattern).get(),
+ new QueryBuilder().put("lastName").regex(caseInsensitivePattern).get()
+ );
+ }
+
+ QueryBuilder builder = new QueryBuilder().and(
+ new QueryBuilder().and("realmId").is(getId()).get(),
+ new QueryBuilder().or(
+ new QueryBuilder().put("loginName").regex(caseInsensitivePattern).get(),
+ new QueryBuilder().put("email").regex(caseInsensitivePattern).get(),
+ nameBuilder.get()
+
+ ).get()
+ );
+
+ List<UserEntity> users = mongoStore.loadObjects(UserEntity.class, builder.get());
+ return convertUserEntities(users);
+ }
+
+ @Override
public List<UserModel> searchForUserByAttributes(Map<String, String> attributes) {
- NoSQLQueryBuilder queryBuilder = noSQL.createQueryBuilder();
+ QueryBuilder queryBuilder = new QueryBuilder()
+ .and("realmId").is(getId());
+
for (Map.Entry<String, String> entry : attributes.entrySet()) {
if (entry.getKey().equals(UserModel.LOGIN_NAME)) {
- queryBuilder.andCondition("loginName", entry.getValue());
+ queryBuilder.and("loginName").regex(Pattern.compile("(?i:" + entry.getValue() + "$)"));
} else if (entry.getKey().equalsIgnoreCase(UserModel.FIRST_NAME)) {
- queryBuilder.andCondition(UserModel.FIRST_NAME, entry.getValue());
+ queryBuilder.and(UserModel.FIRST_NAME).regex(Pattern.compile("(?i:" + entry.getValue() + "$)"));
} else if (entry.getKey().equalsIgnoreCase(UserModel.LAST_NAME)) {
- queryBuilder.andCondition(UserModel.LAST_NAME, entry.getValue());
+ queryBuilder.and(UserModel.LAST_NAME).regex(Pattern.compile("(?i:" + entry.getValue() + "$)"));
} else if (entry.getKey().equalsIgnoreCase(UserModel.EMAIL)) {
- queryBuilder.andCondition(UserModel.EMAIL, entry.getValue());
+ queryBuilder.and(UserModel.EMAIL).regex(Pattern.compile("(?i:" + entry.getValue() + "$)"));
}
}
- List<UserData> users = noSQL.loadObjects(UserData.class, queryBuilder.build());
+ List<UserEntity> users = mongoStore.loadObjects(UserEntity.class, queryBuilder.get());
+ return convertUserEntities(users);
+ }
+
+ protected List<UserModel> convertUserEntities(List<UserEntity> userEntities) {
List<UserModel> userModels = new ArrayList<UserModel>();
- for (UserData user : users) {
- userModels.add(new UserAdapter(user, noSQL));
+ for (UserEntity user : userEntities) {
+ userModels.add(new UserAdapter(user, mongoStore));
}
return userModels;
}
@Override
public Map<String, String> getSmtpConfig() {
- throw new RuntimeException("Not implemented");
+ return realm.getSmtpConfig();
}
@Override
public void setSmtpConfig(Map<String, String> smtpConfig) {
- throw new RuntimeException("Not implemented");
+ realm.setSmtpConfig(smtpConfig);
+ updateRealm();
}
@Override
public Map<String, String> getSocialConfig() {
- throw new RuntimeException("Not implemented");
+ return realm.getSocialConfig();
}
@Override
public void setSocialConfig(Map<String, String> socialConfig) {
- throw new RuntimeException("Not implemented");
+ realm.setSocialConfig(socialConfig);
+ updateRealm();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null) return false;
+ if (!(o instanceof RealmAdapter)) return false;
+ RealmAdapter r = (RealmAdapter)o;
+ return r.getId().equals(getId());
+ }
+
+ @Override
+ public int hashCode() {
+ return getId().hashCode();
}
}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RoleAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RoleAdapter.java
index 7b2692f..21bdc9e 100644
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RoleAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RoleAdapter.java
@@ -1,8 +1,20 @@
package org.keycloak.models.mongo.keycloak.adapters;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import com.mongodb.DBObject;
+import com.mongodb.QueryBuilder;
+import org.keycloak.models.RoleContainerModel;
import org.keycloak.models.RoleModel;
-import org.keycloak.models.mongo.api.NoSQL;
-import org.keycloak.models.mongo.keycloak.data.RoleData;
+import org.keycloak.models.mongo.api.MongoStore;
+import org.keycloak.models.mongo.keycloak.entities.ApplicationEntity;
+import org.keycloak.models.mongo.keycloak.entities.RealmEntity;
+import org.keycloak.models.mongo.keycloak.entities.RoleEntity;
+import org.keycloak.models.mongo.utils.MongoModelUtils;
+import org.keycloak.models.utils.KeycloakModelUtils;
/**
* Wrapper around RoleData object, which will persist wrapped object after each set operation (compatibility with picketlink based impl)
@@ -11,12 +23,23 @@ import org.keycloak.models.mongo.keycloak.data.RoleData;
*/
public class RoleAdapter implements RoleModel {
- private final RoleData role;
- private final NoSQL noSQL;
+ private final RoleEntity role;
+ private RoleContainerModel roleContainer;
+ private final MongoStore mongoStore;
+
+ public RoleAdapter(RoleEntity roleEntity, MongoStore mongoStore) {
+ this(roleEntity, null, mongoStore);
+ }
+
+ public RoleAdapter(RoleEntity roleEntity, RoleContainerModel roleContainer, MongoStore mongoStore) {
+ this.role = roleEntity;
+ this.roleContainer = roleContainer;
+ this.mongoStore = mongoStore;
+ }
- public RoleAdapter(RoleData roleData, NoSQL noSQL) {
- this.role = roleData;
- this.noSQL = noSQL;
+ @Override
+ public String getId() {
+ return role.getId();
}
@Override
@@ -25,6 +48,12 @@ public class RoleAdapter implements RoleModel {
}
@Override
+ public void setName(String name) {
+ role.setName(name);
+ updateRole();
+ }
+
+ @Override
public String getDescription() {
return role.getDescription();
}
@@ -32,21 +61,102 @@ public class RoleAdapter implements RoleModel {
@Override
public void setDescription(String description) {
role.setDescription(description);
- noSQL.saveObject(role);
+ updateRole();
}
@Override
- public String getId() {
- return role.getId();
+ public boolean isComposite() {
+ return role.isComposite();
}
@Override
- public void setName(String name) {
- role.setName(name);
- noSQL.saveObject(role);
+ public void setComposite(boolean flag) {
+ role.setComposite(flag);
+ updateRole();
+ }
+
+ protected void updateRole() {
+ mongoStore.updateObject(role);
+ }
+
+ @Override
+ public void addCompositeRole(RoleModel childRole) {
+ mongoStore.pushItemToList(role, "compositeRoleIds", childRole.getId(), true);
+ }
+
+ @Override
+ public void removeCompositeRole(RoleModel childRole) {
+ mongoStore.pullItemFromList(role, "compositeRoleIds", childRole.getId());
+ }
+
+ @Override
+ public Set<RoleModel> getComposites() {
+ if (role.getCompositeRoleIds() == null || role.getCompositeRoleIds().isEmpty()) {
+ return Collections.EMPTY_SET;
+ }
+
+ DBObject query = new QueryBuilder()
+ .and("_id").in(MongoModelUtils.convertStringsToObjectIds(role.getCompositeRoleIds()))
+ .get();
+ List<RoleEntity> childRoles = mongoStore.loadObjects(RoleEntity.class, query);
+
+ Set<RoleModel> set = new HashSet<RoleModel>();
+ for (RoleEntity childRole : childRoles) {
+ set.add(new RoleAdapter(childRole, roleContainer, mongoStore));
+ }
+ return set;
+ }
+
+ @Override
+ public RoleContainerModel getContainer() {
+ if (roleContainer == null) {
+ // Compute it
+ if (role.getRealmId() != null) {
+ RealmEntity realm = mongoStore.loadObject(RealmEntity.class, role.getRealmId());
+ if (realm == null) {
+ throw new IllegalStateException("Realm with id: " + role.getRealmId() + " doesn't exists");
+ }
+ roleContainer = new RealmAdapter(realm, mongoStore);
+ } else if (role.getApplicationId() != null) {
+ ApplicationEntity appEntity = mongoStore.loadObject(ApplicationEntity.class, role.getApplicationId());
+ if (appEntity == null) {
+ throw new IllegalStateException("Application with id: " + role.getApplicationId() + " doesn't exists");
+ }
+ roleContainer = new ApplicationAdapter(appEntity, mongoStore);
+ } else {
+ throw new IllegalStateException("Both realmId and applicationId are null for role: " + this);
+ }
+ }
+ return roleContainer;
}
- public RoleData getRole() {
+ @Override
+ public boolean hasRole(RoleModel role) {
+ if (this.equals(role)) return true;
+ if (!isComposite()) return false;
+
+ Set<RoleModel> visited = new HashSet<RoleModel>();
+ return KeycloakModelUtils.searchFor(role, this, visited);
+ }
+
+ public RoleEntity getRole() {
return role;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ RoleAdapter that = (RoleAdapter) o;
+
+ if (!getId().equals(that.getId())) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return getId().hashCode();
+ }
}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java
index 3b20848..d3fe02f 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java
@@ -1,10 +1,13 @@
package org.keycloak.models.mongo.keycloak.adapters;
import org.keycloak.models.UserModel;
-import org.keycloak.models.mongo.api.NoSQL;
-import org.keycloak.models.mongo.keycloak.data.UserData;
+import org.keycloak.models.mongo.api.MongoStore;
+import org.keycloak.models.mongo.keycloak.entities.CredentialEntity;
+import org.keycloak.models.mongo.keycloak.entities.UserEntity;
+import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -17,12 +20,12 @@ import java.util.Set;
*/
public class UserAdapter implements UserModel {
- private final UserData user;
- private final NoSQL noSQL;
+ private final UserEntity user;
+ private final MongoStore mongoStore;
- public UserAdapter(UserData userData, NoSQL noSQL) {
- this.user = userData;
- this.noSQL = noSQL;
+ public UserAdapter(UserEntity userEntity, MongoStore mongoStore) {
+ this.user = userEntity;
+ this.mongoStore = mongoStore;
}
@Override
@@ -38,7 +41,7 @@ public class UserAdapter implements UserModel {
@Override
public void setEnabled(boolean enabled) {
user.setEnabled(enabled);
- noSQL.saveObject(user);
+ updateUser();
}
@Override
@@ -49,7 +52,7 @@ public class UserAdapter implements UserModel {
@Override
public void setFirstName(String firstName) {
user.setFirstName(firstName);
- noSQL.saveObject(user);
+ updateUser();
}
@Override
@@ -60,7 +63,7 @@ public class UserAdapter implements UserModel {
@Override
public void setLastName(String lastName) {
user.setLastName(lastName);
- noSQL.saveObject(user);
+ updateUser();
}
@Override
@@ -71,7 +74,7 @@ public class UserAdapter implements UserModel {
@Override
public void setEmail(String email) {
user.setEmail(email);
- noSQL.saveObject(user);
+ updateUser();
}
@Override
@@ -82,111 +85,126 @@ public class UserAdapter implements UserModel {
@Override
public void setEmailVerified(boolean verified) {
user.setEmailVerified(verified);
- noSQL.saveObject(user);
+ updateUser();
}
@Override
public void setAttribute(String name, String value) {
- user.setAttribute(name, value);
+ if (user.getAttributes() == null) {
+ user.setAttributes(new HashMap<String, String>());
+ }
+
+ user.getAttributes().put(name, value);
+ updateUser();
}
@Override
public void removeAttribute(String name) {
- user.removeAttribute(name);
- noSQL.saveObject(user);
+ if (user.getAttributes() == null) return;
+
+ user.getAttributes().remove(name);
+ updateUser();
}
@Override
public String getAttribute(String name) {
- return user.getAttribute(name);
+ return user.getAttributes()==null ? null : user.getAttributes().get(name);
}
@Override
public Map<String, String> getAttributes() {
- return user.getAttributes();
+ return user.getAttributes()==null ? Collections.EMPTY_MAP : Collections.unmodifiableMap(user.getAttributes());
}
- public UserData getUser() {
+ public UserEntity getUser() {
return user;
}
@Override
- public Set<RequiredAction> getRequiredActions() {
- List<RequiredAction> actions = user.getRequiredActions();
-
- // Compatibility with picketlink impl
- if (actions == null) {
- return Collections.emptySet();
- } else {
- Set<RequiredAction> s = new HashSet<RequiredAction>();
- for (RequiredAction a : actions) {
- s.add(a);
- }
- return Collections.unmodifiableSet(s);
+ public Set<String> getWebOrigins() {
+ Set<String> result = new HashSet<String>();
+ if (user.getWebOrigins() != null) {
+ result.addAll(user.getWebOrigins());
}
+ return result;
}
@Override
- public void addRequiredAction(RequiredAction action) {
- // Push action only if it's not already here
- if (user.getRequiredActions() == null || !user.getRequiredActions().contains(action)) {
- noSQL.pushItemToList(user, "requiredActions", action);
- }
+ public void setWebOrigins(Set<String> webOrigins) {
+ List<String> result = new ArrayList<String>();
+ result.addAll(webOrigins);
+ user.setWebOrigins(result);
+ updateUser();
}
@Override
- public void removeRequiredAction(RequiredAction action) {
- noSQL.pullItemFromList(user, "requiredActions", action);
+ public void addWebOrigin(String webOrigin) {
+ mongoStore.pushItemToList(user, "webOrigins", webOrigin, true);
}
@Override
- public boolean isTotp() {
- return user.isTotp();
+ public void removeWebOrigin(String webOrigin) {
+ mongoStore.pullItemFromList(user, "webOrigins", webOrigin);
}
@Override
- public void setTotp(boolean totp) {
- user.setTotp(totp);
- noSQL.saveObject(user);
+ public Set<String> getRedirectUris() {
+ Set<String> result = new HashSet<String>();
+ if (user.getRedirectUris() != null) {
+ result.addAll(user.getRedirectUris());
+ }
+ return result;
}
@Override
- public Set<String> getWebOrigins() {
- return null; //To change body of implemented methods use File | Settings | File Templates.
+ public void setRedirectUris(Set<String> redirectUris) {
+ List<String> result = new ArrayList<String>();
+ result.addAll(redirectUris);
+ user.setRedirectUris(result);
+ updateUser();
}
@Override
- public void setWebOrigins(Set<String> webOrigins) {
- //To change body of implemented methods use File | Settings | File Templates.
+ public void addRedirectUri(String redirectUri) {
+ mongoStore.pushItemToList(user, "redirectUris", redirectUri, true);
}
@Override
- public void addWebOrigin(String webOrigin) {
- //To change body of implemented methods use File | Settings | File Templates.
+ public void removeRedirectUri(String redirectUri) {
+ mongoStore.pullItemFromList(user, "redirectUris", redirectUri);
}
@Override
- public void removeWebOrigin(String webOrigin) {
- //To change body of implemented methods use File | Settings | File Templates.
+ public Set<RequiredAction> getRequiredActions() {
+ Set<RequiredAction> result = new HashSet<RequiredAction>();
+ if (user.getRequiredActions() != null) {
+ result.addAll(user.getRequiredActions());
+ }
+ return result;
}
@Override
- public Set<String> getRedirectUris() {
- return null; //To change body of implemented methods use File | Settings | File Templates.
+ public void addRequiredAction(RequiredAction action) {
+ mongoStore.pushItemToList(user, "requiredActions", action, true);
}
@Override
- public void setRedirectUris(Set<String> redirectUris) {
- //To change body of implemented methods use File | Settings | File Templates.
+ public void removeRequiredAction(RequiredAction action) {
+ mongoStore.pullItemFromList(user, "requiredActions", action);
}
@Override
- public void addRedirectUri(String redirectUri) {
- //To change body of implemented methods use File | Settings | File Templates.
+ public boolean isTotp() {
+ return user.isTotp();
}
@Override
- public void removeRedirectUri(String redirectUri) {
- //To change body of implemented methods use File | Settings | File Templates.
+ public void setTotp(boolean totp) {
+ user.setTotp(totp);
+ updateUser();
+ }
+
+ protected void updateUser() {
+ mongoStore.updateObject(user);
}
}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/CredentialEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/CredentialEntity.java
new file mode 100644
index 0000000..f29eadc
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/CredentialEntity.java
@@ -0,0 +1,51 @@
+package org.keycloak.models.mongo.keycloak.entities;
+
+import org.keycloak.models.mongo.api.AbstractMongoEntity;
+import org.keycloak.models.mongo.api.MongoField;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class CredentialEntity extends AbstractMongoEntity {
+
+ private String type;
+ private String value;
+ private String device;
+ private byte[] salt;
+
+ @MongoField
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ @MongoField
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ @MongoField
+ public String getDevice() {
+ return device;
+ }
+
+ public void setDevice(String device) {
+ this.device = device;
+ }
+
+ @MongoField
+ public byte[] getSalt() {
+ return salt;
+ }
+
+ public void setSalt(byte[] salt) {
+ this.salt = salt;
+ }
+}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/MongoModelProvider.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/MongoModelProvider.java
index c035b56..910adde 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/MongoModelProvider.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/MongoModelProvider.java
@@ -2,6 +2,9 @@ package org.keycloak.models.mongo.keycloak;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.ModelProvider;
+import org.keycloak.models.mongo.keycloak.adapters.MongoKeycloakSessionFactory;
+import org.keycloak.models.mongo.utils.MongoConfiguration;
+import org.keycloak.models.mongo.utils.SystemPropertiesConfigurationProvider;
import java.lang.Override;
@@ -18,16 +21,7 @@ public class MongoModelProvider implements ModelProvider {
@Override
public KeycloakSessionFactory createFactory() {
- String host = PropertiesManager.getMongoHost();
- int port = PropertiesManager.getMongoPort();
- String dbName = PropertiesManager.getMongoDbName();
- boolean dropDatabaseOnStartup = PropertiesManager.dropDatabaseOnStartup();
-
- // Create MongoDBSessionFactory via reflection now
- try {
- return new MongoDBSessionFactory(host, port, dbName, dropDatabaseOnStartup);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
+ MongoConfiguration config = SystemPropertiesConfigurationProvider.createConfiguration();
+ return new MongoKeycloakSessionFactory(config);
}
}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/utils/MongoConfiguration.java b/model/mongo/src/main/java/org/keycloak/models/mongo/utils/MongoConfiguration.java
new file mode 100644
index 0000000..7a22ae6
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/utils/MongoConfiguration.java
@@ -0,0 +1,50 @@
+package org.keycloak.models.mongo.utils;
+
+/**
+ * Encapsulates all info about configuration of MongoDB instance
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class MongoConfiguration {
+
+ private final String host;
+ private final int port;
+ private final String dbName;
+
+ private final boolean clearCollectionsOnStartup;
+ private final boolean startEmbedded;
+
+ public MongoConfiguration(String host, int port, String dbName, boolean clearCollectionsOnStartup, boolean startEmbedded) {
+ this.host = host;
+ this.port = port;
+ this.dbName = dbName;
+ this.clearCollectionsOnStartup = clearCollectionsOnStartup;
+ this.startEmbedded = startEmbedded;
+ }
+
+ public String getHost() {
+ return host;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public String getDbName() {
+ return dbName;
+ }
+
+ public boolean isClearCollectionsOnStartup() {
+ 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);
+ }
+}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/utils/MongoModelUtils.java b/model/mongo/src/main/java/org/keycloak/models/mongo/utils/MongoModelUtils.java
new file mode 100644
index 0000000..260d182
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/utils/MongoModelUtils.java
@@ -0,0 +1,59 @@
+package org.keycloak.models.mongo.utils;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+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.keycloak.adapters.UserAdapter;
+import org.keycloak.models.mongo.keycloak.entities.RoleEntity;
+import org.keycloak.models.mongo.keycloak.entities.UserEntity;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class MongoModelUtils {
+
+ public static List<ObjectId> convertStringsToObjectIds(Collection<String> strings) {
+ List<ObjectId> result = new ArrayList<ObjectId>();
+ for (String id : strings) {
+ result.add(new ObjectId(id));
+ }
+ return result;
+ }
+
+ // Get everything including both application and realm roles
+ public static List<RoleEntity> getAllRolesOfUser(UserModel user, MongoStore mongoStore) {
+ UserEntity userEntity = ((UserAdapter)user).getUser();
+ List<String> roleIds = userEntity.getRoleIds();
+
+ if (roleIds == null || roleIds.isEmpty()) {
+ return Collections.EMPTY_LIST;
+ }
+
+ DBObject query = new QueryBuilder()
+ .and("_id").in(convertStringsToObjectIds(roleIds))
+ .get();
+ return mongoStore.loadObjects(RoleEntity.class, query);
+ }
+
+ // Get everything including both application and realm scopes
+ public static List<RoleEntity> getAllScopesOfUser(UserModel user, MongoStore mongoStore) {
+ UserEntity userEntity = ((UserAdapter)user).getUser();
+ List<String> scopeIds = userEntity.getScopeIds();
+
+ if (scopeIds == null || scopeIds.isEmpty()) {
+ return Collections.EMPTY_LIST;
+ }
+
+ DBObject query = new QueryBuilder()
+ .and("_id").in(convertStringsToObjectIds(scopeIds))
+ .get();
+ return mongoStore.loadObjects(RoleEntity.class, query);
+ }
+}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/utils/SystemPropertiesConfigurationProvider.java b/model/mongo/src/main/java/org/keycloak/models/mongo/utils/SystemPropertiesConfigurationProvider.java
new file mode 100755
index 0000000..d30786f
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/utils/SystemPropertiesConfigurationProvider.java
@@ -0,0 +1,56 @@
+package org.keycloak.models.mongo.utils;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+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";
+
+ // 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;
+
+ public static String getMongoHost() {
+ return System.getProperty(MONGO_HOST, "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;
+ }
+ }
+
+ public static String getMongoDbName() {
+ return System.getProperty(MONGO_DB_NAME, "keycloak");
+ }
+
+ public static boolean isClearCollectionsOnStartup() {
+ return Boolean.parseBoolean(System.getProperty(MONGO_CLEAR_COLLECTIONS_ON_STARTUP, "true"));
+ }
+
+ public static boolean isStartEmbedded() {
+ return Boolean.parseBoolean(System.getProperty(MONGO_START_EMBEDDED, "false"));
+ }
+
+ // Create configuration based on system properties
+ public static MongoConfiguration createConfiguration() {
+ return new MongoConfiguration(
+ getMongoHost(),
+ getMongoPort(),
+ getMongoDbName(),
+ isClearCollectionsOnStartup(),
+ isStartEmbedded()
+ );
+ }
+}
diff --git a/model/mongo/src/test/java/org/keycloak/models/mongo/test/Address.java b/model/mongo/src/test/java/org/keycloak/models/mongo/test/Address.java
index 386ca31..d68ce0d 100755
--- a/model/mongo/src/test/java/org/keycloak/models/mongo/test/Address.java
+++ b/model/mongo/src/test/java/org/keycloak/models/mongo/test/Address.java
@@ -1,20 +1,20 @@
package org.keycloak.models.mongo.test;
-import org.keycloak.models.mongo.api.AbstractNoSQLObject;
-import org.keycloak.models.mongo.api.NoSQLField;
+import org.keycloak.models.mongo.api.AbstractMongoEntity;
+import org.keycloak.models.mongo.api.MongoField;
import java.util.List;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
-public class Address extends AbstractNoSQLObject {
+public class Address extends AbstractMongoEntity {
private String street;
private int number;
private List<String> flatNumbers;
- @NoSQLField
+ @MongoField
public String getStreet() {
return street;
}
@@ -23,7 +23,7 @@ public class Address extends AbstractNoSQLObject {
this.street = street;
}
- @NoSQLField
+ @MongoField
public int getNumber() {
return number;
}
@@ -32,7 +32,7 @@ public class Address extends AbstractNoSQLObject {
this.number = number;
}
- @NoSQLField
+ @MongoField
public List<String> getFlatNumbers() {
return flatNumbers;
}
diff --git a/model/mongo/src/test/java/org/keycloak/models/mongo/test/MongoDBModelTest.java b/model/mongo/src/test/java/org/keycloak/models/mongo/test/MongoDBModelTest.java
index 3b8651a..88f1745 100755
--- a/model/mongo/src/test/java/org/keycloak/models/mongo/test/MongoDBModelTest.java
+++ b/model/mongo/src/test/java/org/keycloak/models/mongo/test/MongoDBModelTest.java
@@ -1,14 +1,16 @@
package org.keycloak.models.mongo.test;
import com.mongodb.DB;
+import com.mongodb.DBObject;
import com.mongodb.MongoClient;
+import com.mongodb.QueryBuilder;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
-import org.keycloak.models.mongo.api.NoSQL;
-import org.keycloak.models.mongo.api.NoSQLObject;
-import org.keycloak.models.mongo.api.query.NoSQLQuery;
-import org.keycloak.models.mongo.impl.MongoDBImpl;
+import org.junit.Test;
+import org.keycloak.models.mongo.api.MongoEntity;
+import org.keycloak.models.mongo.api.MongoStore;
+import org.keycloak.models.mongo.impl.MongoStoreImpl;
import java.net.UnknownHostException;
import java.util.ArrayList;
@@ -20,13 +22,13 @@ import java.util.List;
*/
public class MongoDBModelTest {
- private static final Class<? extends NoSQLObject>[] MANAGED_DATA_TYPES = (Class<? extends NoSQLObject>[])new Class<?>[] {
+ private static final Class<? extends MongoEntity>[] MANAGED_DATA_TYPES = (Class<? extends MongoEntity>[])new Class<?>[] {
Person.class,
Address.class,
};
private MongoClient mongoClient;
- private NoSQL mongoDB;
+ private MongoStore mongoDB;
@Before
public void before() throws Exception {
@@ -35,7 +37,7 @@ public class MongoDBModelTest {
mongoClient = new MongoClient("localhost", 27017);
DB db = mongoClient.getDB("keycloakTest");
- mongoDB = new MongoDBImpl(db, true, MANAGED_DATA_TYPES);
+ mongoDB = new MongoStoreImpl(db, true, MANAGED_DATA_TYPES);
} catch (UnknownHostException e) {
throw new RuntimeException(e);
@@ -47,7 +49,7 @@ public class MongoDBModelTest {
mongoClient.close();
}
- // @Test
+ @Test
public void mongoModelTest() throws Exception {
// Add some user
Person john = new Person();
@@ -55,17 +57,17 @@ public class MongoDBModelTest {
john.setAge(25);
john.setGender(Person.Gender.MALE);
- mongoDB.saveObject(john);
+ mongoDB.insertObject(john);
// Add another user
Person mary = new Person();
mary.setFirstName("mary");
- mary.setKids(Arrays.asList(new String[] {"Peter", "Paul", "Wendy"}));
+ mary.setKids(Arrays.asList("Peter", "Paul", "Wendy"));
Address addr1 = new Address();
addr1.setStreet("Elm");
addr1.setNumber(5);
- addr1.setFlatNumbers(Arrays.asList(new String[] {"flat1", "flat2"}));
+ addr1.setFlatNumbers(Arrays.asList("flat1", "flat2"));
Address addr2 = new Address();
List<Address> addresses = new ArrayList<Address>();
addresses.add(addr1);
@@ -74,12 +76,13 @@ public class MongoDBModelTest {
mary.setAddresses(addresses);
mary.setMainAddress(addr1);
mary.setGender(Person.Gender.FEMALE);
- mary.setGenders(Arrays.asList(new Person.Gender[] {Person.Gender.FEMALE}));
- mongoDB.saveObject(mary);
+ mary.setGenders(Arrays.asList(Person.Gender.FEMALE));
- Assert.assertEquals(2, mongoDB.loadObjects(Person.class, mongoDB.createQueryBuilder().build()).size());
+ mongoDB.insertObject(mary);
- NoSQLQuery query = mongoDB.createQueryBuilder().andCondition("addresses.flatNumbers", "flat1").build();
+ Assert.assertEquals(2, mongoDB.loadObjects(Person.class, new QueryBuilder().get()).size());
+
+ DBObject query = new QueryBuilder().and("addresses.flatNumbers").is("flat1").get();
List<Person> persons = mongoDB.loadObjects(Person.class, query);
Assert.assertEquals(1, persons.size());
mary = persons.get(0);
@@ -89,13 +92,13 @@ public class MongoDBModelTest {
Assert.assertEquals(Address.class, mary.getAddresses().get(0).getClass());
// Test push/pull
- mongoDB.pushItemToList(mary, "kids", "Pauline");
+ mongoDB.pushItemToList(mary, "kids", "Pauline", true);
mongoDB.pullItemFromList(mary, "kids", "Paul");
Address addr3 = new Address();
addr3.setNumber(6);
addr3.setStreet("Broadway");
- mongoDB.pushItemToList(mary, "addresses", addr3);
+ mongoDB.pushItemToList(mary, "addresses", addr3, true);
mary = mongoDB.loadObject(Person.class, mary.getId());
Assert.assertEquals(3, mary.getKids().size());
@@ -107,5 +110,24 @@ public class MongoDBModelTest {
Assert.assertEquals(5, mainAddress.getNumber());
Assert.assertEquals(Person.Gender.FEMALE, mary.getGender());
Assert.assertTrue(mary.getGenders().contains(Person.Gender.FEMALE));
+
+
+ // Some test of Map (attributes)
+ mary.addAttribute("attr1", "value1");
+ mary.addAttribute("attr2", "value2");
+ mary.addAttribute("attr.some3", "value3");
+ mongoDB.updateObject(mary);
+
+ mary = mongoDB.loadObject(Person.class, mary.getId());
+ Assert.assertEquals(3, mary.getAttributes().size());
+
+ mary.removeAttribute("attr2");
+ mary.removeAttribute("nonExisting");
+ mongoDB.updateObject(mary);
+
+ mary = mongoDB.loadObject(Person.class, mary.getId());
+ Assert.assertEquals(2, mary.getAttributes().size());
+ Assert.assertEquals("value1", mary.getAttributes().get("attr1"));
+ Assert.assertEquals("value3", mary.getAttributes().get("attr.some3"));
}
}
diff --git a/model/mongo/src/test/java/org/keycloak/models/mongo/test/Person.java b/model/mongo/src/test/java/org/keycloak/models/mongo/test/Person.java
index 7f8d0d9..7298c6e 100755
--- a/model/mongo/src/test/java/org/keycloak/models/mongo/test/Person.java
+++ b/model/mongo/src/test/java/org/keycloak/models/mongo/test/Person.java
@@ -1,17 +1,19 @@
package org.keycloak.models.mongo.test;
-import org.keycloak.models.mongo.api.AbstractNoSQLObject;
-import org.keycloak.models.mongo.api.NoSQLCollection;
-import org.keycloak.models.mongo.api.NoSQLField;
-import org.keycloak.models.mongo.api.NoSQLId;
+import org.keycloak.models.mongo.api.AbstractMongoEntity;
+import org.keycloak.models.mongo.api.MongoCollection;
+import org.keycloak.models.mongo.api.MongoField;
+import org.keycloak.models.mongo.api.MongoId;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
-@NoSQLCollection(collectionName = "persons")
-public class Person extends AbstractNoSQLObject {
+@MongoCollection(collectionName = "persons")
+public class Person extends AbstractMongoEntity {
private String id;
private String firstName;
@@ -21,9 +23,10 @@ public class Person extends AbstractNoSQLObject {
private Address mainAddress;
private Gender gender;
private List<Gender> genders;
+ private Map<String, String> attributes = new HashMap<String, String>();
- @NoSQLId
+ @MongoId
public String getId() {
return id;
}
@@ -32,7 +35,7 @@ public class Person extends AbstractNoSQLObject {
this.id = id;
}
- @NoSQLField
+ @MongoField
public String getFirstName() {
return firstName;
}
@@ -41,7 +44,7 @@ public class Person extends AbstractNoSQLObject {
this.firstName = firstName;
}
- @NoSQLField
+ @MongoField
public int getAge() {
return age;
}
@@ -50,7 +53,7 @@ public class Person extends AbstractNoSQLObject {
this.age = age;
}
- @NoSQLField
+ @MongoField
public Gender getGender() {
return gender;
}
@@ -59,7 +62,7 @@ public class Person extends AbstractNoSQLObject {
this.gender = gender;
}
- @NoSQLField
+ @MongoField
public List<Gender> getGenders() {
return genders;
}
@@ -68,7 +71,7 @@ public class Person extends AbstractNoSQLObject {
this.genders = genders;
}
- @NoSQLField
+ @MongoField
public List<String> getKids() {
return kids;
}
@@ -77,7 +80,7 @@ public class Person extends AbstractNoSQLObject {
this.kids = kids;
}
- @NoSQLField
+ @MongoField
public List<Address> getAddresses() {
return addresses;
}
@@ -86,7 +89,7 @@ public class Person extends AbstractNoSQLObject {
this.addresses = addresses;
}
- @NoSQLField
+ @MongoField
public Address getMainAddress() {
return mainAddress;
}
@@ -95,6 +98,23 @@ public class Person extends AbstractNoSQLObject {
this.mainAddress = mainAddress;
}
+ @MongoField
+ public Map<String, String> getAttributes() {
+ return attributes;
+ }
+
+ public void setAttributes(Map<String, String> attributes) {
+ this.attributes = attributes;
+ }
+
+ public void addAttribute(String key, String value) {
+ attributes.put(key, value);
+ }
+
+ public void removeAttribute(String key) {
+ attributes.remove(key);
+ }
+
public static enum Gender {
MALE, FEMALE
}
model/pom.xml 2(+1 -1)
diff --git a/model/pom.xml b/model/pom.xml
index ab454ca..3301b71 100755
--- a/model/pom.xml
+++ b/model/pom.xml
@@ -37,6 +37,6 @@
<module>api</module>
<!-- <module>picketlink</module> -->
<module>jpa</module>
- <!-- <module>mongo</module> -->
+ <module>mongo</module>
</modules>
</project>
pom.xml 4(+2 -2)
diff --git a/pom.xml b/pom.xml
index b1aaefd..fc58b75 100755
--- a/pom.xml
+++ b/pom.xml
@@ -16,7 +16,7 @@
<resteasy.version>3.0.6.Final</resteasy.version>
<undertow.version>1.0.0.Beta30</undertow.version>
<picketlink.version>2.5.0.Beta6</picketlink.version>
- <mongo.driver.version>2.11.2</mongo.driver.version>
+ <mongo.driver.version>2.11.3</mongo.driver.version>
<jboss.logging.version>3.1.1.GA</jboss.logging.version>
<jboss-logging-tools.version>1.2.0.Beta1</jboss-logging-tools.version>
<hibernate.javax.persistence.version>1.0.1.Final</hibernate.javax.persistence.version>
@@ -316,7 +316,7 @@
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
- <version>1.27</version>
+ <version>1.40</version>
</dependency>
<dependency>
<groupId>org.apache.jmeter</groupId>
services/pom.xml 18(+16 -2)
diff --git a/services/pom.xml b/services/pom.xml
index d97d6b4..de0ae59 100755
--- a/services/pom.xml
+++ b/services/pom.xml
@@ -72,11 +72,25 @@
</dependency>
-->
- <!--<dependency>
+ <!-- TODO: remove -->
+ <dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-mongo</artifactId>
<version>${project.version}</version>
- </dependency>-->
+ <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>
diff --git a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
index 6c89c12..080e28f 100755
--- a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
+++ b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
@@ -8,6 +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 javax.servlet.ServletContext;
import javax.ws.rs.core.Application;
@@ -15,7 +16,6 @@ import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriInfo;
import java.net.URI;
import java.util.HashSet;
-import java.util.ServiceLoader;
import java.util.Set;
/**
@@ -26,9 +26,6 @@ public class KeycloakApplication extends Application {
private static final Logger log = Logger.getLogger(KeycloakApplication.class);
- private static final String MODEL_PROVIDER = "keycloak.model";
- private static final String DEFAULT_MODEL_PROVIDER = "jpa";
-
protected Set<Object> singletons = new HashSet<Object>();
protected Set<Class<?>> classes = new HashSet<Class<?>>();
@@ -73,28 +70,7 @@ public class KeycloakApplication extends Application {
public static KeycloakSessionFactory createSessionFactory() {
- ServiceLoader<ModelProvider> providers = ServiceLoader.load(ModelProvider.class);
- String configuredProvider = System.getProperty(MODEL_PROVIDER);
- ModelProvider provider = null;
-
- if (configuredProvider != null) {
- for (ModelProvider p : providers) {
- if (p.getId().equals(configuredProvider)) {
- provider = p;
- }
- }
- } else {
- for (ModelProvider p : providers) {
- if (provider == null) {
- provider = p;
- }
-
- if (p.getId().equals(DEFAULT_MODEL_PROVIDER)) {
- provider = p;
- break;
- }
- }
- }
+ ModelProvider provider = ModelProviderUtils.getConfiguredModelProvider();
if (provider != null) {
log.debug("Model provider: " + provider.getId());
diff --git a/services/src/main/java/org/keycloak/services/utils/ModelProviderUtils.java b/services/src/main/java/org/keycloak/services/utils/ModelProviderUtils.java
new file mode 100644
index 0000000..7dcfc95
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/utils/ModelProviderUtils.java
@@ -0,0 +1,50 @@
+package org.keycloak.services.utils;
+
+import java.util.ServiceLoader;
+
+import org.keycloak.models.ModelProvider;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class ModelProviderUtils {
+
+ public static final String MODEL_PROVIDER = "keycloak.model";
+ public static final String DEFAULT_MODEL_PROVIDER = "jpa";
+
+ public static Iterable<ModelProvider> getRegisteredProviders() {
+ return ServiceLoader.load(ModelProvider.class);
+ }
+
+ public static ModelProvider getConfiguredModelProvider(Iterable<ModelProvider> providers) {
+ String configuredProvider = System.getProperty(MODEL_PROVIDER);
+ ModelProvider provider = null;
+
+ if (configuredProvider != null) {
+ for (ModelProvider p : providers) {
+ if (p.getId().equals(configuredProvider)) {
+ provider = p;
+ }
+ }
+ } else {
+ for (ModelProvider p : providers) {
+ if (provider == null) {
+ provider = p;
+ }
+
+ if (p.getId().equals(DEFAULT_MODEL_PROVIDER)) {
+ provider = p;
+ break;
+ }
+ }
+ }
+
+ return provider;
+ }
+
+ public static ModelProvider getConfiguredModelProvider() {
+ return getConfiguredModelProvider(getRegisteredProviders());
+ }
+
+
+}
diff --git a/services/src/test/java/org/keycloak/services/managers/AuthenticationManagerTest.java b/services/src/test/java/org/keycloak/services/managers/AuthenticationManagerTest.java
index 527838d..404e8f2 100755
--- a/services/src/test/java/org/keycloak/services/managers/AuthenticationManagerTest.java
+++ b/services/src/test/java/org/keycloak/services/managers/AuthenticationManagerTest.java
@@ -11,7 +11,6 @@ import org.keycloak.models.utils.TimeBasedOTP;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.services.managers.AuthenticationManager.AuthenticationStatus;
import org.keycloak.test.common.AbstractKeycloakTest;
-import org.keycloak.test.common.SessionFactoryTestContext;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap;
@@ -25,10 +24,9 @@ public class AuthenticationManagerTest extends AbstractKeycloakTest {
private RealmModel realm;
private UserModel user;
- public AuthenticationManagerTest(SessionFactoryTestContext testContext) {
- super(testContext);
+ public AuthenticationManagerTest(String providerId) {
+ super(providerId);
}
-
@Test
public void authForm() {
AuthenticationStatus status = am.authenticateForm(realm, user, formData);
@@ -126,7 +124,7 @@ public class AuthenticationManagerTest extends AbstractKeycloakTest {
@Before
public void before() throws Exception {
super.before();
- realm = getRealmManager().createRealm("Test");
+ realm = realmManager.createRealm("Test");
realm.setAccessCodeLifespan(100);
realm.setEnabled(true);
realm.setName("Test");
diff --git a/services/src/test/java/org/keycloak/test/AdapterTest.java b/services/src/test/java/org/keycloak/test/AdapterTest.java
index 9f49a98..1e45914 100755
--- a/services/src/test/java/org/keycloak/test/AdapterTest.java
+++ b/services/src/test/java/org/keycloak/test/AdapterTest.java
@@ -18,7 +18,6 @@ import org.keycloak.services.managers.ApplianceBootstrap;
import org.keycloak.services.managers.OAuthClientManager;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.test.common.AbstractKeycloakTest;
-import org.keycloak.test.common.SessionFactoryTestContext;
import java.util.ArrayList;
import java.util.Arrays;
@@ -35,8 +34,8 @@ import java.util.StringTokenizer;
public class AdapterTest extends AbstractKeycloakTest {
private RealmModel realmModel;
- public AdapterTest(SessionFactoryTestContext testContext) {
- super(testContext);
+ public AdapterTest(String providerId) {
+ super(providerId);
}
@Test
@@ -57,7 +56,7 @@ public class AdapterTest extends AbstractKeycloakTest {
@Test
public void test1CreateRealm() throws Exception {
- realmModel = getRealmManager().createRealm("JUGGLER");
+ realmModel = realmManager.createRealm("JUGGLER");
realmModel.setAccessCodeLifespan(100);
realmModel.setAccessCodeLifespanUserAction(600);
realmModel.setEnabled(true);
@@ -69,7 +68,7 @@ public class AdapterTest extends AbstractKeycloakTest {
realmModel.addDefaultRole("foo");
System.out.println(realmModel.getId());
- realmModel = getRealmManager().getRealm(realmModel.getId());
+ realmModel = realmManager.getRealm(realmModel.getId());
Assert.assertNotNull(realmModel);
Assert.assertEquals(realmModel.getAccessCodeLifespan(), 100);
Assert.assertEquals(600, realmModel.getAccessCodeLifespanUserAction());
@@ -85,7 +84,7 @@ public class AdapterTest extends AbstractKeycloakTest {
@Test
public void testRealmListing() throws Exception {
- realmModel = getRealmManager().createRealm("JUGGLER");
+ realmModel = realmManager.createRealm("JUGGLER");
realmModel.setAccessCodeLifespan(100);
realmModel.setAccessCodeLifespanUserAction(600);
realmModel.setEnabled(true);
@@ -97,7 +96,7 @@ public class AdapterTest extends AbstractKeycloakTest {
realmModel.addDefaultRole("foo");
System.out.println(realmModel.getId());
- realmModel = getRealmManager().getRealm(realmModel.getId());
+ realmModel = realmManager.getRealm(realmModel.getId());
Assert.assertNotNull(realmModel);
Assert.assertEquals(realmModel.getAccessCodeLifespan(), 100);
Assert.assertEquals(600, realmModel.getAccessCodeLifespanUserAction());
@@ -300,7 +299,7 @@ public class AdapterTest extends AbstractKeycloakTest {
user3.setEmail("knut@redhat.com");
}
- RealmManager adapter = getRealmManager();
+ RealmManager adapter = realmManager;
{
List<UserModel> userModels = adapter.searchUsers("total junk query", realmModel);
diff --git a/services/src/test/java/org/keycloak/test/ApplicationModelTest.java b/services/src/test/java/org/keycloak/test/ApplicationModelTest.java
index 1942b41..8c3c3f2 100755
--- a/services/src/test/java/org/keycloak/test/ApplicationModelTest.java
+++ b/services/src/test/java/org/keycloak/test/ApplicationModelTest.java
@@ -1,19 +1,15 @@
package org.keycloak.test;
-import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.models.ApplicationModel;
-import org.keycloak.models.KeycloakSession;
-import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.representations.idm.ApplicationRepresentation;
import org.keycloak.services.managers.ApplicationManager;
-import org.keycloak.services.managers.RealmManager;
-import org.keycloak.services.resources.KeycloakApplication;
+import org.keycloak.test.common.AbstractKeycloakTest;
import java.util.Iterator;
import java.util.List;
@@ -21,24 +17,21 @@ import java.util.List;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
-public class ApplicationModelTest extends AbstractKeycloakServerTest {
- private KeycloakSessionFactory factory;
- private KeycloakSession identitySession;
- private RealmManager manager;
+public class ApplicationModelTest extends AbstractKeycloakTest {
private ApplicationModel application;
private RealmModel realm;
private ApplicationManager appManager;
+ public ApplicationModelTest(String providerId) {
+ super(providerId);
+ }
+
@Before
public void before() throws Exception {
- factory = KeycloakApplication.createSessionFactory();
- identitySession = factory.createSession();
- identitySession.getTransaction().begin();
- manager = new RealmManager(identitySession);
-
- appManager = new ApplicationManager(manager);
+ super.before();
+ appManager = new ApplicationManager(realmManager);
- realm = manager.createRealm("original");
+ realm = realmManager.createRealm("original");
application = realm.addApplication("application");
application.setBaseUrl("http://base");
application.setManagementUrl("http://management");
@@ -57,16 +50,9 @@ public class ApplicationModelTest extends AbstractKeycloakServerTest {
application.updateApplication();
}
- @After
- public void after() throws Exception {
- identitySession.getTransaction().commit();
- identitySession.close();
- factory.close();
- }
-
@Test
public void persist() {
- RealmModel persisted = manager.getRealm(realm.getId());
+ RealmModel persisted = realmManager.getRealm(realm.getId());
assertEquals(application, persisted.getApplicationNameMap().get("app-name"));
}
@@ -75,7 +61,7 @@ public class ApplicationModelTest extends AbstractKeycloakServerTest {
public void json() {
ApplicationRepresentation representation = appManager.toRepresentation(application);
- RealmModel realm = manager.createRealm("copy");
+ RealmModel realm = realmManager.createRealm("copy");
ApplicationModel copy = appManager.createApplication(realm, representation);
assertEquals(application, copy);
diff --git a/services/src/test/java/org/keycloak/test/common/AbstractKeycloakTest.java b/services/src/test/java/org/keycloak/test/common/AbstractKeycloakTest.java
index 11512d7..63c411d 100755
--- a/services/src/test/java/org/keycloak/test/common/AbstractKeycloakTest.java
+++ b/services/src/test/java/org/keycloak/test/common/AbstractKeycloakTest.java
@@ -1,5 +1,6 @@
package org.keycloak.test.common;
+import org.jboss.resteasy.logging.Logger;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
@@ -8,11 +9,15 @@ 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;
+import java.util.ServiceLoader;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@@ -20,58 +25,42 @@ import java.util.List;
@RunWith(Parameterized.class)
public abstract class AbstractKeycloakTest {
- protected static final SessionFactoryTestContext[] TEST_CONTEXTS;
+ private static final Logger log = Logger.getLogger(AbstractKeycloakTest.class);
- private final SessionFactoryTestContext testContext;
protected KeycloakSessionFactory factory;
protected KeycloakSession identitySession;
protected RealmManager realmManager;
- // STATIC METHODS
-
- static
- {
- // TODO: MongoDB disabled by default
- TEST_CONTEXTS = new SessionFactoryTestContext[] {
- //new PicketlinkSessionFactoryTestContext(),
- new JpaSessionFactoryTestContext(),
- // new MongoDBSessionFactoryTestContext()
- };
- }
-
@Parameterized.Parameters
public static Iterable<Object[]> parameters() {
- List<Object[]> params = new ArrayList<Object[]>();
-
- for (SessionFactoryTestContext testContext : TEST_CONTEXTS) {
- params.add(new Object[] {testContext});
+ 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);
}
- return params;
- }
- @BeforeClass
- public static void baseBeforeClass() {
- for (SessionFactoryTestContext testContext : TEST_CONTEXTS) {
- testContext.beforeTestClass();
- }
- }
+ log.debug("Will use model providers: " + modelProviders);
+
+ List<Object[]> params = new ArrayList<Object[]>();
- @AfterClass
- public static void baseAfterClass() {
- for (SessionFactoryTestContext testContext : TEST_CONTEXTS) {
- testContext.afterTestClass();
+ for (ModelProvider provider : modelProviders) {
+ params.add(new Object[] { provider.getId() });
}
+ return params;
}
- // NON-STATIC METHODS
- public AbstractKeycloakTest(SessionFactoryTestContext testContext) {
- this.testContext = testContext;
+ public AbstractKeycloakTest(String providerId) {
+ System.setProperty(ModelProviderUtils.MODEL_PROVIDER, providerId);
}
@Before
public void before() throws Exception {
- testContext.initEnvironment();
factory = KeycloakApplication.createSessionFactory();
identitySession = factory.createSession();
identitySession.getTransaction().begin();
@@ -85,12 +74,4 @@ public abstract class AbstractKeycloakTest {
factory.close();
}
- protected RealmManager getRealmManager() {
- return realmManager;
- }
-
- protected KeycloakSession getIdentitySession() {
- return identitySession;
- }
-
}
diff --git a/services/src/test/java/org/keycloak/test/ImportTest.java b/services/src/test/java/org/keycloak/test/ImportTest.java
index d7ad50c..8b7287c 100755
--- a/services/src/test/java/org/keycloak/test/ImportTest.java
+++ b/services/src/test/java/org/keycloak/test/ImportTest.java
@@ -13,7 +13,6 @@ import org.keycloak.models.UserModel;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.test.common.AbstractKeycloakTest;
-import org.keycloak.test.common.SessionFactoryTestContext;
import java.util.List;
import java.util.Set;
@@ -25,13 +24,13 @@ import java.util.Set;
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class ImportTest extends AbstractKeycloakTest {
- public ImportTest(SessionFactoryTestContext testContext) {
- super(testContext);
+ public ImportTest(String providerId) {
+ super(providerId);
}
@Test
public void install() throws Exception {
- RealmManager manager = getRealmManager();
+ RealmManager manager = realmManager;
RealmRepresentation rep = AbstractKeycloakServerTest.loadJson("testrealm.json");
RealmModel realm = manager.createRealm("demo", rep.getRealm());
manager.importRealm(rep, realm);
@@ -91,7 +90,7 @@ public class ImportTest extends AbstractKeycloakTest {
@Test
public void install2() throws Exception {
- RealmManager manager = getRealmManager();
+ RealmManager manager = realmManager;
RealmRepresentation rep = AbstractKeycloakServerTest.loadJson("testrealm-demo.json");
RealmModel realm = manager.createRealm("demo", rep.getRealm());
manager.importRealm(rep, realm);
diff --git a/services/src/test/java/org/keycloak/test/ModelTest.java b/services/src/test/java/org/keycloak/test/ModelTest.java
index cdec004..1c9b5d5 100755
--- a/services/src/test/java/org/keycloak/test/ModelTest.java
+++ b/services/src/test/java/org/keycloak/test/ModelTest.java
@@ -1,46 +1,27 @@
package org.keycloak.test;
-import org.junit.After;
import org.junit.Assert;
-import org.junit.Before;
import org.junit.Test;
-import org.keycloak.models.KeycloakSession;
-import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.services.managers.ModelToRepresentation;
-import org.keycloak.services.managers.RealmManager;
-import org.keycloak.services.resources.KeycloakApplication;
+import org.keycloak.test.common.AbstractKeycloakTest;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
-public class ModelTest extends AbstractKeycloakServerTest {
- private KeycloakSessionFactory factory;
- private KeycloakSession identitySession;
- private RealmManager manager;
-
- @Before
- public void before() throws Exception {
- factory = KeycloakApplication.createSessionFactory();
- identitySession = factory.createSession();
- identitySession.getTransaction().begin();
- manager = new RealmManager(identitySession);
- }
+public class ModelTest extends AbstractKeycloakTest {
- @After
- public void after() throws Exception {
- identitySession.getTransaction().commit();
- identitySession.close();
- factory.close();
+ public ModelTest(String providerId) {
+ super(providerId);
}
@Test
public void importExportRealm() {
- RealmModel realm = manager.createRealm("original");
+ RealmModel realm = realmManager.createRealm("original");
realm.setRegistrationAllowed(true);
realm.setResetPasswordAllowed(true);
realm.setSocial(true);
@@ -62,10 +43,10 @@ public class ModelTest extends AbstractKeycloakServerTest {
HashMap<String, String> social = new HashMap<String,String>();
social.put("google.key", "1234");
social.put("google.secret", "5678");
- realm.setSmtpConfig(social);
+ realm.setSocialConfig(social);
- RealmModel peristed = manager.getRealm(realm.getId());
- assertEquals(realm, peristed);
+ RealmModel persisted = realmManager.getRealm(realm.getId());
+ assertEquals(realm, persisted);
RealmModel copy = importExport(realm, "copy");
assertEquals(realm, copy);
@@ -103,9 +84,9 @@ public class ModelTest extends AbstractKeycloakServerTest {
private RealmModel importExport(RealmModel src, String copyName) {
RealmRepresentation representation = ModelToRepresentation.toRepresentation(src);
- RealmModel copy = manager.createRealm(copyName);
- manager.importRealm(representation, copy);
- return manager.getRealm(copy.getId());
+ RealmModel copy = realmManager.createRealm(copyName);
+ realmManager.importRealm(representation, copy);
+ return realmManager.getRealm(copy.getId());
}
}
diff --git a/services/src/test/java/org/keycloak/test/UserModelTest.java b/services/src/test/java/org/keycloak/test/UserModelTest.java
index 338af77..d808174 100755
--- a/services/src/test/java/org/keycloak/test/UserModelTest.java
+++ b/services/src/test/java/org/keycloak/test/UserModelTest.java
@@ -1,17 +1,13 @@
package org.keycloak.test;
-import org.junit.After;
import org.junit.Assert;
-import org.junit.Before;
import org.junit.Test;
-import org.keycloak.models.KeycloakSession;
-import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserModel.RequiredAction;
import org.keycloak.services.managers.RealmManager;
-import org.keycloak.services.resources.KeycloakApplication;
+import org.keycloak.test.common.AbstractKeycloakTest;
import java.util.Iterator;
import java.util.List;
@@ -19,29 +15,15 @@ import java.util.List;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
-public class UserModelTest extends AbstractKeycloakServerTest {
- private KeycloakSessionFactory factory;
- private KeycloakSession identitySession;
- private RealmManager manager;
-
- @Before
- public void before() throws Exception {
- factory = KeycloakApplication.createSessionFactory();
- identitySession = factory.createSession();
- identitySession.getTransaction().begin();
- manager = new RealmManager(identitySession);
- }
+public class UserModelTest extends AbstractKeycloakTest {
- @After
- public void after() throws Exception {
- identitySession.getTransaction().commit();
- identitySession.close();
- factory.close();
+ public UserModelTest(String providerId) {
+ super(providerId);
}
@Test
public void persistUser() {
- RealmModel realm = manager.createRealm("original");
+ RealmModel realm = realmManager.createRealm("original");
UserModel user = realm.addUser("user");
user.setFirstName("first-name");
user.setLastName("last-name");
@@ -56,14 +38,14 @@ public class UserModelTest extends AbstractKeycloakServerTest {
user.addWebOrigin("origin-1");
user.addWebOrigin("origin-2");
- UserModel persisted = manager.getRealm(realm.getId()).getUser("user");
+ UserModel persisted = realmManager.getRealm(realm.getId()).getUser("user");
assertEquals(user, persisted);
}
@Test
public void webOriginSetTest() {
- RealmModel realm = manager.createRealm("original");
+ RealmModel realm = realmManager.createRealm("original");
UserModel user = realm.addUser("user");
Assert.assertTrue(user.getWebOrigins().isEmpty());
@@ -83,7 +65,7 @@ public class UserModelTest extends AbstractKeycloakServerTest {
@Test
public void testUserRequiredActions() throws Exception {
- RealmModel realm = manager.createRealm("original");
+ RealmModel realm = realmManager.createRealm("original");
UserModel user = realm.addUser("user");
Assert.assertTrue(user.getRequiredActions().isEmpty());
@@ -91,7 +73,7 @@ public class UserModelTest extends AbstractKeycloakServerTest {
user.addRequiredAction(UserModel.RequiredAction.CONFIGURE_TOTP);
String id = realm.getId();
commit();
- realm = manager.getRealm(id);
+ realm = realmManager.getRealm(id);
user = realm.getUser("user");
Assert.assertEquals(1, user.getRequiredActions().size());
@@ -127,7 +109,7 @@ public class UserModelTest extends AbstractKeycloakServerTest {
identitySession.close();
identitySession = factory.createSession();
identitySession.getTransaction().begin();
- manager = new RealmManager(identitySession);
+ realmManager = new RealmManager(identitySession);
}
public static void assertEquals(UserModel expected, UserModel actual) {