keycloak-memoizeit
Changes
services/src/main/java/org/keycloak/services/models/nosql/adapters/MongoDBSessionFactory.java 43(+43 -0)
services/src/main/java/org/keycloak/services/models/nosql/api/AbstractAttributedNoSQLObject.java 37(+37 -0)
Details
diff --git a/services/src/main/java/org/keycloak/services/models/nosql/adapters/MongoDBSessionFactory.java b/services/src/main/java/org/keycloak/services/models/nosql/adapters/MongoDBSessionFactory.java
new file mode 100644
index 0000000..1c82f49
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/models/nosql/adapters/MongoDBSessionFactory.java
@@ -0,0 +1,43 @@
+package org.keycloak.services.models.nosql.adapters;
+
+import java.net.UnknownHostException;
+
+import com.mongodb.DB;
+import com.mongodb.MongoClient;
+import org.keycloak.services.models.KeycloakSession;
+import org.keycloak.services.models.KeycloakSessionFactory;
+import org.keycloak.services.models.nosql.api.NoSQL;
+import org.keycloak.services.models.nosql.impl.MongoDBImpl;
+
+/**
+ * NoSQL implementation based on MongoDB
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class MongoDBSessionFactory implements KeycloakSessionFactory {
+
+ private final MongoClient mongoClient;
+ private final NoSQL mongoDB;
+
+ public MongoDBSessionFactory(String host, int port, String dbName) {
+ try {
+ // TODO: authentication support
+ mongoClient = new MongoClient(host, port);
+
+ DB db = mongoClient.getDB(dbName);
+ mongoDB = new MongoDBImpl(db);
+ } catch (UnknownHostException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public KeycloakSession createSession() {
+ return new NoSQLSession(mongoDB);
+ }
+
+ @Override
+ public void close() {
+ mongoClient.close();
+ }
+}
diff --git a/services/src/main/java/org/keycloak/services/models/nosql/adapters/NoSQLSession.java b/services/src/main/java/org/keycloak/services/models/nosql/adapters/NoSQLSession.java
new file mode 100644
index 0000000..10e5413
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/models/nosql/adapters/NoSQLSession.java
@@ -0,0 +1,67 @@
+package org.keycloak.services.models.nosql.adapters;
+
+import java.util.List;
+
+import org.jboss.resteasy.spi.NotImplementedYetException;
+import org.keycloak.services.models.KeycloakSession;
+import org.keycloak.services.models.KeycloakTransaction;
+import org.keycloak.services.models.RealmModel;
+import org.keycloak.services.models.UserModel;
+import org.keycloak.services.models.nosql.data.RealmData;
+import org.keycloak.services.models.nosql.api.NoSQL;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class NoSQLSession implements KeycloakSession {
+
+ private static final NoSQLTransaction PLACEHOLDER = new NoSQLTransaction();
+ private final NoSQL noSQL;
+
+ public NoSQLSession(NoSQL noSQL) {
+ this.noSQL = noSQL;
+ }
+
+ @Override
+ public KeycloakTransaction getTransaction() {
+ return PLACEHOLDER;
+ }
+
+ @Override
+ public void close() {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public RealmModel createRealm(String name) {
+ RealmData newRealm = new RealmData();
+ newRealm.setName(name);
+
+ noSQL.saveObject(newRealm);
+
+ RealmAdapter realm = new RealmAdapter(newRealm, noSQL);
+ return realm;
+ }
+
+ @Override
+ public RealmModel createRealm(String id, String name) {
+ // Ignore ID for now. It seems that it exists just for workaround picketlink
+ return createRealm(name);
+ }
+
+ @Override
+ public RealmModel getRealm(String id) {
+ RealmData realmData = noSQL.loadObject(RealmData.class, id);
+ return new RealmAdapter(realmData, noSQL);
+ }
+
+ @Override
+ public List<RealmModel> getRealms(UserModel admin) {
+ throw new NotImplementedYetException();
+ }
+
+ @Override
+ public void deleteRealm(RealmModel realm) {
+ noSQL.removeObject(RealmData.class, realm.getId());
+ }
+}
diff --git a/services/src/main/java/org/keycloak/services/models/nosql/adapters/NoSQLTransaction.java b/services/src/main/java/org/keycloak/services/models/nosql/adapters/NoSQLTransaction.java
new file mode 100644
index 0000000..60a1338
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/models/nosql/adapters/NoSQLTransaction.java
@@ -0,0 +1,39 @@
+package org.keycloak.services.models.nosql.adapters;
+
+import org.keycloak.services.models.KeycloakTransaction;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class NoSQLTransaction implements KeycloakTransaction {
+
+ @Override
+ public void begin() {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void commit() {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void rollback() {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void setRollbackOnly() {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public boolean getRollbackOnly() {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public boolean isActive() {
+ return true;
+ }
+}
diff --git a/services/src/main/java/org/keycloak/services/models/nosql/adapters/RealmAdapter.java b/services/src/main/java/org/keycloak/services/models/nosql/adapters/RealmAdapter.java
new file mode 100644
index 0000000..58f73ef
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/models/nosql/adapters/RealmAdapter.java
@@ -0,0 +1,492 @@
+package org.keycloak.services.models.nosql.adapters;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.bouncycastle.openssl.PEMWriter;
+import org.jboss.resteasy.security.PemUtils;
+import org.keycloak.services.models.ApplicationModel;
+import org.keycloak.services.models.RealmModel;
+import org.keycloak.services.models.RequiredCredentialModel;
+import org.keycloak.services.models.RoleModel;
+import org.keycloak.services.models.SocialLinkModel;
+import org.keycloak.services.models.UserCredentialModel;
+import org.keycloak.services.models.UserModel;
+import org.keycloak.services.models.nosql.api.NoSQL;
+import org.keycloak.services.models.nosql.api.NoSQLQuery;
+import org.keycloak.services.models.nosql.data.RealmData;
+import org.keycloak.services.models.nosql.data.RoleData;
+import org.keycloak.services.models.nosql.data.UserData;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class RealmAdapter implements RealmModel {
+
+ private final RealmData realm;
+ private final NoSQL noSQL;
+
+ protected volatile transient PublicKey publicKey;
+ protected volatile transient PrivateKey privateKey;
+
+ public RealmAdapter(RealmData realmData, NoSQL noSQL) {
+ this.realm = realmData;
+ this.noSQL = noSQL;
+ }
+
+ @Override
+ public String getId() {
+ return realm.getId();
+ }
+
+ @Override
+ public String getName() {
+ return realm.getName();
+ }
+
+ @Override
+ public void setName(String name) {
+ realm.setName(name);
+ updateRealm();
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return realm.isEnabled();
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ realm.setEnabled(enabled);
+ updateRealm();
+ }
+
+ @Override
+ public boolean isSocial() {
+ return realm.isSocial();
+ }
+
+ @Override
+ public void setSocial(boolean social) {
+ realm.setSocial(social);
+ updateRealm();
+ }
+
+ @Override
+ public boolean isAutomaticRegistrationAfterSocialLogin() {
+ return realm.isAutomaticRegistrationAfterSocialLogin();
+ }
+
+ @Override
+ public void setAutomaticRegistrationAfterSocialLogin(boolean automaticRegistrationAfterSocialLogin) {
+ realm.setAutomaticRegistrationAfterSocialLogin(automaticRegistrationAfterSocialLogin);
+ updateRealm();
+ }
+
+ @Override
+ public boolean isSslNotRequired() {
+ return realm.isSslNotRequired();
+ }
+
+ @Override
+ public void setSslNotRequired(boolean sslNotRequired) {
+ realm.setSslNotRequired(sslNotRequired);
+ updateRealm();
+ }
+
+ @Override
+ public boolean isCookieLoginAllowed() {
+ return realm.isCookieLoginAllowed();
+ }
+
+ @Override
+ public void setCookieLoginAllowed(boolean cookieLoginAllowed) {
+ realm.setCookieLoginAllowed(cookieLoginAllowed);
+ updateRealm();
+ }
+
+ @Override
+ public boolean isRegistrationAllowed() {
+ return realm.isRegistrationAllowed();
+ }
+
+ @Override
+ public void setRegistrationAllowed(boolean registrationAllowed) {
+ realm.setRegistrationAllowed(registrationAllowed);
+ updateRealm();
+ }
+
+ @Override
+ public int getTokenLifespan() {
+ return realm.getTokenLifespan();
+ }
+
+ @Override
+ public void setTokenLifespan(int tokenLifespan) {
+ realm.setTokenLifespan(tokenLifespan);
+ updateRealm();
+ }
+
+ @Override
+ public int getAccessCodeLifespan() {
+ return realm.getAccessCodeLifespan();
+ }
+
+ @Override
+ public void setAccessCodeLifespan(int accessCodeLifespan) {
+ realm.setAccessCodeLifespan(accessCodeLifespan);
+ updateRealm();
+ }
+
+ @Override
+ public String getPublicKeyPem() {
+ return realm.getPublicKeyPem();
+ }
+
+ @Override
+ public void setPublicKeyPem(String publicKeyPem) {
+ realm.setPublicKeyPem(publicKeyPem);
+ this.publicKey = null;
+ updateRealm();
+ }
+
+ @Override
+ public String getPrivateKeyPem() {
+ return realm.getPrivateKeyPem();
+ }
+
+ @Override
+ public void setPrivateKeyPem(String privateKeyPem) {
+ realm.setPrivateKeyPem(privateKeyPem);
+ this.privateKey = null;
+ updateRealm();
+ }
+
+ @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);
+ }
+ }
+ 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));
+ }
+
+ @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);
+ }
+ }
+ 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));
+ }
+
+ @Override
+ public List<RequiredCredentialModel> getRequiredCredentials() {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void addRequiredCredential(String cred) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public boolean validatePassword(UserModel user, String password) {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public boolean validateTOTP(UserModel user, String password, String token) {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void updateCredential(UserModel user, UserCredentialModel cred) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public UserModel getUser(String name) {
+ NoSQLQuery query = NoSQLQuery.create().put("loginName", name).put("realmId", getId());
+ UserData user = noSQL.loadSingleObject(UserData.class, query);
+
+ if (user == null) {
+ return null;
+ } else {
+ return new UserAdapter(user, noSQL);
+ }
+ }
+
+ @Override
+ public UserModel addUser(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(getId());
+
+ noSQL.saveObject(userData);
+ return new UserAdapter(userData, noSQL);
+ }
+
+ @Override
+ public RoleAdapter getRole(String name) {
+ NoSQLQuery query = NoSQLQuery.create().put("name", name).put("realmId", getId());
+ RoleData role = noSQL.loadSingleObject(RoleData.class, query);
+ if (role == null) {
+ return null;
+ } else {
+ return new RoleAdapter(role, noSQL);
+ }
+ }
+
+ @Override
+ public RoleModel addRole(String name) {
+ if (getRole(name) != null) {
+ throw new IllegalArgumentException("Role " + name + " already exists");
+ }
+
+ RoleData roleData = new RoleData();
+ roleData.setName(name);
+ roleData.setRealmId(getId());
+
+ noSQL.saveObject(roleData);
+ return new RoleAdapter(roleData, noSQL);
+ }
+
+ @Override
+ public List<RoleModel> getRoles() {
+ NoSQLQuery query = NoSQLQuery.create().put("realmId", getId());
+ List<RoleData> roles = noSQL.loadObjects(RoleData.class, query);
+
+ List<RoleModel> result = new ArrayList<RoleModel>();
+ for (RoleData role : roles) {
+ result.add(new RoleAdapter(role, noSQL));
+ }
+
+ return result;
+ }
+
+ @Override
+ public List<RoleModel> getDefaultRoles() {
+ List<RoleModel> defaultRoleModels = new ArrayList<RoleModel>();
+ if (realm.getDefaultRoles() != null) {
+ for (String name : realm.getDefaultRoles()) {
+ RoleAdapter role = getRole(name);
+ if (role != null) {
+ defaultRoleModels.add(role);
+ }
+ }
+ }
+ return defaultRoleModels;
+ }
+
+ @Override
+ public void addDefaultRole(String name) {
+ if (getRole(name) == null) {
+ addRole(name);
+ }
+
+ String[] defaultRoles = realm.getDefaultRoles();
+ if (defaultRoles == null) {
+ defaultRoles = new String[1];
+ } else {
+ defaultRoles = Arrays.copyOf(defaultRoles, defaultRoles.length + 1);
+ }
+ defaultRoles[defaultRoles.length - 1] = name;
+
+ realm.setDefaultRoles(defaultRoles);
+ updateRealm();
+ }
+
+ @Override
+ public void updateDefaultRoles(String[] defaultRoles) {
+ for (String name : defaultRoles) {
+ if (getRole(name) == null) {
+ addRole(name);
+ }
+ }
+
+ realm.setDefaultRoles(defaultRoles);
+ updateRealm();
+ }
+
+ @Override
+ public Map<String, ApplicationModel> getResourceNameMap() {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public List<ApplicationModel> getApplications() {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public ApplicationModel addApplication(String name) {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public boolean hasRole(UserModel user, RoleModel role) {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void grantRole(UserModel user, RoleModel role) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public Set<String> getRoleMappings(UserModel user) {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void addScope(UserModel agent, String roleName) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public Set<String> getScope(UserModel agent) {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public boolean isRealmAdmin(UserModel agent) {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void addRealmAdmin(UserModel agent) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public RoleModel getRoleById(String id) {
+ RoleData role = noSQL.loadObject(RoleData.class, id);
+ if (role == null) {
+ return null;
+ } else {
+ return new RoleAdapter(role, noSQL);
+ }
+ }
+
+ @Override
+ public List<RequiredCredentialModel> getRequiredApplicationCredentials() {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public List<RequiredCredentialModel> getRequiredOAuthClientCredentials() {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public boolean hasRole(UserModel user, String role) {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public ApplicationModel getApplicationById(String id) {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void addRequiredOAuthClientCredential(String type) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void addRequiredResourceCredential(String type) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void updateRequiredCredentials(Set<String> creds) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void updateRequiredOAuthClientCredentials(Set<String> creds) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void updateRequiredApplicationCredentials(Set<String> creds) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public UserModel getUserBySocialLink(SocialLinkModel socialLink) {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public Set<SocialLinkModel> getSocialLinks(UserModel user) {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void addSocialLink(UserModel user, SocialLinkModel socialLink) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public void removeSocialLink(UserModel user, SocialLinkModel socialLink) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ protected void updateRealm() {
+ noSQL.saveObject(realm);
+ }
+}
diff --git a/services/src/main/java/org/keycloak/services/models/nosql/adapters/RoleAdapter.java b/services/src/main/java/org/keycloak/services/models/nosql/adapters/RoleAdapter.java
new file mode 100644
index 0000000..edde2e4
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/models/nosql/adapters/RoleAdapter.java
@@ -0,0 +1,49 @@
+package org.keycloak.services.models.nosql.adapters;
+
+import org.keycloak.services.models.RoleModel;
+import org.keycloak.services.models.nosql.api.NoSQL;
+import org.keycloak.services.models.nosql.data.RoleData;
+import org.keycloak.services.models.nosql.data.UserData;
+
+/**
+ * Wrapper around RoleData object, which will persist wrapped object after each set operation (compatibility with picketlink based impl)
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class RoleAdapter implements RoleModel {
+
+ private final RoleData role;
+ private final NoSQL noSQL;
+
+ public RoleAdapter(RoleData roleData, NoSQL noSQL) {
+ this.role = roleData;
+ this.noSQL = noSQL;
+ }
+
+ @Override
+ public String getName() {
+ return role.getName();
+ }
+
+ @Override
+ public String getDescription() {
+ return role.getDescription();
+ }
+
+ @Override
+ public void setDescription(String description) {
+ role.setDescription(description);
+ noSQL.saveObject(role);
+ }
+
+ @Override
+ public String getId() {
+ return role.getId();
+ }
+
+ @Override
+ public void setName(String name) {
+ role.setName(name);
+ noSQL.saveObject(role);
+ }
+}
diff --git a/services/src/main/java/org/keycloak/services/models/nosql/adapters/UserAdapter.java b/services/src/main/java/org/keycloak/services/models/nosql/adapters/UserAdapter.java
new file mode 100644
index 0000000..781ba26
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/models/nosql/adapters/UserAdapter.java
@@ -0,0 +1,93 @@
+package org.keycloak.services.models.nosql.adapters;
+
+import java.util.Map;
+
+import org.keycloak.services.models.UserModel;
+import org.keycloak.services.models.nosql.api.NoSQL;
+import org.keycloak.services.models.nosql.data.UserData;
+
+/**
+ * Wrapper around UserData object, which will persist wrapped object after each set operation (compatibility with picketlink based impl)
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class UserAdapter implements UserModel {
+
+ private final UserData user;
+ private final NoSQL noSQL;
+
+ public UserAdapter(UserData userData, NoSQL noSQL) {
+ this.user = userData;
+ this.noSQL = noSQL;
+ }
+
+ @Override
+ public String getLoginName() {
+ return user.getLoginName();
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return user.isEnabled();
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ user.setEnabled(enabled);
+ noSQL.saveObject(user);
+ }
+
+ @Override
+ public String getFirstName() {
+ return user.getFirstName();
+ }
+
+ @Override
+ public void setFirstName(String firstName) {
+ user.setFirstName(firstName);
+ noSQL.saveObject(user);
+ }
+
+ @Override
+ public String getLastName() {
+ return user.getLastName();
+ }
+
+ @Override
+ public void setLastName(String lastName) {
+ user.setLastName(lastName);
+ noSQL.saveObject(user);
+ }
+
+ @Override
+ public String getEmail() {
+ return user.getEmail();
+ }
+
+ @Override
+ public void setEmail(String email) {
+ user.setEmail(email);
+ noSQL.saveObject(user);
+ }
+
+ @Override
+ public void setAttribute(String name, String value) {
+ user.setAttribute(name, value);
+ }
+
+ @Override
+ public void removeAttribute(String name) {
+ user.removeAttribute(name);
+ noSQL.saveObject(user);
+ }
+
+ @Override
+ public String getAttribute(String name) {
+ return user.getAttribute(name);
+ }
+
+ @Override
+ public Map<String, String> getAttributes() {
+ return user.getAttributes();
+ }
+}
diff --git a/services/src/main/java/org/keycloak/services/models/nosql/api/AbstractAttributedNoSQLObject.java b/services/src/main/java/org/keycloak/services/models/nosql/api/AbstractAttributedNoSQLObject.java
new file mode 100644
index 0000000..efdddee
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/models/nosql/api/AbstractAttributedNoSQLObject.java
@@ -0,0 +1,37 @@
+package org.keycloak.services.models.nosql.api;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public abstract class AbstractAttributedNoSQLObject implements AttributedNoSQLObject {
+
+ // Simple hashMap for now (no thread-safe)
+ private Map<String, String> attributes = new HashMap<String, String>();
+
+ @Override
+ public void setAttribute(String name, String value) {
+ attributes.put(name, value);
+ }
+
+ @Override
+ public void removeAttribute(String name) {
+ // attributes.remove(name);
+
+ // ensure that particular attribute has null value, so it will be deleted in DB. TODO: needs to be improved
+ attributes.put(name, null);
+ }
+
+ @Override
+ public String getAttribute(String name) {
+ return attributes.get(name);
+ }
+
+ @Override
+ public Map<String, String> getAttributes() {
+ return Collections.unmodifiableMap(attributes);
+ }
+}
diff --git a/services/src/main/java/org/keycloak/services/models/nosql/api/NoSQL.java b/services/src/main/java/org/keycloak/services/models/nosql/api/NoSQL.java
index dc16bb6..9da8166 100644
--- a/services/src/main/java/org/keycloak/services/models/nosql/api/NoSQL.java
+++ b/services/src/main/java/org/keycloak/services/models/nosql/api/NoSQL.java
@@ -15,12 +15,14 @@ public interface NoSQL {
<T extends NoSQLObject> T loadObject(Class<T> type, String oid);
- <T extends NoSQLObject> List<T> loadObjects(Class<T> type, Map<String, Object> queryAttributes);
+ <T extends NoSQLObject> T loadSingleObject(Class<T> type, NoSQLQuery query);
+
+ <T extends NoSQLObject> List<T> loadObjects(Class<T> type, NoSQLQuery query);
// Object must have filled oid
void removeObject(NoSQLObject object);
void removeObject(Class<? extends NoSQLObject> type, String oid);
- void removeObjects(Class<? extends NoSQLObject> type, Map<String, Object> queryAttributes);
+ void removeObjects(Class<? extends NoSQLObject> type, NoSQLQuery query);
}
diff --git a/services/src/main/java/org/keycloak/services/models/nosql/api/NoSQLQuery.java b/services/src/main/java/org/keycloak/services/models/nosql/api/NoSQLQuery.java
new file mode 100644
index 0000000..d260644
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/models/nosql/api/NoSQLQuery.java
@@ -0,0 +1,34 @@
+package org.keycloak.services.models.nosql.api;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class NoSQLQuery {
+
+ private Map<String, Object> queryAttributes = new HashMap<String, Object>();
+
+ private NoSQLQuery() {};
+
+ public static NoSQLQuery create() {
+ return new NoSQLQuery();
+ }
+
+ public NoSQLQuery put(String name, Object value) {
+ queryAttributes.put(name, value);
+ return this;
+ }
+
+ public Map<String, Object> getQueryAttributes() {
+ return Collections.unmodifiableMap(queryAttributes);
+ }
+
+ @Override
+ public String toString() {
+ return "NoSQLQuery [" + queryAttributes + "]";
+ }
+
+}
diff --git a/services/src/main/java/org/keycloak/services/models/nosql/api/types/Converter.java b/services/src/main/java/org/keycloak/services/models/nosql/api/types/Converter.java
new file mode 100644
index 0000000..221db2b
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/models/nosql/api/types/Converter.java
@@ -0,0 +1,18 @@
+package org.keycloak.services.models.nosql.api.types;
+
+/**
+ * SPI object to convert object from application type to database type and vice versa. Shouldn't be directly used by application.
+ * Various converters should be registered in TypeConverter, which is main entry point to be used by application
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public interface Converter<T, S> {
+
+ T convertDBObjectToApplicationObject(S dbObject);
+
+ S convertApplicationObjectToDBObject(T applicationObject);
+
+ Class<? extends T> getApplicationObjectType();
+
+ Class<S> getDBObjectType();
+}
diff --git a/services/src/main/java/org/keycloak/services/models/nosql/api/types/ConverterKey.java b/services/src/main/java/org/keycloak/services/models/nosql/api/types/ConverterKey.java
new file mode 100644
index 0000000..f4b6fe0
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/models/nosql/api/types/ConverterKey.java
@@ -0,0 +1,30 @@
+package org.keycloak.services.models.nosql.api.types;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+class ConverterKey {
+
+ private final Class<?> applicationObjectType;
+ private final Class<?> dbObjectType;
+
+ public ConverterKey(Class<?> applicationObjectType, Class<?> dbObjectType) {
+ this.applicationObjectType = applicationObjectType;
+ this.dbObjectType = dbObjectType;
+ }
+
+ @Override
+ public int hashCode() {
+ return applicationObjectType.hashCode() * 13 + dbObjectType.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !obj.getClass().equals(this.getClass())) {
+ return false;
+ }
+
+ ConverterKey tc = (ConverterKey)obj;
+ return tc.applicationObjectType.equals(this.applicationObjectType) && tc.dbObjectType.equals(this.dbObjectType);
+ }
+}
diff --git a/services/src/main/java/org/keycloak/services/models/nosql/api/types/TypeConverter.java b/services/src/main/java/org/keycloak/services/models/nosql/api/types/TypeConverter.java
new file mode 100644
index 0000000..7bdb384
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/models/nosql/api/types/TypeConverter.java
@@ -0,0 +1,43 @@
+package org.keycloak.services.models.nosql.api.types;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Registry of converters, which allow to convert application object to database objects. TypeConverter is main entry point to be used by application.
+ * Application can create instance of TypeConverter and then register required Converter objects.
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class TypeConverter {
+
+ private Map<ConverterKey, Converter<?, ?>> converterRegistry = new HashMap<ConverterKey, Converter<?, ?>>();
+
+ public <T, S> void addConverter(Converter<T, S> converter) {
+ ConverterKey converterKey = new ConverterKey(converter.getApplicationObjectType(), converter.getDBObjectType());
+ converterRegistry.put(converterKey, converter);
+ }
+
+ public <T, S> T convertDBObjectToApplicationObject(S dbObject, Class<T> expectedApplicationObjectType, Class<S> expectedDBObjectType) {
+ Converter<T, S> converter = getConverter(expectedApplicationObjectType, expectedDBObjectType);
+ return converter.convertDBObjectToApplicationObject(dbObject);
+ }
+
+ public <T, S> S convertApplicationObjectToDBObject(T applicationobject, Class<T> expectedApplicationObjectType, Class<S> expectedDBObjectType) {
+ Converter<T, S> converter = getConverter(expectedApplicationObjectType, expectedDBObjectType);
+ return converter.convertApplicationObjectToDBObject(applicationobject);
+ }
+
+ private <T, S> Converter<T, S> getConverter( Class<T> expectedApplicationObjectType, Class<S> expectedDBObjectType) {
+ ConverterKey key = new ConverterKey(expectedApplicationObjectType, expectedDBObjectType);
+ Converter<T, S> converter = (Converter<T, S>)converterRegistry.get(key);
+
+ if (converter == null) {
+ throw new IllegalStateException("Can't found converter for expectedApplicationObject=" + expectedApplicationObjectType + ", expectedDBObjectType=" + expectedDBObjectType);
+ }
+
+ return converter;
+ }
+
+
+}
diff --git a/services/src/main/java/org/keycloak/services/models/nosql/data/RealmData.java b/services/src/main/java/org/keycloak/services/models/nosql/data/RealmData.java
new file mode 100644
index 0000000..8e6a663
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/models/nosql/data/RealmData.java
@@ -0,0 +1,144 @@
+package org.keycloak.services.models.nosql.data;
+
+import org.keycloak.services.models.nosql.api.NoSQLCollection;
+import org.keycloak.services.models.nosql.api.NoSQLField;
+import org.keycloak.services.models.nosql.api.NoSQLId;
+import org.keycloak.services.models.nosql.api.NoSQLObject;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+@NoSQLCollection(collectionName = "realms")
+public class RealmData implements NoSQLObject {
+
+ private String id;
+ private String name;
+ private boolean enabled;
+ private boolean sslNotRequired;
+ private boolean cookieLoginAllowed;
+ private boolean registrationAllowed;
+ private boolean social;
+ private boolean automaticRegistrationAfterSocialLogin;
+ private int tokenLifespan;
+ private int accessCodeLifespan;
+ private String publicKeyPem;
+ private String privateKeyPem;
+ private String[] defaultRoles;
+
+ @NoSQLId
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ @NoSQLField
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String realmName) {
+ this.name = realmName;
+ }
+
+ @NoSQLField
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ @NoSQLField
+ public boolean isSslNotRequired() {
+ return sslNotRequired;
+ }
+
+ public void setSslNotRequired(boolean sslNotRequired) {
+ this.sslNotRequired = sslNotRequired;
+ }
+
+ @NoSQLField
+ public boolean isCookieLoginAllowed() {
+ return cookieLoginAllowed;
+ }
+
+ public void setCookieLoginAllowed(boolean cookieLoginAllowed) {
+ this.cookieLoginAllowed = cookieLoginAllowed;
+ }
+
+ @NoSQLField
+ public boolean isRegistrationAllowed() {
+ return registrationAllowed;
+ }
+
+ public void setRegistrationAllowed(boolean registrationAllowed) {
+ this.registrationAllowed = registrationAllowed;
+ }
+
+ @NoSQLField
+ public boolean isSocial() {
+ return social;
+ }
+
+ public void setSocial(boolean social) {
+ this.social = social;
+ }
+
+ @NoSQLField
+ public boolean isAutomaticRegistrationAfterSocialLogin() {
+ return automaticRegistrationAfterSocialLogin;
+ }
+
+ public void setAutomaticRegistrationAfterSocialLogin(boolean automaticRegistrationAfterSocialLogin) {
+ this.automaticRegistrationAfterSocialLogin = automaticRegistrationAfterSocialLogin;
+ }
+
+ @NoSQLField
+ public int getTokenLifespan() {
+ return tokenLifespan;
+ }
+
+ public void setTokenLifespan(int tokenLifespan) {
+ this.tokenLifespan = tokenLifespan;
+ }
+
+ @NoSQLField
+ public int getAccessCodeLifespan() {
+ return accessCodeLifespan;
+ }
+
+ public void setAccessCodeLifespan(int accessCodeLifespan) {
+ this.accessCodeLifespan = accessCodeLifespan;
+ }
+
+ @NoSQLField
+ public String getPublicKeyPem() {
+ return publicKeyPem;
+ }
+
+ public void setPublicKeyPem(String publicKeyPem) {
+ this.publicKeyPem = publicKeyPem;
+ }
+
+ @NoSQLField
+ public String getPrivateKeyPem() {
+ return privateKeyPem;
+ }
+
+ public void setPrivateKeyPem(String privateKeyPem) {
+ this.privateKeyPem = privateKeyPem;
+ }
+
+ @NoSQLField
+ public String[] getDefaultRoles() {
+ return defaultRoles;
+ }
+
+ public void setDefaultRoles(String[] defaultRoles) {
+ this.defaultRoles = defaultRoles;
+ }
+}
diff --git a/services/src/main/java/org/keycloak/services/models/nosql/data/RoleData.java b/services/src/main/java/org/keycloak/services/models/nosql/data/RoleData.java
new file mode 100644
index 0000000..7d4ba4b
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/models/nosql/data/RoleData.java
@@ -0,0 +1,65 @@
+package org.keycloak.services.models.nosql.data;
+
+import org.keycloak.services.models.nosql.api.NoSQLCollection;
+import org.keycloak.services.models.nosql.api.NoSQLField;
+import org.keycloak.services.models.nosql.api.NoSQLId;
+import org.keycloak.services.models.nosql.api.NoSQLObject;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+@NoSQLCollection(collectionName = "roles")
+public class RoleData implements NoSQLObject {
+
+ private String id;
+ private String name;
+ private String description;
+
+ private String realmId;
+ private String applicationId;
+
+ @NoSQLId
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ @NoSQLField
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @NoSQLField
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ @NoSQLField
+ public String getRealmId() {
+ return realmId;
+ }
+
+ public void setRealmId(String realmId) {
+ this.realmId = realmId;
+ }
+
+ @NoSQLField
+ public String getApplicationId() {
+ return applicationId;
+ }
+
+ public void setApplicationId(String applicationId) {
+ this.applicationId = applicationId;
+ }
+}
diff --git a/services/src/main/java/org/keycloak/services/models/nosql/data/UserData.java b/services/src/main/java/org/keycloak/services/models/nosql/data/UserData.java
new file mode 100644
index 0000000..70aab2a
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/models/nosql/data/UserData.java
@@ -0,0 +1,85 @@
+package org.keycloak.services.models.nosql.data;
+
+import org.keycloak.services.models.nosql.api.AbstractAttributedNoSQLObject;
+import org.keycloak.services.models.nosql.api.NoSQLCollection;
+import org.keycloak.services.models.nosql.api.NoSQLField;
+import org.keycloak.services.models.nosql.api.NoSQLId;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+@NoSQLCollection(collectionName = "users")
+public class UserData extends AbstractAttributedNoSQLObject {
+
+ private String id;
+ private String loginName;
+ private String firstName;
+ private String lastName;
+ private String email;
+ private boolean enabled;
+
+ private String realmId;
+
+ @NoSQLId
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ @NoSQLField
+ public String getLoginName() {
+ return loginName;
+ }
+
+ public void setLoginName(String loginName) {
+ this.loginName = loginName;
+ }
+
+ @NoSQLField
+ public String getFirstName() {
+ return firstName;
+ }
+
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
+ }
+
+ @NoSQLField
+ public String getLastName() {
+ return lastName;
+ }
+
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
+ }
+
+ @NoSQLField
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ @NoSQLField
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ @NoSQLField
+ public String getRealmId() {
+ return realmId;
+ }
+
+ public void setRealmId(String realmId) {
+ this.realmId = realmId;
+ }
+}
diff --git a/services/src/main/java/org/keycloak/services/models/nosql/impl/MongoDBImpl.java b/services/src/main/java/org/keycloak/services/models/nosql/impl/MongoDBImpl.java
index bbdd846..7d61b85 100644
--- a/services/src/main/java/org/keycloak/services/models/nosql/impl/MongoDBImpl.java
+++ b/services/src/main/java/org/keycloak/services/models/nosql/impl/MongoDBImpl.java
@@ -13,6 +13,7 @@ import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import org.bson.types.ObjectId;
import org.jboss.resteasy.logging.Logger;
+import org.jboss.resteasy.spi.NotImplementedYetException;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.models.nosql.api.AttributedNoSQLObject;
import org.keycloak.services.models.nosql.api.NoSQL;
@@ -20,9 +21,14 @@ import org.keycloak.services.models.nosql.api.NoSQLCollection;
import org.keycloak.services.models.nosql.api.NoSQLField;
import org.keycloak.services.models.nosql.api.NoSQLId;
import org.keycloak.services.models.nosql.api.NoSQLObject;
+import org.keycloak.services.models.nosql.api.NoSQLQuery;
+import org.keycloak.services.models.nosql.api.types.Converter;
+import org.keycloak.services.models.nosql.api.types.TypeConverter;
+import org.keycloak.services.models.nosql.impl.types.BasicDBListToStringArrayConverter;
import org.picketlink.common.properties.Property;
import org.picketlink.common.properties.query.AnnotatedPropertyCriteria;
import org.picketlink.common.properties.query.PropertyQueries;
+import org.picketlink.common.reflection.Types;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@@ -32,14 +38,20 @@ public class MongoDBImpl implements NoSQL {
private final DB database;
// private static final Logger logger = Logger.getLogger(MongoDBImpl.class);
+ private final TypeConverter typeConverter;
+
public MongoDBImpl(DB database) {
this.database = database;
+
+ typeConverter = new TypeConverter();
+ typeConverter.addConverter(new BasicDBListToStringArrayConverter());
}
private ConcurrentMap<Class<? extends NoSQLObject>, ObjectInfo<? extends NoSQLObject>> objectInfoCache =
new ConcurrentHashMap<Class<? extends NoSQLObject>, ObjectInfo<? extends NoSQLObject>>();
+
@Override
public void saveObject(NoSQLObject object) {
Class<?> clazz = object.getClass();
@@ -96,32 +108,62 @@ public class MongoDBImpl implements NoSQL {
}
@Override
- public <T extends NoSQLObject> List<T> loadObjects(Class<T> type, Map<String, Object> queryAttributes) {
+ public <T extends NoSQLObject> T loadSingleObject(Class<T> type, NoSQLQuery query) {
+ List<T> result = loadObjects(type, query);
+ if (result.size() > 1) {
+ throw new IllegalStateException("There are " + result.size() + " results for type=" + type + ", query=" + query + ". We expect just one");
+ } else if (result.size() == 1) {
+ return result.get(0);
+ } else {
+ // 0 results
+ return null;
+ }
+ }
+
+ @Override
+ public <T extends NoSQLObject> List<T> loadObjects(Class<T> type, NoSQLQuery query) {
+ Map<String, Object> queryAttributes = query.getQueryAttributes();
+
ObjectInfo<T> objectInfo = getObjectInfo(type);
DBCollection dbCollection = database.getCollection(objectInfo.getDbCollectionName());
- BasicDBObject query = new BasicDBObject();
+ BasicDBObject dbQuery = new BasicDBObject();
for (Map.Entry<String, Object> queryAttr : queryAttributes.entrySet()) {
- query.append(queryAttr.getKey(), queryAttr.getValue());
+ dbQuery.append(queryAttr.getKey(), queryAttr.getValue());
}
- DBCursor cursor = dbCollection.find(query);
+ DBCursor cursor = dbCollection.find(dbQuery);
return convertCursor(type, cursor);
}
@Override
public void removeObject(NoSQLObject object) {
- //To change body of implemented methods use File | Settings | File Templates.
+ Class<? extends NoSQLObject> type = object.getClass();
+ ObjectInfo<?> objectInfo = getObjectInfo(type);
+
+ Property<String> idProperty = objectInfo.getOidProperty();
+ String oid = idProperty.getValue(object);
+
+ removeObject(type, oid);
}
@Override
public void removeObject(Class<? extends NoSQLObject> type, String oid) {
- //To change body of implemented methods use File | Settings | File Templates.
+ ObjectInfo<?> objectInfo = getObjectInfo(type);
+ DBCollection dbCollection = database.getCollection(objectInfo.getDbCollectionName());
+
+ BasicDBObject query = new BasicDBObject("_id", new ObjectId(oid));
+ dbCollection.remove(query);
}
@Override
- public void removeObjects(Class<? extends NoSQLObject> type, Map<String, Object> queryAttributes) {
- //To change body of implemented methods use File | Settings | File Templates.
+ public void removeObjects(Class<? extends NoSQLObject> type, NoSQLQuery query) {
+ throw new NotImplementedYetException();
+ }
+
+ // Possibility to add converters
+ public void addConverter(Converter<?, ?> converter) {
+ typeConverter.addConverter(converter);
}
private <T extends NoSQLObject> ObjectInfo<T> getObjectInfo(Class<?> objectClass) {
@@ -173,7 +215,20 @@ public class MongoDBImpl implements NoSQL {
} else if ((property = objectInfo.getPropertyByName(key)) != null) {
// It's declared property with @DBField annotation
- property.setValue(object, value);
+ Class<?> expectedType = property.getJavaClass();
+ Class actualType = value != null ? value.getClass() : expectedType;
+
+ // handle primitives
+ expectedType = Types.boxedClass(expectedType);
+ actualType = Types.boxedClass(actualType);
+
+ if (actualType.isAssignableFrom(expectedType)) {
+ property.setValue(object, value);
+ } else {
+ // we need to convert
+ Object convertedValue = typeConverter.convertDBObjectToApplicationObject(value, expectedType, actualType);
+ property.setValue(object, convertedValue);
+ }
} else if (object instanceof AttributedNoSQLObject) {
// It's attributed object and property is not declared, so we will call setAttribute
diff --git a/services/src/main/java/org/keycloak/services/models/nosql/impl/types/BasicDBListToStringArrayConverter.java b/services/src/main/java/org/keycloak/services/models/nosql/impl/types/BasicDBListToStringArrayConverter.java
new file mode 100644
index 0000000..0f9d5d5
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/models/nosql/impl/types/BasicDBListToStringArrayConverter.java
@@ -0,0 +1,41 @@
+package org.keycloak.services.models.nosql.impl.types;
+
+import com.mongodb.BasicDBList;
+import org.keycloak.services.models.nosql.api.types.Converter;
+
+/**
+ * Convert BasicDBList to String[] and viceversa (T needs to be declared as Object as Array is not possible here :/ )
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class BasicDBListToStringArrayConverter implements Converter<Object, BasicDBList> {
+
+ private static final String[] PLACEHOLDER = new String[] {};
+
+ @Override
+ public Object convertDBObjectToApplicationObject(BasicDBList dbObject) {
+ return dbObject.toArray(PLACEHOLDER);
+ }
+
+ @Override
+ public BasicDBList convertApplicationObjectToDBObject(Object applicationObject) {
+ BasicDBList list = new BasicDBList();
+
+ String[] array = (String[])applicationObject;
+ for (String key : array) {
+ list.add(key);
+ }
+
+ return list;
+ }
+
+ @Override
+ public Class<?> getApplicationObjectType() {
+ return PLACEHOLDER.getClass();
+ }
+
+ @Override
+ public Class<BasicDBList> getDBObjectType() {
+ return BasicDBList.class;
+ }
+}
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 49855b9..089ab39 100755
--- a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
+++ b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
@@ -7,6 +7,11 @@ import org.keycloak.models.picketlink.PicketlinkKeycloakSession;
import org.keycloak.models.picketlink.PicketlinkKeycloakSessionFactory;
import org.keycloak.models.picketlink.mappings.ApplicationEntity;
import org.keycloak.models.picketlink.mappings.RealmEntity;
+import org.keycloak.services.models.KeycloakSessionFactory;
+import org.keycloak.services.models.nosql.adapters.MongoDBSessionFactory;
+import org.keycloak.services.models.picketlink.PicketlinkKeycloakSession;
+import org.keycloak.services.models.picketlink.mappings.ApplicationEntity;
+import org.keycloak.services.models.picketlink.mappings.RealmEntity;
import org.keycloak.social.SocialRequestManager;
import org.picketlink.idm.PartitionManager;
import org.picketlink.idm.config.IdentityConfigurationBuilder;
@@ -54,8 +59,9 @@ public class KeycloakApplication extends Application {
}
public static KeycloakSessionFactory buildSessionFactory() {
- EntityManagerFactory emf = Persistence.createEntityManagerFactory("keycloak-identity-store");
- return new PicketlinkKeycloakSessionFactory(emf, buildPartitionManager());
+ // EntityManagerFactory emf = Persistence.createEntityManagerFactory("keycloak-identity-store");
+ // return new PicketlinkKeycloakSessionFactory(emf, buildPartitionManager());
+ return new MongoDBSessionFactory("localhost", 27017, "keycloak");
}
public KeycloakSessionFactory getFactory() {