keycloak-uncached
Changes
export-import/export-import-impl/pom.xml 13(+13 -0)
export-import/export-import-impl/src/test/java/org/keycloak/exportimport/JPAToMongoExportImportTest.java 4(+0 -4)
export-import/export-import-impl/src/test/java/org/keycloak/exportimport/MongoToJPAExportImportTest.java 4(+0 -4)
model/jpa/pom.xml 139(+139 -0)
model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticationProviderEntity.java 80(+80 -0)
model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientUserSessionAssociationEntity.java 84(+84 -0)
server/pom.xml 22(+1 -21)
testsuite/integration/pom.xml 22(+1 -21)
testsuite/performance/pom.xml 25(+0 -25)
testsuite/tools/pom.xml 20(+0 -20)
Details
export-import/export-import-impl/pom.xml 13(+13 -0)
diff --git a/export-import/export-import-impl/pom.xml b/export-import/export-import-impl/pom.xml
index 683cfe9..9b10c8a 100755
--- a/export-import/export-import-impl/pom.xml
+++ b/export-import/export-import-impl/pom.xml
@@ -103,6 +103,19 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-model-jpa</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-model-jpa</artifactId>
+ <version>${project.version}</version>
+ <classifier>tests</classifier>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-model-mongo</artifactId>
<version>${project.version}</version>
<scope>test</scope>
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 c1491d9..8cc6a17 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 <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
- *
- * 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 0de953e..5fe08f7 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 <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
- *
- * TODO Update to work with hybrid model provider
*/
-@Ignore
public class MongoToJPAExportImportTest extends ExportImportTestBase {
private static final String zipFile = "keycloak-export.zip";
model/jpa/pom.xml 139(+139 -0)
diff --git a/model/jpa/pom.xml b/model/jpa/pom.xml
new file mode 100755
index 0000000..987abd2
--- /dev/null
+++ b/model/jpa/pom.xml
@@ -0,0 +1,139 @@
+<?xml version="1.0"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <artifactId>keycloak-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.0-beta-4-SNAPSHOT</version>
+ <relativePath>../../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>keycloak-model-jpa</artifactId>
+ <name>Keycloak Model JPA</name>
+ <description/>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk16</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>net.iharder</groupId>
+ <artifactId>base64</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-core</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-model-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-invalidation-cache-model</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.hibernate.javax.persistence</groupId>
+ <artifactId>hibernate-jpa-2.0-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-entitymanager</artifactId>
+ <version>${hibernate.entitymanager.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.resteasy</groupId>
+ <artifactId>resteasy-jaxrs</artifactId>
+ <scope>provided</scope>
+ <exclusions>
+ <exclusion>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-model-tests</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-model-tests</artifactId>
+ <version>${project.version}</version>
+ <classifier>tests</classifier>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.h2database</groupId>
+ <artifactId>h2</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>${maven.compiler.source}</source>
+ <target>${maven.compiler.target}</target>
+ </configuration>
+ </plugin>
+
+ <!-- Test jar used in export-import -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>package-tests-jar</id>
+ <phase>package</phase>
+ <goals>
+ <goal>test-jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>default-test</id>
+ <configuration>
+ <dependenciesToScan>
+ <dependency>org.keycloak:keycloak-model-tests</dependency>
+ </dependenciesToScan>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+
+ </plugins>
+ </build>
+
+</project>
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 0000000..0ebe017
--- /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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @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<RoleEntity> query = em.createNamedQuery("getAppRoleByName", RoleEntity.class);
+ query.setParameter("name", name);
+ query.setParameter("application", entity);
+ List<RoleEntity> 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<RoleModel> getRoles() {
+ Set<RoleModel> list = new HashSet<RoleModel>();
+ Collection<RoleEntity> roles = applicationEntity.getRoles();
+ if (roles == null) return list;
+ for (RoleEntity entity : roles) {
+ list.add(new RoleAdapter(realm, em, entity));
+ }
+ return list;
+ }
+
+ @Override
+ public Set<RoleModel> getApplicationScopeMappings(ClientModel client) {
+ Set<RoleModel> roleMappings = client.getScopeMappings();
+
+ Set<RoleModel> appRoles = new HashSet<RoleModel>();
+ 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<String> getDefaultRoles() {
+ Collection<RoleEntity> entities = applicationEntity.getDefaultRoles();
+ List<String> roles = new ArrayList<String>();
+ 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<RoleEntity> 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<RoleEntity> entities = applicationEntity.getDefaultRoles();
+ Set<String> already = new HashSet<String>();
+ List<RoleEntity> remove = new ArrayList<RoleEntity>();
+ 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 0000000..2e99658
--- /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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @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<String> getWebOrigins() {
+ Set<String> result = new HashSet<String>();
+ result.addAll(entity.getWebOrigins());
+ return result;
+ }
+
+
+
+ @Override
+ public void setWebOrigins(Set<String> 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<String> getRedirectUris() {
+ Set<String> result = new HashSet<String>();
+ result.addAll(entity.getRedirectUris());
+ return result;
+ }
+
+ @Override
+ public void setRedirectUris(Set<String> 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<UserSessionModel> getUserSessions() {
+ Set<UserSessionModel> list = new HashSet<UserSessionModel>();
+ TypedQuery<ClientUserSessionAssociationEntity> query = em.createNamedQuery("getClientUserSessionByClient", ClientUserSessionAssociationEntity.class);
+ String id = getId();
+ query.setParameter("clientId", id);
+ List<ClientUserSessionAssociationEntity> 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<RoleModel> getRealmScopeMappings() {
+ Set<RoleModel> roleMappings = getScopeMappings();
+
+ Set<RoleModel> appRoles = new HashSet<RoleModel>();
+ 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<RoleModel> getScopeMappings() {
+ TypedQuery<String> query = em.createNamedQuery("clientScopeMappingIds", String.class);
+ query.setParameter("client", getEntity());
+ List<String> ids = query.getResultList();
+ Set<RoleModel> roles = new HashSet<RoleModel>();
+ 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<ScopeMappingEntity> query = getRealmScopeMappingQuery(role);
+ List<ScopeMappingEntity> results = query.getResultList();
+ if (results.size() == 0) return;
+ for (ScopeMappingEntity entity : results) {
+ em.remove(entity);
+ }
+ }
+
+ protected TypedQuery<ScopeMappingEntity> getRealmScopeMappingQuery(RoleModel role) {
+ TypedQuery<ScopeMappingEntity> 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<RoleModel> 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/entities/AbstractRoleMappingEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AbstractRoleMappingEntity.java
new file mode 100755
index 0000000..2ab0d00
--- /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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @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 0000000..8327bd0
--- /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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @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<RoleEntity> roles = new ArrayList<RoleEntity>();
+
+ @OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true)
+ @JoinTable(name="ApplicationDefaultRoles")
+ Collection<RoleEntity> defaultRoles = new ArrayList<RoleEntity>();
+
+ 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<RoleEntity> getRoles() {
+ return roles;
+ }
+
+ public void setRoles(Collection<RoleEntity> roles) {
+ this.roles = roles;
+ }
+
+ public Collection<RoleEntity> getDefaultRoles() {
+ return defaultRoles;
+ }
+
+ public void setDefaultRoles(Collection<RoleEntity> 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 0000000..e6ae8d4
--- /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 <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+@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 0000000..2ce4c12
--- /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 <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+@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<String, String> 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<String, String> getConfig() {
+ return config;
+ }
+
+ public void setConfig(Map<String, String> 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 0000000..9367d25
--- /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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @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<String> webOrigins = new HashSet<String>();
+ @ElementCollection
+ @CollectionTable
+ protected Set<String> redirectUris = new HashSet<String>();
+
+ 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<String> getWebOrigins() {
+ return webOrigins;
+ }
+
+ public void setWebOrigins(Set<String> webOrigins) {
+ this.webOrigins = webOrigins;
+ }
+
+ public Set<String> getRedirectUris() {
+ return redirectUris;
+ }
+
+ public void setRedirectUris(Set<String> 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 0000000..490e75d
--- /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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @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 0000000..6d792f8
--- /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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @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 0000000..dbdf080
--- /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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @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 0000000..8fd8a56
--- /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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @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<RequiredCredentialEntity> requiredCredentials = new ArrayList<RequiredCredentialEntity>();
+
+
+ @OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true)
+ @JoinTable(name="AuthProviders")
+ List<AuthenticationProviderEntity> authenticationProviders = new ArrayList<AuthenticationProviderEntity>();
+
+ @OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true)
+ Collection<ApplicationEntity> applications = new ArrayList<ApplicationEntity>();
+
+ @OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm")
+ Collection<RoleEntity> roles = new ArrayList<RoleEntity>();
+
+ @ElementCollection
+ @MapKeyColumn(name="name")
+ @Column(name="value")
+ @CollectionTable
+ protected Map<String, String> smtpConfig = new HashMap<String, String>();
+
+ @ElementCollection
+ @MapKeyColumn(name="name")
+ @Column(name="value")
+ @CollectionTable
+ protected Map<String, String> socialConfig = new HashMap<String, String>();
+
+ @ElementCollection
+ @MapKeyColumn(name="name")
+ @Column(name="value")
+ @CollectionTable
+ protected Map<String, String> ldapServerConfig = new HashMap<String, String>();
+
+ @OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true)
+ @JoinTable(name="RealmDefaultRoles")
+ protected Collection<RoleEntity> defaultRoles = new ArrayList<RoleEntity>();
+
+ protected boolean auditEnabled;
+ protected long auditExpiration;
+
+ @ElementCollection
+ protected Set<String> auditListeners= new HashSet<String>();
+
+ @OneToOne
+ protected ApplicationEntity masterAdminApp;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public boolean isSslNotRequired() {
+ return sslNotRequired;
+ }
+
+ public void setSslNotRequired(boolean sslNotRequired) {
+ this.sslNotRequired = sslNotRequired;
+ }
+
+ public boolean isPasswordCredentialGrantAllowed() {
+ return passwordCredentialGrantAllowed;
+ }
+
+ public void setPasswordCredentialGrantAllowed(boolean passwordCredentialGrantAllowed) {
+ this.passwordCredentialGrantAllowed = passwordCredentialGrantAllowed;
+ }
+
+ public boolean isRegistrationAllowed() {
+ return registrationAllowed;
+ }
+
+ public void setRegistrationAllowed(boolean registrationAllowed) {
+ this.registrationAllowed = registrationAllowed;
+ }
+
+ public boolean isRememberMe() {
+ return rememberMe;
+ }
+
+ public void setRememberMe(boolean rememberMe) {
+ this.rememberMe = rememberMe;
+ }
+
+ public boolean isVerifyEmail() {
+ return verifyEmail;
+ }
+
+ public void setVerifyEmail(boolean verifyEmail) {
+ this.verifyEmail = verifyEmail;
+ }
+
+ public boolean isResetPasswordAllowed() {
+ return resetPasswordAllowed;
+ }
+
+ public void setResetPasswordAllowed(boolean resetPasswordAllowed) {
+ this.resetPasswordAllowed = resetPasswordAllowed;
+ }
+
+ public boolean isSocial() {
+ return social;
+ }
+
+ public void setSocial(boolean social) {
+ this.social = social;
+ }
+
+ public boolean isUpdateProfileOnInitialSocialLogin() {
+ return updateProfileOnInitialSocialLogin;
+ }
+
+ public void setUpdateProfileOnInitialSocialLogin(boolean updateProfileOnInitialSocialLogin) {
+ this.updateProfileOnInitialSocialLogin = updateProfileOnInitialSocialLogin;
+ }
+
+ public int getSsoSessionIdleTimeout() {
+ return ssoSessionIdleTimeout;
+ }
+
+ public void setSsoSessionIdleTimeout(int ssoSessionIdleTimeout) {
+ this.ssoSessionIdleTimeout = ssoSessionIdleTimeout;
+ }
+
+ public int getSsoSessionMaxLifespan() {
+ return ssoSessionMaxLifespan;
+ }
+
+ public void setSsoSessionMaxLifespan(int ssoSessionMaxLifespan) {
+ this.ssoSessionMaxLifespan = ssoSessionMaxLifespan;
+ }
+
+ public int getAccessTokenLifespan() {
+ return accessTokenLifespan;
+ }
+
+ public void setAccessTokenLifespan(int accessTokenLifespan) {
+ this.accessTokenLifespan = accessTokenLifespan;
+ }
+
+ public int getAccessCodeLifespan() {
+ return accessCodeLifespan;
+ }
+
+ public void setAccessCodeLifespan(int accessCodeLifespan) {
+ this.accessCodeLifespan = accessCodeLifespan;
+ }
+
+ public int getAccessCodeLifespanUserAction() {
+ return accessCodeLifespanUserAction;
+ }
+
+ public void setAccessCodeLifespanUserAction(int accessCodeLifespanUserAction) {
+ this.accessCodeLifespanUserAction = accessCodeLifespanUserAction;
+ }
+
+ public String getPublicKeyPem() {
+ return publicKeyPem;
+ }
+
+ public void setPublicKeyPem(String publicKeyPem) {
+ this.publicKeyPem = publicKeyPem;
+ }
+
+ public String getPrivateKeyPem() {
+ return privateKeyPem;
+ }
+
+ public void setPrivateKeyPem(String privateKeyPem) {
+ this.privateKeyPem = privateKeyPem;
+ }
+
+ public Collection<RequiredCredentialEntity> getRequiredCredentials() {
+ return requiredCredentials;
+ }
+
+ public void setRequiredCredentials(Collection<RequiredCredentialEntity> requiredCredentials) {
+ this.requiredCredentials = requiredCredentials;
+ }
+
+ public List<AuthenticationProviderEntity> getAuthenticationProviders() {
+ return authenticationProviders;
+ }
+
+ public void setAuthenticationProviders(List<AuthenticationProviderEntity> authenticationProviders) {
+ this.authenticationProviders = authenticationProviders;
+ }
+
+ public Collection<ApplicationEntity> getApplications() {
+ return applications;
+ }
+
+ public void setApplications(Collection<ApplicationEntity> applications) {
+ this.applications = applications;
+ }
+
+ public Collection<RoleEntity> getRoles() {
+ return roles;
+ }
+
+ public void setRoles(Collection<RoleEntity> roles) {
+ this.roles = roles;
+ }
+
+ public void addRole(RoleEntity role) {
+ if (roles == null) {
+ roles = new ArrayList<RoleEntity>();
+ }
+ roles.add(role);
+ }
+
+ public Map<String, String> getSmtpConfig() {
+ return smtpConfig;
+ }
+
+ public void setSmtpConfig(Map<String, String> smtpConfig) {
+ this.smtpConfig = smtpConfig;
+ }
+
+ public Map<String, String> getSocialConfig() {
+ return socialConfig;
+ }
+
+ public void setSocialConfig(Map<String, String> socialConfig) {
+ this.socialConfig = socialConfig;
+ }
+
+ public Map<String, String> getLdapServerConfig() {
+ return ldapServerConfig;
+ }
+
+ public void setLdapServerConfig(Map<String, String> ldapServerConfig) {
+ this.ldapServerConfig = ldapServerConfig;
+ }
+
+ public Collection<RoleEntity> getDefaultRoles() {
+ return defaultRoles;
+ }
+
+ public void setDefaultRoles(Collection<RoleEntity> defaultRoles) {
+ this.defaultRoles = defaultRoles;
+ }
+
+ public String getPasswordPolicy() {
+ return passwordPolicy;
+ }
+
+ public void setPasswordPolicy(String passwordPolicy) {
+ this.passwordPolicy = passwordPolicy;
+ }
+
+ public String getLoginTheme() {
+ return loginTheme;
+ }
+
+ public void setLoginTheme(String theme) {
+ this.loginTheme = theme;
+ }
+
+ public String getAccountTheme() {
+ return accountTheme;
+ }
+
+ public void setAccountTheme(String theme) {
+ this.accountTheme = theme;
+ }
+
+ public String getAdminTheme() {
+ return adminTheme;
+ }
+
+ public void setAdminTheme(String adminTheme) {
+ this.adminTheme = adminTheme;
+ }
+
+ public String getEmailTheme() {
+ return emailTheme;
+ }
+
+ public void setEmailTheme(String emailTheme) {
+ this.emailTheme = emailTheme;
+ }
+
+ public int getNotBefore() {
+ return notBefore;
+ }
+
+ public void setNotBefore(int notBefore) {
+ this.notBefore = notBefore;
+ }
+
+ public boolean isBruteForceProtected() {
+ return bruteForceProtected;
+ }
+
+ public void setBruteForceProtected(boolean bruteForceProtected) {
+ this.bruteForceProtected = bruteForceProtected;
+ }
+
+ public int getMaxFailureWaitSeconds() {
+ return maxFailureWaitSeconds;
+ }
+
+ public void setMaxFailureWaitSeconds(int maxFailureWaitSeconds) {
+ this.maxFailureWaitSeconds = maxFailureWaitSeconds;
+ }
+
+ public int getMinimumQuickLoginWaitSeconds() {
+ return minimumQuickLoginWaitSeconds;
+ }
+
+ public void setMinimumQuickLoginWaitSeconds(int minimumQuickLoginWaitSeconds) {
+ this.minimumQuickLoginWaitSeconds = minimumQuickLoginWaitSeconds;
+ }
+
+ public int getWaitIncrementSeconds() {
+ return waitIncrementSeconds;
+ }
+
+ public void setWaitIncrementSeconds(int waitIncrementSeconds) {
+ this.waitIncrementSeconds = waitIncrementSeconds;
+ }
+
+ public long getQuickLoginCheckMilliSeconds() {
+ return quickLoginCheckMilliSeconds;
+ }
+
+ public void setQuickLoginCheckMilliSeconds(long quickLoginCheckMilliSeconds) {
+ this.quickLoginCheckMilliSeconds = quickLoginCheckMilliSeconds;
+ }
+
+ public int getMaxDeltaTimeSeconds() {
+ return maxDeltaTimeSeconds;
+ }
+
+ public void setMaxDeltaTimeSeconds(int maxDeltaTimeSeconds) {
+ this.maxDeltaTimeSeconds = maxDeltaTimeSeconds;
+ }
+
+ public int getFailureFactor() {
+ return failureFactor;
+ }
+
+ public void setFailureFactor(int failureFactor) {
+ this.failureFactor = failureFactor;
+ }
+
+ public boolean isAuditEnabled() {
+ return auditEnabled;
+ }
+
+ public void setAuditEnabled(boolean auditEnabled) {
+ this.auditEnabled = auditEnabled;
+ }
+
+ public long getAuditExpiration() {
+ return auditExpiration;
+ }
+
+ public void setAuditExpiration(long auditExpiration) {
+ this.auditExpiration = auditExpiration;
+ }
+
+ public Set<String> getAuditListeners() {
+ return auditListeners;
+ }
+
+ public void setAuditListeners(Set<String> auditListeners) {
+ this.auditListeners = auditListeners;
+ }
+
+ public ApplicationEntity getMasterAdminApp() {
+ return masterAdminApp;
+ }
+
+ public void setMasterAdminApp(ApplicationEntity masterAdminApp) {
+ this.masterAdminApp = masterAdminApp;
+ }
+
+}
+
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RequiredCredentialEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RequiredCredentialEntity.java
new file mode 100755
index 0000000..9487022
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RequiredCredentialEntity.java
@@ -0,0 +1,64 @@
+package org.keycloak.models.jpa.entities;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+
+import org.hibernate.annotations.GenericGenerator;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@Entity
+public class RequiredCredentialEntity {
+ @Id
+ @GenericGenerator(name="keycloak_generator", strategy="org.keycloak.models.jpa.utils.JpaIdGenerator")
+ @GeneratedValue(generator = "keycloak_generator")
+ protected String id;
+
+ protected String type;
+ protected boolean input;
+ protected boolean secret;
+ protected String formLabel;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public boolean isInput() {
+ return input;
+ }
+
+ public void setInput(boolean input) {
+ this.input = input;
+ }
+
+ public boolean isSecret() {
+ return secret;
+ }
+
+ public void setSecret(boolean secret) {
+ this.secret = secret;
+ }
+
+ public String getFormLabel() {
+ return formLabel;
+ }
+
+ public void setFormLabel(String formLabel) {
+ this.formLabel = formLabel;
+ }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RoleEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RoleEntity.java
new file mode 100755
index 0000000..d3fc6df
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RoleEntity.java
@@ -0,0 +1,157 @@
+package org.keycloak.models.jpa.entities;
+
+import javax.persistence.Column;
+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.JoinTable;
+import javax.persistence.ManyToMany;
+import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.hibernate.annotations.GenericGenerator;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@Entity
+@Table(uniqueConstraints = {
+ @UniqueConstraint(columnNames = { "name", "appRealmConstraint" })
+})
+@NamedQueries({
+ @NamedQuery(name="getAppRoleByName", query="select role from RoleEntity role where role.name = :name and role.application = :application"),
+ @NamedQuery(name="getRealmRoleByName", query="select role from RoleEntity role where role.applicationRole = false and role.name = :name and role.realm = :realm")
+})
+
+public class RoleEntity {
+ @Id
+ @Column(name="id")
+ private String id;
+
+ private String name;
+ private String description;
+
+ // hax! couldn't get constraint to work properly
+ private String realmId;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "realm")
+ private RealmEntity realm;
+
+ @Column(name="applicationRole")
+ private boolean applicationRole;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "application")
+ private ApplicationEntity application;
+
+ // Hack to ensure that either name+application or name+realm are unique. Needed due to MS-SQL as it don't allow multiple NULL values in the column, which is part of constraint
+ private String appRealmConstraint;
+
+ @ManyToMany(fetch = FetchType.LAZY, cascade = {})
+ @JoinTable(name = "CompositeRole", joinColumns = @JoinColumn(name = "composite"), inverseJoinColumns = @JoinColumn(name = "childRole"))
+ private Collection<RoleEntity> compositeRoles = new ArrayList<RoleEntity>();
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getRealmId() {
+ return realmId;
+ }
+
+ public void setRealmId(String realmId) {
+ this.realmId = realmId;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public Collection<RoleEntity> getCompositeRoles() {
+ return compositeRoles;
+ }
+
+ public void setCompositeRoles(Collection<RoleEntity> compositeRoles) {
+ this.compositeRoles = compositeRoles;
+ }
+
+ public boolean isApplicationRole() {
+ return applicationRole;
+ }
+
+ public void setApplicationRole(boolean applicationRole) {
+ this.applicationRole = applicationRole;
+ }
+
+ public RealmEntity getRealm() {
+ return realm;
+ }
+
+ public void setRealm(RealmEntity realm) {
+ this.realm = realm;
+ this.appRealmConstraint = realm.getId();
+ }
+
+ public ApplicationEntity getApplication() {
+ return application;
+ }
+
+ public void setApplication(ApplicationEntity application) {
+ this.application = application;
+ if (application != null) {
+ this.appRealmConstraint = application.getId();
+ }
+ }
+
+ public String getAppRealmConstraint() {
+ return appRealmConstraint;
+ }
+
+ public void setAppRealmConstraint(String appRealmConstraint) {
+ this.appRealmConstraint = appRealmConstraint;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ RoleEntity that = (RoleEntity) o;
+
+ if (!id.equals(that.id)) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return id.hashCode();
+ }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ScopeMappingEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ScopeMappingEntity.java
new file mode 100755
index 0000000..b45b69b
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ScopeMappingEntity.java
@@ -0,0 +1,60 @@
+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.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@NamedQueries({
+ @NamedQuery(name="hasScope", query="select m from ScopeMappingEntity m where m.client = :client and m.role = :role"),
+ @NamedQuery(name="clientScopeMappings", query="select m from ScopeMappingEntity m where m.client = :client"),
+ @NamedQuery(name="clientScopeMappingIds", query="select m.role.id from ScopeMappingEntity m where m.client = :client")
+})
+@Entity
+public class ScopeMappingEntity {
+ @Id
+ @GenericGenerator(name="keycloak_generator", strategy="org.keycloak.models.jpa.utils.JpaIdGenerator")
+ @GeneratedValue(generator = "keycloak_generator")
+ protected String id;
+ @ManyToOne(fetch= FetchType.LAZY)
+ protected ClientEntity client;
+
+ @ManyToOne(fetch= FetchType.LAZY)
+ @JoinColumn(name="roleId")
+ protected RoleEntity role;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public ClientEntity getClient() {
+ return client;
+ }
+
+ public void setClient(ClientEntity client) {
+ this.client = client;
+ }
+
+ 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/SocialLinkEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/SocialLinkEntity.java
new file mode 100755
index 0000000..a1de3af
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/SocialLinkEntity.java
@@ -0,0 +1,86 @@
+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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@NamedQueries({
+ @NamedQuery(name="findSocialLinkByUser", query="select link from SocialLinkEntity link where link.user = :user"),
+ @NamedQuery(name="findSocialLinkByUserAndProvider", query="select link from SocialLinkEntity link where link.user = :user and link.socialProvider = :socialProvider"),
+ @NamedQuery(name="findUserByLinkAndRealm", query="select link.user from SocialLinkEntity link where link.realm = :realm and link.socialProvider = :socialProvider and link.socialUserId = :socialUserId")
+})
+@Entity
+public class SocialLinkEntity {
+ @Id
+ @GenericGenerator(name="keycloak_generator", strategy="org.keycloak.models.jpa.utils.JpaIdGenerator")
+ @GeneratedValue(generator = "keycloak_generator")
+ private String id;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ private UserEntity user;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ protected RealmEntity realm;
+
+ protected String socialProvider;
+ protected String socialUserId;
+ protected String socialUsername;
+
+ 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 String getSocialProvider() {
+ return socialProvider;
+ }
+
+ public void setSocialProvider(String socialProvider) {
+ this.socialProvider = socialProvider;
+ }
+
+ public String getSocialUserId() {
+ return socialUserId;
+ }
+
+ public void setSocialUserId(String socialUserId) {
+ this.socialUserId = socialUserId;
+ }
+
+ public String getSocialUsername() {
+ return socialUsername;
+ }
+
+ public void setSocialUsername(String socialUsername) {
+ this.socialUsername = socialUsername;
+ }
+
+ public RealmEntity getRealm() {
+ return realm;
+ }
+
+ public void setRealm(RealmEntity realm) {
+ this.realm = realm;
+ }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserEntity.java
new file mode 100755
index 0000000..75389c9
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserEntity.java
@@ -0,0 +1,196 @@
+package org.keycloak.models.jpa.entities;
+
+import org.hibernate.annotations.GenericGenerator;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
+
+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.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.MapKeyColumn;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+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.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@NamedQueries({
+ @NamedQuery(name="getRealmUserById", query="select u from UserEntity u where u.id = :id and u.realm = :realm"),
+ @NamedQuery(name="getRealmUserByUsername", query="select u from UserEntity u where u.username = :username and u.realm = :realm"),
+ @NamedQuery(name="getRealmUserByEmail", query="select u from UserEntity u where u.email = :email and u.realm = :realm"),
+ @NamedQuery(name="getRealmUserByLastName", query="select u from UserEntity u where u.lastName = :lastName and u.realm = :realm"),
+ @NamedQuery(name="getRealmUserByFirstLastName", query="select u from UserEntity u where u.firstName = :first and u.lastName = :last and u.realm = :realm")
+})
+@Entity
+@Table(uniqueConstraints = {
+ @UniqueConstraint(columnNames = { "realm", "username" }),
+ @UniqueConstraint(columnNames = { "realm", "emailConstraint" })
+})
+public class UserEntity {
+ @Id
+ protected String id;
+
+ protected String username;
+ protected String firstName;
+ protected String lastName;
+ protected String email;
+ protected boolean enabled;
+ protected boolean totp;
+ protected boolean emailVerified;
+
+ // Hack just to workaround the fact that on MS-SQL you can't have unique constraint with multiple NULL values TODO: Find better solution (like unique index with 'where' but that's proprietary)
+ protected String emailConstraint = KeycloakModelUtils.generateId();
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "realm")
+ protected RealmEntity realm;
+
+ @ElementCollection
+ @MapKeyColumn(name="name")
+ @Column(name="value")
+ @CollectionTable
+ protected Map<String, String> attributes = new HashMap<String, String>();
+
+ @ElementCollection
+ @CollectionTable
+ protected Set<UserModel.RequiredAction> requiredActions = new HashSet<UserModel.RequiredAction>();
+
+ @OneToMany(cascade = CascadeType.REMOVE, orphanRemoval = true)
+ protected Collection<CredentialEntity> credentials = new ArrayList<CredentialEntity>();
+
+ @OneToOne(cascade = CascadeType.REMOVE, orphanRemoval = true)
+ protected AuthenticationLinkEntity authenticationLink;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getFirstName() {
+ return firstName;
+ }
+
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
+
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ this.emailConstraint = email != null ? email : KeycloakModelUtils.generateId();
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public String getEmailConstraint() {
+ return emailConstraint;
+ }
+
+ public void setEmailConstraint(String emailConstraint) {
+ this.emailConstraint = emailConstraint;
+ }
+
+ public boolean isTotp() {
+ return totp;
+ }
+
+ public void setTotp(boolean totp) {
+ this.totp = totp;
+ }
+
+ public boolean isEmailVerified() {
+ return emailVerified;
+ }
+
+ public void setEmailVerified(boolean emailVerified) {
+ this.emailVerified = emailVerified;
+ }
+
+ public Map<String, String> getAttributes() {
+ return attributes;
+ }
+
+ public void setAttributes(Map<String, String> attributes) {
+ this.attributes = attributes;
+ }
+
+ public Set<UserModel.RequiredAction> getRequiredActions() {
+ return requiredActions;
+ }
+
+ public void setRequiredActions(Set<UserModel.RequiredAction> requiredActions) {
+ this.requiredActions = requiredActions;
+ }
+
+ public RealmEntity getRealm() {
+ return realm;
+ }
+
+ public void setRealm(RealmEntity realm) {
+ this.realm = realm;
+ }
+
+ public Collection<CredentialEntity> getCredentials() {
+ return credentials;
+ }
+
+ public void setCredentials(Collection<CredentialEntity> credentials) {
+ this.credentials = credentials;
+ }
+
+ public AuthenticationLinkEntity getAuthenticationLink() {
+ return authenticationLink;
+ }
+
+ public void setAuthenticationLink(AuthenticationLinkEntity authenticationLink) {
+ this.authenticationLink = authenticationLink;
+ }
+
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UsernameLoginFailureEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UsernameLoginFailureEntity.java
new file mode 100755
index 0000000..f3cffb4
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UsernameLoginFailureEntity.java
@@ -0,0 +1,88 @@
+package org.keycloak.models.jpa.entities;
+
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Id;
+import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@Entity
+@NamedQueries({
+ @NamedQuery(name="getAllFailures", query="select failure from UsernameLoginFailureEntity failure"),
+})
+public class UsernameLoginFailureEntity {
+ // we manually set the id to be username-realmid
+ // we may have a concurrent creation of the same login failure entry that we want to avoid
+ @Id
+ protected String id;
+ protected String username;
+ protected int failedLoginNotBefore;
+ protected int numFailures;
+ protected long lastFailure;
+ protected String lastIPFailure;
+
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ protected RealmEntity realm;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public int getFailedLoginNotBefore() {
+ return failedLoginNotBefore;
+ }
+
+ public void setFailedLoginNotBefore(int failedLoginNotBefore) {
+ this.failedLoginNotBefore = failedLoginNotBefore;
+ }
+
+ public int getNumFailures() {
+ return numFailures;
+ }
+
+ public void setNumFailures(int numFailures) {
+ this.numFailures = numFailures;
+ }
+
+ public long getLastFailure() {
+ return lastFailure;
+ }
+
+ public void setLastFailure(long lastFailure) {
+ this.lastFailure = lastFailure;
+ }
+
+ public String getLastIPFailure() {
+ return lastIPFailure;
+ }
+
+ public void setLastIPFailure(String lastIPFailure) {
+ this.lastIPFailure = lastIPFailure;
+ }
+
+ public RealmEntity getRealm() {
+ return realm;
+ }
+
+ public void setRealm(RealmEntity realm) {
+ this.realm = realm;
+ }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserRoleMappingEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserRoleMappingEntity.java
new file mode 100755
index 0000000..647acea
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserRoleMappingEntity.java
@@ -0,0 +1,19 @@
+package org.keycloak.models.jpa.entities;
+
+import javax.persistence.Entity;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@NamedQueries({
+ @NamedQuery(name="userHasRole", query="select m from UserRoleMappingEntity m where m.user = :user and m.role = :role"),
+ @NamedQuery(name="userRoleMappings", query="select m from UserRoleMappingEntity m where m.user = :user"),
+ @NamedQuery(name="userRoleMappingIds", query="select m.role.id from UserRoleMappingEntity m where m.user = :user")
+})
+@Entity
+public class UserRoleMappingEntity extends AbstractRoleMappingEntity {
+
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserSessionEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserSessionEntity.java
new file mode 100755
index 0000000..4be2853
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserSessionEntity.java
@@ -0,0 +1,107 @@
+package org.keycloak.models.jpa.entities;
+
+import org.hibernate.annotations.GenericGenerator;
+import org.keycloak.models.UserModel;
+
+import javax.persistence.CascadeType;
+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.OneToMany;
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+@Entity
+@NamedQueries({
+ @NamedQuery(name = "getUserSessionByUser", query = "select s from UserSessionEntity s where s.userId = :userId"),
+ @NamedQuery(name = "removeRealmUserSessions", query = "delete from UserSessionEntity s where s.realmId = :realmId"),
+ @NamedQuery(name = "removeUserSessionByUser", query = "delete from UserSessionEntity s where s.userId = :userId"),
+ @NamedQuery(name = "getUserSessionExpired", query = "select s from UserSessionEntity s where s.started < :maxTime or s.lastSessionRefresh < :idleTime"),
+ @NamedQuery(name = "removeUserSessionExpired", query = "delete from UserSessionEntity s where s.started < :maxTime or s.lastSessionRefresh < :idleTime")
+})
+public class UserSessionEntity {
+
+ @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;
+
+ private String ipAddress;
+
+ private int started;
+
+ private int lastSessionRefresh;
+
+ @OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy="session")
+ private Collection<ClientUserSessionAssociationEntity> clients = new ArrayList<ClientUserSessionAssociationEntity>();
+
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+
+ 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;
+ }
+
+ public String getIpAddress() {
+ return ipAddress;
+ }
+
+ public void setIpAddress(String ipAddress) {
+ this.ipAddress = ipAddress;
+ }
+
+ public int getStarted() {
+ return started;
+ }
+
+ public void setStarted(int started) {
+ this.started = started;
+ }
+
+ public int getLastSessionRefresh() {
+ return lastSessionRefresh;
+ }
+
+ public void setLastSessionRefresh(int lastSessionRefresh) {
+ this.lastSessionRefresh = lastSessionRefresh;
+ }
+
+ public Collection<ClientUserSessionAssociationEntity> getClients() {
+ return clients;
+ }
+
+ public void setClients(Collection<ClientUserSessionAssociationEntity> clients) {
+ this.clients = clients;
+ }
+
+}
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 0000000..095b341
--- /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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @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 0000000..940e067
--- /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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @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<RealmModel> getRealms() {
+ TypedQuery<RealmEntity> query = em.createNamedQuery("getAllRealms", RealmEntity.class);
+ List<RealmEntity> entities = query.getResultList();
+ List<RealmModel> realms = new ArrayList<RealmModel>();
+ for (RealmEntity entity : entities) {
+ realms.add(new RealmAdapter(session, em, entity));
+ }
+ return realms;
+ }
+
+ @Override
+ public RealmModel getRealmByName(String name) {
+ TypedQuery<RealmEntity> query = em.createNamedQuery("getRealmByName", RealmEntity.class);
+ query.setParameter("name", name);
+ List<RealmEntity> 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<UserEntity> query = em.createNamedQuery("getRealmUserById", UserEntity.class);
+ query.setParameter("id", id);
+ RealmEntity realm = em.getReference(RealmEntity.class, realmModel.getId());
+ query.setParameter("realm", realm);
+ List<UserEntity> 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<UserEntity> query = em.createNamedQuery("getRealmUserByUsername", UserEntity.class);
+ query.setParameter("username", username);
+ RealmEntity realm = em.getReference(RealmEntity.class, realmModel.getId());
+ query.setParameter("realm", realm);
+ List<UserEntity> 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<UserEntity> query = em.createNamedQuery("getRealmUserByEmail", UserEntity.class);
+ query.setParameter("email", email);
+ RealmEntity realm = em.getReference(RealmEntity.class, realmModel.getId());
+ query.setParameter("realm", realm);
+ List<UserEntity> 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<ApplicationEntity>(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<RealmModel> realms = getRealms();
+ for (RealmModel realm : realms) {
+ removeRealm(realm.getId());
+ }
+ }
+
+ @Override
+ public UserModel getUserBySocialLink(SocialLinkModel socialLink, RealmModel realm) {
+ TypedQuery<UserEntity> 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<UserEntity> 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<UserModel> getUsers(RealmModel realm) {
+ TypedQuery<UserEntity> 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<UserEntity> results = query.getResultList();
+ List<UserModel> users = new ArrayList<UserModel>();
+ for (UserEntity entity : results) users.add(new UserAdapter(realm, em, entity));
+ return users;
+ }
+
+ @Override
+ public List<UserModel> searchForUser(String search, RealmModel realm) {
+ TypedQuery<UserEntity> 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<UserEntity> results = query.getResultList();
+ List<UserModel> users = new ArrayList<UserModel>();
+ for (UserEntity entity : results) users.add(new UserAdapter(realm, em, entity));
+ return users;
+ }
+
+ @Override
+ public List<UserModel> searchForUserByAttributes(Map<String, String> attributes, RealmModel realm) {
+ StringBuilder builder = new StringBuilder("select u from UserEntity u");
+ boolean first = true;
+ for (Map.Entry<String, String> 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<UserEntity> query = em.createQuery(q, UserEntity.class);
+ RealmEntity realmEntity = em.getReference(RealmEntity.class, realm.getId());
+ query.setParameter("realm", realmEntity);
+ List<UserEntity> results = query.getResultList();
+ List<UserModel> users = new ArrayList<UserModel>();
+ for (UserEntity entity : results) users.add(new UserAdapter(realm, em, entity));
+ return users;
+ }
+
+ private SocialLinkEntity findSocialLink(UserModel user, String socialProvider) {
+ TypedQuery<SocialLinkEntity> query = em.createNamedQuery("findSocialLinkByUserAndProvider", SocialLinkEntity.class);
+ UserEntity userEntity = em.getReference(UserEntity.class, user.getId());
+ query.setParameter("user", userEntity);
+ query.setParameter("socialProvider", socialProvider);
+ List<SocialLinkEntity> results = query.getResultList();
+ return results.size() > 0 ? results.get(0) : null;
+ }
+
+
+ @Override
+ public Set<SocialLinkModel> getSocialLinks(UserModel user, RealmModel realm) {
+ TypedQuery<SocialLinkEntity> query = em.createNamedQuery("findSocialLinkByUser", SocialLinkEntity.class);
+ UserEntity userEntity = em.getReference(UserEntity.class, user.getId());
+ query.setParameter("user", userEntity);
+ List<SocialLinkEntity> results = query.getResultList();
+ Set<SocialLinkModel> set = new HashSet<SocialLinkModel>();
+ 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<UsernameLoginFailureModel> getAllUserLoginFailures(RealmModel realm) {
+ TypedQuery<UsernameLoginFailureEntity> query = em.createNamedQuery("getAllFailures", UsernameLoginFailureEntity.class);
+ List<UsernameLoginFailureEntity> entities = query.getResultList();
+ List<UsernameLoginFailureModel> models = new ArrayList<UsernameLoginFailureModel>();
+ 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<UserSessionModel> getUserSessions(UserModel user, RealmModel realm) {
+ List<UserSessionModel> sessions = new LinkedList<UserSessionModel>();
+ 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<UserSessionModel> getUserSessions(RealmModel realm, ClientModel client) {
+ Set<UserSessionModel> list = new HashSet<UserSessionModel>();
+ TypedQuery<ClientUserSessionAssociationEntity> query = em.createNamedQuery("getClientUserSessionByClient", ClientUserSessionAssociationEntity.class);
+ String id = client.getId();
+ query.setParameter("clientId", id);
+ List<ClientUserSessionAssociationEntity> 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<UserSessionEntity> query = em.createNamedQuery("getUserSessionExpired", UserSessionEntity.class)
+ .setParameter("maxTime", Time.currentTime() - realm.getSsoSessionMaxLifespan())
+ .setParameter("idleTime", Time.currentTime() - realm.getSsoSessionIdleTimeout());
+ List<UserSessionEntity> 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 0000000..ad4e6dc
--- /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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @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 0000000..c890c9f
--- /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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @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 0000000..dfa5053
--- /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 <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+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 0000000..23602ec
--- /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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @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<String> creds) {
+ Collection<RequiredCredentialEntity> relationships = realm.getRequiredCredentials();
+ if (relationships == null) relationships = new ArrayList<RequiredCredentialEntity>();
+
+ Set<String> already = new HashSet<String>();
+ List<RequiredCredentialEntity> remove = new ArrayList<RequiredCredentialEntity>();
+ 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<RequiredCredentialModel> getRequiredCredentials() {
+ List<RequiredCredentialModel> requiredCredentialModels = new ArrayList<RequiredCredentialModel>();
+ Collection<RequiredCredentialEntity> 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<UsernameLoginFailureModel> 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<UserEntity> query = em.createNamedQuery("getRealmUserByUsername", UserEntity.class);
+ query.setParameter("username", name);
+ query.setParameter("realm", realm);
+ List<UserEntity> 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<String> getDefaultRoles() {
+ Collection<RoleEntity> entities = realm.getDefaultRoles();
+ List<String> roles = new ArrayList<String>();
+ 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<RoleEntity> 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<RoleEntity> entities = realm.getDefaultRoles();
+ Set<String> already = new HashSet<String>();
+ List<RoleEntity> remove = new ArrayList<RoleEntity>();
+ 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<String, ApplicationModel> getApplicationNameMap() {
+ Map<String, ApplicationModel> map = new HashMap<String, ApplicationModel>();
+ 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<ApplicationModel> getApplications() {
+ List<ApplicationModel> list = new ArrayList<ApplicationModel>();
+ 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<ApplicationEntity> 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<SocialLinkModel> 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<SocialLinkEntity> query = em.createNamedQuery("findSocialLinkByUserAndProvider", SocialLinkEntity.class);
+ UserEntity userEntity = em.getReference(UserEntity.class, user.getId());
+ query.setParameter("user", userEntity);
+ query.setParameter("socialProvider", socialProvider);
+ List<SocialLinkEntity> 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<UserModel> getUsers() {
+ return session.getUsers(this);
+ }
+
+ @Override
+ public List<UserModel> searchForUser(String search) {
+ return session.searchForUser(search, this);
+ }
+
+ @Override
+ public List<UserModel> searchForUserByAttributes(Map<String, String> 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<OAuthClientEntity> query = em.createNamedQuery("findOAuthClientByName", OAuthClientEntity.class);
+ query.setParameter("name", name);
+ query.setParameter("realm", realm);
+ List<OAuthClientEntity> 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<OAuthClientModel> getOAuthClients() {
+ TypedQuery<OAuthClientEntity> query = em.createNamedQuery("findOAuthClientByRealm", OAuthClientEntity.class);
+ query.setParameter("realm", realm);
+ List<OAuthClientEntity> entities = query.getResultList();
+ List<OAuthClientModel> list = new ArrayList<OAuthClientModel>();
+ for (OAuthClientEntity entity : entities) list.add(new OAuthClientAdapter(this, entity, em));
+ return list;
+ }
+
+ @Override
+ public Map<String, String> getSmtpConfig() {
+ return realm.getSmtpConfig();
+ }
+
+ @Override
+ public void setSmtpConfig(Map<String, String> smtpConfig) {
+ realm.setSmtpConfig(smtpConfig);
+ em.flush();
+ }
+
+ @Override
+ public Map<String, String> getSocialConfig() {
+ return realm.getSocialConfig();
+ }
+
+ @Override
+ public void setSocialConfig(Map<String, String> socialConfig) {
+ realm.setSocialConfig(socialConfig);
+ em.flush();
+ }
+
+ @Override
+ public Map<String, String> getLdapServerConfig() {
+ return realm.getLdapServerConfig();
+ }
+
+ @Override
+ public void setLdapServerConfig(Map<String, String> ldapServerConfig) {
+ realm.setLdapServerConfig(ldapServerConfig);
+ em.flush();
+ }
+
+ @Override
+ public List<AuthenticationProviderModel> getAuthenticationProviders() {
+ List<AuthenticationProviderEntity> entities = realm.getAuthenticationProviders();
+ List<AuthenticationProviderEntity> copy = new ArrayList<AuthenticationProviderEntity>();
+ for (AuthenticationProviderEntity entity : entities) {
+ copy.add(entity);
+
+ }
+ Collections.sort(copy, new Comparator<AuthenticationProviderEntity>() {
+
+ @Override
+ public int compare(AuthenticationProviderEntity o1, AuthenticationProviderEntity o2) {
+ return o1.getPriority() - o2.getPriority();
+ }
+
+ });
+ List<AuthenticationProviderModel> result = new ArrayList<AuthenticationProviderModel>();
+ for (AuthenticationProviderEntity entity : copy) {
+ result.add(new AuthenticationProviderModel(entity.getProviderName(), entity.isPasswordUpdateSupported(), entity.getConfig()));
+ }
+
+ return result;
+ }
+
+ @Override
+ public void setAuthenticationProviders(List<AuthenticationProviderModel> authenticationProviders) {
+ List<AuthenticationProviderEntity> newEntities = new ArrayList<AuthenticationProviderEntity>();
+ 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<AuthenticationProviderEntity> existing = realm.getAuthenticationProviders();
+ Collection<AuthenticationProviderEntity> copy = new ArrayList<AuthenticationProviderEntity>(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<RoleEntity> query = em.createNamedQuery("getRealmRoleByName", RoleEntity.class);
+ query.setParameter("name", name);
+ query.setParameter("realm", realm);
+ List<RoleEntity> 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<RoleModel> getRoles() {
+ Set<RoleModel> list = new HashSet<RoleModel>();
+ Collection<RoleEntity> 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<String> getAuditListeners() {
+ return realm.getAuditListeners();
+ }
+
+ @Override
+ public void setAuditListeners(Set<String> 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<UserSessionModel> 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 0000000..28a1f47
--- /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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @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<RoleEntity> it = getRole().getCompositeRoles().iterator();
+ while (it.hasNext()) {
+ if (it.next().equals(entity)) it.remove();
+ }
+ }
+
+ @Override
+ public Set<RoleModel> getComposites() {
+ Set<RoleModel> set = new HashSet<RoleModel>();
+
+ 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<RoleModel> visited = new HashSet<RoleModel>();
+ 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 0000000..18219f6
--- /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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @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<String, String> attributes = user.getAttributes();
+ if (attributes == null) {
+ attributes = new HashMap<String, String>();
+ }
+ attributes.put(name, value);
+ user.setAttributes(attributes);
+ }
+
+ @Override
+ public void removeAttribute(String name) {
+ Map<String, String> attributes = user.getAttributes();
+ if (attributes == null) {
+ attributes = new HashMap<String, String>();
+ }
+ 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<String, String> getAttributes() {
+ Map<String, String> result = new HashMap<String, String>();
+ result.putAll(user.getAttributes());
+ return result;
+ }
+
+ @Override
+ public Set<RequiredAction> getRequiredActions() {
+ Set<RequiredAction> result = new HashSet<RequiredAction>();
+ 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<UserCredentialValueModel> getCredentialsDirectly() {
+ List<CredentialEntity> credentials = new ArrayList<CredentialEntity>(user.getCredentials());
+ List<UserCredentialValueModel> result = new ArrayList<UserCredentialValueModel>();
+
+ 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<RoleModel> roles = getRoleMappings();
+ if (roles.contains(role)) return true;
+
+ for (RoleModel mapping : roles) {
+ if (mapping.hasRole(role)) return true;
+ }
+ return false;
+ }
+
+ protected TypedQuery<UserRoleMappingEntity> getUserRoleMappingEntityTypedQuery(RoleModel role) {
+ TypedQuery<UserRoleMappingEntity> 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<RoleModel> getRealmRoleMappings() {
+ Set<RoleModel> roleMappings = getRoleMappings();
+
+ Set<RoleModel> realmRoles = new HashSet<RoleModel>();
+ for (RoleModel role : roleMappings) {
+ RoleContainerModel container = role.getContainer();
+ if (container instanceof RealmModel) {
+ realmRoles.add(role);
+ }
+ }
+ return realmRoles;
+ }
+
+
+ @Override
+ public Set<RoleModel> 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<String> query = em.createNamedQuery("userRoleMappingIds", String.class);
+ query.setParameter("user", getUser());
+ List<String> ids = query.getResultList();
+ Set<RoleModel> roles = new HashSet<RoleModel>();
+ 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<UserRoleMappingEntity> query = getUserRoleMappingEntityTypedQuery(role);
+ List<UserRoleMappingEntity> results = query.getResultList();
+ if (results.size() == 0) return;
+ for (UserRoleMappingEntity entity : results) {
+ em.remove(entity);
+ }
+ em.flush();
+ }
+
+ @Override
+ public Set<RoleModel> getApplicationRoleMappings(ApplicationModel app) {
+ Set<RoleModel> roleMappings = getRoleMappings();
+
+ Set<RoleModel> roles = new HashSet<RoleModel>();
+ 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/UsernameLoginFailureAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/UsernameLoginFailureAdapter.java
new file mode 100755
index 0000000..ba43c82
--- /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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @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/UserSessionAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/UserSessionAdapter.java
new file mode 100755
index 0000000..333f7d1
--- /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 <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+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<ClientModel> getClientAssociations() {
+ List<ClientModel> clients = new ArrayList<ClientModel>();
+ 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/utils/JpaIdGenerator.java b/model/jpa/src/main/java/org/keycloak/models/jpa/utils/JpaIdGenerator.java
new file mode 100644
index 0000000..6fc10e8
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/utils/JpaIdGenerator.java
@@ -0,0 +1,19 @@
+package org.keycloak.models.jpa.utils;
+
+import java.io.Serializable;
+
+import org.hibernate.HibernateException;
+import org.hibernate.engine.spi.SessionImplementor;
+import org.hibernate.id.IdentifierGenerator;
+import org.keycloak.models.utils.KeycloakModelUtils;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class JpaIdGenerator implements IdentifierGenerator {
+
+ @Override
+ public Serializable generate(SessionImplementor session, Object object) throws HibernateException {
+ return KeycloakModelUtils.generateId();
+ }
+}
diff --git a/model/jpa/src/main/resources/META-INF/services/org.keycloak.models.ModelProviderFactory b/model/jpa/src/main/resources/META-INF/services/org.keycloak.models.ModelProviderFactory
new file mode 100644
index 0000000..d981d8c
--- /dev/null
+++ b/model/jpa/src/main/resources/META-INF/services/org.keycloak.models.ModelProviderFactory
@@ -0,0 +1 @@
+org.keycloak.models.jpa.JpaModelProviderFactory
\ No newline at end of file
diff --git a/model/jpa/src/test/resources/META-INF/persistence.xml b/model/jpa/src/test/resources/META-INF/persistence.xml
new file mode 100755
index 0000000..a150d05
--- /dev/null
+++ b/model/jpa/src/test/resources/META-INF/persistence.xml
@@ -0,0 +1,70 @@
+<persistence xmlns="http://java.sun.com/xml/ns/persistence"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
+ version="1.0">
+ <persistence-unit name="jpa-keycloak-identity-store" transaction-type="RESOURCE_LOCAL">
+ <provider>org.hibernate.ejb.HibernatePersistence</provider>
+
+ <class>org.keycloak.models.jpa.entities.ApplicationEntity</class>
+ <class>org.keycloak.models.jpa.entities.CredentialEntity</class>
+ <class>org.keycloak.models.jpa.entities.OAuthClientEntity</class>
+ <class>org.keycloak.models.jpa.entities.RealmEntity</class>
+ <class>org.keycloak.models.jpa.entities.RequiredCredentialEntity</class>
+ <class>org.keycloak.models.jpa.entities.AuthenticationProviderEntity</class>
+ <class>org.keycloak.models.jpa.entities.RoleEntity</class>
+ <class>org.keycloak.models.jpa.entities.SocialLinkEntity</class>
+ <class>org.keycloak.models.jpa.entities.AuthenticationLinkEntity</class>
+ <class>org.keycloak.models.jpa.entities.UserEntity</class>
+ <class>org.keycloak.models.jpa.entities.UserSessionEntity</class>
+ <class>org.keycloak.models.jpa.entities.ClientUserSessionAssociationEntity</class>
+ <class>org.keycloak.models.jpa.entities.UserRoleMappingEntity</class>
+ <class>org.keycloak.models.jpa.entities.UsernameLoginFailureEntity</class>
+ <class>org.keycloak.models.jpa.entities.ScopeMappingEntity</class>
+
+ <exclude-unlisted-classes>true</exclude-unlisted-classes>
+
+ <properties>
+ <property name="hibernate.connection.url" value="jdbc:h2:mem:test"/>
+ <property name="hibernate.connection.driver_class" value="org.h2.Driver"/>
+ <property name="hibernate.connection.username" value="sa"/>
+ <property name="hibernate.connection.password" value=""/>
+ <property name="hibernate.hbm2ddl.auto" value="create-drop" />
+ <property name="hibernate.show_sql" value="false" />
+ <property name="hibernate.format_sql" value="true" />
+ </properties>
+ </persistence-unit>
+
+ <!--
+ <persistence-unit name="picketlink-keycloak-identity-store" transaction-type="RESOURCE_LOCAL">
+ <provider>org.hibernate.ejb.HibernatePersistence</provider>
+
+ <class>org.picketlink.idm.jpa.model.sample.simple.AttributedTypeEntity</class>
+ <class>org.picketlink.idm.jpa.model.sample.simple.AccountTypeEntity</class>
+ <class>org.picketlink.idm.jpa.model.sample.simple.RoleTypeEntity</class>
+ <class>org.picketlink.idm.jpa.model.sample.simple.GroupTypeEntity</class>
+ <class>org.picketlink.idm.jpa.model.sample.simple.IdentityTypeEntity</class>
+ <class>org.picketlink.idm.jpa.model.sample.simple.RelationshipTypeEntity</class>
+ <class>org.picketlink.idm.jpa.model.sample.simple.RelationshipIdentityTypeEntity</class>
+ <class>org.picketlink.idm.jpa.model.sample.simple.PartitionTypeEntity</class>
+ <class>org.picketlink.idm.jpa.model.sample.simple.PasswordCredentialTypeEntity</class>
+ <class>org.picketlink.idm.jpa.model.sample.simple.DigestCredentialTypeEntity</class>
+ <class>org.picketlink.idm.jpa.model.sample.simple.X509CredentialTypeEntity</class>
+ <class>org.picketlink.idm.jpa.model.sample.simple.OTPCredentialTypeEntity</class>
+ <class>org.picketlink.idm.jpa.model.sample.simple.AttributeTypeEntity</class>
+ <class>org.keycloak.models.picketlink.mappings.RealmEntity</class>
+ <class>org.keycloak.models.picketlink.mappings.ApplicationEntity</class>
+
+ <exclude-unlisted-classes>true</exclude-unlisted-classes>
+
+ <properties>
+ <property name="hibernate.connection.url" value="jdbc:h2:mem:test"/>
+ <property name="hibernate.connection.driver_class" value="org.h2.Driver"/>
+ <property name="hibernate.connection.username" value="sa"/>
+ <property name="hibernate.connection.password" value=""/>
+ <property name="hibernate.hbm2ddl.auto" value="create-drop" />
+ <property name="hibernate.show_sql" value="false" />
+ <property name="hibernate.format_sql" value="true" />
+ </properties>
+ </persistence-unit>
+ -->
+</persistence>
server/pom.xml 22(+1 -21)
diff --git a/server/pom.xml b/server/pom.xml
index d0318f4..2d8e61d 100755
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -49,27 +49,7 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
- <artifactId>keycloak-model-hybrid</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-model-realms-jpa</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-model-users-jpa</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-model-sessions-mem</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-model-sessions-jpa</artifactId>
+ <artifactId>keycloak-model-jpa</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
diff --git a/server/src/main/resources/META-INF/keycloak-server.json b/server/src/main/resources/META-INF/keycloak-server.json
index 6133aa4..7e3f246 100755
--- a/server/src/main/resources/META-INF/keycloak-server.json
+++ b/server/src/main/resources/META-INF/keycloak-server.json
@@ -11,25 +11,13 @@
},
"model": {
- "provider": "hybrid"
+ "provider": "jpa"
},
"modelCache": {
"provider": "${keycloak.model.cache.provider:}"
},
- "modelRealms": {
- "provider": "jpa"
- },
-
- "modelUsers": {
- "provider": "jpa"
- },
-
- "modelSessions": {
- "provider": "mem"
- },
-
"timer": {
"provider": "basic"
},
diff --git a/server/src/main/resources/META-INF/persistence.xml b/server/src/main/resources/META-INF/persistence.xml
index bba0831..a704014 100755
--- a/server/src/main/resources/META-INF/persistence.xml
+++ b/server/src/main/resources/META-INF/persistence.xml
@@ -4,23 +4,21 @@
version="1.0">
<persistence-unit name="jpa-keycloak-identity-store" transaction-type="RESOURCE_LOCAL">
<jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>
-
- <class>org.keycloak.models.realms.jpa.entities.ApplicationEntity</class>
- <class>org.keycloak.models.realms.jpa.entities.OAuthClientEntity</class>
- <class>org.keycloak.models.realms.jpa.entities.RealmEntity</class>
- <class>org.keycloak.models.realms.jpa.entities.RequiredCredentialEntity</class>
- <class>org.keycloak.models.realms.jpa.entities.AuthenticationProviderEntity</class>
- <class>org.keycloak.models.realms.jpa.entities.RoleEntity</class>
- <class>org.keycloak.models.realms.jpa.entities.ScopeMappingEntity</class>
-
- <class>org.keycloak.models.users.jpa.entities.UserAttributeEntity</class>
- <class>org.keycloak.models.users.jpa.entities.UserCredentialEntity</class>
- <class>org.keycloak.models.users.jpa.entities.UserRoleMappingEntity</class>
- <class>org.keycloak.models.users.jpa.entities.UserEntity</class>
-
- <class>org.keycloak.models.sessions.jpa.entities.ClientUserSessionAssociationEntity</class>
- <class>org.keycloak.models.sessions.jpa.entities.UsernameLoginFailureEntity</class>
- <class>org.keycloak.models.sessions.jpa.entities.UserSessionEntity</class>
+ <class>org.keycloak.models.jpa.entities.ApplicationEntity</class>
+ <class>org.keycloak.models.jpa.entities.CredentialEntity</class>
+ <class>org.keycloak.models.jpa.entities.OAuthClientEntity</class>
+ <class>org.keycloak.models.jpa.entities.RealmEntity</class>
+ <class>org.keycloak.models.jpa.entities.RequiredCredentialEntity</class>
+ <class>org.keycloak.models.jpa.entities.AuthenticationProviderEntity</class>
+ <class>org.keycloak.models.jpa.entities.RoleEntity</class>
+ <class>org.keycloak.models.jpa.entities.SocialLinkEntity</class>
+ <class>org.keycloak.models.jpa.entities.AuthenticationLinkEntity</class>
+ <class>org.keycloak.models.jpa.entities.UserEntity</class>
+ <class>org.keycloak.models.jpa.entities.UserSessionEntity</class>
+ <class>org.keycloak.models.jpa.entities.ClientUserSessionAssociationEntity</class>
+ <class>org.keycloak.models.jpa.entities.UsernameLoginFailureEntity</class>
+ <class>org.keycloak.models.jpa.entities.UserRoleMappingEntity</class>
+ <class>org.keycloak.models.jpa.entities.ScopeMappingEntity</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
@@ -28,7 +26,7 @@
<property name="hibernate.hbm2ddl.auto" value="update" />
</properties>
</persistence-unit>
-
+
<persistence-unit name="jpa-keycloak-audit-store" transaction-type="RESOURCE_LOCAL">
<jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>
<class>org.keycloak.audit.jpa.EventEntity</class>
@@ -39,4 +37,5 @@
<property name="hibernate.hbm2ddl.auto" value="update" />
</properties>
</persistence-unit>
+
</persistence>
testsuite/integration/pom.xml 22(+1 -21)
diff --git a/testsuite/integration/pom.xml b/testsuite/integration/pom.xml
index 50466c8..052f6b1 100755
--- a/testsuite/integration/pom.xml
+++ b/testsuite/integration/pom.xml
@@ -122,27 +122,7 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
- <artifactId>keycloak-model-hybrid</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-model-realms-jpa</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-model-users-jpa</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-model-sessions-mem</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-model-sessions-jpa</artifactId>
+ <artifactId>keycloak-model-jpa</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
diff --git a/testsuite/integration/src/main/resources/META-INF/keycloak-server.json b/testsuite/integration/src/main/resources/META-INF/keycloak-server.json
index c39a794..326c697 100755
--- a/testsuite/integration/src/main/resources/META-INF/keycloak-server.json
+++ b/testsuite/integration/src/main/resources/META-INF/keycloak-server.json
@@ -14,7 +14,7 @@
},
"model": {
- "provider": "${keycloak.model.provider:hybrid}",
+ "provider": "${keycloak.model.provider:jpa}",
"mongo": {
"host": "${keycloak.model.mongo.host:127.0.0.1}",
"port": "${keycloak.model.mongo.port:27017}",
@@ -27,18 +27,6 @@
"provider": "${keycloak.model.cache.provider:simple}"
},
- "modelRealms": {
- "provider": "jpa"
- },
-
- "modelUsers": {
- "provider": "jpa"
- },
-
- "modelSessions": {
- "provider": "mem"
- },
-
"timer": {
"provider": "basic"
},
diff --git a/testsuite/integration/src/main/resources/META-INF/persistence.xml b/testsuite/integration/src/main/resources/META-INF/persistence.xml
index 5a261d7..39dd390 100755
--- a/testsuite/integration/src/main/resources/META-INF/persistence.xml
+++ b/testsuite/integration/src/main/resources/META-INF/persistence.xml
@@ -5,22 +5,21 @@
<persistence-unit name="jpa-keycloak-identity-store" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
- <class>org.keycloak.models.realms.jpa.entities.ApplicationEntity</class>
- <class>org.keycloak.models.realms.jpa.entities.OAuthClientEntity</class>
- <class>org.keycloak.models.realms.jpa.entities.RealmEntity</class>
- <class>org.keycloak.models.realms.jpa.entities.RequiredCredentialEntity</class>
- <class>org.keycloak.models.realms.jpa.entities.AuthenticationProviderEntity</class>
- <class>org.keycloak.models.realms.jpa.entities.RoleEntity</class>
- <class>org.keycloak.models.realms.jpa.entities.ScopeMappingEntity</class>
-
- <class>org.keycloak.models.users.jpa.entities.UserAttributeEntity</class>
- <class>org.keycloak.models.users.jpa.entities.UserCredentialEntity</class>
- <class>org.keycloak.models.users.jpa.entities.UserRoleMappingEntity</class>
- <class>org.keycloak.models.users.jpa.entities.UserEntity</class>
-
- <class>org.keycloak.models.sessions.jpa.entities.ClientUserSessionAssociationEntity</class>
- <class>org.keycloak.models.sessions.jpa.entities.UsernameLoginFailureEntity</class>
- <class>org.keycloak.models.sessions.jpa.entities.UserSessionEntity</class>
+ <class>org.keycloak.models.jpa.entities.ApplicationEntity</class>
+ <class>org.keycloak.models.jpa.entities.CredentialEntity</class>
+ <class>org.keycloak.models.jpa.entities.OAuthClientEntity</class>
+ <class>org.keycloak.models.jpa.entities.RealmEntity</class>
+ <class>org.keycloak.models.jpa.entities.RequiredCredentialEntity</class>
+ <class>org.keycloak.models.jpa.entities.AuthenticationProviderEntity</class>
+ <class>org.keycloak.models.jpa.entities.RoleEntity</class>
+ <class>org.keycloak.models.jpa.entities.SocialLinkEntity</class>
+ <class>org.keycloak.models.jpa.entities.AuthenticationLinkEntity</class>
+ <class>org.keycloak.models.jpa.entities.UserEntity</class>
+ <class>org.keycloak.models.jpa.entities.ClientUserSessionAssociationEntity</class>
+ <class>org.keycloak.models.jpa.entities.UserSessionEntity</class>
+ <class>org.keycloak.models.jpa.entities.UsernameLoginFailureEntity</class>
+ <class>org.keycloak.models.jpa.entities.UserRoleMappingEntity</class>
+ <class>org.keycloak.models.jpa.entities.ScopeMappingEntity</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
@@ -52,4 +51,38 @@
<property name="hibernate.format_sql" value="true" />
</properties>
</persistence-unit>
+
+ <!--
+ <persistence-unit name="picketlink-keycloak-identity-store" transaction-type="RESOURCE_LOCAL">
+ <provider>org.hibernate.ejb.HibernatePersistence</provider>
+
+ <class>org.picketlink.idm.jpa.model.sample.simple.AttributedTypeEntity</class>
+ <class>org.picketlink.idm.jpa.model.sample.simple.AccountTypeEntity</class>
+ <class>org.picketlink.idm.jpa.model.sample.simple.RoleTypeEntity</class>
+ <class>org.picketlink.idm.jpa.model.sample.simple.GroupTypeEntity</class>
+ <class>org.picketlink.idm.jpa.model.sample.simple.IdentityTypeEntity</class>
+ <class>org.picketlink.idm.jpa.model.sample.simple.RelationshipTypeEntity</class>
+ <class>org.picketlink.idm.jpa.model.sample.simple.RelationshipIdentityTypeEntity</class>
+ <class>org.picketlink.idm.jpa.model.sample.simple.PartitionTypeEntity</class>
+ <class>org.picketlink.idm.jpa.model.sample.simple.PasswordCredentialTypeEntity</class>
+ <class>org.picketlink.idm.jpa.model.sample.simple.DigestCredentialTypeEntity</class>
+ <class>org.picketlink.idm.jpa.model.sample.simple.X509CredentialTypeEntity</class>
+ <class>org.picketlink.idm.jpa.model.sample.simple.OTPCredentialTypeEntity</class>
+ <class>org.picketlink.idm.jpa.model.sample.simple.AttributeTypeEntity</class>
+ <class>org.keycloak.models.picketlink.mappings.RealmEntity</class>
+ <class>org.keycloak.models.picketlink.mappings.ApplicationEntity</class>
+
+ <exclude-unlisted-classes>true</exclude-unlisted-classes>
+
+ <properties>
+ <property name="hibernate.connection.url" value="jdbc:h2:mem:test"/>
+ <property name="hibernate.connection.driver_class" value="org.h2.Driver"/>
+ <property name="hibernate.connection.username" value="sa"/>
+ <property name="hibernate.connection.password" value=""/>
+ <property name="hibernate.hbm2ddl.auto" value="create-drop" />
+ <property name="hibernate.show_sql" value="false" />
+ <property name="hibernate.format_sql" value="true" />
+ </properties>
+ </persistence-unit>
+ -->
</persistence>
testsuite/performance/pom.xml 25(+0 -25)
diff --git a/testsuite/performance/pom.xml b/testsuite/performance/pom.xml
index 201abf3..1ba9c0f 100755
--- a/testsuite/performance/pom.xml
+++ b/testsuite/performance/pom.xml
@@ -42,31 +42,6 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
- <artifactId>keycloak-model-hybrid</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-model-realms-jpa</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-model-users-jpa</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-model-sessions-mem</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-model-sessions-jpa</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
<artifactId>keycloak-model-mongo</artifactId>
<version>${project.version}</version>
</dependency>
testsuite/tools/pom.xml 20(+0 -20)
diff --git a/testsuite/tools/pom.xml b/testsuite/tools/pom.xml
index 31d1492..c630624 100755
--- a/testsuite/tools/pom.xml
+++ b/testsuite/tools/pom.xml
@@ -68,26 +68,6 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
- <artifactId>keycloak-model-hybrid</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-model-realms-jpa</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-model-users-jpa</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-model-sessions-mem</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
<artifactId>keycloak-audit-api</artifactId>
<version>${project.version}</version>
</dependency>