diff --git a/export-import/export-import-impl/pom.xml b/export-import/export-import-impl/pom.xml
index 683cfe91ca..9b10c8a026 100755
--- a/export-import/export-import-impl/pom.xml
+++ b/export-import/export-import-impl/pom.xml
@@ -101,6 +101,19 @@
${project.version}
test
+
+ org.keycloak
+ keycloak-model-jpa
+ ${project.version}
+ test
+
+
+ org.keycloak
+ keycloak-model-jpa
+ ${project.version}
+ tests
+ test
+
org.keycloak
keycloak-model-mongo
diff --git a/export-import/export-import-impl/src/test/java/org/keycloak/exportimport/JPAToMongoExportImportTest.java b/export-import/export-import-impl/src/test/java/org/keycloak/exportimport/JPAToMongoExportImportTest.java
index c1491d9f96..8cc6a176b7 100644
--- a/export-import/export-import-impl/src/test/java/org/keycloak/exportimport/JPAToMongoExportImportTest.java
+++ b/export-import/export-import-impl/src/test/java/org/keycloak/exportimport/JPAToMongoExportImportTest.java
@@ -1,6 +1,5 @@
package org.keycloak.exportimport;
-import org.junit.Ignore;
import org.keycloak.exportimport.io.directory.TmpDirExportImportIOProvider;
import org.keycloak.models.KeycloakSessionFactory;
@@ -8,10 +7,7 @@ import org.keycloak.models.KeycloakSessionFactory;
* Test for full export of data from JPA and import them to Mongo. Using "directory" provider
*
* @author Marek Posolda
- *
- * TODO Update to work with hybrid model provider
*/
-@Ignore
public class JPAToMongoExportImportTest extends ExportImportTestBase {
@Override
diff --git a/export-import/export-import-impl/src/test/java/org/keycloak/exportimport/MongoToJPAExportImportTest.java b/export-import/export-import-impl/src/test/java/org/keycloak/exportimport/MongoToJPAExportImportTest.java
index 0de953e452..5fe08f76e8 100644
--- a/export-import/export-import-impl/src/test/java/org/keycloak/exportimport/MongoToJPAExportImportTest.java
+++ b/export-import/export-import-impl/src/test/java/org/keycloak/exportimport/MongoToJPAExportImportTest.java
@@ -1,7 +1,6 @@
package org.keycloak.exportimport;
import org.junit.Assert;
-import org.junit.Ignore;
import org.keycloak.exportimport.io.zip.EncryptedZIPIOProvider;
import org.keycloak.models.KeycloakSessionFactory;
@@ -11,10 +10,7 @@ import java.io.File;
* Test for full export of data from Mongo and import them to JPA. Using export into encrypted ZIP and import from it
*
* @author Marek Posolda
- *
- * TODO Update to work with hybrid model provider
*/
-@Ignore
public class MongoToJPAExportImportTest extends ExportImportTestBase {
private static final String zipFile = "keycloak-export.zip";
diff --git a/model/jpa/pom.xml b/model/jpa/pom.xml
new file mode 100755
index 0000000000..987abd23b1
--- /dev/null
+++ b/model/jpa/pom.xml
@@ -0,0 +1,139 @@
+
+
+
+ keycloak-parent
+ org.keycloak
+ 1.0-beta-4-SNAPSHOT
+ ../../pom.xml
+
+ 4.0.0
+
+ keycloak-model-jpa
+ Keycloak Model JPA
+
+
+
+
+ org.bouncycastle
+ bcprov-jdk16
+ provided
+
+
+ net.iharder
+ base64
+ provided
+
+
+ org.keycloak
+ keycloak-core
+ ${project.version}
+ provided
+
+
+ org.keycloak
+ keycloak-model-api
+ ${project.version}
+
+
+ org.keycloak
+ keycloak-invalidation-cache-model
+ ${project.version}
+ test
+
+
+ org.hibernate.javax.persistence
+ hibernate-jpa-2.0-api
+ provided
+
+
+ org.hibernate
+ hibernate-entitymanager
+ ${hibernate.entitymanager.version}
+ provided
+
+
+ org.jboss.resteasy
+ resteasy-jaxrs
+ provided
+
+
+ log4j
+ log4j
+
+
+ org.slf4j
+ slf4j-api
+
+
+ org.slf4j
+ slf4j-simple
+
+
+
+
+
+ org.keycloak
+ keycloak-model-tests
+ ${project.version}
+ test
+
+
+ org.keycloak
+ keycloak-model-tests
+ ${project.version}
+ tests
+ test
+
+
+ com.h2database
+ h2
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ ${maven.compiler.target}
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+ package-tests-jar
+ package
+
+ test-jar
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ default-test
+
+
+ org.keycloak:keycloak-model-tests
+
+
+
+
+
+
+
+
+
+
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/ApplicationAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/ApplicationAdapter.java
new file mode 100755
index 0000000000..0ebe0170be
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/ApplicationAdapter.java
@@ -0,0 +1,268 @@
+package org.keycloak.models.jpa;
+
+import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.ModelDuplicateException;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleContainerModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.jpa.entities.*;
+import org.keycloak.models.utils.KeycloakModelUtils;
+
+import javax.persistence.EntityManager;
+import javax.persistence.TypedQuery;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class ApplicationAdapter extends ClientAdapter implements ApplicationModel {
+
+ protected EntityManager em;
+ protected ApplicationEntity applicationEntity;
+
+ public ApplicationAdapter(RealmModel realm, EntityManager em, ApplicationEntity applicationEntity) {
+ super(realm, applicationEntity, em);
+ this.realm = realm;
+ this.em = em;
+ this.applicationEntity = applicationEntity;
+ }
+
+ @Override
+ public void updateApplication() {
+ em.flush();
+ }
+
+ @Override
+ public String getName() {
+ return entity.getName();
+ }
+
+ @Override
+ public void setName(String name) {
+ entity.setName(name);
+ }
+
+ @Override
+ public boolean isSurrogateAuthRequired() {
+ return applicationEntity.isSurrogateAuthRequired();
+ }
+
+ @Override
+ public void setSurrogateAuthRequired(boolean surrogateAuthRequired) {
+ applicationEntity.setSurrogateAuthRequired(surrogateAuthRequired);
+ }
+
+ @Override
+ public String getManagementUrl() {
+ return applicationEntity.getManagementUrl();
+ }
+
+ @Override
+ public void setManagementUrl(String url) {
+ applicationEntity.setManagementUrl(url);
+ }
+
+ @Override
+ public String getBaseUrl() {
+ return applicationEntity.getBaseUrl();
+ }
+
+ @Override
+ public void setBaseUrl(String url) {
+ applicationEntity.setBaseUrl(url);
+ }
+
+ @Override
+ public boolean isBearerOnly() {
+ return applicationEntity.isBearerOnly();
+ }
+
+ @Override
+ public void setBearerOnly(boolean only) {
+ applicationEntity.setBearerOnly(only);
+ }
+
+ @Override
+ public boolean isDirectGrantsOnly() {
+ return false; // applications can't be grant only
+ }
+
+ @Override
+ public void setDirectGrantsOnly(boolean flag) {
+ // applications can't be grant only
+ }
+
+ @Override
+ public RoleModel getRole(String name) {
+ TypedQuery query = em.createNamedQuery("getAppRoleByName", RoleEntity.class);
+ query.setParameter("name", name);
+ query.setParameter("application", entity);
+ List roles = query.getResultList();
+ if (roles.size() == 0) return null;
+ return new RoleAdapter(realm, em, roles.get(0));
+ }
+
+ @Override
+ public RoleModel addRole(String name) {
+ return this.addRole(KeycloakModelUtils.generateId(), name);
+ }
+
+ @Override
+ public RoleModel addRole(String id, String name) {
+ RoleEntity roleEntity = new RoleEntity();
+ roleEntity.setId(id);
+ roleEntity.setName(name);
+ roleEntity.setApplication(applicationEntity);
+ roleEntity.setApplicationRole(true);
+ roleEntity.setRealmId(realm.getId());
+ em.persist(roleEntity);
+ applicationEntity.getRoles().add(roleEntity);
+ em.flush();
+ return new RoleAdapter(realm, em, roleEntity);
+ }
+
+ @Override
+ public boolean removeRole(RoleModel roleModel) {
+ if (roleModel == null) {
+ return false;
+ }
+ if (!roleModel.getContainer().equals(this)) return false;
+
+ RoleEntity role = RoleAdapter.toRoleEntity(roleModel, em);
+ if (!role.isApplicationRole()) return false;
+
+
+ applicationEntity.getRoles().remove(role);
+ applicationEntity.getDefaultRoles().remove(role);
+ em.createNativeQuery("delete from CompositeRole where childRole = :role").setParameter("role", role).executeUpdate();
+ em.createQuery("delete from " + ScopeMappingEntity.class.getSimpleName() + " where role = :role").setParameter("role", role).executeUpdate();
+ em.createQuery("delete from " + UserRoleMappingEntity.class.getSimpleName() + " where role = :role").setParameter("role", role).executeUpdate();
+ role.setApplication(null);
+ em.flush();
+ em.remove(role);
+ em.flush();
+
+ return true;
+ }
+
+ @Override
+ public Set getRoles() {
+ Set list = new HashSet();
+ Collection roles = applicationEntity.getRoles();
+ if (roles == null) return list;
+ for (RoleEntity entity : roles) {
+ list.add(new RoleAdapter(realm, em, entity));
+ }
+ return list;
+ }
+
+ @Override
+ public Set getApplicationScopeMappings(ClientModel client) {
+ Set roleMappings = client.getScopeMappings();
+
+ Set appRoles = new HashSet();
+ for (RoleModel role : roleMappings) {
+ RoleContainerModel container = role.getContainer();
+ if (container instanceof RealmModel) {
+ } else {
+ ApplicationModel app = (ApplicationModel)container;
+ if (app.getId().equals(getId())) {
+ appRoles.add(role);
+ }
+ }
+ }
+
+ return appRoles;
+ }
+
+
+
+
+ @Override
+ public List getDefaultRoles() {
+ Collection entities = applicationEntity.getDefaultRoles();
+ List roles = new ArrayList();
+ if (entities == null) return roles;
+ for (RoleEntity entity : entities) {
+ roles.add(entity.getName());
+ }
+ return roles;
+ }
+
+ @Override
+ public void addDefaultRole(String name) {
+ RoleModel role = getRole(name);
+ if (role == null) {
+ role = addRole(name);
+ }
+ Collection entities = applicationEntity.getDefaultRoles();
+ for (RoleEntity entity : entities) {
+ if (entity.getId().equals(role.getId())) {
+ return;
+ }
+ }
+ RoleEntity roleEntity = RoleAdapter.toRoleEntity(role, em);
+ entities.add(roleEntity);
+ em.flush();
+ }
+
+ public static boolean contains(String str, String[] array) {
+ for (String s : array) {
+ if (str.equals(s)) return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void updateDefaultRoles(String[] defaultRoles) {
+ Collection entities = applicationEntity.getDefaultRoles();
+ Set already = new HashSet();
+ List remove = new ArrayList();
+ for (RoleEntity rel : entities) {
+ if (!contains(rel.getName(), defaultRoles)) {
+ remove.add(rel);
+ } else {
+ already.add(rel.getName());
+ }
+ }
+ for (RoleEntity entity : remove) {
+ entities.remove(entity);
+ }
+ em.flush();
+ for (String roleName : defaultRoles) {
+ if (!already.contains(roleName)) {
+ addDefaultRole(roleName);
+ }
+ }
+ em.flush();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || !(o instanceof ApplicationModel)) return false;
+
+ ApplicationModel that = (ApplicationModel) o;
+ return that.getId().equals(getId());
+ }
+
+ @Override
+ public int hashCode() {
+ return getId().hashCode();
+ }
+
+ public String toString() {
+ return getName();
+ }
+
+ ApplicationEntity getJpaEntity() {
+ return applicationEntity;
+ }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java
new file mode 100755
index 0000000000..2e996580a5
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java
@@ -0,0 +1,268 @@
+package org.keycloak.models.jpa;
+
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleContainerModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.models.jpa.entities.ClientEntity;
+import org.keycloak.models.jpa.entities.ClientUserSessionAssociationEntity;
+import org.keycloak.models.jpa.entities.RoleEntity;
+import org.keycloak.models.jpa.entities.ScopeMappingEntity;
+
+import javax.persistence.EntityManager;
+import javax.persistence.Query;
+import javax.persistence.TypedQuery;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public abstract class ClientAdapter implements ClientModel {
+ protected ClientEntity entity;
+ protected RealmModel realm;
+ protected EntityManager em;
+
+ public ClientAdapter(RealmModel realm, ClientEntity entity, EntityManager em) {
+ this.realm = realm;
+ this.entity = entity;
+ this.em = em;
+ }
+
+ public ClientEntity getEntity() {
+ return entity;
+ }
+
+ @Override
+ public String getId() {
+ return entity.getId();
+ }
+
+ @Override
+ public RealmModel getRealm() {
+ return realm;
+ }
+
+ @Override
+ public String getClientId() {
+ return entity.getName();
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return entity.isEnabled();
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ entity.setEnabled(enabled);
+ }
+
+ @Override
+ public long getAllowedClaimsMask() {
+ return entity.getAllowedClaimsMask();
+ }
+
+ @Override
+ public void setAllowedClaimsMask(long mask) {
+ entity.setAllowedClaimsMask(mask);
+ }
+
+ @Override
+ public boolean isPublicClient() {
+ return entity.isPublicClient();
+ }
+
+ @Override
+ public void setPublicClient(boolean flag) {
+ entity.setPublicClient(flag);
+ }
+
+ @Override
+ public Set getWebOrigins() {
+ Set result = new HashSet();
+ result.addAll(entity.getWebOrigins());
+ return result;
+ }
+
+
+
+ @Override
+ public void setWebOrigins(Set webOrigins) {
+ entity.setWebOrigins(webOrigins);
+ }
+
+ @Override
+ public void addWebOrigin(String webOrigin) {
+ entity.getWebOrigins().add(webOrigin);
+ }
+
+ @Override
+ public void removeWebOrigin(String webOrigin) {
+ entity.getWebOrigins().remove(webOrigin);
+ }
+
+ @Override
+ public Set getRedirectUris() {
+ Set result = new HashSet();
+ result.addAll(entity.getRedirectUris());
+ return result;
+ }
+
+ @Override
+ public void setRedirectUris(Set redirectUris) {
+ entity.setRedirectUris(redirectUris);
+ }
+
+ @Override
+ public void addRedirectUri(String redirectUri) {
+ entity.getRedirectUris().add(redirectUri);
+ }
+
+ @Override
+ public void removeRedirectUri(String redirectUri) {
+ entity.getRedirectUris().remove(redirectUri);
+ }
+
+ @Override
+ public String getSecret() {
+ return entity.getSecret();
+ }
+
+ @Override
+ public void setSecret(String secret) {
+ entity.setSecret(secret);
+ }
+
+ @Override
+ public boolean validateSecret(String secret) {
+ return secret.equals(entity.getSecret());
+ }
+
+ @Override
+ public int getNotBefore() {
+ return entity.getNotBefore();
+ }
+
+ @Override
+ public void setNotBefore(int notBefore) {
+ entity.setNotBefore(notBefore);
+ }
+
+ @Override
+ public int getActiveUserSessions() {
+ Query query = em.createNamedQuery("getActiveClientSessions");
+ query.setParameter("clientId", getId());
+ Object count = query.getSingleResult();
+ return ((Number)count).intValue();
+ }
+
+ @Override
+ public Set getUserSessions() {
+ Set list = new HashSet();
+ TypedQuery query = em.createNamedQuery("getClientUserSessionByClient", ClientUserSessionAssociationEntity.class);
+ String id = getId();
+ query.setParameter("clientId", id);
+ List results = query.getResultList();
+ for (ClientUserSessionAssociationEntity entity : results) {
+ list.add(new UserSessionAdapter(em, realm, entity.getSession()));
+ }
+ return list;
+ }
+
+ public void deleteUserSessionAssociation() {
+ em.createNamedQuery("removeClientUserSessionByClient").setParameter("clientId", getId()).executeUpdate();
+ }
+
+ @Override
+ public Set getRealmScopeMappings() {
+ Set roleMappings = getScopeMappings();
+
+ Set appRoles = new HashSet();
+ for (RoleModel role : roleMappings) {
+ RoleContainerModel container = role.getContainer();
+ if (container instanceof RealmModel) {
+ if (((RealmModel) container).getId().equals(realm.getId())) {
+ appRoles.add(role);
+ }
+ }
+ }
+
+ return appRoles;
+ }
+
+
+
+ @Override
+ public Set getScopeMappings() {
+ TypedQuery query = em.createNamedQuery("clientScopeMappingIds", String.class);
+ query.setParameter("client", getEntity());
+ List ids = query.getResultList();
+ Set roles = new HashSet();
+ for (String roleId : ids) {
+ RoleModel role = realm.getRoleById(roleId);
+ if (role == null) continue;
+ roles.add(role);
+ }
+ return roles;
+ }
+
+ @Override
+ public void addScopeMapping(RoleModel role) {
+ if (hasScope(role)) return;
+ ScopeMappingEntity entity = new ScopeMappingEntity();
+ entity.setClient(getEntity());
+ RoleEntity roleEntity = RoleAdapter.toRoleEntity(role, em);
+ entity.setRole(roleEntity);
+ em.persist(entity);
+ em.flush();
+ em.detach(entity);
+ }
+
+ @Override
+ public void deleteScopeMapping(RoleModel role) {
+ TypedQuery query = getRealmScopeMappingQuery(role);
+ List results = query.getResultList();
+ if (results.size() == 0) return;
+ for (ScopeMappingEntity entity : results) {
+ em.remove(entity);
+ }
+ }
+
+ protected TypedQuery getRealmScopeMappingQuery(RoleModel role) {
+ TypedQuery query = em.createNamedQuery("hasScope", ScopeMappingEntity.class);
+ query.setParameter("client", getEntity());
+ RoleEntity roleEntity = RoleAdapter.toRoleEntity(role, em);
+ query.setParameter("role", roleEntity);
+ return query;
+ }
+
+ @Override
+ public boolean hasScope(RoleModel role) {
+ Set roles = getScopeMappings();
+ if (roles.contains(role)) return true;
+
+ for (RoleModel mapping : roles) {
+ if (mapping.hasRole(role)) return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!this.getClass().equals(o.getClass())) return false;
+
+ ClientAdapter that = (ClientAdapter) o;
+ return that.getId().equals(getId());
+ }
+
+ @Override
+ public int hashCode() {
+ return entity.getId().hashCode();
+ }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaKeycloakTransaction.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaKeycloakTransaction.java
new file mode 100755
index 0000000000..095b3414a1
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaKeycloakTransaction.java
@@ -0,0 +1,53 @@
+package org.keycloak.models.jpa;
+
+import org.keycloak.models.KeycloakTransaction;
+
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceException;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class JpaKeycloakTransaction implements KeycloakTransaction {
+
+ protected EntityManager em;
+
+ public JpaKeycloakTransaction(EntityManager em) {
+ this.em = em;
+ }
+
+ @Override
+ public void begin() {
+ em.getTransaction().begin();
+ }
+
+ @Override
+ public void commit() {
+ try {
+ em.getTransaction().commit();
+ } catch (PersistenceException e) {
+ throw PersistenceExceptionConverter.convert(e.getCause() != null ? e.getCause() : e);
+ }
+ }
+
+ @Override
+ public void rollback() {
+ em.getTransaction().rollback();
+ }
+
+ @Override
+ public void setRollbackOnly() {
+ em.getTransaction().setRollbackOnly();
+ }
+
+ @Override
+ public boolean getRollbackOnly() {
+ return em.getTransaction().getRollbackOnly();
+ }
+
+ @Override
+ public boolean isActive() {
+ return em.getTransaction().isActive();
+ }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaModelProvider.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaModelProvider.java
new file mode 100755
index 0000000000..940e0671ff
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaModelProvider.java
@@ -0,0 +1,400 @@
+package org.keycloak.models.jpa;
+
+import org.keycloak.models.*;
+import org.keycloak.models.jpa.entities.*;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.util.Time;
+
+import javax.persistence.EntityManager;
+import javax.persistence.Query;
+import javax.persistence.TypedQuery;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class JpaModelProvider implements ModelProvider {
+ private final KeycloakSession session;
+ protected EntityManager em;
+
+ public JpaModelProvider(KeycloakSession session, EntityManager em) {
+ this.session = session;
+ this.em = em;
+ this.em = PersistenceExceptionConverter.create(em);
+ }
+
+ @Override
+ public KeycloakTransaction getTransaction() {
+ return new JpaKeycloakTransaction(em);
+ }
+
+ @Override
+ public RealmModel createRealm(String name) {
+ return createRealm(KeycloakModelUtils.generateId(), name);
+ }
+
+ @Override
+ public RealmModel createRealm(String id, String name) {
+ RealmEntity realm = new RealmEntity();
+ realm.setName(name);
+ realm.setId(id);
+ em.persist(realm);
+ em.flush();
+ return new RealmAdapter(session, em, realm);
+ }
+
+ @Override
+ public RealmModel getRealm(String id) {
+ RealmEntity realm = em.find(RealmEntity.class, id);
+ if (realm == null) return null;
+ return new RealmAdapter(session, em, realm);
+ }
+
+ @Override
+ public List getRealms() {
+ TypedQuery query = em.createNamedQuery("getAllRealms", RealmEntity.class);
+ List entities = query.getResultList();
+ List realms = new ArrayList();
+ for (RealmEntity entity : entities) {
+ realms.add(new RealmAdapter(session, em, entity));
+ }
+ return realms;
+ }
+
+ @Override
+ public RealmModel getRealmByName(String name) {
+ TypedQuery query = em.createNamedQuery("getRealmByName", RealmEntity.class);
+ query.setParameter("name", name);
+ List entities = query.getResultList();
+ if (entities.size() == 0) return null;
+ if (entities.size() > 1) throw new IllegalStateException("Should not be more than one realm with same name");
+ RealmEntity realm = query.getResultList().get(0);
+ if (realm == null) return null;
+ return new RealmAdapter(session, em, realm);
+ }
+
+ @Override
+ public UserModel getUserById(String id, RealmModel realmModel) {
+ TypedQuery query = em.createNamedQuery("getRealmUserById", UserEntity.class);
+ query.setParameter("id", id);
+ RealmEntity realm = em.getReference(RealmEntity.class, realmModel.getId());
+ query.setParameter("realm", realm);
+ List entities = query.getResultList();
+ if (entities.size() == 0) return null;
+ return new UserAdapter(realmModel, em, entities.get(0));
+ }
+
+ @Override
+ public UserModel getUserByUsername(String username, RealmModel realmModel) {
+ TypedQuery query = em.createNamedQuery("getRealmUserByUsername", UserEntity.class);
+ query.setParameter("username", username);
+ RealmEntity realm = em.getReference(RealmEntity.class, realmModel.getId());
+ query.setParameter("realm", realm);
+ List results = query.getResultList();
+ if (results.size() == 0) return null;
+ return new UserAdapter(realmModel, em, results.get(0));
+ }
+
+ @Override
+ public UserModel getUserByEmail(String email, RealmModel realmModel) {
+ TypedQuery query = em.createNamedQuery("getRealmUserByEmail", UserEntity.class);
+ query.setParameter("email", email);
+ RealmEntity realm = em.getReference(RealmEntity.class, realmModel.getId());
+ query.setParameter("realm", realm);
+ List results = query.getResultList();
+ return results.isEmpty() ? null : new UserAdapter(realmModel, em, results.get(0));
+ }
+
+ @Override
+ public boolean removeRealm(String id) {
+ RealmEntity realm = em.find(RealmEntity.class, id);
+ if (realm == null) {
+ return false;
+ }
+
+ RealmAdapter adapter = new RealmAdapter(session, em, realm);
+ adapter.removeUserSessions();
+ for (ApplicationEntity a : new LinkedList(realm.getApplications())) {
+ adapter.removeApplication(a.getId());
+ }
+
+ for (OAuthClientModel oauth : adapter.getOAuthClients()) {
+ adapter.removeOAuthClient(oauth.getId());
+ }
+
+ for (UserEntity u : em.createQuery("from UserEntity u where u.realm = :realm", UserEntity.class).setParameter("realm", realm).getResultList()) {
+ adapter.removeUser(u.getUsername());
+ }
+
+ em.remove(realm);
+
+ return true;
+ }
+
+ @Override
+ public void close() {
+ if (em.getTransaction().isActive()) em.getTransaction().rollback();
+ if (em.isOpen()) em.close();
+ }
+
+ @Override
+ public void removeAllData() {
+ // Should be sufficient to delete all realms. Rest data should be removed in cascade
+ List realms = getRealms();
+ for (RealmModel realm : realms) {
+ removeRealm(realm.getId());
+ }
+ }
+
+ @Override
+ public UserModel getUserBySocialLink(SocialLinkModel socialLink, RealmModel realm) {
+ TypedQuery query = em.createNamedQuery("findUserByLinkAndRealm", UserEntity.class);
+ RealmEntity realmEntity = em.getReference(RealmEntity.class, realm.getId());
+ query.setParameter("realm", realmEntity);
+ query.setParameter("socialProvider", socialLink.getSocialProvider());
+ query.setParameter("socialUserId", socialLink.getSocialUserId());
+ List results = query.getResultList();
+ if (results.isEmpty()) {
+ return null;
+ } else if (results.size() > 1) {
+ throw new IllegalStateException("More results found for socialProvider=" + socialLink.getSocialProvider() +
+ ", socialUserId=" + socialLink.getSocialUserId() + ", results=" + results);
+ } else {
+ UserEntity user = results.get(0);
+ return new UserAdapter(realm, em, user);
+ }
+ }
+
+ @Override
+ public List getUsers(RealmModel realm) {
+ TypedQuery query = em.createQuery("select u from UserEntity u where u.realm = :realm", UserEntity.class);
+ RealmEntity realmEntity = em.getReference(RealmEntity.class, realm.getId());
+ query.setParameter("realm", realmEntity);
+ List results = query.getResultList();
+ List users = new ArrayList();
+ for (UserEntity entity : results) users.add(new UserAdapter(realm, em, entity));
+ return users;
+ }
+
+ @Override
+ public List searchForUser(String search, RealmModel realm) {
+ TypedQuery query = em.createQuery("select u from UserEntity u where u.realm = :realm and ( lower(u.username) like :search or lower(concat(u.firstName, ' ', u.lastName)) like :search or u.email like :search )", UserEntity.class);
+ RealmEntity realmEntity = em.getReference(RealmEntity.class, realm.getId());
+ query.setParameter("realm", realmEntity);
+ query.setParameter("search", "%" + search.toLowerCase() + "%");
+ List results = query.getResultList();
+ List users = new ArrayList();
+ for (UserEntity entity : results) users.add(new UserAdapter(realm, em, entity));
+ return users;
+ }
+
+ @Override
+ public List searchForUserByAttributes(Map attributes, RealmModel realm) {
+ StringBuilder builder = new StringBuilder("select u from UserEntity u");
+ boolean first = true;
+ for (Map.Entry entry : attributes.entrySet()) {
+ String attribute = null;
+ if (entry.getKey().equals(UserModel.LOGIN_NAME)) {
+ attribute = "lower(username)";
+ } else if (entry.getKey().equalsIgnoreCase(UserModel.FIRST_NAME)) {
+ attribute = "lower(firstName)";
+ } else if (entry.getKey().equalsIgnoreCase(UserModel.LAST_NAME)) {
+ attribute = "lower(lastName)";
+ } else if (entry.getKey().equalsIgnoreCase(UserModel.EMAIL)) {
+ attribute = "lower(email)";
+ }
+ if (attribute == null) continue;
+ if (first) {
+ first = false;
+ builder.append(" where realm = :realm");
+ } else {
+ builder.append(" and ");
+ }
+ builder.append(attribute).append(" like '%").append(entry.getValue().toLowerCase()).append("%'");
+ }
+ String q = builder.toString();
+ TypedQuery query = em.createQuery(q, UserEntity.class);
+ RealmEntity realmEntity = em.getReference(RealmEntity.class, realm.getId());
+ query.setParameter("realm", realmEntity);
+ List results = query.getResultList();
+ List users = new ArrayList();
+ for (UserEntity entity : results) users.add(new UserAdapter(realm, em, entity));
+ return users;
+ }
+
+ private SocialLinkEntity findSocialLink(UserModel user, String socialProvider) {
+ TypedQuery query = em.createNamedQuery("findSocialLinkByUserAndProvider", SocialLinkEntity.class);
+ UserEntity userEntity = em.getReference(UserEntity.class, user.getId());
+ query.setParameter("user", userEntity);
+ query.setParameter("socialProvider", socialProvider);
+ List results = query.getResultList();
+ return results.size() > 0 ? results.get(0) : null;
+ }
+
+
+ @Override
+ public Set getSocialLinks(UserModel user, RealmModel realm) {
+ TypedQuery query = em.createNamedQuery("findSocialLinkByUser", SocialLinkEntity.class);
+ UserEntity userEntity = em.getReference(UserEntity.class, user.getId());
+ query.setParameter("user", userEntity);
+ List results = query.getResultList();
+ Set set = new HashSet();
+ for (SocialLinkEntity entity : results) {
+ set.add(new SocialLinkModel(entity.getSocialProvider(), entity.getSocialUserId(), entity.getSocialUsername()));
+ }
+ return set;
+ }
+
+ @Override
+ public SocialLinkModel getSocialLink(UserModel user, String socialProvider, RealmModel realm) {
+ SocialLinkEntity entity = findSocialLink(user, socialProvider);
+ return (entity != null) ? new SocialLinkModel(entity.getSocialProvider(), entity.getSocialUserId(), entity.getSocialUsername()) : null;
+ }
+
+ @Override
+ public RoleModel getRoleById(String id, RealmModel realm) {
+ RoleEntity entity = em.find(RoleEntity.class, id);
+ if (entity == null) return null;
+ if (!realm.getId().equals(entity.getRealmId())) return null;
+ return new RoleAdapter(realm, em, entity);
+ }
+
+ @Override
+ public ApplicationModel getApplicationById(String id, RealmModel realm) {
+ ApplicationEntity app = em.find(ApplicationEntity.class, id);
+
+ // Check if application belongs to this realm
+ if (app == null || !realm.getId().equals(app.getRealm().getId())) return null;
+ return new ApplicationAdapter(realm, em, app);
+ }
+
+ @Override
+ public OAuthClientModel getOAuthClientById(String id, RealmModel realm) {
+ OAuthClientEntity client = em.find(OAuthClientEntity.class, id);
+
+ // Check if client belongs to this realm
+ if (client == null || !realm.getId().equals(client.getRealm().getId())) return null;
+ return new OAuthClientAdapter(realm, client, em);
+ }
+
+ @Override
+ public UsernameLoginFailureModel getUserLoginFailure(String username, RealmModel realm) {
+ String id = username + "-" + realm.getId();
+ UsernameLoginFailureEntity entity = em.find(UsernameLoginFailureEntity.class, id);
+ if (entity == null) return null;
+ return new UsernameLoginFailureAdapter(entity);
+ }
+
+ @Override
+ public UsernameLoginFailureModel addUserLoginFailure(String username, RealmModel realm) {
+ UsernameLoginFailureModel model = getUserLoginFailure(username, realm);
+ if (model != null) return model;
+ String id = username + "-" + realm.getId();
+ UsernameLoginFailureEntity entity = new UsernameLoginFailureEntity();
+ entity.setId(id);
+ entity.setUsername(username);
+ RealmEntity realmEntity = em.getReference(RealmEntity.class, realm.getId());
+ entity.setRealm(realmEntity);
+ em.persist(entity);
+ return new UsernameLoginFailureAdapter(entity);
+ }
+
+ @Override
+ public List getAllUserLoginFailures(RealmModel realm) {
+ TypedQuery query = em.createNamedQuery("getAllFailures", UsernameLoginFailureEntity.class);
+ List entities = query.getResultList();
+ List models = new ArrayList();
+ for (UsernameLoginFailureEntity entity : entities) {
+ models.add(new UsernameLoginFailureAdapter(entity));
+ }
+ return models;
+ }
+
+ @Override
+ public UserSessionModel createUserSession(RealmModel realm, UserModel user, String ipAddress) {
+ UserSessionEntity entity = new UserSessionEntity();
+ entity.setRealmId(realm.getId());
+ entity.setUserId(user.getId());
+ entity.setIpAddress(ipAddress);
+
+ int currentTime = Time.currentTime();
+
+ entity.setStarted(currentTime);
+ entity.setLastSessionRefresh(currentTime);
+
+ em.persist(entity);
+ return new UserSessionAdapter(em, realm, entity);
+ }
+
+ @Override
+ public UserSessionModel getUserSession(String id, RealmModel realm) {
+ UserSessionEntity entity = em.find(UserSessionEntity.class, id);
+ return entity != null ? new UserSessionAdapter(em, realm, entity) : null;
+ }
+
+ @Override
+ public List getUserSessions(UserModel user, RealmModel realm) {
+ List sessions = new LinkedList();
+ for (UserSessionEntity e : em.createNamedQuery("getUserSessionByUser", UserSessionEntity.class)
+ .setParameter("userId", user.getId()).getResultList()) {
+ sessions.add(new UserSessionAdapter(em, realm, e));
+ }
+ return sessions;
+ }
+
+ @Override
+ public Set getUserSessions(RealmModel realm, ClientModel client) {
+ Set list = new HashSet();
+ TypedQuery query = em.createNamedQuery("getClientUserSessionByClient", ClientUserSessionAssociationEntity.class);
+ String id = client.getId();
+ query.setParameter("clientId", id);
+ List results = query.getResultList();
+ for (ClientUserSessionAssociationEntity entity : results) {
+ list.add(new UserSessionAdapter(em, realm, entity.getSession()));
+ }
+ return list;
+ }
+
+ @Override
+ public int getActiveUserSessions(RealmModel realm, ClientModel client) {
+ Query query = em.createNamedQuery("getActiveClientSessions");
+ query.setParameter("clientId", client.getId());
+ Object count = query.getSingleResult();
+ return ((Number)count).intValue();
+ }
+
+ @Override
+ public void removeUserSession(UserSessionModel session) {
+ em.remove(((UserSessionAdapter) session).getEntity());
+ }
+
+ @Override
+ public void removeUserSessions(RealmModel realm, UserModel user) {
+ em.createNamedQuery("removeClientUserSessionByUser").setParameter("userId", user.getId()).executeUpdate();
+ em.createNamedQuery("removeUserSessionByUser").setParameter("userId", user.getId()).executeUpdate();
+ }
+
+ @Override
+ public void removeExpiredUserSessions(RealmModel realm) {
+ TypedQuery query = em.createNamedQuery("getUserSessionExpired", UserSessionEntity.class)
+ .setParameter("maxTime", Time.currentTime() - realm.getSsoSessionMaxLifespan())
+ .setParameter("idleTime", Time.currentTime() - realm.getSsoSessionIdleTimeout());
+ List results = query.getResultList();
+ for (UserSessionEntity entity : results) {
+ em.remove(entity);
+ }
+ }
+
+ @Override
+ public void removeUserSessions(RealmModel realm) {
+ em.createNamedQuery("removeClientUserSessionByRealm").setParameter("realmId", realm.getId()).executeUpdate();
+ em.createNamedQuery("removeRealmUserSessions").setParameter("realmId", realm.getId()).executeUpdate();
+ }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaModelProviderFactory.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaModelProviderFactory.java
new file mode 100755
index 0000000000..ad4e6dce46
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaModelProviderFactory.java
@@ -0,0 +1,40 @@
+package org.keycloak.models.jpa;
+
+import org.keycloak.Config;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.ModelProvider;
+import org.keycloak.models.ModelProviderFactory;
+import org.keycloak.util.JpaUtils;
+
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.Persistence;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class JpaModelProviderFactory implements ModelProviderFactory {
+
+ protected EntityManagerFactory emf;
+
+ @Override
+ public void init(Config.Scope config) {
+ emf = Persistence.createEntityManagerFactory("jpa-keycloak-identity-store", JpaUtils.getHibernateProperties());
+ }
+
+ @Override
+ public String getId() {
+ return "jpa";
+ }
+
+ @Override
+ public ModelProvider create(KeycloakSession session) {
+ return new JpaModelProvider(session, emf.createEntityManager());
+ }
+
+ @Override
+ public void close() {
+ emf.close();
+ }
+
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/OAuthClientAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/OAuthClientAdapter.java
new file mode 100755
index 0000000000..c890c9f922
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/OAuthClientAdapter.java
@@ -0,0 +1,56 @@
+package org.keycloak.models.jpa;
+
+import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.OAuthClientModel;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.jpa.entities.OAuthClientEntity;
+
+import javax.persistence.EntityManager;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class OAuthClientAdapter extends ClientAdapter implements OAuthClientModel {
+
+ protected final OAuthClientEntity oAuthClientEntity;
+
+ public OAuthClientAdapter(RealmModel realm, OAuthClientEntity entity, EntityManager em) {
+ super(realm, entity, em);
+ oAuthClientEntity = entity;
+ }
+
+ @Override
+ public void setClientId(String id) {
+ entity.setName(id);
+
+ }
+
+ @Override
+ public boolean isDirectGrantsOnly() {
+ return oAuthClientEntity.isDirectGrantsOnly();
+ }
+
+ @Override
+ public void setDirectGrantsOnly(boolean flag) {
+ oAuthClientEntity.setDirectGrantsOnly(flag);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || !(o instanceof OAuthClientModel)) return false;
+
+ OAuthClientModel that = (OAuthClientModel) o;
+ return that.getId().equals(getId());
+ }
+
+ @Override
+ public int hashCode() {
+ return getId().hashCode();
+ }
+
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/PersistenceExceptionConverter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/PersistenceExceptionConverter.java
new file mode 100644
index 0000000000..dfa5053822
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/PersistenceExceptionConverter.java
@@ -0,0 +1,49 @@
+package org.keycloak.models.jpa;
+
+import org.hibernate.exception.ConstraintViolationException;
+import org.keycloak.models.ModelException;
+import org.keycloak.models.ModelDuplicateException;
+
+import javax.persistence.EntityExistsException;
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceException;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+/**
+ * @author Stian Thorgersen
+ */
+public class PersistenceExceptionConverter implements InvocationHandler {
+
+ private EntityManager em;
+
+ public static EntityManager create(EntityManager em) {
+ return (EntityManager) Proxy.newProxyInstance(EntityManager.class.getClassLoader(), new Class[]{EntityManager.class}, new PersistenceExceptionConverter(em));
+ }
+
+ private PersistenceExceptionConverter(EntityManager em) {
+ this.em = em;
+ }
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ try {
+ return method.invoke(em, args);
+ } catch (InvocationTargetException e) {
+ throw convert(e.getCause());
+ }
+ }
+
+ public static ModelException convert(Throwable t) {
+ if (t.getCause() != null && t.getCause() instanceof ConstraintViolationException) {
+ throw new ModelDuplicateException(t);
+ } if (t instanceof EntityExistsException) {
+ throw new ModelDuplicateException(t);
+ } else {
+ throw new ModelException(t);
+ }
+ }
+
+}
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
new file mode 100755
index 0000000000..23602ec49c
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
@@ -0,0 +1,1142 @@
+package org.keycloak.models.jpa;
+
+import org.keycloak.models.AuthenticationLinkModel;
+import org.keycloak.models.AuthenticationProviderModel;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.CredentialValidation;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.ModelDuplicateException;
+import org.keycloak.models.RoleContainerModel;
+import org.keycloak.models.UserCredentialValueModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.models.UsernameLoginFailureModel;
+import org.keycloak.models.jpa.entities.ApplicationEntity;
+import org.keycloak.models.jpa.entities.AuthenticationLinkEntity;
+import org.keycloak.models.jpa.entities.AuthenticationProviderEntity;
+import org.keycloak.models.jpa.entities.OAuthClientEntity;
+import org.keycloak.models.jpa.entities.RealmEntity;
+import org.keycloak.models.jpa.entities.RequiredCredentialEntity;
+import org.keycloak.models.jpa.entities.RoleEntity;
+import org.keycloak.models.jpa.entities.ScopeMappingEntity;
+import org.keycloak.models.jpa.entities.SocialLinkEntity;
+import org.keycloak.models.jpa.entities.UserEntity;
+import org.keycloak.models.jpa.entities.UserSessionEntity;
+import org.keycloak.models.jpa.entities.UserRoleMappingEntity;
+import org.keycloak.models.jpa.entities.UsernameLoginFailureEntity;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.models.utils.Pbkdf2PasswordEncoder;
+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.utils.TimeBasedOTP;
+import org.keycloak.util.Time;
+
+import javax.persistence.EntityManager;
+import javax.persistence.TypedQuery;
+
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.keycloak.models.utils.Pbkdf2PasswordEncoder.*;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class RealmAdapter implements RealmModel {
+ protected RealmEntity realm;
+ protected EntityManager em;
+ protected volatile transient PublicKey publicKey;
+ protected volatile transient PrivateKey privateKey;
+ protected KeycloakSession session;
+ private PasswordPolicy passwordPolicy;
+
+ public RealmAdapter(KeycloakSession session, EntityManager em, RealmEntity realm) {
+ this.session = session;
+ this.em = em;
+ this.realm = realm;
+ }
+
+ public RealmEntity getEntity() {
+ return realm;
+ }
+
+ @Override
+ public String getId() {
+ return realm.getId();
+ }
+
+ @Override
+ public String getName() {
+ return realm.getName();
+ }
+
+ @Override
+ public void setName(String name) {
+ realm.setName(name);
+ em.flush();
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return realm.isEnabled();
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ realm.setEnabled(enabled);
+ em.flush();
+ }
+
+ @Override
+ public boolean isSslNotRequired() {
+ return realm.isSslNotRequired();
+ }
+
+ @Override
+ public void setSslNotRequired(boolean sslNotRequired) {
+ realm.setSslNotRequired(sslNotRequired);
+ em.flush();
+ }
+
+ @Override
+ public boolean isPasswordCredentialGrantAllowed() {
+ return realm.isPasswordCredentialGrantAllowed();
+ }
+
+ @Override
+ public void setPasswordCredentialGrantAllowed(boolean passwordCredentialGrantAllowed) {
+ realm.setPasswordCredentialGrantAllowed(passwordCredentialGrantAllowed);
+ em.flush();
+ }
+
+ @Override
+ public boolean isRegistrationAllowed() {
+ return realm.isRegistrationAllowed();
+ }
+
+ @Override
+ public void setRegistrationAllowed(boolean registrationAllowed) {
+ realm.setRegistrationAllowed(registrationAllowed);
+ em.flush();
+ }
+
+ @Override
+ public boolean isRememberMe() {
+ return realm.isRememberMe();
+ }
+
+ @Override
+ public void setRememberMe(boolean rememberMe) {
+ realm.setRememberMe(rememberMe);
+ em.flush();
+ }
+
+ @Override
+ public boolean isBruteForceProtected() {
+ return realm.isBruteForceProtected();
+ }
+
+ @Override
+ public void setBruteForceProtected(boolean value) {
+ realm.setBruteForceProtected(value);
+ }
+
+ @Override
+ public int getMaxFailureWaitSeconds() {
+ return realm.getMaxFailureWaitSeconds();
+ }
+
+ @Override
+ public void setMaxFailureWaitSeconds(int val) {
+ realm.setMaxFailureWaitSeconds(val);
+ }
+
+ @Override
+ public int getWaitIncrementSeconds() {
+ return realm.getWaitIncrementSeconds();
+ }
+
+ @Override
+ public void setWaitIncrementSeconds(int val) {
+ realm.setWaitIncrementSeconds(val);
+ }
+
+ @Override
+ public long getQuickLoginCheckMilliSeconds() {
+ return realm.getQuickLoginCheckMilliSeconds();
+ }
+
+ @Override
+ public void setQuickLoginCheckMilliSeconds(long val) {
+ realm.setQuickLoginCheckMilliSeconds(val);
+ }
+
+ @Override
+ public int getMinimumQuickLoginWaitSeconds() {
+ return realm.getMinimumQuickLoginWaitSeconds();
+ }
+
+ @Override
+ public void setMinimumQuickLoginWaitSeconds(int val) {
+ realm.setMinimumQuickLoginWaitSeconds(val);
+ }
+
+ @Override
+ public int getMaxDeltaTimeSeconds() {
+ return realm.getMaxDeltaTimeSeconds();
+ }
+
+ @Override
+ public void setMaxDeltaTimeSeconds(int val) {
+ realm.setMaxDeltaTimeSeconds(val);
+ }
+
+ @Override
+ public int getFailureFactor() {
+ return realm.getFailureFactor();
+ }
+
+ @Override
+ public void setFailureFactor(int failureFactor) {
+ realm.setFailureFactor(failureFactor);
+ }
+
+ @Override
+ public boolean isVerifyEmail() {
+ return realm.isVerifyEmail();
+ }
+
+ @Override
+ public void setVerifyEmail(boolean verifyEmail) {
+ realm.setVerifyEmail(verifyEmail);
+ em.flush();
+ }
+
+ @Override
+ public boolean isResetPasswordAllowed() {
+ return realm.isResetPasswordAllowed();
+ }
+
+ @Override
+ public void setResetPasswordAllowed(boolean resetPasswordAllowed) {
+ realm.setResetPasswordAllowed(resetPasswordAllowed);
+ em.flush();
+ }
+
+ @Override
+ public int getNotBefore() {
+ return realm.getNotBefore();
+ }
+
+ @Override
+ public void setNotBefore(int notBefore) {
+ realm.setNotBefore(notBefore);
+ }
+
+ @Override
+ public int getAccessTokenLifespan() {
+ return realm.getAccessTokenLifespan();
+ }
+
+ @Override
+ public void setAccessTokenLifespan(int tokenLifespan) {
+ realm.setAccessTokenLifespan(tokenLifespan);
+ em.flush();
+ }
+
+ @Override
+ public int getSsoSessionIdleTimeout() {
+ return realm.getSsoSessionIdleTimeout();
+ }
+
+ @Override
+ public void setSsoSessionIdleTimeout(int seconds) {
+ realm.setSsoSessionIdleTimeout(seconds);
+ }
+
+ @Override
+ public int getSsoSessionMaxLifespan() {
+ return realm.getSsoSessionMaxLifespan();
+ }
+
+ @Override
+ public void setSsoSessionMaxLifespan(int seconds) {
+ realm.setSsoSessionMaxLifespan(seconds);
+ }
+
+ @Override
+ public int getAccessCodeLifespan() {
+ return realm.getAccessCodeLifespan();
+ }
+
+ @Override
+ public void setAccessCodeLifespan(int accessCodeLifespan) {
+ realm.setAccessCodeLifespan(accessCodeLifespan);
+ em.flush();
+ }
+
+ @Override
+ public int getAccessCodeLifespanUserAction() {
+ return realm.getAccessCodeLifespanUserAction();
+ }
+
+ @Override
+ public void setAccessCodeLifespanUserAction(int accessCodeLifespanUserAction) {
+ realm.setAccessCodeLifespanUserAction(accessCodeLifespanUserAction);
+ em.flush();
+ }
+
+ @Override
+ public String getPublicKeyPem() {
+ return realm.getPublicKeyPem();
+ }
+
+ @Override
+ public void setPublicKeyPem(String publicKeyPem) {
+ realm.setPublicKeyPem(publicKeyPem);
+ em.flush();
+ }
+
+ @Override
+ public String getPrivateKeyPem() {
+ return realm.getPrivateKeyPem();
+ }
+
+ @Override
+ public void setPrivateKeyPem(String privateKeyPem) {
+ realm.setPrivateKeyPem(privateKeyPem);
+ em.flush();
+ }
+
+ @Override
+ public PublicKey getPublicKey() {
+ if (publicKey != null) return publicKey;
+ publicKey = KeycloakModelUtils.getPublicKey(getPublicKeyPem());
+ return publicKey;
+ }
+
+ @Override
+ public void setPublicKey(PublicKey publicKey) {
+ this.publicKey = publicKey;
+ String publicKeyPem = KeycloakModelUtils.getPemFromKey(publicKey);
+ setPublicKeyPem(publicKeyPem);
+ }
+
+ @Override
+ public PrivateKey getPrivateKey() {
+ if (privateKey != null) return privateKey;
+ privateKey = KeycloakModelUtils.getPrivateKey(getPrivateKeyPem());
+ return privateKey;
+ }
+
+ @Override
+ public void setPrivateKey(PrivateKey privateKey) {
+ this.privateKey = privateKey;
+ String privateKeyPem = KeycloakModelUtils.getPemFromKey(privateKey);
+ setPrivateKeyPem(privateKeyPem);
+ }
+
+ protected RequiredCredentialModel initRequiredCredentialModel(String type) {
+ RequiredCredentialModel model = RequiredCredentialModel.BUILT_IN.get(type);
+ if (model == null) {
+ throw new RuntimeException("Unknown credential type " + type);
+ }
+ return model;
+ }
+
+ @Override
+ public void addRequiredCredential(String type) {
+ RequiredCredentialModel model = initRequiredCredentialModel(type);
+ addRequiredCredential(model);
+ em.flush();
+ }
+
+ public void addRequiredCredential(RequiredCredentialModel model) {
+ RequiredCredentialEntity entity = new RequiredCredentialEntity();
+ entity.setInput(model.isInput());
+ entity.setSecret(model.isSecret());
+ entity.setType(model.getType());
+ entity.setFormLabel(model.getFormLabel());
+ em.persist(entity);
+ realm.getRequiredCredentials().add(entity);
+ em.flush();
+ }
+
+ @Override
+ public void updateRequiredCredentials(Set creds) {
+ Collection relationships = realm.getRequiredCredentials();
+ if (relationships == null) relationships = new ArrayList();
+
+ Set already = new HashSet();
+ List remove = new ArrayList();
+ for (RequiredCredentialEntity rel : relationships) {
+ if (!creds.contains(rel.getType())) {
+ remove.add(rel);
+ } else {
+ already.add(rel.getType());
+ }
+ }
+ for (RequiredCredentialEntity entity : remove) {
+ relationships.remove(entity);
+ em.remove(entity);
+ }
+ for (String cred : creds) {
+ if (!already.contains(cred)) {
+ addRequiredCredential(cred);
+ }
+ }
+ em.flush();
+ }
+
+
+ @Override
+ public List getRequiredCredentials() {
+ List requiredCredentialModels = new ArrayList();
+ Collection entities = realm.getRequiredCredentials();
+ if (entities == null) return requiredCredentialModels;
+ for (RequiredCredentialEntity entity : entities) {
+ RequiredCredentialModel model = new RequiredCredentialModel();
+ model.setFormLabel(entity.getFormLabel());
+ model.setType(entity.getType());
+ model.setSecret(entity.isSecret());
+ model.setInput(entity.isInput());
+ requiredCredentialModels.add(model);
+ }
+ return requiredCredentialModels; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+
+ @Override
+ public UserModel getUser(String name) {
+ return session.getUserByUsername(name, this);
+ }
+
+ @Override
+ public UsernameLoginFailureModel getUserLoginFailure(String username) {
+ return session.getUserLoginFailure(username, this);
+ }
+
+ @Override
+ public UsernameLoginFailureModel addUserLoginFailure(String username) {
+ return session.addUserLoginFailure(username, this);
+ }
+
+ @Override
+ public List getAllUserLoginFailures() {
+ return session.getAllUserLoginFailures(this);
+ }
+
+ @Override
+ public UserModel getUserByEmail(String email) {
+ return session.getUserByEmail(email, this);
+ }
+
+ @Override
+ public UserModel getUserById(String id) {
+ return session.getUserById(id, this);
+ }
+
+ @Override
+ public UserModel addUser(String username) {
+ return this.addUser(KeycloakModelUtils.generateId(), username, true);
+ }
+
+ @Override
+ public UserModel addUser(String id, String username, boolean addDefaultRoles) {
+ if (id == null) {
+ id = KeycloakModelUtils.generateId();
+ }
+
+ UserEntity entity = new UserEntity();
+ entity.setId(id);
+ entity.setUsername(username);
+ entity.setRealm(realm);
+ em.persist(entity);
+ em.flush();
+ UserModel userModel = new UserAdapter(this, em, entity);
+
+ if (addDefaultRoles) {
+ for (String r : getDefaultRoles()) {
+ userModel.grantRole(getRole(r));
+ }
+
+ for (ApplicationModel application : getApplications()) {
+ for (String r : application.getDefaultRoles()) {
+ userModel.grantRole(application.getRole(r));
+ }
+ }
+ }
+
+ return userModel;
+ }
+
+ @Override
+ public boolean removeUser(String name) {
+ TypedQuery query = em.createNamedQuery("getRealmUserByUsername", UserEntity.class);
+ query.setParameter("username", name);
+ query.setParameter("realm", realm);
+ List results = query.getResultList();
+ if (results.size() == 0) return false;
+ removeUser(results.get(0));
+ return true;
+ }
+
+ private void removeUser(UserEntity user) {
+ em.createNamedQuery("removeClientUserSessionByUser").setParameter("userId", user.getId()).executeUpdate();
+ em.createNamedQuery("removeUserSessionByUser").setParameter("userId", user.getId()).executeUpdate();
+
+ em.createQuery("delete from " + UserRoleMappingEntity.class.getSimpleName() + " where user = :user").setParameter("user", user).executeUpdate();
+ em.createQuery("delete from " + SocialLinkEntity.class.getSimpleName() + " where user = :user").setParameter("user", user).executeUpdate();
+ if (user.getAuthenticationLink() != null) {
+ em.remove(user.getAuthenticationLink());
+ }
+ em.remove(user);
+ }
+
+ @Override
+ public List getDefaultRoles() {
+ Collection entities = realm.getDefaultRoles();
+ List roles = new ArrayList();
+ if (entities == null) return roles;
+ for (RoleEntity entity : entities) {
+ roles.add(entity.getName());
+ }
+ return roles;
+ }
+
+ @Override
+ public void addDefaultRole(String name) {
+ RoleModel role = getRole(name);
+ if (role == null) {
+ role = addRole(name);
+ }
+ Collection entities = realm.getDefaultRoles();
+ for (RoleEntity entity : entities) {
+ if (entity.getId().equals(role.getId())) {
+ return;
+ }
+ }
+ RoleEntity roleEntity = RoleAdapter.toRoleEntity(role, em);
+ entities.add(roleEntity);
+ em.flush();
+ }
+
+ public static boolean contains(String str, String[] array) {
+ for (String s : array) {
+ if (str.equals(s)) return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void updateDefaultRoles(String[] defaultRoles) {
+ Collection entities = realm.getDefaultRoles();
+ Set already = new HashSet();
+ List remove = new ArrayList();
+ for (RoleEntity rel : entities) {
+ if (!contains(rel.getName(), defaultRoles)) {
+ remove.add(rel);
+ } else {
+ already.add(rel.getName());
+ }
+ }
+ for (RoleEntity entity : remove) {
+ entities.remove(entity);
+ }
+ em.flush();
+ for (String roleName : defaultRoles) {
+ if (!already.contains(roleName)) {
+ addDefaultRole(roleName);
+ }
+ }
+ em.flush();
+ }
+
+ @Override
+ public ClientModel findClient(String clientId) {
+ ClientModel model = getApplicationByName(clientId);
+ if (model != null) return model;
+ return getOAuthClient(clientId);
+ }
+
+ @Override
+ public ClientModel findClientById(String id) {
+ ClientModel model = getApplicationById(id);
+ if (model != null) return model;
+ return getOAuthClientById(id);
+ }
+
+ @Override
+ public Map getApplicationNameMap() {
+ Map map = new HashMap();
+ for (ApplicationModel app : getApplications()) {
+ map.put(app.getName(), app);
+ }
+ return map; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public List getApplications() {
+ List list = new ArrayList();
+ if (realm.getApplications() == null) return list;
+ for (ApplicationEntity entity : realm.getApplications()) {
+ list.add(new ApplicationAdapter(this, em, entity));
+ }
+ return list;
+ }
+
+ @Override
+ public ApplicationModel addApplication(String name) {
+ return this.addApplication(KeycloakModelUtils.generateId(), name);
+ }
+
+ @Override
+ public ApplicationModel addApplication(String id, String name) {
+ ApplicationEntity applicationData = new ApplicationEntity();
+ applicationData.setId(id);
+ applicationData.setName(name);
+ applicationData.setEnabled(true);
+ applicationData.setRealm(realm);
+ realm.getApplications().add(applicationData);
+ em.persist(applicationData);
+ em.flush();
+ ApplicationModel resource = new ApplicationAdapter(this, em, applicationData);
+ em.flush();
+ return resource;
+ }
+
+ @Override
+ public boolean removeApplication(String id) {
+ if (id == null) return false;
+ ApplicationModel application = getApplicationById(id);
+ if (application == null) return false;
+
+ em.createNamedQuery("removeClientUserSessionByClient").setParameter("clientId", application.getId()).executeUpdate();
+
+ for (RoleModel role : application.getRoles()) {
+ application.removeRole(role);
+ }
+
+ ApplicationEntity applicationEntity = null;
+ Iterator it = realm.getApplications().iterator();
+ while (it.hasNext()) {
+ ApplicationEntity ae = it.next();
+ if (ae.getId().equals(id)) {
+ applicationEntity = ae;
+ it.remove();
+ break;
+ }
+ }
+ for (ApplicationEntity a : realm.getApplications()) {
+ if (a.getId().equals(id)) {
+ applicationEntity = a;
+ }
+ }
+ if (application == null) {
+ return false;
+ }
+ em.remove(applicationEntity);
+ em.createQuery("delete from " + ScopeMappingEntity.class.getSimpleName() + " where client = :client").setParameter("client", applicationEntity).executeUpdate();
+
+ return true;
+ }
+
+ @Override
+ public ApplicationModel getApplicationById(String id) {
+ return session.getApplicationById(id, this);
+ }
+
+ @Override
+ public ApplicationModel getApplicationByName(String name) {
+ return getApplicationNameMap().get(name);
+ }
+
+ @Override
+ public UserModel getUserBySocialLink(SocialLinkModel socialLink) {
+ return session.getUserBySocialLink(socialLink, this);
+ }
+
+ @Override
+ public Set getSocialLinks(UserModel user) {
+ return session.getSocialLinks(user, this);
+ }
+
+ @Override
+ public SocialLinkModel getSocialLink(UserModel user, String socialProvider) {
+ return session.getSocialLink(user, socialProvider, this);
+ }
+
+ @Override
+ public void addSocialLink(UserModel user, SocialLinkModel socialLink) {
+ SocialLinkEntity entity = new SocialLinkEntity();
+ entity.setRealm(realm);
+ entity.setSocialProvider(socialLink.getSocialProvider());
+ entity.setSocialUserId(socialLink.getSocialUserId());
+ entity.setSocialUsername(socialLink.getSocialUsername());
+ UserEntity userEntity = em.getReference(UserEntity.class, user.getId());
+ entity.setUser(userEntity);
+ em.persist(entity);
+ em.flush();
+ }
+
+ private SocialLinkEntity findSocialLink(UserModel user, String socialProvider) {
+ TypedQuery query = em.createNamedQuery("findSocialLinkByUserAndProvider", SocialLinkEntity.class);
+ UserEntity userEntity = em.getReference(UserEntity.class, user.getId());
+ query.setParameter("user", userEntity);
+ query.setParameter("socialProvider", socialProvider);
+ List results = query.getResultList();
+ return results.size() > 0 ? results.get(0) : null;
+ }
+
+
+ @Override
+ public boolean removeSocialLink(UserModel user, String socialProvider) {
+ SocialLinkEntity entity = findSocialLink(user, socialProvider);
+ if (entity != null) {
+ em.remove(entity);
+ em.flush();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+
+ @Override
+ public boolean isSocial() {
+ return realm.isSocial();
+ }
+
+ @Override
+ public void setSocial(boolean social) {
+ realm.setSocial(social);
+ em.flush();
+ }
+
+ @Override
+ public boolean isUpdateProfileOnInitialSocialLogin() {
+ return realm.isUpdateProfileOnInitialSocialLogin();
+ }
+
+ @Override
+ public void setUpdateProfileOnInitialSocialLogin(boolean updateProfileOnInitialSocialLogin) {
+ realm.setUpdateProfileOnInitialSocialLogin(updateProfileOnInitialSocialLogin);
+ em.flush();
+ }
+
+ @Override
+ public List getUsers() {
+ return session.getUsers(this);
+ }
+
+ @Override
+ public List searchForUser(String search) {
+ return session.searchForUser(search, this);
+ }
+
+ @Override
+ public List searchForUserByAttributes(Map attributes) {
+ return session.searchForUserByAttributes(attributes, this);
+ }
+
+ @Override
+ public OAuthClientModel addOAuthClient(String name) {
+ return this.addOAuthClient(KeycloakModelUtils.generateId(), name);
+ }
+
+ @Override
+ public OAuthClientModel addOAuthClient(String id, String name) {
+ OAuthClientEntity data = new OAuthClientEntity();
+ data.setId(id);
+ data.setEnabled(true);
+ data.setName(name);
+ data.setRealm(realm);
+ em.persist(data);
+ em.flush();
+ return new OAuthClientAdapter(this, data, em);
+ }
+
+ @Override
+ public boolean removeOAuthClient(String id) {
+ OAuthClientModel oauth = getOAuthClientById(id);
+ if (oauth == null) return false;
+ ((OAuthClientAdapter)oauth).deleteUserSessionAssociation();
+ OAuthClientEntity client = (OAuthClientEntity) ((OAuthClientAdapter) oauth).getEntity();
+ em.createQuery("delete from " + ScopeMappingEntity.class.getSimpleName() + " where client = :client").setParameter("client", client).executeUpdate();
+ em.remove(client);
+ return true;
+ }
+
+
+ @Override
+ public OAuthClientModel getOAuthClient(String name) {
+ TypedQuery query = em.createNamedQuery("findOAuthClientByName", OAuthClientEntity.class);
+ query.setParameter("name", name);
+ query.setParameter("realm", realm);
+ List entities = query.getResultList();
+ if (entities.size() == 0) return null;
+ return new OAuthClientAdapter(this, entities.get(0), em);
+ }
+
+ @Override
+ public OAuthClientModel getOAuthClientById(String id) {
+ return session.getOAuthClientById(id, this);
+ }
+
+
+ @Override
+ public List getOAuthClients() {
+ TypedQuery query = em.createNamedQuery("findOAuthClientByRealm", OAuthClientEntity.class);
+ query.setParameter("realm", realm);
+ List entities = query.getResultList();
+ List list = new ArrayList();
+ for (OAuthClientEntity entity : entities) list.add(new OAuthClientAdapter(this, entity, em));
+ return list;
+ }
+
+ @Override
+ public Map getSmtpConfig() {
+ return realm.getSmtpConfig();
+ }
+
+ @Override
+ public void setSmtpConfig(Map smtpConfig) {
+ realm.setSmtpConfig(smtpConfig);
+ em.flush();
+ }
+
+ @Override
+ public Map getSocialConfig() {
+ return realm.getSocialConfig();
+ }
+
+ @Override
+ public void setSocialConfig(Map socialConfig) {
+ realm.setSocialConfig(socialConfig);
+ em.flush();
+ }
+
+ @Override
+ public Map getLdapServerConfig() {
+ return realm.getLdapServerConfig();
+ }
+
+ @Override
+ public void setLdapServerConfig(Map ldapServerConfig) {
+ realm.setLdapServerConfig(ldapServerConfig);
+ em.flush();
+ }
+
+ @Override
+ public List getAuthenticationProviders() {
+ List entities = realm.getAuthenticationProviders();
+ List copy = new ArrayList();
+ for (AuthenticationProviderEntity entity : entities) {
+ copy.add(entity);
+
+ }
+ Collections.sort(copy, new Comparator() {
+
+ @Override
+ public int compare(AuthenticationProviderEntity o1, AuthenticationProviderEntity o2) {
+ return o1.getPriority() - o2.getPriority();
+ }
+
+ });
+ List result = new ArrayList();
+ for (AuthenticationProviderEntity entity : copy) {
+ result.add(new AuthenticationProviderModel(entity.getProviderName(), entity.isPasswordUpdateSupported(), entity.getConfig()));
+ }
+
+ return result;
+ }
+
+ @Override
+ public void setAuthenticationProviders(List authenticationProviders) {
+ List newEntities = new ArrayList();
+ int counter = 1;
+ for (AuthenticationProviderModel model : authenticationProviders) {
+ AuthenticationProviderEntity entity = new AuthenticationProviderEntity();
+ entity.setProviderName(model.getProviderName());
+ entity.setPasswordUpdateSupported(model.isPasswordUpdateSupported());
+ entity.setConfig(model.getConfig());
+ entity.setPriority(counter++);
+ newEntities.add(entity);
+ }
+
+ // Remove all existing first
+ Collection existing = realm.getAuthenticationProviders();
+ Collection copy = new ArrayList(existing);
+ for (AuthenticationProviderEntity apToRemove : copy) {
+ existing.remove(apToRemove);
+ em.remove(apToRemove);
+ }
+
+ // Now create all new providers
+ for (AuthenticationProviderEntity apToAdd : newEntities) {
+ existing.add(apToAdd);
+ em.persist(apToAdd);
+ }
+
+ em.flush();
+ }
+
+ @Override
+ public RoleModel getRole(String name) {
+ TypedQuery query = em.createNamedQuery("getRealmRoleByName", RoleEntity.class);
+ query.setParameter("name", name);
+ query.setParameter("realm", realm);
+ List roles = query.getResultList();
+ if (roles.size() == 0) return null;
+ return new RoleAdapter(this, em, roles.get(0));
+ }
+
+ @Override
+ public RoleModel addRole(String name) {
+ return this.addRole(KeycloakModelUtils.generateId(), name);
+ }
+
+ @Override
+ public RoleModel addRole(String id, String name) {
+ RoleEntity entity = new RoleEntity();
+ entity.setId(id);
+ entity.setName(name);
+ entity.setRealm(realm);
+ entity.setRealmId(realm.getId());
+ realm.getRoles().add(entity);
+ em.persist(entity);
+ em.flush();
+ return new RoleAdapter(this, em, entity);
+ }
+
+ @Override
+ public boolean removeRole(RoleModel role) {
+ if (role == null) {
+ return false;
+ }
+ if (!role.getContainer().equals(this)) return false;
+
+ RoleEntity roleEntity = RoleAdapter.toRoleEntity(role, em);
+ realm.getRoles().remove(role);
+ realm.getDefaultRoles().remove(role);
+
+ em.createNativeQuery("delete from CompositeRole where childRole = :role").setParameter("role", roleEntity).executeUpdate();
+ em.createQuery("delete from " + UserRoleMappingEntity.class.getSimpleName() + " where role = :role").setParameter("role", roleEntity).executeUpdate();
+ em.createQuery("delete from " + ScopeMappingEntity.class.getSimpleName() + " where role = :role").setParameter("role", roleEntity).executeUpdate();
+
+ em.remove(roleEntity);
+
+ return true;
+ }
+
+ @Override
+ public Set getRoles() {
+ Set list = new HashSet();
+ Collection roles = realm.getRoles();
+ if (roles == null) return list;
+ for (RoleEntity entity : roles) {
+ list.add(new RoleAdapter(this, em, entity));
+ }
+ return list;
+ }
+
+ @Override
+ public RoleModel getRoleById(String id) {
+ return session.getRoleById(id, this);
+ }
+
+ @Override
+ public boolean removeRoleById(String id) {
+ RoleModel role = getRoleById(id);
+ if (role == null) return false;
+ return role.getContainer().removeRole(role);
+ }
+
+ @Override
+ public boolean validatePassword(UserModel user, String password) {
+ return CredentialValidation.validatePassword(this, user, password);
+ }
+
+ @Override
+ public boolean validateTOTP(UserModel user, String password, String token) {
+ if (!validatePassword(user, password)) return false;
+ for (UserCredentialValueModel cred : user.getCredentialsDirectly()) {
+ if (cred.getType().equals(UserCredentialModel.TOTP)) {
+ return new TimeBasedOTP().validate(token, cred.getValue().getBytes());
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public PasswordPolicy getPasswordPolicy() {
+ if (passwordPolicy == null) {
+ passwordPolicy = new PasswordPolicy(realm.getPasswordPolicy());
+ }
+ return passwordPolicy;
+ }
+
+ @Override
+ public void setPasswordPolicy(PasswordPolicy policy) {
+ this.passwordPolicy = policy;
+ realm.setPasswordPolicy(policy.toString());
+ em.flush();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || !(o instanceof RealmModel)) return false;
+
+ RealmModel that = (RealmModel) o;
+ return that.getId().equals(getId());
+ }
+
+ @Override
+ public int hashCode() {
+ return getId().hashCode();
+ }
+
+ @Override
+ public String getLoginTheme() {
+ return realm.getLoginTheme();
+ }
+
+ @Override
+ public void setLoginTheme(String name) {
+ realm.setLoginTheme(name);
+ em.flush();
+ }
+
+ @Override
+ public String getAccountTheme() {
+ return realm.getAccountTheme();
+ }
+
+ @Override
+ public void setAccountTheme(String name) {
+ realm.setAccountTheme(name);
+ em.flush();
+ }
+
+ @Override
+ public String getAdminTheme() {
+ return realm.getAdminTheme();
+ }
+
+ @Override
+ public void setAdminTheme(String name) {
+ realm.setAdminTheme(name);
+ em.flush();
+ }
+
+ @Override
+ public String getEmailTheme() {
+ return realm.getEmailTheme();
+ }
+
+ @Override
+ public void setEmailTheme(String name) {
+ realm.setEmailTheme(name);
+ em.flush();
+ }
+
+ @Override
+ public boolean isAuditEnabled() {
+ return realm.isAuditEnabled();
+ }
+
+ @Override
+ public void setAuditEnabled(boolean enabled) {
+ realm.setAuditEnabled(enabled);
+ em.flush();
+ }
+
+ @Override
+ public long getAuditExpiration() {
+ return realm.getAuditExpiration();
+ }
+
+ @Override
+ public void setAuditExpiration(long expiration) {
+ realm.setAuditExpiration(expiration);
+ em.flush();
+ }
+
+ @Override
+ public Set getAuditListeners() {
+ return realm.getAuditListeners();
+ }
+
+ @Override
+ public void setAuditListeners(Set listeners) {
+ realm.setAuditListeners(listeners);
+ em.flush();
+ }
+
+ @Override
+ public ApplicationModel getMasterAdminApp() {
+ return new ApplicationAdapter(this, em, realm.getMasterAdminApp());
+ }
+
+ @Override
+ public void setMasterAdminApp(ApplicationModel app) {
+ realm.setMasterAdminApp(((ApplicationAdapter) app).getJpaEntity());
+ em.flush();
+ }
+
+ @Override
+ public UserSessionModel createUserSession(UserModel user, String ipAddress) {
+ return session.createUserSession(this, user, ipAddress);
+ }
+
+ @Override
+ public UserSessionModel getUserSession(String id) {
+ return session.getUserSession(id, this);
+ }
+
+ @Override
+ public List getUserSessions(UserModel user) {
+ return session.getUserSessions(user, this);
+ }
+
+ @Override
+ public void removeUserSession(UserSessionModel session) {
+ this.session.removeUserSession(session);
+ }
+
+ @Override
+ public void removeUserSessions() {
+ session.removeUserSessions(this);
+
+ }
+
+ @Override
+ public void removeUserSessions(UserModel user) {
+ session.removeUserSessions(this, user);
+ }
+
+ @Override
+ public void removeExpiredUserSessions() {
+ session.removeExpiredUserSessions(this);
+ }
+
+}
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
new file mode 100755
index 0000000000..28a1f47d02
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RoleAdapter.java
@@ -0,0 +1,135 @@
+package org.keycloak.models.jpa;
+
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleContainerModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.jpa.entities.RoleEntity;
+import org.keycloak.models.utils.KeycloakModelUtils;
+
+import javax.persistence.EntityManager;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class RoleAdapter implements RoleModel {
+ protected RoleEntity role;
+ protected EntityManager em;
+ protected RealmModel realm;
+
+ public RoleAdapter(RealmModel realm, EntityManager em, RoleEntity role) {
+ this.em = em;
+ this.realm = realm;
+ this.role = role;
+ }
+
+ public RoleEntity getRole() {
+ return role;
+ }
+
+ public void setRole(RoleEntity role) {
+ this.role = role;
+ }
+
+ @Override
+ public String getName() {
+ return role.getName();
+ }
+
+ @Override
+ public String getDescription() {
+ return role.getDescription();
+ }
+
+ @Override
+ public void setDescription(String description) {
+ role.setDescription(description);
+ }
+
+ @Override
+ public String getId() {
+ return role.getId();
+ }
+
+ @Override
+ public void setName(String name) {
+ role.setName(name);
+ }
+
+ @Override
+ public boolean isComposite() {
+ return getComposites().size() > 0;
+ }
+
+ @Override
+ public void addCompositeRole(RoleModel role) {
+ RoleEntity entity = RoleAdapter.toRoleEntity(role, em);
+ for (RoleEntity composite : getRole().getCompositeRoles()) {
+ if (composite.equals(entity)) return;
+ }
+ getRole().getCompositeRoles().add(entity);
+ em.flush();
+ }
+
+ @Override
+ public void removeCompositeRole(RoleModel role) {
+ RoleEntity entity = RoleAdapter.toRoleEntity(role, em);
+ Iterator it = getRole().getCompositeRoles().iterator();
+ while (it.hasNext()) {
+ if (it.next().equals(entity)) it.remove();
+ }
+ }
+
+ @Override
+ public Set getComposites() {
+ Set set = new HashSet();
+
+ for (RoleEntity composite : getRole().getCompositeRoles()) {
+ set.add(new RoleAdapter(realm, em, composite));
+ }
+ return set;
+ }
+
+ @Override
+ public boolean hasRole(RoleModel role) {
+ if (this.equals(role)) return true;
+ if (!isComposite()) return false;
+
+ Set visited = new HashSet();
+ return KeycloakModelUtils.searchFor(role, this, visited);
+ }
+
+ @Override
+ public RoleContainerModel getContainer() {
+ if (role.isApplicationRole()) {
+ return realm.getApplicationById(role.getApplication().getId());
+
+ } else {
+ return realm;
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || !(o instanceof RoleModel)) return false;
+
+ RoleModel that = (RoleModel) o;
+ return that.getId().equals(getId());
+ }
+
+ @Override
+ public int hashCode() {
+ return getId().hashCode();
+ }
+
+ public static RoleEntity toRoleEntity(RoleModel model, EntityManager em) {
+ if (model instanceof RoleAdapter) {
+ return ((RoleAdapter)model).getRole();
+ }
+ return em.getReference(RoleEntity.class, model.getId());
+ }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java
new file mode 100755
index 0000000000..18219f62d4
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java
@@ -0,0 +1,384 @@
+package org.keycloak.models.jpa;
+
+import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.AuthenticationLinkModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.PasswordPolicy;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleContainerModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserCredentialValueModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.jpa.entities.AuthenticationLinkEntity;
+import org.keycloak.models.jpa.entities.CredentialEntity;
+import org.keycloak.models.jpa.entities.RoleEntity;
+import org.keycloak.models.jpa.entities.UserEntity;
+import org.keycloak.models.jpa.entities.UserRoleMappingEntity;
+import org.keycloak.models.utils.Pbkdf2PasswordEncoder;
+
+import javax.persistence.EntityManager;
+import javax.persistence.TypedQuery;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.keycloak.models.utils.Pbkdf2PasswordEncoder.getSalt;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class UserAdapter implements UserModel {
+
+ protected UserEntity user;
+ protected EntityManager em;
+ protected RealmModel realm;
+
+ public UserAdapter(RealmModel realm, EntityManager em, UserEntity user) {
+ this.em = em;
+ this.user = user;
+ this.realm = realm;
+ }
+
+ public UserEntity getUser() {
+ return user;
+ }
+
+ @Override
+ public String getId() {
+ return user.getId();
+ }
+
+ @Override
+ public String getUsername() {
+ return user.getUsername();
+ }
+
+ @Override
+ public void setUsername(String username) {
+ user.setUsername(username);
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return user.isEnabled();
+ }
+
+ @Override
+ public boolean isTotp() {
+ return user.isTotp();
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ user.setEnabled(enabled);
+ }
+
+ @Override
+ public void setAttribute(String name, String value) {
+ Map attributes = user.getAttributes();
+ if (attributes == null) {
+ attributes = new HashMap();
+ }
+ attributes.put(name, value);
+ user.setAttributes(attributes);
+ }
+
+ @Override
+ public void removeAttribute(String name) {
+ Map attributes = user.getAttributes();
+ if (attributes == null) {
+ attributes = new HashMap();
+ }
+ attributes.remove(name);
+ user.setAttributes(attributes);
+ }
+
+ @Override
+ public String getAttribute(String name) {
+ if (user.getAttributes() == null) return null;
+ return user.getAttributes().get(name);
+ }
+
+ @Override
+ public Map getAttributes() {
+ Map result = new HashMap();
+ result.putAll(user.getAttributes());
+ return result;
+ }
+
+ @Override
+ public Set getRequiredActions() {
+ Set result = new HashSet();
+ result.addAll(user.getRequiredActions());
+ return result;
+ }
+
+ @Override
+ public void addRequiredAction(RequiredAction action) {
+ user.getRequiredActions().add(action);
+ }
+
+ @Override
+ public void removeRequiredAction(RequiredAction action) {
+ user.getRequiredActions().remove(action);
+ }
+
+
+ @Override
+ public String getFirstName() {
+ return user.getFirstName();
+ }
+
+ @Override
+ public void setFirstName(String firstName) {
+ user.setFirstName(firstName);
+ }
+
+ @Override
+ public String getLastName() {
+ return user.getLastName();
+ }
+
+ @Override
+ public void setLastName(String lastName) {
+ user.setLastName(lastName);
+ }
+
+ @Override
+ public String getEmail() {
+ return user.getEmail();
+ }
+
+ @Override
+ public void setEmail(String email) {
+ user.setEmail(email);
+ }
+
+ @Override
+ public boolean isEmailVerified() {
+ return user.isEmailVerified();
+ }
+
+ @Override
+ public void setEmailVerified(boolean verified) {
+ user.setEmailVerified(verified);
+ }
+
+ @Override
+ public void setTotp(boolean totp) {
+ user.setTotp(totp);
+ }
+
+ @Override
+ public void updateCredential(UserCredentialModel cred) {
+ CredentialEntity credentialEntity = getCredentialEntity(user, cred.getType());
+
+ if (credentialEntity == null) {
+ credentialEntity = new CredentialEntity();
+ credentialEntity.setType(cred.getType());
+ credentialEntity.setDevice(cred.getDevice());
+ credentialEntity.setUser(user);
+ em.persist(credentialEntity);
+ user.getCredentials().add(credentialEntity);
+ }
+ if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
+ byte[] salt = getSalt();
+ int hashIterations = 1;
+ PasswordPolicy policy = realm.getPasswordPolicy();
+ if (policy != null) {
+ hashIterations = policy.getHashIterations();
+ if (hashIterations == -1) hashIterations = 1;
+ }
+ credentialEntity.setValue(new Pbkdf2PasswordEncoder(salt).encode(cred.getValue(), hashIterations));
+ credentialEntity.setSalt(salt);
+ credentialEntity.setHashIterations(hashIterations);
+ } else {
+ credentialEntity.setValue(cred.getValue());
+ }
+ credentialEntity.setDevice(cred.getDevice());
+ em.flush();
+ }
+
+ private CredentialEntity getCredentialEntity(UserEntity userEntity, String credType) {
+ for (CredentialEntity entity : userEntity.getCredentials()) {
+ if (entity.getType().equals(credType)) {
+ return entity;
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public List getCredentialsDirectly() {
+ List credentials = new ArrayList(user.getCredentials());
+ List result = new ArrayList();
+
+ if (credentials != null) {
+ for (CredentialEntity credEntity : credentials) {
+ UserCredentialValueModel credModel = new UserCredentialValueModel();
+ credModel.setType(credEntity.getType());
+ credModel.setDevice(credEntity.getDevice());
+ credModel.setValue(credEntity.getValue());
+ credModel.setSalt(credEntity.getSalt());
+ credModel.setHashIterations(credEntity.getHashIterations());
+
+ result.add(credModel);
+ }
+ }
+
+ return result;
+ }
+
+ @Override
+ public void updateCredentialDirectly(UserCredentialValueModel credModel) {
+ CredentialEntity credentialEntity = getCredentialEntity(user, credModel.getType());
+
+ if (credentialEntity == null) {
+ credentialEntity = new CredentialEntity();
+ credentialEntity.setType(credModel.getType());
+ credentialEntity.setUser(user);
+ em.persist(credentialEntity);
+ user.getCredentials().add(credentialEntity);
+ }
+
+ credentialEntity.setValue(credModel.getValue());
+ credentialEntity.setSalt(credModel.getSalt());
+ credentialEntity.setDevice(credModel.getDevice());
+ credentialEntity.setHashIterations(credModel.getHashIterations());
+
+ em.flush();
+ }
+
+ @Override
+ public boolean hasRole(RoleModel role) {
+ Set roles = getRoleMappings();
+ if (roles.contains(role)) return true;
+
+ for (RoleModel mapping : roles) {
+ if (mapping.hasRole(role)) return true;
+ }
+ return false;
+ }
+
+ protected TypedQuery getUserRoleMappingEntityTypedQuery(RoleModel role) {
+ TypedQuery query = em.createNamedQuery("userHasRole", UserRoleMappingEntity.class);
+ query.setParameter("user", getUser());
+ RoleEntity roleEntity = em.getReference(RoleEntity.class, role.getId());
+ query.setParameter("role", roleEntity);
+ return query;
+ }
+
+ @Override
+ public void grantRole(RoleModel role) {
+ if (hasRole(role)) return;
+ UserRoleMappingEntity entity = new UserRoleMappingEntity();
+ entity.setUser(getUser());
+ RoleEntity roleEntity = em.getReference(RoleEntity.class, role.getId());
+ entity.setRole(roleEntity);
+ em.persist(entity);
+ em.flush();
+ em.detach(entity);
+ }
+
+ @Override
+ public Set getRealmRoleMappings() {
+ Set roleMappings = getRoleMappings();
+
+ Set realmRoles = new HashSet();
+ for (RoleModel role : roleMappings) {
+ RoleContainerModel container = role.getContainer();
+ if (container instanceof RealmModel) {
+ realmRoles.add(role);
+ }
+ }
+ return realmRoles;
+ }
+
+
+ @Override
+ public Set getRoleMappings() {
+ // we query ids only as the role might be cached and following the @ManyToOne will result in a load
+ // even if we're getting just the id.
+ TypedQuery query = em.createNamedQuery("userRoleMappingIds", String.class);
+ query.setParameter("user", getUser());
+ List ids = query.getResultList();
+ Set roles = new HashSet();
+ for (String roleId : ids) {
+ RoleModel roleById = realm.getRoleById(roleId);
+ if (roleById == null) continue;
+ roles.add(roleById);
+ }
+ return roles;
+ }
+
+ @Override
+ public void deleteRoleMapping(RoleModel role) {
+ if (user == null || role == null) return;
+
+ TypedQuery query = getUserRoleMappingEntityTypedQuery(role);
+ List results = query.getResultList();
+ if (results.size() == 0) return;
+ for (UserRoleMappingEntity entity : results) {
+ em.remove(entity);
+ }
+ em.flush();
+ }
+
+ @Override
+ public Set getApplicationRoleMappings(ApplicationModel app) {
+ Set roleMappings = getRoleMappings();
+
+ Set roles = new HashSet();
+ for (RoleModel role : roleMappings) {
+ RoleContainerModel container = role.getContainer();
+ if (container instanceof ApplicationModel) {
+ ApplicationModel appModel = (ApplicationModel)container;
+ if (appModel.getId().equals(app.getId())) {
+ roles.add(role);
+ }
+ }
+ }
+ return roles;
+ }
+
+ @Override
+ public AuthenticationLinkModel getAuthenticationLink() {
+ AuthenticationLinkEntity authLinkEntity = user.getAuthenticationLink();
+ return authLinkEntity == null ? null : new AuthenticationLinkModel(authLinkEntity.getAuthProvider(), authLinkEntity.getAuthUserId());
+ }
+
+ @Override
+ public void setAuthenticationLink(AuthenticationLinkModel authenticationLink) {
+ AuthenticationLinkEntity entity = new AuthenticationLinkEntity();
+ entity.setAuthProvider(authenticationLink.getAuthProvider());
+ entity.setAuthUserId(authenticationLink.getAuthUserId());
+
+ user.setAuthenticationLink(entity);
+ em.persist(entity);
+ em.persist(user);
+ em.flush();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || !(o instanceof UserModel)) return false;
+
+ UserModel that = (UserModel) o;
+ return that.getId().equals(getId());
+ }
+
+ @Override
+ public int hashCode() {
+ return getId().hashCode();
+ }
+
+
+
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/UserSessionAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/UserSessionAdapter.java
new file mode 100755
index 0000000000..333f7d19f4
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/UserSessionAdapter.java
@@ -0,0 +1,130 @@
+package org.keycloak.models.jpa;
+
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.ModelException;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.jpa.entities.ClientUserSessionAssociationEntity;
+import org.keycloak.models.jpa.entities.UserSessionEntity;
+
+import javax.persistence.EntityManager;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Stian Thorgersen
+ */
+public class UserSessionAdapter implements UserSessionModel {
+
+ private RealmModel realm;
+ private UserSessionEntity entity;
+ private EntityManager em;
+
+ public UserSessionAdapter(EntityManager em, RealmModel realm, UserSessionEntity entity) {
+ this.entity = entity;
+ this.em = em;
+ this.realm = realm;
+ }
+
+ public UserSessionEntity getEntity() {
+ return entity;
+ }
+
+ @Override
+ public String getId() {
+ return entity.getId();
+ }
+
+ @Override
+ public void setId(String id) {
+ entity.setId(id);
+ }
+
+ @Override
+ public UserModel getUser() {
+ return realm.getUserById(entity.getUserId());
+ }
+
+ @Override
+ public void setUser(UserModel user) {
+ entity.setUserId(user.getId());
+ }
+
+ @Override
+ public String getIpAddress() {
+ return entity.getIpAddress();
+ }
+
+ @Override
+ public void setIpAddress(String ipAddress) {
+ entity.setIpAddress(ipAddress);
+ }
+
+ @Override
+ public int getStarted() {
+ return entity.getStarted();
+ }
+
+ @Override
+ public void setStarted(int started) {
+ entity.setStarted(started);
+ }
+
+ @Override
+ public int getLastSessionRefresh() {
+ return entity.getLastSessionRefresh();
+ }
+
+ @Override
+ public void setLastSessionRefresh(int seconds) {
+ entity.setLastSessionRefresh(seconds);
+ }
+
+ @Override
+ public void associateClient(ClientModel client) {
+ for (ClientUserSessionAssociationEntity ass : entity.getClients()) {
+ if (ass.getClientId().equals(client.getId())) return;
+ }
+ ClientUserSessionAssociationEntity association = new ClientUserSessionAssociationEntity();
+ association.setClientId(client.getId());
+ association.setSession(entity);
+ association.setUserId(entity.getUserId());
+ association.setRealmId(realm.getId());
+ em.persist(association);
+ entity.getClients().add(association);
+ }
+
+ @Override
+ public void removeAssociatedClient(ClientModel client) {
+ em.createNamedQuery("removeClientUserSessionByClient").setParameter("clientId", client.getId()).executeUpdate();
+
+ }
+
+ @Override
+ public List getClientAssociations() {
+ List clients = new ArrayList();
+ for (ClientUserSessionAssociationEntity association : entity.getClients()) {
+ ClientModel client = realm.findClientById(association.getClientId());
+ if (client == null) {
+ throw new ModelException("couldnt find client");
+ }
+ clients.add(client);
+ }
+ return clients;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || !(o instanceof UserSessionModel)) return false;
+
+ UserSessionModel that = (UserSessionModel) o;
+ return that.getId().equals(getId());
+ }
+
+ @Override
+ public int hashCode() {
+ return getId().hashCode();
+ }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/UsernameLoginFailureAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/UsernameLoginFailureAdapter.java
new file mode 100755
index 0000000000..ba43c82388
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/UsernameLoginFailureAdapter.java
@@ -0,0 +1,70 @@
+package org.keycloak.models.jpa;
+
+import org.keycloak.models.UsernameLoginFailureModel;
+import org.keycloak.models.jpa.entities.UsernameLoginFailureEntity;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class UsernameLoginFailureAdapter implements UsernameLoginFailureModel
+{
+ protected UsernameLoginFailureEntity user;
+
+ public UsernameLoginFailureAdapter(UsernameLoginFailureEntity user)
+ {
+ this.user = user;
+ }
+
+ @Override
+ public String getUsername()
+ {
+ return user.getUsername();
+ }
+
+ @Override
+ public int getFailedLoginNotBefore() {
+ return user.getFailedLoginNotBefore();
+ }
+
+ @Override
+ public void setFailedLoginNotBefore(int notBefore) {
+ user.setFailedLoginNotBefore(notBefore);
+ }
+
+ @Override
+ public int getNumFailures() {
+ return user.getNumFailures();
+ }
+
+ @Override
+ public void incrementFailures() {
+ user.setNumFailures(getNumFailures() + 1);
+ }
+
+ @Override
+ public void clearFailures() {
+ user.setNumFailures(0);
+ }
+
+ @Override
+ public long getLastFailure() {
+ return user.getLastFailure();
+ }
+
+ @Override
+ public void setLastFailure(long lastFailure) {
+ user.setLastFailure(lastFailure);
+ }
+
+ @Override
+ public String getLastIPFailure() {
+ return user.getLastIPFailure();
+ }
+
+ @Override
+ public void setLastIPFailure(String ip) {
+ user.setLastIPFailure(ip);
+ }
+
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AbstractRoleMappingEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AbstractRoleMappingEntity.java
new file mode 100755
index 0000000000..2ab0d00cc7
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AbstractRoleMappingEntity.java
@@ -0,0 +1,52 @@
+package org.keycloak.models.jpa.entities;
+
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.MappedSuperclass;
+
+import org.hibernate.annotations.GenericGenerator;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+@MappedSuperclass
+public class AbstractRoleMappingEntity {
+ @Id
+ @GenericGenerator(name="keycloak_generator", strategy="org.keycloak.models.jpa.utils.JpaIdGenerator")
+ @GeneratedValue(generator = "keycloak_generator")
+ protected String id;
+ @ManyToOne(fetch= FetchType.LAZY)
+ protected UserEntity user;
+
+ @ManyToOne(fetch= FetchType.LAZY)
+ @JoinColumn(name="roleId")
+ protected RoleEntity role;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public UserEntity getUser() {
+ return user;
+ }
+
+ public void setUser(UserEntity user) {
+ this.user = user;
+ }
+
+ public RoleEntity getRole() {
+ return role;
+ }
+
+ public void setRole(RoleEntity role) {
+ this.role = role;
+ }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ApplicationEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ApplicationEntity.java
new file mode 100755
index 0000000000..8327bd0e04
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ApplicationEntity.java
@@ -0,0 +1,90 @@
+package org.keycloak.models.jpa.entities;
+
+import javax.persistence.CascadeType;
+import javax.persistence.CollectionTable;
+import javax.persistence.ElementCollection;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.JoinTable;
+import javax.persistence.ManyToOne;
+import javax.persistence.OneToMany;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.hibernate.annotations.GenericGenerator;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+@Entity
+public class ApplicationEntity extends ClientEntity {
+
+ private boolean surrogateAuthRequired;
+ private String baseUrl;
+ private String managementUrl;
+ private boolean bearerOnly;
+
+ @OneToMany(fetch = FetchType.EAGER, cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "application")
+ Collection roles = new ArrayList();
+
+ @OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true)
+ @JoinTable(name="ApplicationDefaultRoles")
+ Collection defaultRoles = new ArrayList();
+
+ public boolean isSurrogateAuthRequired() {
+ return surrogateAuthRequired;
+ }
+
+ public void setSurrogateAuthRequired(boolean surrogateAuthRequired) {
+ this.surrogateAuthRequired = surrogateAuthRequired;
+ }
+
+ public String getBaseUrl() {
+ return baseUrl;
+ }
+
+ public void setBaseUrl(String baseUrl) {
+ this.baseUrl = baseUrl;
+ }
+
+ public String getManagementUrl() {
+ return managementUrl;
+ }
+
+ public void setManagementUrl(String managementUrl) {
+ this.managementUrl = managementUrl;
+ }
+
+ public Collection getRoles() {
+ return roles;
+ }
+
+ public void setRoles(Collection roles) {
+ this.roles = roles;
+ }
+
+ public Collection getDefaultRoles() {
+ return defaultRoles;
+ }
+
+ public void setDefaultRoles(Collection defaultRoles) {
+ this.defaultRoles = defaultRoles;
+ }
+
+ public boolean isBearerOnly() {
+ return bearerOnly;
+ }
+
+ public void setBearerOnly(boolean bearerOnly) {
+ this.bearerOnly = bearerOnly;
+ }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticationLinkEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticationLinkEntity.java
new file mode 100644
index 0000000000..e6ae8d421c
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticationLinkEntity.java
@@ -0,0 +1,50 @@
+package org.keycloak.models.jpa.entities;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.OneToOne;
+
+import org.hibernate.annotations.GenericGenerator;
+
+/**
+ * @author Marek Posolda
+ */
+@Entity
+public class AuthenticationLinkEntity {
+
+ @Id
+ @GenericGenerator(name="keycloak_generator", strategy="org.keycloak.models.jpa.utils.JpaIdGenerator")
+ @GeneratedValue(generator = "keycloak_generator")
+ private String id;
+
+ protected String authProvider;
+ protected String authUserId;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getAuthProvider() {
+ return authProvider;
+ }
+
+ public void setAuthProvider(String authProvider) {
+ this.authProvider = authProvider;
+ }
+
+ public String getAuthUserId() {
+ return authUserId;
+ }
+
+ public void setAuthUserId(String authUserId) {
+ this.authUserId = authUserId;
+ }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticationProviderEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticationProviderEntity.java
new file mode 100644
index 0000000000..2ce4c127a5
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticationProviderEntity.java
@@ -0,0 +1,80 @@
+package org.keycloak.models.jpa.entities;
+
+import java.util.Map;
+
+import javax.persistence.CollectionTable;
+import javax.persistence.Column;
+import javax.persistence.ElementCollection;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.MapKeyColumn;
+import javax.persistence.Table;
+
+import org.hibernate.annotations.GenericGenerator;
+
+/**
+ * @author Marek Posolda
+ */
+@Entity
+@Table(name="AuthProviderEntity")
+public class AuthenticationProviderEntity {
+
+ @Id
+ @GenericGenerator(name="keycloak_generator", strategy="org.keycloak.models.jpa.utils.JpaIdGenerator")
+ @GeneratedValue(generator = "keycloak_generator")
+ protected String id;
+
+ private String providerName;
+ private boolean passwordUpdateSupported;
+ private int priority;
+
+ @ElementCollection
+ @MapKeyColumn(name="name")
+ @Column(name="value")
+ @CollectionTable(name="AuthProviderEntity_cfg", joinColumns = {
+ @JoinColumn(name = "AuthProviderEntity_id")
+ })
+ private Map config;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getProviderName() {
+ return providerName;
+ }
+
+ public void setProviderName(String providerName) {
+ this.providerName = providerName;
+ }
+
+ public boolean isPasswordUpdateSupported() {
+ return passwordUpdateSupported;
+ }
+
+ public void setPasswordUpdateSupported(boolean passwordUpdateSupported) {
+ this.passwordUpdateSupported = passwordUpdateSupported;
+ }
+
+ public int getPriority() {
+ return priority;
+ }
+
+ public void setPriority(int priority) {
+ this.priority = priority;
+ }
+
+ public Map getConfig() {
+ return config;
+ }
+
+ public void setConfig(Map config) {
+ this.config = config;
+ }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java
new file mode 100755
index 0000000000..9367d251d7
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java
@@ -0,0 +1,131 @@
+package org.keycloak.models.jpa.entities;
+
+import org.hibernate.annotations.GenericGenerator;
+
+import javax.persistence.CascadeType;
+import javax.persistence.CollectionTable;
+import javax.persistence.Column;
+import javax.persistence.ElementCollection;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.Inheritance;
+import javax.persistence.InheritanceType;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.OneToMany;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+@Entity
+@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
+@Table(uniqueConstraints = {@UniqueConstraint(columnNames = {"realm", "name"})})
+public abstract class ClientEntity {
+ @Id
+ private String id;
+ @Column(name = "name")
+ private String name;
+ private boolean enabled;
+ private String secret;
+ private long allowedClaimsMask;
+ private int notBefore;
+ private boolean publicClient;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "realm")
+ protected RealmEntity realm;
+
+ @ElementCollection
+ @CollectionTable
+ protected Set webOrigins = new HashSet();
+ @ElementCollection
+ @CollectionTable
+ protected Set redirectUris = new HashSet();
+
+ public RealmEntity getRealm() {
+ return realm;
+ }
+
+ public void setRealm(RealmEntity realm) {
+ this.realm = realm;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public long getAllowedClaimsMask() {
+ return allowedClaimsMask;
+ }
+
+ public void setAllowedClaimsMask(long allowedClaimsMask) {
+ this.allowedClaimsMask = allowedClaimsMask;
+ }
+
+ public Set getWebOrigins() {
+ return webOrigins;
+ }
+
+ public void setWebOrigins(Set webOrigins) {
+ this.webOrigins = webOrigins;
+ }
+
+ public Set getRedirectUris() {
+ return redirectUris;
+ }
+
+ public void setRedirectUris(Set redirectUris) {
+ this.redirectUris = redirectUris;
+ }
+
+ public String getSecret() {
+ return secret;
+ }
+
+ public void setSecret(String secret) {
+ this.secret = secret;
+ }
+
+ public int getNotBefore() {
+ return notBefore;
+ }
+
+ public void setNotBefore(int notBefore) {
+ this.notBefore = notBefore;
+ }
+
+ public boolean isPublicClient() {
+ return publicClient;
+ }
+
+ public void setPublicClient(boolean publicClient) {
+ this.publicClient = publicClient;
+ }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientUserSessionAssociationEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientUserSessionAssociationEntity.java
new file mode 100755
index 0000000000..490e75d7c2
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientUserSessionAssociationEntity.java
@@ -0,0 +1,84 @@
+package org.keycloak.models.jpa.entities;
+
+import org.hibernate.annotations.GenericGenerator;
+
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.Table;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+@Entity
+@Table(name = "ClientUserSessionAscEntity")
+@NamedQueries({
+ @NamedQuery(name = "getAllClientUserSessions", query = "select s from ClientUserSessionAssociationEntity s"),
+ @NamedQuery(name = "getClientUserSessionBySession", query = "select s from ClientUserSessionAssociationEntity s where s.session = :session"),
+ @NamedQuery(name = "getClientUserSessionByClient", query = "select s from ClientUserSessionAssociationEntity s where s.clientId = :clientId"),
+ @NamedQuery(name = "getActiveClientSessions", query = "select COUNT(s) from ClientUserSessionAssociationEntity s where s.clientId = :clientId"),
+ @NamedQuery(name = "removeClientUserSessionByClient", query = "delete from ClientUserSessionAssociationEntity s where s.clientId = :clientId"),
+ @NamedQuery(name = "removeClientUserSessionByUser", query = "delete from ClientUserSessionAssociationEntity s where s.userId = :userId"),
+ @NamedQuery(name = "removeClientUserSessionByRealm", query = "delete from ClientUserSessionAssociationEntity s where s.realmId = :realmId")})
+public class ClientUserSessionAssociationEntity {
+ @Id
+ @GenericGenerator(name="uuid_generator", strategy="org.keycloak.models.jpa.utils.JpaIdGenerator")
+ @GeneratedValue(generator = "uuid_generator")
+ private String id;
+
+ // we use ids to avoid select for update contention
+ private String userId;
+ private String realmId;
+
+ @ManyToOne(fetch= FetchType.LAZY)
+ private UserSessionEntity session;
+
+
+
+ private String clientId;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public UserSessionEntity getSession() {
+ return session;
+ }
+
+ public void setSession(UserSessionEntity session) {
+ this.session = session;
+ }
+
+ public String getClientId() {
+ return clientId;
+ }
+
+ public void setClientId(String clientId) {
+ this.clientId = clientId;
+ }
+
+ public String getUserId() {
+ return userId;
+ }
+
+ public void setUserId(String userId) {
+ this.userId = userId;
+ }
+
+ public String getRealmId() {
+ return realmId;
+ }
+
+ public void setRealmId(String realmId) {
+ this.realmId = realmId;
+ }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/CredentialEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/CredentialEntity.java
new file mode 100755
index 0000000000..6d792f887c
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/CredentialEntity.java
@@ -0,0 +1,91 @@
+package org.keycloak.models.jpa.entities;
+
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+
+import org.hibernate.annotations.GenericGenerator;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+@NamedQueries({
+ @NamedQuery(name="credentialByUserAndType", query="select cred from CredentialEntity cred where cred.user = :user and cred.type = :type")
+})
+@Entity
+public class CredentialEntity {
+ @Id
+ @GenericGenerator(name="keycloak_generator", strategy="org.keycloak.models.jpa.utils.JpaIdGenerator")
+ @GeneratedValue(generator = "keycloak_generator")
+ protected String id;
+
+ protected String type;
+ protected String value;
+ protected String device;
+ protected byte[] salt;
+ protected int hashIterations;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ protected UserEntity user;
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getDevice() {
+ return device;
+ }
+
+ public void setDevice(String device) {
+ this.device = device;
+ }
+
+ public UserEntity getUser() {
+ return user;
+ }
+
+ public void setUser(UserEntity user) {
+ this.user = user;
+ }
+
+ public byte[] getSalt() {
+ return salt;
+ }
+
+ public void setSalt(byte[] salt) {
+ this.salt = salt;
+ }
+
+ public int getHashIterations() {
+ return hashIterations;
+ }
+
+ public void setHashIterations(int hashIterations) {
+ this.hashIterations = hashIterations;
+ }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/OAuthClientEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/OAuthClientEntity.java
new file mode 100755
index 0000000000..dbdf080272
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/OAuthClientEntity.java
@@ -0,0 +1,27 @@
+package org.keycloak.models.jpa.entities;
+
+import javax.persistence.Entity;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+@NamedQueries({
+ @NamedQuery(name="findOAuthClientByName", query="select o from OAuthClientEntity o where o.name=:name and o.realm = :realm"),
+ @NamedQuery(name="findOAuthClientByRealm", query="select o from OAuthClientEntity o where o.realm = :realm")
+
+})
+@Entity
+public class OAuthClientEntity extends ClientEntity {
+ protected boolean directGrantsOnly;
+
+ public boolean isDirectGrantsOnly() {
+ return directGrantsOnly;
+ }
+
+ public void setDirectGrantsOnly(boolean directGrantsOnly) {
+ this.directGrantsOnly = directGrantsOnly;
+ }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java
new file mode 100755
index 0000000000..8fd8a5660a
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java
@@ -0,0 +1,479 @@
+package org.keycloak.models.jpa.entities;
+
+
+import javax.persistence.CascadeType;
+import javax.persistence.CollectionTable;
+import javax.persistence.Column;
+import javax.persistence.ElementCollection;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Id;
+import javax.persistence.JoinTable;
+import javax.persistence.MapKeyColumn;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.OneToMany;
+import javax.persistence.OneToOne;
+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;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+@Entity
+@NamedQueries({
+ @NamedQuery(name="getAllRealms", query="select realm from RealmEntity realm"),
+ @NamedQuery(name="getRealmByName", query="select realm from RealmEntity realm where realm.name = :name"),
+})
+public class RealmEntity {
+ @Id
+ protected String id;
+
+ @Column(unique = true)
+ protected String name;
+
+ protected boolean enabled;
+ protected boolean sslNotRequired;
+ protected boolean registrationAllowed;
+ protected boolean passwordCredentialGrantAllowed;
+ protected boolean verifyEmail;
+ protected boolean resetPasswordAllowed;
+ protected boolean social;
+ protected boolean rememberMe;
+ //--- brute force settings
+ protected boolean bruteForceProtected;
+ protected int maxFailureWaitSeconds;
+ protected int minimumQuickLoginWaitSeconds;
+ protected int waitIncrementSeconds;
+ protected long quickLoginCheckMilliSeconds;
+ protected int maxDeltaTimeSeconds;
+ protected int failureFactor;
+ //--- end brute force settings
+
+
+ @Column(name="updateProfileOnInitSocLogin")
+ protected boolean updateProfileOnInitialSocialLogin;
+ protected String passwordPolicy;
+
+ private int ssoSessionIdleTimeout;
+ private int ssoSessionMaxLifespan;
+ protected int accessTokenLifespan;
+ protected int accessCodeLifespan;
+ protected int accessCodeLifespanUserAction;
+ protected int notBefore;
+
+ @Column(length = 2048)
+ protected String publicKeyPem;
+ @Column(length = 2048)
+ protected String privateKeyPem;
+
+ protected String loginTheme;
+ protected String accountTheme;
+ protected String adminTheme;
+ protected String emailTheme;
+
+ @OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true)
+ @JoinTable(name="User_RequiredCreds")
+ Collection requiredCredentials = new ArrayList();
+
+
+ @OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true)
+ @JoinTable(name="AuthProviders")
+ List authenticationProviders = new ArrayList();
+
+ @OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true)
+ Collection applications = new ArrayList();
+
+ @OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm")
+ Collection roles = new ArrayList();
+
+ @ElementCollection
+ @MapKeyColumn(name="name")
+ @Column(name="value")
+ @CollectionTable
+ protected Map smtpConfig = new HashMap();
+
+ @ElementCollection
+ @MapKeyColumn(name="name")
+ @Column(name="value")
+ @CollectionTable
+ protected Map socialConfig = new HashMap();
+
+ @ElementCollection
+ @MapKeyColumn(name="name")
+ @Column(name="value")
+ @CollectionTable
+ protected Map ldapServerConfig = new HashMap();
+
+ @OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true)
+ @JoinTable(name="RealmDefaultRoles")
+ protected Collection defaultRoles = new ArrayList();
+
+ protected boolean auditEnabled;
+ protected long auditExpiration;
+
+ @ElementCollection
+ protected Set