Full export-import implemented. Remove dependency of mongo on picketlink-common
This commit is contained in:
parent
e4e02a2615
commit
0801c9c120
100 changed files with 4878 additions and 1004 deletions
38
export-import/export-import-api/pom.xml
Normal file
38
export-import/export-import-api/pom.xml
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?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-export-import</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.0-beta-1-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-export-import-api</artifactId>
|
||||
<name>Keycloak Export Import API</name>
|
||||
<description/>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-model-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</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>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,11 @@
|
|||
package org.keycloak.exportimport;
|
||||
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public interface ExportImportProvider {
|
||||
|
||||
void checkExportImport(KeycloakSessionFactory identitySessionFactory);
|
||||
}
|
228
export-import/export-import-impl/pom.xml
Normal file
228
export-import/export-import-impl/pom.xml
Normal file
|
@ -0,0 +1,228 @@
|
|||
<?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-export-import</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.0-beta-1-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-export-import-impl</artifactId>
|
||||
<name>Keycloak Export Import Impl</name>
|
||||
<description/>
|
||||
|
||||
<dependencies>
|
||||
<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>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-export-import-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-audit-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>jaxrs-api</artifactId>
|
||||
<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.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.jackson</groupId>
|
||||
<artifactId>jackson-core-asl</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.jackson</groupId>
|
||||
<artifactId>jackson-mapper-asl</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.iharder</groupId>
|
||||
<artifactId>base64</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk16</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!-- Encrypted ZIP -->
|
||||
<dependency>
|
||||
<groupId>de.idyl</groupId>
|
||||
<artifactId>winzipaes</artifactId>
|
||||
<scope>provided</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>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>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate.javax.persistence</groupId>
|
||||
<artifactId>hibernate-jpa-2.0-api</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
<artifactId>hibernate-entitymanager</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mongodb</groupId>
|
||||
<artifactId>mongo-java-driver</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<properties>
|
||||
<keycloak.mongo.host>localhost</keycloak.mongo.host>
|
||||
<keycloak.mongo.port>27018</keycloak.mongo.port>
|
||||
<keycloak.mongo.db>keycloak</keycloak.mongo.db>
|
||||
<keycloak.mongo.clearOnStartup>true</keycloak.mongo.clearOnStartup>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>${maven.compiler.source}</source>
|
||||
<target>${maven.compiler.target}</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<!-- Postpone tests to "integration-test" phase, so that we can bootstrap embedded mongo on 27018 before running tests -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>test</id>
|
||||
<phase>integration-test</phase>
|
||||
<goals>
|
||||
<goal>test</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<systemPropertyVariables>
|
||||
<keycloak.mongo.host>${keycloak.mongo.host}</keycloak.mongo.host>
|
||||
<keycloak.mongo.port>${keycloak.mongo.port}</keycloak.mongo.port>
|
||||
<keycloak.mongo.db>${keycloak.mongo.db}</keycloak.mongo.db>
|
||||
<keycloak.mongo.clearOnStartup>${keycloak.mongo.clearOnStartup}</keycloak.mongo.clearOnStartup>
|
||||
</systemPropertyVariables>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>default-test</id>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<!-- Embedded mongo -->
|
||||
<plugin>
|
||||
<groupId>com.github.joelittlejohn.embedmongo</groupId>
|
||||
<artifactId>embedmongo-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>start-mongodb</id>
|
||||
<phase>pre-integration-test</phase>
|
||||
<goals>
|
||||
<goal>start</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<port>${keycloak.mongo.port}</port>
|
||||
<logging>file</logging>
|
||||
<logFile>${project.build.directory}/mongodb.log</logFile>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>stop-mongodb</id>
|
||||
<phase>post-integration-test</phase>
|
||||
<goals>
|
||||
<goal>stop</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,152 @@
|
|||
package org.keycloak.exportimport;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.models.utils.reflection.Property;
|
||||
import org.keycloak.models.utils.reflection.PropertyCriteria;
|
||||
import org.keycloak.models.utils.reflection.PropertyQueries;
|
||||
|
||||
/**
|
||||
* Not thread safe (objectProperties). Assumption is that export/import is executed once per JVM
|
||||
*
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class ExportImportPropertiesManager {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ExportImportPropertiesManager.class);
|
||||
|
||||
private Map<Class<?>, Map<String, Property<Object>>> objectProperties = new HashMap<Class<?>, Map<String, Property<Object>>>();
|
||||
|
||||
// Add properties of class to objectProperties
|
||||
protected void initClassProperties(Class<?> clazz) {
|
||||
Map<String, Property<Object>> classProps = PropertyQueries.createQuery(clazz).addCriteria(new NonEmptyGetterCriteria()).getWritableResultList();
|
||||
this.objectProperties.put(clazz, classProps);
|
||||
}
|
||||
|
||||
public void setBasicPropertiesFromModel(Object model, Object entity) {
|
||||
Class<?> modelClass = getModelClass(model);
|
||||
|
||||
// Lazy init of properties
|
||||
checkPropertiesAvailable(modelClass, entity.getClass());
|
||||
|
||||
Map<String, Property<Object>> modelProps = this.objectProperties.get(modelClass);
|
||||
Map<String, Property<Object>> entityProps = this.objectProperties.get(entity.getClass());
|
||||
|
||||
Map<String, Property<Object>> entityPropsCopy = new HashMap<String, Property<Object>>(entityProps);
|
||||
|
||||
logger.debugf("Properties of entity %s: %s", entity, entityProps.keySet());
|
||||
for (Property<Object> modelProperty : modelProps.values()) {
|
||||
Property<Object> entityProperty = entityPropsCopy.get(modelProperty.getName());
|
||||
|
||||
entityPropsCopy.remove(modelProperty.getName());
|
||||
|
||||
if (entityProperty != null) {
|
||||
Object propertyValue = modelProperty.getValue(model);
|
||||
|
||||
// Workaround needed because model classes have many getters/setters with "Set", but for entity classes, there are usually "List"
|
||||
if (propertyValue instanceof Set) {
|
||||
Set propValueAsSet = (Set)propertyValue;
|
||||
entityProperty.setValue(entity, new ArrayList(propValueAsSet));
|
||||
} else {
|
||||
entityProperty.setValue(entity, propertyValue);
|
||||
}
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.tracef("Property %s successfully set in JSON to entity %s", modelProperty.getName(), entity);
|
||||
}
|
||||
} else {
|
||||
logger.debugf("Property %s not known in JSON for entity %s", modelProperty.getName(), entity);
|
||||
}
|
||||
}
|
||||
|
||||
logger.debugf("Entity properties for manual setup: %s", entityPropsCopy.keySet());
|
||||
}
|
||||
|
||||
private void checkPropertiesAvailable(Class<?> modelClass, Class<?> entityClass) {
|
||||
if (!objectProperties.containsKey(modelClass)) {
|
||||
initClassProperties(modelClass);
|
||||
}
|
||||
if (!objectProperties.containsKey(entityClass)) {
|
||||
initClassProperties(entityClass);
|
||||
}
|
||||
}
|
||||
|
||||
public void setBasicPropertiesToModel(Object model, Object entity) {
|
||||
Class<?> modelClass = getModelClass(model);
|
||||
|
||||
// Lazy init of properties
|
||||
checkPropertiesAvailable(modelClass, entity.getClass());
|
||||
|
||||
Map<String, Property<Object>> modelProps = this.objectProperties.get(modelClass);
|
||||
Map<String, Property<Object>> entityProps = this.objectProperties.get(entity.getClass());
|
||||
|
||||
Map<String, Property<Object>> entityPropsCopy = new HashMap<String, Property<Object>>(entityProps);
|
||||
|
||||
logger.debugf("Properties of exported entity %s: %s", entity, entityProps.keySet());
|
||||
|
||||
for (Property<Object> modelProperty : modelProps.values()) {
|
||||
Property<Object> entityProperty = entityPropsCopy.get(modelProperty.getName());
|
||||
|
||||
entityPropsCopy.remove(modelProperty.getName());
|
||||
|
||||
if (entityProperty != null) {
|
||||
Object propertyValue = entityProperty.getValue(entity);
|
||||
|
||||
// Workaround needed because model classes have many getters/setters with "Set", but for entity classes, there are usually "List"
|
||||
if (propertyValue instanceof List && Set.class.isAssignableFrom(modelProperty.getJavaClass())) {
|
||||
List propValueAsList = (List)propertyValue;
|
||||
modelProperty.setValue(model, new HashSet(propValueAsList));
|
||||
} else {
|
||||
modelProperty.setValue(model, propertyValue);
|
||||
}
|
||||
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.tracef("Property %s successfully set in model from entity %s", modelProperty.getName(), entity);
|
||||
}
|
||||
} else {
|
||||
logger.debugf("Property %s not known for entity %s", modelProperty.getName(), entity);
|
||||
}
|
||||
}
|
||||
|
||||
logger.debugf("Entity properties for manual setup: %s", entityPropsCopy.keySet());
|
||||
}
|
||||
|
||||
protected Class<?> getModelClass(Object model) {
|
||||
Class<?> modelClass = model.getClass();
|
||||
Class<?>[] interfaces = modelClass.getInterfaces();
|
||||
|
||||
// Bit unsafe, but looks that it works for all "model adapters" so far
|
||||
if (interfaces.length == 0) {
|
||||
return modelClass;
|
||||
} else {
|
||||
return interfaces[0];
|
||||
}
|
||||
}
|
||||
|
||||
public static class NonEmptyGetterCriteria implements PropertyCriteria {
|
||||
|
||||
private static final List<String> IGNORED_METHODS = Arrays.asList("getPasswordPolicy", "getAuthenticationProviders");
|
||||
|
||||
@Override
|
||||
public boolean methodMatches(Method m) {
|
||||
// Ignore non-empty getters
|
||||
if (m.getParameterTypes().length > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ignore some "known" getters (for example incompatible types between model and entity)
|
||||
if (IGNORED_METHODS.contains(m.getName())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
package org.keycloak.exportimport;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.exportimport.io.ExportImportIOProvider;
|
||||
import org.keycloak.exportimport.io.ExportWriter;
|
||||
import org.keycloak.exportimport.io.ImportReader;
|
||||
import org.keycloak.models.Config;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.KeycloakTransaction;
|
||||
import org.keycloak.util.ProviderLoader;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class ExportImportProviderImpl implements ExportImportProvider {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ExportImportProviderImpl.class);
|
||||
|
||||
public static final String ACTION_EXPORT = "export";
|
||||
public static final String ACTION_IMPORT = "import";
|
||||
|
||||
@Override
|
||||
public void checkExportImport(KeycloakSessionFactory identitySessionFactory) {
|
||||
String exportImportAction = Config.getExportImportAction();
|
||||
|
||||
boolean export = false;
|
||||
boolean importt = false;
|
||||
if (ACTION_EXPORT.equals(exportImportAction)) {
|
||||
logger.infof("Full model export requested");
|
||||
export = true;
|
||||
} else if (ACTION_IMPORT.equals(exportImportAction)) {
|
||||
logger.infof("Full model import requested");
|
||||
importt = true;
|
||||
}
|
||||
|
||||
if (export || importt) {
|
||||
KeycloakSession session = identitySessionFactory.createSession();
|
||||
KeycloakTransaction transaction = session.getTransaction();
|
||||
try {
|
||||
transaction.begin();
|
||||
|
||||
if (export) {
|
||||
ExportWriter exportWriter = getProvider().getExportWriter();
|
||||
new ModelExporter().exportModel(session, exportWriter);
|
||||
logger.infof("Export finished successfully");
|
||||
} else {
|
||||
ImportReader importReader = getProvider().getImportReader();
|
||||
new ModelImporter().importModel(session, importReader);
|
||||
logger.infof("Import finished successfully");
|
||||
}
|
||||
|
||||
if (transaction.isActive()) {
|
||||
if (transaction.getRollbackOnly()) {
|
||||
transaction.rollback();
|
||||
} else {
|
||||
transaction.commit();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (transaction.isActive()) {
|
||||
session.getTransaction().rollback();
|
||||
}
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
session.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ExportImportIOProvider getProvider() {
|
||||
String providerId = Config.getExportImportProvider();
|
||||
logger.infof("Requested migration provider: " + providerId);
|
||||
|
||||
Iterable<ExportImportIOProvider> providers = ProviderLoader.load(ExportImportIOProvider.class);
|
||||
for (ExportImportIOProvider provider : providers) {
|
||||
if (providerId.equals(provider.getId())) {
|
||||
return provider;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Provider " + providerId + " not found");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,334 @@
|
|||
package org.keycloak.exportimport;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.exportimport.io.ExportWriter;
|
||||
import org.keycloak.models.ApplicationModel;
|
||||
import org.keycloak.models.AuthenticationLinkModel;
|
||||
import org.keycloak.models.AuthenticationProviderModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.OAuthClientModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RequiredCredentialModel;
|
||||
import org.keycloak.models.RoleContainerModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.SocialLinkModel;
|
||||
import org.keycloak.models.UserCredentialValueModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UsernameLoginFailureModel;
|
||||
import org.keycloak.models.entities.ApplicationEntity;
|
||||
import org.keycloak.models.entities.AuthenticationLinkEntity;
|
||||
import org.keycloak.models.entities.AuthenticationProviderEntity;
|
||||
import org.keycloak.models.entities.CredentialEntity;
|
||||
import org.keycloak.models.entities.OAuthClientEntity;
|
||||
import org.keycloak.models.entities.RealmEntity;
|
||||
import org.keycloak.models.entities.RequiredCredentialEntity;
|
||||
import org.keycloak.models.entities.RoleEntity;
|
||||
import org.keycloak.models.entities.SocialLinkEntity;
|
||||
import org.keycloak.models.entities.UserEntity;
|
||||
import org.keycloak.models.entities.UsernameLoginFailureEntity;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class ModelExporter {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ModelExporter.class);
|
||||
|
||||
private ExportWriter exportWriter;
|
||||
private ExportImportPropertiesManager propertiesManager;
|
||||
|
||||
public void exportModel(KeycloakSession keycloakSession, ExportWriter exportWriter) {
|
||||
// Initialize needed objects
|
||||
this.exportWriter = exportWriter;
|
||||
this.propertiesManager = new ExportImportPropertiesManager();
|
||||
|
||||
// Create separate files for "realms", "applications", "oauthClients", "roles" and finally "users". Users may be done in more files (pagination)
|
||||
exportRealms(keycloakSession, "realms.json");
|
||||
exportApplications(keycloakSession, "applications.json");
|
||||
exportOAuthClients(keycloakSession, "oauthClients.json");
|
||||
exportRoles(keycloakSession, "roles.json");
|
||||
exportUsers(keycloakSession, "users.json");
|
||||
exportUserFailures(keycloakSession, "userFailures.json");
|
||||
|
||||
this.exportWriter.closeExportWriter();
|
||||
}
|
||||
|
||||
protected void exportRealms(KeycloakSession keycloakSession, String fileName) {
|
||||
List<RealmModel> realms = keycloakSession.getRealms();
|
||||
|
||||
// Convert models to entities, which will be written into JSON file
|
||||
List<RealmEntity> result = new LinkedList<RealmEntity>();
|
||||
for (RealmModel realmModel : realms) {
|
||||
RealmEntity entity = new RealmEntity();
|
||||
entity.setId(realmModel.getId());
|
||||
result.add(entity);
|
||||
|
||||
// Export all basic properties from realm
|
||||
this.propertiesManager.setBasicPropertiesFromModel(realmModel, entity);
|
||||
|
||||
// Export 'advanced' properties
|
||||
ApplicationModel realmAdminApp = realmModel.getAdminApp();
|
||||
if (realmAdminApp != null) {
|
||||
entity.setAdminAppId(realmAdminApp.getId());
|
||||
}
|
||||
entity.setDefaultRoles(realmModel.getDefaultRoles());
|
||||
|
||||
List<RequiredCredentialEntity> reqCredEntities = new ArrayList<RequiredCredentialEntity>();
|
||||
List<RequiredCredentialModel> requiredCredModels = realmModel.getRequiredCredentials();
|
||||
for (RequiredCredentialModel requiredCredModel : requiredCredModels) {
|
||||
RequiredCredentialEntity reqCredEntity = new RequiredCredentialEntity();
|
||||
this.propertiesManager.setBasicPropertiesFromModel(requiredCredModel, reqCredEntity);
|
||||
reqCredEntities.add(reqCredEntity);
|
||||
}
|
||||
entity.setRequiredCredentials(reqCredEntities);
|
||||
|
||||
// password policy
|
||||
entity.setPasswordPolicy(realmModel.getPasswordPolicy().toString());
|
||||
|
||||
// authentication providers
|
||||
List<AuthenticationProviderEntity> authProviderEntities = new ArrayList<AuthenticationProviderEntity>();
|
||||
for (AuthenticationProviderModel authProvider : realmModel.getAuthenticationProviders()) {
|
||||
AuthenticationProviderEntity authProviderEntity = new AuthenticationProviderEntity();
|
||||
this.propertiesManager.setBasicPropertiesFromModel(authProvider, authProviderEntity);
|
||||
authProviderEntities.add(authProviderEntity);
|
||||
|
||||
}
|
||||
entity.setAuthenticationProviders(authProviderEntities);
|
||||
}
|
||||
|
||||
this.exportWriter.writeEntities(fileName, result);
|
||||
logger.infof("Realms exported: " + result);
|
||||
}
|
||||
|
||||
protected void exportApplications(KeycloakSession keycloakSession, String fileName) {
|
||||
List<ApplicationModel> allApplications = getAllApplications(keycloakSession);
|
||||
|
||||
List<ApplicationEntity> result = new LinkedList<ApplicationEntity>();
|
||||
for (ApplicationModel appModel : allApplications) {
|
||||
ApplicationEntity appEntity = new ApplicationEntity();
|
||||
appEntity.setId(appModel.getId());
|
||||
result.add(appEntity);
|
||||
|
||||
this.propertiesManager.setBasicPropertiesFromModel(appModel, appEntity);
|
||||
|
||||
// Export 'advanced' properties of application
|
||||
appEntity.setRealmId(appModel.getRealm().getId());
|
||||
appEntity.setDefaultRoles(appModel.getDefaultRoles());
|
||||
|
||||
List<String> scopeIds = getScopeIds(appModel);
|
||||
appEntity.setScopeIds(scopeIds);
|
||||
}
|
||||
|
||||
this.exportWriter.writeEntities(fileName, result);
|
||||
logger.infof("Applications exported: " + result);
|
||||
}
|
||||
|
||||
protected void exportOAuthClients(KeycloakSession keycloakSession, String fileName) {
|
||||
List<RealmModel> realms = keycloakSession.getRealms();
|
||||
List<OAuthClientModel> allClients = new ArrayList<OAuthClientModel>();
|
||||
for (RealmModel realmModel : realms) {
|
||||
allClients.addAll(realmModel.getOAuthClients());
|
||||
}
|
||||
|
||||
List<OAuthClientEntity> result = new LinkedList<OAuthClientEntity>();
|
||||
for (OAuthClientModel clientModel : allClients) {
|
||||
OAuthClientEntity clientEntity = new OAuthClientEntity();
|
||||
clientEntity.setId(clientModel.getId());
|
||||
result.add(clientEntity);
|
||||
|
||||
this.propertiesManager.setBasicPropertiesFromModel(clientModel, clientEntity);
|
||||
|
||||
// Export 'advanced' properties of client
|
||||
clientEntity.setName(clientModel.getClientId());
|
||||
clientEntity.setRealmId(clientModel.getRealm().getId());
|
||||
|
||||
List<String> scopeIds = getScopeIds(clientModel);
|
||||
clientEntity.setScopeIds(scopeIds);
|
||||
}
|
||||
|
||||
this.exportWriter.writeEntities(fileName, result);
|
||||
logger.infof("OAuth clients exported: " + result);
|
||||
}
|
||||
|
||||
protected void exportRoles(KeycloakSession keycloakSession, String fileName) {
|
||||
List<RoleModel> allRoles = getAllRoles(keycloakSession);
|
||||
|
||||
List<RoleEntity> result = new LinkedList<RoleEntity>();
|
||||
for (RoleModel roleModel : allRoles) {
|
||||
RoleEntity roleEntity = new RoleEntity();
|
||||
roleEntity.setId(roleModel.getId());
|
||||
result.add(roleEntity);
|
||||
|
||||
roleEntity.setName(roleModel.getName());
|
||||
roleEntity.setDescription(roleModel.getDescription());
|
||||
|
||||
RoleContainerModel roleContainer = roleModel.getContainer();
|
||||
if (roleContainer instanceof RealmModel) {
|
||||
RealmModel realm = (RealmModel)roleContainer;
|
||||
roleEntity.setRealmId(realm.getId());
|
||||
} else {
|
||||
ApplicationModel appModel = (ApplicationModel)roleContainer;
|
||||
roleEntity.setApplicationId(appModel.getId());
|
||||
}
|
||||
|
||||
List<String> compositeRolesIds = null;
|
||||
for (RoleModel composite : roleModel.getComposites()) {
|
||||
|
||||
// Lazy init
|
||||
if (compositeRolesIds == null) {
|
||||
compositeRolesIds = new ArrayList<String>();
|
||||
}
|
||||
|
||||
compositeRolesIds.add(composite.getId());
|
||||
}
|
||||
roleEntity.setCompositeRoleIds(compositeRolesIds);
|
||||
}
|
||||
|
||||
this.exportWriter.writeEntities(fileName, result);
|
||||
|
||||
logger.infof("%d roles exported: ", result.size());
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Exported roles: " + result);
|
||||
}
|
||||
}
|
||||
|
||||
protected void exportUsers(KeycloakSession keycloakSession, String fileName) {
|
||||
List<RealmModel> realms = keycloakSession.getRealms();
|
||||
List<UserEntity> result = new LinkedList<UserEntity>();
|
||||
|
||||
for (RealmModel realm : realms) {
|
||||
List<UserModel> userModels = realm.getUsers();
|
||||
for (UserModel userModel : userModels) {
|
||||
UserEntity userEntity = new UserEntity();
|
||||
userEntity.setId(userModel.getId());
|
||||
result.add(userEntity);
|
||||
|
||||
this.propertiesManager.setBasicPropertiesFromModel(userModel, userEntity);
|
||||
|
||||
userEntity.setLoginName(userModel.getLoginName());
|
||||
userEntity.setRealmId(realm.getId());
|
||||
|
||||
// authentication links
|
||||
AuthenticationLinkModel authLink = realm.getAuthenticationLink(userModel);
|
||||
if (authLink != null) {
|
||||
AuthenticationLinkEntity authLinkEntity = new AuthenticationLinkEntity();
|
||||
this.propertiesManager.setBasicPropertiesFromModel(authLink, authLinkEntity);
|
||||
|
||||
userEntity.setAuthenticationLink(authLinkEntity);
|
||||
}
|
||||
|
||||
// social links
|
||||
Set<SocialLinkModel> socialLinks = realm.getSocialLinks(userModel);
|
||||
if (socialLinks != null && !socialLinks.isEmpty()) {
|
||||
List<SocialLinkEntity> socialLinkEntities = new ArrayList<SocialLinkEntity>();
|
||||
for (SocialLinkModel socialLink : socialLinks) {
|
||||
SocialLinkEntity socialLinkEntity = new SocialLinkEntity();
|
||||
this.propertiesManager.setBasicPropertiesFromModel(socialLink, socialLinkEntity);
|
||||
|
||||
socialLinkEntities.add(socialLinkEntity);
|
||||
}
|
||||
|
||||
userEntity.setSocialLinks(socialLinkEntities);
|
||||
}
|
||||
|
||||
// required actions
|
||||
Set<UserModel.RequiredAction> requiredActions = userModel.getRequiredActions();
|
||||
if (requiredActions != null && !requiredActions.isEmpty()) {
|
||||
userEntity.setRequiredActions(new ArrayList<UserModel.RequiredAction>(requiredActions));
|
||||
}
|
||||
|
||||
// attributes
|
||||
userEntity.setAttributes(userModel.getAttributes());
|
||||
|
||||
// roleIds
|
||||
Set<RoleModel> roles = realm.getRoleMappings(userModel);
|
||||
List<String> roleIds = new ArrayList<String>();
|
||||
for (RoleModel role : roles) {
|
||||
roleIds.add(role.getId());
|
||||
}
|
||||
userEntity.setRoleIds(roleIds);
|
||||
|
||||
// credentials
|
||||
List<UserCredentialValueModel> credentials = realm.getCredentialsDirectly(userModel);
|
||||
List<CredentialEntity> credEntities = new ArrayList<CredentialEntity>();
|
||||
for (UserCredentialValueModel credModel : credentials) {
|
||||
CredentialEntity credEntity = new CredentialEntity();
|
||||
this.propertiesManager.setBasicPropertiesFromModel(credModel, credEntity);
|
||||
credEntities.add(credEntity);
|
||||
}
|
||||
|
||||
userEntity.setCredentials(credEntities);
|
||||
}
|
||||
}
|
||||
|
||||
this.exportWriter.writeEntities(fileName, result);
|
||||
|
||||
logger.infof("%d users exported: ", result.size());
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Exported users: " + result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Does it makes sense to export user failures ?
|
||||
protected void exportUserFailures(KeycloakSession keycloakSession, String fileName) {
|
||||
List<RealmModel> realms = keycloakSession.getRealms();
|
||||
List<UsernameLoginFailureModel> allFailures = new ArrayList<UsernameLoginFailureModel>();
|
||||
for (RealmModel realmModel : realms) {
|
||||
allFailures.addAll(realmModel.getAllUserLoginFailures());
|
||||
}
|
||||
|
||||
List<UsernameLoginFailureEntity> result = new LinkedList<UsernameLoginFailureEntity>();
|
||||
for (UsernameLoginFailureModel failureModel : allFailures) {
|
||||
UsernameLoginFailureEntity failureEntity = new UsernameLoginFailureEntity();
|
||||
this.propertiesManager.setBasicPropertiesFromModel(failureModel, failureEntity);
|
||||
result.add(failureEntity);
|
||||
|
||||
failureEntity.setUsername(failureModel.getUsername());
|
||||
failureEntity.setNumFailures(failureModel.getNumFailures());
|
||||
}
|
||||
|
||||
this.exportWriter.writeEntities(fileName, result);
|
||||
}
|
||||
|
||||
private List<String> getScopeIds(ClientModel clientModel) {
|
||||
Set<RoleModel> allScopes = clientModel.getRealm().getScopeMappings(clientModel);
|
||||
List<String> scopeIds = new ArrayList<String>();
|
||||
for (RoleModel role : allScopes) {
|
||||
scopeIds.add(role.getId());
|
||||
}
|
||||
return scopeIds;
|
||||
}
|
||||
|
||||
private List<ApplicationModel> getAllApplications(KeycloakSession keycloakSession) {
|
||||
List<RealmModel> realms = keycloakSession.getRealms();
|
||||
List<ApplicationModel> allApplications = new ArrayList<ApplicationModel>();
|
||||
for (RealmModel realmModel : realms) {
|
||||
allApplications.addAll(realmModel.getApplications());
|
||||
}
|
||||
return allApplications;
|
||||
}
|
||||
|
||||
private List<RoleModel> getAllRoles(KeycloakSession keycloakSession) {
|
||||
List<RoleModel> allRoles = new ArrayList<RoleModel>();
|
||||
|
||||
List<RealmModel> realms = keycloakSession.getRealms();
|
||||
for (RealmModel realmModel : realms) {
|
||||
allRoles.addAll(realmModel.getRoles());
|
||||
}
|
||||
|
||||
List<ApplicationModel> allApplications = getAllApplications(keycloakSession);
|
||||
for (ApplicationModel appModel : allApplications) {
|
||||
allRoles.addAll(appModel.getRoles());
|
||||
}
|
||||
|
||||
return allRoles;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,329 @@
|
|||
package org.keycloak.exportimport;
|
||||
|
||||
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 org.jboss.logging.Logger;
|
||||
import org.keycloak.exportimport.io.ImportReader;
|
||||
import org.keycloak.models.ApplicationModel;
|
||||
import org.keycloak.models.AuthenticationLinkModel;
|
||||
import org.keycloak.models.AuthenticationProviderModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.Config;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.OAuthClientModel;
|
||||
import org.keycloak.models.PasswordPolicy;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleContainerModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.SocialLinkModel;
|
||||
import org.keycloak.models.UserCredentialValueModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UsernameLoginFailureModel;
|
||||
import org.keycloak.models.entities.ApplicationEntity;
|
||||
import org.keycloak.models.entities.AuthenticationLinkEntity;
|
||||
import org.keycloak.models.entities.AuthenticationProviderEntity;
|
||||
import org.keycloak.models.entities.ClientEntity;
|
||||
import org.keycloak.models.entities.CredentialEntity;
|
||||
import org.keycloak.models.entities.OAuthClientEntity;
|
||||
import org.keycloak.models.entities.RealmEntity;
|
||||
import org.keycloak.models.entities.RequiredCredentialEntity;
|
||||
import org.keycloak.models.entities.RoleEntity;
|
||||
import org.keycloak.models.entities.SocialLinkEntity;
|
||||
import org.keycloak.models.entities.UserEntity;
|
||||
import org.keycloak.models.entities.UsernameLoginFailureEntity;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class ModelImporter {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ModelImporter.class);
|
||||
|
||||
private ImportReader importReader;
|
||||
private ExportImportPropertiesManager propertiesManager;
|
||||
|
||||
public void importModel(KeycloakSession keycloakSession, ImportReader importReader) {
|
||||
// Initialize needed objects
|
||||
this.importReader = importReader;
|
||||
this.propertiesManager = new ExportImportPropertiesManager();
|
||||
|
||||
// Delete all the data from current model
|
||||
keycloakSession.removeAllData();
|
||||
|
||||
importRealms(keycloakSession, "realms.json");
|
||||
importApplications(keycloakSession, "applications.json");
|
||||
importRoles(keycloakSession, "roles.json");
|
||||
|
||||
// Now we have all realms,applications and roles filled. So fill other objects (default roles, scopes etc)
|
||||
importRealmsStep2(keycloakSession, "realms.json");
|
||||
importApplicationsStep2(keycloakSession, "applications.json");
|
||||
|
||||
importOAuthClients(keycloakSession, "oauthClients.json");
|
||||
importUsers(keycloakSession, "users.json");
|
||||
importUserFailures(keycloakSession, "userFailures.json");
|
||||
|
||||
this.importReader.closeImportReader();
|
||||
}
|
||||
|
||||
protected void importRealms(KeycloakSession keycloakSession, String fileName) {
|
||||
List<RealmEntity> realms = this.importReader.readEntities(fileName, RealmEntity.class);
|
||||
|
||||
for (RealmEntity realmEntity : realms) {
|
||||
RealmModel realm = keycloakSession.createRealm(realmEntity.getId(), realmEntity.getName());
|
||||
|
||||
this.propertiesManager.setBasicPropertiesToModel(realm, realmEntity);
|
||||
|
||||
Set<String> reqCredModels = new HashSet<String>();
|
||||
for (RequiredCredentialEntity requiredCredEntity : realmEntity.getRequiredCredentials()) {
|
||||
reqCredModels.add(requiredCredEntity.getType());
|
||||
}
|
||||
realm.updateRequiredCredentials(reqCredModels);
|
||||
|
||||
// AdminApp and defaultRoles are set in step2
|
||||
|
||||
// password policy
|
||||
realm.setPasswordPolicy(new PasswordPolicy(realmEntity.getPasswordPolicy()));
|
||||
|
||||
// authentication providers
|
||||
List<AuthenticationProviderModel> authProviderModels = new ArrayList<AuthenticationProviderModel>();
|
||||
for (AuthenticationProviderEntity authProviderEntity : realmEntity.getAuthenticationProviders()) {
|
||||
AuthenticationProviderModel authProvider = new AuthenticationProviderModel();
|
||||
this.propertiesManager.setBasicPropertiesToModel(authProvider, authProviderEntity);
|
||||
authProviderModels.add(authProvider);
|
||||
|
||||
}
|
||||
realm.setAuthenticationProviders(authProviderModels);
|
||||
}
|
||||
|
||||
logger.infof("Realms imported: " + realms);
|
||||
}
|
||||
|
||||
protected void importApplications(KeycloakSession keycloakSession, String fileName) {
|
||||
List<ApplicationEntity> apps = this.importReader.readEntities(fileName, ApplicationEntity.class);
|
||||
for (ApplicationEntity appEntity : apps) {
|
||||
RealmModel realm = keycloakSession.getRealm(appEntity.getRealmId());
|
||||
ApplicationModel app = realm.addApplication(appEntity.getId(), appEntity.getName());
|
||||
|
||||
this.propertiesManager.setBasicPropertiesToModel(app , appEntity);
|
||||
|
||||
// scopeIds and default roles will be done in step2
|
||||
}
|
||||
|
||||
logger.infof("Applications imported: " + apps);
|
||||
}
|
||||
|
||||
protected void importRoles(KeycloakSession keycloakSession, String fileName) {
|
||||
// helper map for composite roles
|
||||
Map<String, RoleEntity> rolesMap = new HashMap<String, RoleEntity>();
|
||||
|
||||
List<RoleEntity> roles = this.importReader.readEntities(fileName, RoleEntity.class);
|
||||
for (RoleEntity roleEntity : roles) {
|
||||
RoleModel role = null;
|
||||
if (roleEntity.getRealmId() != null) {
|
||||
RealmModel realm = keycloakSession.getRealm(roleEntity.getRealmId());
|
||||
role = realm.addRole(roleEntity.getId(), roleEntity.getName());
|
||||
} else if (roleEntity.getApplicationId() != null) {
|
||||
ApplicationModel app = findApplicationById(keycloakSession, roleEntity.getApplicationId());
|
||||
role = app.addRole(roleEntity.getId(), roleEntity.getName());
|
||||
} else {
|
||||
throw new IllegalStateException("Role " + roleEntity.getId() + " doesn't have realmId nor applicationId");
|
||||
}
|
||||
|
||||
role.setDescription(roleEntity.getDescription());
|
||||
|
||||
rolesMap.put(roleEntity.getId(), roleEntity);
|
||||
}
|
||||
|
||||
// All roles were added. Fill composite roles now
|
||||
for (RealmModel realm : keycloakSession.getRealms()) {
|
||||
|
||||
// realm roles
|
||||
fillCompositeRoles(rolesMap, realm, realm);
|
||||
|
||||
// app roles
|
||||
for (ApplicationModel app : realm.getApplications()) {
|
||||
fillCompositeRoles(rolesMap, app, realm);
|
||||
}
|
||||
}
|
||||
|
||||
logger.infof("%d roles imported: ", roles);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Imported roles: " + roles);
|
||||
}
|
||||
}
|
||||
|
||||
private void fillCompositeRoles(Map<String, RoleEntity> rolesMap, RoleContainerModel roleContainer, RealmModel realm) {
|
||||
for (RoleModel role : roleContainer.getRoles()) {
|
||||
RoleEntity roleEntity = rolesMap.get(role.getId());
|
||||
|
||||
if (roleEntity.getCompositeRoleIds() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (String compositeRoleId : roleEntity.getCompositeRoleIds()) {
|
||||
RoleModel compositeRole = realm.getRoleById(compositeRoleId);
|
||||
role.addCompositeRole(compositeRole);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void importRealmsStep2(KeycloakSession keycloakSession, String fileName) {
|
||||
List<RealmEntity> realms = this.importReader.readEntities(fileName, RealmEntity.class);
|
||||
RealmModel adminRealm = keycloakSession.getRealm(Config.getAdminRealm());
|
||||
|
||||
for (RealmEntity realmEntity : realms) {
|
||||
RealmModel realm = keycloakSession.getRealm(realmEntity.getId());
|
||||
|
||||
// admin app
|
||||
String adminAppId = realmEntity.getAdminAppId();
|
||||
if (adminAppId != null) {
|
||||
realm.setAdminApp(adminRealm.getApplicationById(adminAppId));
|
||||
}
|
||||
|
||||
// Default roles
|
||||
realm.updateDefaultRoles(realmEntity.getDefaultRoles().toArray(new String[] {}));
|
||||
}
|
||||
}
|
||||
|
||||
protected void importApplicationsStep2(KeycloakSession keycloakSession, String fileName) {
|
||||
List<ApplicationEntity> apps = this.importReader.readEntities(fileName, ApplicationEntity.class);
|
||||
for (ApplicationEntity appEntity : apps) {
|
||||
RealmModel realm = keycloakSession.getRealm(appEntity.getRealmId());
|
||||
ApplicationModel application = realm.getApplicationById(appEntity.getId());
|
||||
|
||||
// Default roles
|
||||
application.updateDefaultRoles(appEntity.getDefaultRoles().toArray(new String[] {}));
|
||||
|
||||
// Scopes
|
||||
addScopes(realm, application, appEntity);
|
||||
}
|
||||
}
|
||||
|
||||
private void addScopes(RealmModel realm, ClientModel client, ClientEntity clientEntity) {
|
||||
for (String scopeId : clientEntity.getScopeIds()) {
|
||||
RoleModel scope = realm.getRoleById(scopeId);
|
||||
realm.addScopeMapping(client, scope);
|
||||
}
|
||||
}
|
||||
|
||||
protected void importOAuthClients(KeycloakSession keycloakSession, String fileName) {
|
||||
List<OAuthClientEntity> clients = this.importReader.readEntities(fileName, OAuthClientEntity.class);
|
||||
for (OAuthClientEntity clientEntity : clients) {
|
||||
RealmModel realm = keycloakSession.getRealm(clientEntity.getRealmId());
|
||||
OAuthClientModel client = realm.addOAuthClient(clientEntity.getId(), clientEntity.getName());
|
||||
|
||||
this.propertiesManager.setBasicPropertiesToModel(client, clientEntity);
|
||||
|
||||
client.setClientId(clientEntity.getName());
|
||||
|
||||
// Scopes. All roles are already added at this point
|
||||
addScopes(realm, client, clientEntity);
|
||||
}
|
||||
|
||||
logger.infof("OAuth clients imported: " + clients);
|
||||
}
|
||||
|
||||
protected ApplicationModel findApplicationById(KeycloakSession keycloakSession, String applicationId) {
|
||||
for (RealmModel realm : keycloakSession.getRealms()) {
|
||||
ApplicationModel appModel = realm.getApplicationById(applicationId);
|
||||
if (appModel != null) {
|
||||
return appModel;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void importUsers(KeycloakSession keycloakSession, String fileName) {
|
||||
List<UserEntity> users = this.importReader.readEntities(fileName, UserEntity.class);
|
||||
for (UserEntity userEntity : users) {
|
||||
RealmModel realm = keycloakSession.getRealm(userEntity.getRealmId());
|
||||
UserModel user = realm.addUser(userEntity.getId(), userEntity.getLoginName());
|
||||
|
||||
// We need to remove defaultRoles here as realm.addUser is automatically adding them. We may add them later during roles mapping processing
|
||||
for (RoleModel role : realm.getRoleMappings(user)) {
|
||||
realm.deleteRoleMapping(user, role);
|
||||
}
|
||||
|
||||
this.propertiesManager.setBasicPropertiesToModel(user, userEntity);
|
||||
|
||||
// authentication links
|
||||
AuthenticationLinkEntity authLinkEntity = userEntity.getAuthenticationLink();
|
||||
if (authLinkEntity != null) {
|
||||
AuthenticationLinkModel authLinkModel = new AuthenticationLinkModel();
|
||||
this.propertiesManager.setBasicPropertiesToModel(authLinkModel, authLinkEntity);
|
||||
|
||||
realm.setAuthenticationLink(user, authLinkModel);
|
||||
}
|
||||
|
||||
// social links
|
||||
List<SocialLinkEntity> socialLinks = userEntity.getSocialLinks();
|
||||
if (socialLinks != null && !socialLinks.isEmpty()) {
|
||||
for (SocialLinkEntity socialLinkEntity : socialLinks) {
|
||||
SocialLinkModel socialLink = new SocialLinkModel();
|
||||
this.propertiesManager.setBasicPropertiesToModel(socialLink, socialLinkEntity);
|
||||
|
||||
realm.addSocialLink(user, socialLink);
|
||||
}
|
||||
}
|
||||
|
||||
// required actions
|
||||
List<UserModel.RequiredAction> requiredActions = userEntity.getRequiredActions();
|
||||
if (requiredActions != null && !requiredActions.isEmpty()) {
|
||||
for (UserModel.RequiredAction reqAction : requiredActions) {
|
||||
user.addRequiredAction(reqAction);
|
||||
}
|
||||
}
|
||||
|
||||
// attributes
|
||||
if (userEntity.getAttributes() != null) {
|
||||
for (Map.Entry<String, String> attr : userEntity.getAttributes().entrySet()) {
|
||||
user.setAttribute(attr.getKey(), attr.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
// roles
|
||||
if (userEntity.getRoleIds() != null) {
|
||||
for (String roleId : userEntity.getRoleIds()) {
|
||||
RoleModel role = realm.getRoleById(roleId);
|
||||
realm.grantRole(user, role);
|
||||
}
|
||||
}
|
||||
|
||||
// credentials
|
||||
List<CredentialEntity> credentials = userEntity.getCredentials();
|
||||
if (credentials != null) {
|
||||
for (CredentialEntity credEntity : credentials) {
|
||||
UserCredentialValueModel credModel = new UserCredentialValueModel();
|
||||
this.propertiesManager.setBasicPropertiesToModel(credModel, credEntity);
|
||||
|
||||
realm.updateCredentialDirectly(user, credModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.infof("%d users imported: ", users.size());
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Imported users: " + users);
|
||||
}
|
||||
}
|
||||
|
||||
public void importUserFailures(KeycloakSession keycloakSession, String fileName) {
|
||||
List<UsernameLoginFailureEntity> userFailures = this.importReader.readEntities(fileName, UsernameLoginFailureEntity.class);
|
||||
for (UsernameLoginFailureEntity entity : userFailures) {
|
||||
RealmModel realm = keycloakSession.getRealm(entity.getRealmId());
|
||||
UsernameLoginFailureModel model = realm.addUserLoginFailure(entity.getUsername());
|
||||
|
||||
this.propertiesManager.setBasicPropertiesToModel(model , entity);
|
||||
|
||||
for (int i=0 ; i<entity.getNumFailures() ; i++) {
|
||||
model.incrementFailures();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package org.keycloak.exportimport.io;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public interface ExportImportIOProvider {
|
||||
|
||||
String getId();
|
||||
|
||||
ExportWriter getExportWriter();
|
||||
|
||||
ImportReader getImportReader();
|
||||
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package org.keycloak.exportimport.io;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public interface ExportWriter {
|
||||
|
||||
<T> void writeEntities(String fileName, List<T> entities);
|
||||
|
||||
void closeExportWriter();
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package org.keycloak.exportimport.io;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public interface ImportReader {
|
||||
|
||||
<T> List<T> readEntities(String fileName, Class<T> entityClass);
|
||||
|
||||
void closeImportReader();
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package org.keycloak.exportimport.io.directory;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.keycloak.exportimport.io.ExportImportIOProvider;
|
||||
import org.keycloak.exportimport.io.ExportWriter;
|
||||
import org.keycloak.exportimport.io.ImportReader;
|
||||
import org.keycloak.models.Config;
|
||||
|
||||
/**
|
||||
* Export/import into JSON files inside "tmp" directory. This implementation is used mainly for testing
|
||||
* (shouldn't be used in production due to passwords in JSON files)
|
||||
*
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class TmpDirExportImportIOProvider implements ExportImportIOProvider {
|
||||
|
||||
public static final String PROVIDER_ID = "dir";
|
||||
|
||||
@Override
|
||||
public ExportWriter getExportWriter() {
|
||||
String dir = Config.getExportImportDir();
|
||||
return dir!=null ? new TmpDirExportWriter(new File(dir)) : new TmpDirExportWriter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImportReader getImportReader() {
|
||||
String dir = Config.getExportImportDir();
|
||||
return dir!=null ? new TmpDirImportReader(new File(dir)) : new TmpDirImportReader();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
package org.keycloak.exportimport.io.directory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import org.codehaus.jackson.map.ObjectMapper;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.exportimport.io.ExportWriter;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class TmpDirExportWriter implements ExportWriter {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(TmpDirExportWriter.class);
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
private final File rootDirectory;
|
||||
|
||||
public TmpDirExportWriter() {
|
||||
// Determine system tmp directory
|
||||
String tempDir = System.getProperty("java.io.tmpdir");
|
||||
|
||||
// Delete and recreate directory inside tmp
|
||||
this.rootDirectory = new File(tempDir + "/keycloak-export");
|
||||
if (this.rootDirectory .exists()) {
|
||||
recursiveDeleteDir(this.rootDirectory );
|
||||
}
|
||||
this.rootDirectory.mkdirs();
|
||||
|
||||
logger.infof("Exporting into directory %s", this.rootDirectory.getAbsolutePath());
|
||||
this.objectMapper = getObjectMapper();
|
||||
}
|
||||
|
||||
public TmpDirExportWriter(File rootDirectory) {
|
||||
this.rootDirectory = rootDirectory;
|
||||
this.rootDirectory.mkdirs();
|
||||
this.objectMapper = getObjectMapper();
|
||||
|
||||
logger.infof("Exporting into directory %s", this.rootDirectory.getAbsolutePath());
|
||||
}
|
||||
|
||||
private ObjectMapper getObjectMapper() {
|
||||
return JsonSerialization.prettyMapper;
|
||||
}
|
||||
|
||||
protected boolean recursiveDeleteDir(File dirPath) {
|
||||
if (dirPath.exists()) {
|
||||
File[] files = dirPath.listFiles();
|
||||
for (int i = 0; i < files.length; i++) {
|
||||
if (files[i].isDirectory()) {
|
||||
recursiveDeleteDir(files[i]);
|
||||
} else {
|
||||
files[i].delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dirPath.exists())
|
||||
return dirPath.delete();
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void writeEntities(String fileName, List<T> entities) {
|
||||
try {
|
||||
File file = new File(this.rootDirectory, fileName);
|
||||
FileOutputStream stream = new FileOutputStream(file);
|
||||
this.objectMapper.writeValue(stream, entities);
|
||||
} catch (IOException ioe) {
|
||||
throw new RuntimeException(ioe);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeExportWriter() {
|
||||
//To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
package org.keycloak.exportimport.io.directory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.codehaus.jackson.map.ObjectMapper;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.exportimport.io.ImportReader;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class TmpDirImportReader implements ImportReader {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(TmpDirImportReader.class);
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
private final File rootDirectory;
|
||||
|
||||
public TmpDirImportReader() {
|
||||
// Determine system tmp directory
|
||||
String tempDir = System.getProperty("java.io.tmpdir");
|
||||
|
||||
// Delete and recreate directory inside tmp
|
||||
this.rootDirectory = new File(tempDir + "/keycloak-export");
|
||||
if (!this.rootDirectory .exists()) {
|
||||
throw new IllegalStateException("Directory " + this.rootDirectory + " doesn't exists");
|
||||
}
|
||||
|
||||
logger.infof("Importing from directory %s", this.rootDirectory.getAbsolutePath());
|
||||
this.objectMapper = getObjectMapper();
|
||||
}
|
||||
|
||||
public TmpDirImportReader(File rootDirectory) {
|
||||
this.rootDirectory = rootDirectory;
|
||||
this.objectMapper = getObjectMapper();
|
||||
|
||||
logger.infof("Importing from directory %s", this.rootDirectory.getAbsolutePath());
|
||||
}
|
||||
|
||||
private ObjectMapper getObjectMapper() {
|
||||
return JsonSerialization.prettyMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> readEntities(String fileName, Class<T> entityClass) {
|
||||
try {
|
||||
File file = new File(this.rootDirectory, fileName);
|
||||
FileInputStream stream = new FileInputStream(file);
|
||||
T[] template = (T[]) Array.newInstance(entityClass, 0);
|
||||
T[] result = (T[])this.objectMapper.readValue(stream, template.getClass());
|
||||
return Arrays.asList(result);
|
||||
} catch (IOException ioe) {
|
||||
throw new RuntimeException(ioe);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeImportReader() {
|
||||
//TODO: Should directory be deleted after import?
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
package org.keycloak.exportimport.io.zip;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import de.idyl.winzipaes.AesZipFileEncrypter;
|
||||
import de.idyl.winzipaes.impl.AESEncrypter;
|
||||
import de.idyl.winzipaes.impl.AESEncrypterBC;
|
||||
import org.codehaus.jackson.map.ObjectMapper;
|
||||
import org.keycloak.exportimport.io.ExportWriter;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class EncryptedZIPExportWriter implements ExportWriter {
|
||||
|
||||
private final File zipFile;
|
||||
private final ObjectMapper objectMapper;
|
||||
private final String password;
|
||||
|
||||
private final AesZipFileEncrypter encrypter;
|
||||
|
||||
|
||||
public EncryptedZIPExportWriter(String zipFileName, String password) {
|
||||
try {
|
||||
this.zipFile = new File(zipFileName);
|
||||
if (zipFile.exists()) {
|
||||
throw new IllegalStateException("File " + zipFileName + " already exists");
|
||||
}
|
||||
|
||||
this.objectMapper = JsonSerialization.mapper;
|
||||
|
||||
AESEncrypter encrypter = new AESEncrypterBC();
|
||||
this.encrypter = new AesZipFileEncrypter(this.zipFile, encrypter);
|
||||
this.password = password;
|
||||
} catch (IOException ioe) {
|
||||
throw new RuntimeException(ioe);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void writeEntities(String fileName, List<T> entities) {
|
||||
try {
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
this.objectMapper.writeValue(stream, entities);
|
||||
|
||||
byte[] byteArray = stream.toByteArray();
|
||||
this.encrypter.add(fileName, new ByteArrayInputStream(byteArray), this.password);
|
||||
} catch (IOException ioe) {
|
||||
throw new RuntimeException(ioe);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeExportWriter() {
|
||||
try {
|
||||
this.encrypter.close();
|
||||
} catch (IOException ioe) {
|
||||
throw new RuntimeException(ioe);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package org.keycloak.exportimport.io.zip;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.exportimport.io.ExportImportIOProvider;
|
||||
import org.keycloak.exportimport.io.ExportWriter;
|
||||
import org.keycloak.exportimport.io.ImportReader;
|
||||
import org.keycloak.models.Config;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class EncryptedZIPIOProvider implements ExportImportIOProvider {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(EncryptedZIPIOProvider.class);
|
||||
|
||||
public static final String PROVIDER_ID = "zip";
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExportWriter getExportWriter() {
|
||||
String zipFile = Config.getExportImportZipFile();
|
||||
String zipPassword = Config.getExportImportZipPassword();
|
||||
logger.infof("Using zip for export: " + zipFile);
|
||||
|
||||
if (zipFile==null || zipPassword==null) {
|
||||
throw new IllegalArgumentException("zipFile or zipPassword are null");
|
||||
}
|
||||
|
||||
return new EncryptedZIPExportWriter(zipFile, zipPassword);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImportReader getImportReader() {
|
||||
String zipFile = Config.getExportImportZipFile();
|
||||
String zipPassword = Config.getExportImportZipPassword();
|
||||
logger.infof("Using zip for import: " + zipFile);
|
||||
|
||||
if (zipFile==null || zipPassword==null) {
|
||||
throw new IllegalArgumentException("zipFile or zipPassword are null");
|
||||
}
|
||||
|
||||
return new EncryptedZIPImportReader(zipFile, zipPassword);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
package org.keycloak.exportimport.io.zip;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import de.idyl.winzipaes.AesZipFileDecrypter;
|
||||
import de.idyl.winzipaes.impl.AESDecrypter;
|
||||
import de.idyl.winzipaes.impl.AESDecrypterBC;
|
||||
import org.codehaus.jackson.map.ObjectMapper;
|
||||
import org.keycloak.exportimport.io.ImportReader;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class EncryptedZIPImportReader implements ImportReader {
|
||||
|
||||
private final File zipFile;
|
||||
private final ObjectMapper objectMapper;
|
||||
private final String password;
|
||||
|
||||
private final AesZipFileDecrypter decrypter;
|
||||
|
||||
|
||||
public EncryptedZIPImportReader(String zipFileName, String password) {
|
||||
try {
|
||||
this.zipFile = new File(zipFileName);
|
||||
if (!zipFile.exists()) {
|
||||
throw new IllegalStateException("File " + zipFileName + " doesn't exists");
|
||||
}
|
||||
|
||||
this.objectMapper = JsonSerialization.mapper;
|
||||
|
||||
AESDecrypter decrypter = new AESDecrypterBC();
|
||||
this.decrypter = new AesZipFileDecrypter(this.zipFile, decrypter);
|
||||
this.password = password;
|
||||
} catch (IOException ioe) {
|
||||
throw new RuntimeException(ioe);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> readEntities(String fileName, Class<T> entityClass) {
|
||||
try {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
this.decrypter.extractEntry(this.decrypter.getEntry(fileName), bos, this.password);
|
||||
|
||||
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
|
||||
T[] template = (T[]) Array.newInstance(entityClass, 0);
|
||||
T[] result = (T[])this.objectMapper.readValue(bis, template.getClass());
|
||||
return Arrays.asList(result);
|
||||
} catch (Exception ioe) {
|
||||
throw new RuntimeException(ioe);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeImportReader() {
|
||||
try {
|
||||
this.decrypter.close();
|
||||
} catch (Exception ioe) {
|
||||
throw new RuntimeException(ioe);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
org.keycloak.exportimport.ExportImportProviderImpl
|
|
@ -0,0 +1,2 @@
|
|||
org.keycloak.exportimport.io.directory.TmpDirExportImportIOProvider
|
||||
org.keycloak.exportimport.io.zip.EncryptedZIPIOProvider
|
|
@ -0,0 +1,110 @@
|
|||
package org.keycloak.exportimport;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.model.test.AbstractModelTest;
|
||||
import org.keycloak.model.test.ImportTest;
|
||||
import org.keycloak.models.Config;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.services.managers.ApplianceBootstrap;
|
||||
import org.keycloak.services.managers.RealmManager;
|
||||
import org.keycloak.services.resources.KeycloakApplication;
|
||||
import org.keycloak.util.ProviderLoader;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public abstract class ExportImportTestBase {
|
||||
|
||||
protected KeycloakSessionFactory factory;
|
||||
|
||||
protected KeycloakSession identitySession;
|
||||
protected RealmManager realmManager;
|
||||
|
||||
@Test
|
||||
public void testExportImport() throws Exception {
|
||||
// Init JPA model
|
||||
Config.setModelProvider(getExportModelProvider());
|
||||
factory = KeycloakApplication.createSessionFactory();
|
||||
|
||||
// Bootstrap admin realm
|
||||
beginTransaction();
|
||||
new ApplianceBootstrap().bootstrap(identitySession, "/auth");
|
||||
commitTransaction();
|
||||
|
||||
// Classic import of realm to JPA model
|
||||
beginTransaction();
|
||||
RealmRepresentation rep = AbstractModelTest.loadJson("testrealm.json");
|
||||
realmManager = new RealmManager(identitySession);
|
||||
RealmModel realm = realmManager.createRealm("demo", rep.getRealm());
|
||||
realmManager.importRealm(rep, realm);
|
||||
|
||||
commitTransaction();
|
||||
|
||||
// Full export of realm
|
||||
exportModel(factory);
|
||||
|
||||
beginTransaction();
|
||||
realm = identitySession.getRealm("demo");
|
||||
String wburkeId = realm.getUser("wburke").getId();
|
||||
String appId = realm.getApplicationByName("Application").getId();
|
||||
|
||||
// Commit transaction and close JPA now
|
||||
commitTransaction();
|
||||
factory.close();
|
||||
|
||||
// Bootstrap mongo session and factory
|
||||
Config.setModelProvider(getImportModelProvider());
|
||||
factory = KeycloakApplication.createSessionFactory();
|
||||
|
||||
// Full import of previous export into mongo
|
||||
importModel(factory);
|
||||
|
||||
// Verify it's imported in mongo (reusing ImportTest)
|
||||
beginTransaction();
|
||||
RealmModel importedRealm = identitySession.getRealm("demo");
|
||||
System.out.println("Exported realm: " + realm + ", Imported realm: " + importedRealm);
|
||||
|
||||
Assert.assertEquals(wburkeId, importedRealm.getUser("wburke").getId());
|
||||
Assert.assertEquals(appId, importedRealm.getApplicationByName("Application").getId());
|
||||
ImportTest.assertDataImportedInRealm(importedRealm);
|
||||
|
||||
// Commit and close Mongo
|
||||
commitTransaction();
|
||||
factory.close();
|
||||
}
|
||||
|
||||
protected abstract String getExportModelProvider();
|
||||
|
||||
protected abstract String getImportModelProvider();
|
||||
|
||||
protected abstract void exportModel(KeycloakSessionFactory factory);
|
||||
|
||||
protected abstract void importModel(KeycloakSessionFactory factory);
|
||||
|
||||
protected void beginTransaction() {
|
||||
identitySession = factory.createSession();
|
||||
identitySession.getTransaction().begin();
|
||||
realmManager = new RealmManager(identitySession);
|
||||
}
|
||||
|
||||
protected void commitTransaction() {
|
||||
identitySession.getTransaction().commit();
|
||||
identitySession.close();
|
||||
}
|
||||
|
||||
protected ExportImportProvider getExportImportProvider() {
|
||||
Iterator<ExportImportProvider> providers = ProviderLoader.load(ExportImportProvider.class).iterator();
|
||||
|
||||
if (providers.hasNext()) {
|
||||
return providers.next();
|
||||
} else {
|
||||
throw new IllegalStateException("ExportImportProvider not found");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package org.keycloak.exportimport;
|
||||
|
||||
import org.keycloak.exportimport.io.directory.TmpDirExportImportIOProvider;
|
||||
import org.keycloak.models.Config;
|
||||
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>
|
||||
*/
|
||||
public class JPAToMongoExportImportTest extends ExportImportTestBase {
|
||||
|
||||
@Override
|
||||
protected String getExportModelProvider() {
|
||||
return "jpa";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getImportModelProvider() {
|
||||
return "mongo";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void exportModel(KeycloakSessionFactory factory) {
|
||||
Config.setExportImportAction(ExportImportProviderImpl.ACTION_EXPORT);
|
||||
Config.setExportImportProvider(TmpDirExportImportIOProvider.PROVIDER_ID);
|
||||
getExportImportProvider().checkExportImport(factory);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void importModel(KeycloakSessionFactory factory) {
|
||||
Config.setExportImportAction(ExportImportProviderImpl.ACTION_IMPORT);
|
||||
Config.setExportImportProvider(TmpDirExportImportIOProvider.PROVIDER_ID);
|
||||
getExportImportProvider().checkExportImport(factory);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
package org.keycloak.exportimport;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.keycloak.exportimport.io.zip.EncryptedZIPIOProvider;
|
||||
import org.keycloak.models.Config;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
|
||||
/**
|
||||
* 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>
|
||||
*/
|
||||
public class MongoToJPAExportImportTest extends ExportImportTestBase {
|
||||
|
||||
private static final String zipFile = "keycloak-export.zip";
|
||||
|
||||
@Override
|
||||
protected String getExportModelProvider() {
|
||||
return "mongo";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getImportModelProvider() {
|
||||
return "jpa";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void exportModel(KeycloakSessionFactory factory) {
|
||||
Config.setExportImportAction(ExportImportProviderImpl.ACTION_EXPORT);
|
||||
Config.setExportImportProvider(EncryptedZIPIOProvider.PROVIDER_ID);
|
||||
File zipFile = getZipFile();
|
||||
Config.setExportImportZipFile(zipFile.getAbsolutePath());
|
||||
Config.setExportImportZipPassword("password123");
|
||||
|
||||
if (zipFile.exists()) {
|
||||
zipFile.delete();
|
||||
}
|
||||
|
||||
new ExportImportProviderImpl().checkExportImport(factory);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void importModel(KeycloakSessionFactory factory) {
|
||||
Config.setExportImportAction(ExportImportProviderImpl.ACTION_IMPORT);
|
||||
Config.setExportImportProvider(EncryptedZIPIOProvider.PROVIDER_ID);
|
||||
File zipFile = getZipFile();
|
||||
Config.setExportImportZipFile(zipFile.getAbsolutePath());
|
||||
Config.setExportImportZipPassword("password-invalid");
|
||||
|
||||
// Try invalid password
|
||||
try {
|
||||
new ExportImportProviderImpl().checkExportImport(factory);
|
||||
Assert.fail("Not expected to be here. Exception should be thrown");
|
||||
} catch (Exception e) {};
|
||||
|
||||
Config.setExportImportZipPassword("password123");
|
||||
new ExportImportProviderImpl().checkExportImport(factory);
|
||||
|
||||
if (zipFile.exists()) {
|
||||
zipFile.delete();
|
||||
}
|
||||
}
|
||||
|
||||
private File getZipFile() {
|
||||
String tempDir = System.getProperty("java.io.tmpdir");
|
||||
return new File(tempDir + File.separator + "keycloak-export.zip");
|
||||
}
|
||||
}
|
22
export-import/pom.xml
Normal file
22
export-import/pom.xml
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?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-1-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<artifactId>keycloak-export-import</artifactId>
|
||||
<name>Keycloak Export And Import</name>
|
||||
<description/>
|
||||
|
||||
<modules>
|
||||
<module>export-import-api</module>
|
||||
<module>export-import-impl</module>
|
||||
</modules>
|
||||
|
||||
</project>
|
|
@ -7,8 +7,10 @@ package org.keycloak.models;
|
|||
*/
|
||||
public class AuthenticationLinkModel {
|
||||
|
||||
private final String authProvider;
|
||||
private final String authUserId;
|
||||
private String authProvider;
|
||||
private String authUserId;
|
||||
|
||||
public AuthenticationLinkModel() {};
|
||||
|
||||
public AuthenticationLinkModel(String authProvider, String authUserId) {
|
||||
this.authProvider = authProvider;
|
||||
|
@ -19,7 +21,15 @@ public class AuthenticationLinkModel {
|
|||
return authUserId;
|
||||
}
|
||||
|
||||
public void setAuthUserId(String authUserId) {
|
||||
this.authUserId = authUserId;
|
||||
}
|
||||
|
||||
public String getAuthProvider() {
|
||||
return authProvider;
|
||||
}
|
||||
|
||||
public void setAuthProvider(String authProvider) {
|
||||
this.authProvider = authProvider;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,8 @@ public class AuthenticationProviderModel {
|
|||
private boolean passwordUpdateSupported = true;
|
||||
private Map<String, String> config;
|
||||
|
||||
public AuthenticationProviderModel() {};
|
||||
|
||||
public AuthenticationProviderModel(String providerName, boolean passwordUpdateSupported, Map<String, String> config) {
|
||||
this.providerName = providerName;
|
||||
this.passwordUpdateSupported = passwordUpdateSupported;
|
||||
|
|
|
@ -33,6 +33,16 @@ public class Config {
|
|||
public static final String TIMER_PROVIDER_KEY = "keycloak.timer";
|
||||
public static final String TIMER_PROVIDER_DEFAULT = "basic";
|
||||
|
||||
public static final String EXPORT_IMPORT_ACTION = "keycloak.migration.action";
|
||||
public static final String EXPORT_IMPORT_PROVIDER = "keycloak.migration.provider";
|
||||
public static final String EXPORT_IMPORT_PROVIDER_DEFAULT = "zip";
|
||||
// used for "directory" provider
|
||||
public static final String EXPORT_IMPORT_DIR = "keycloak.migration.dir";
|
||||
// used for "zip" provider
|
||||
public static final String EXPORT_IMPORT_ZIP_FILE = "keycloak.migration.zipFile";
|
||||
public static final String EXPORT_IMPORT_ZIP_PASSWORD = "keycloak.migration.zipPassword";
|
||||
|
||||
|
||||
public static String getAdminRealm() {
|
||||
return System.getProperty(ADMIN_REALM_KEY, ADMIN_REALM_DEFAULT);
|
||||
}
|
||||
|
@ -117,4 +127,45 @@ public class Config {
|
|||
System.setProperty(THEME_ADMIN_KEY, adminTheme);
|
||||
}
|
||||
|
||||
// EXPORT + IMPORT
|
||||
|
||||
public static String getExportImportAction() {
|
||||
return System.getProperty(EXPORT_IMPORT_ACTION);
|
||||
}
|
||||
|
||||
public static void setExportImportAction(String exportImportAction) {
|
||||
System.setProperty(EXPORT_IMPORT_ACTION, exportImportAction);
|
||||
}
|
||||
|
||||
public static String getExportImportProvider() {
|
||||
return System.getProperty(EXPORT_IMPORT_PROVIDER, EXPORT_IMPORT_PROVIDER_DEFAULT);
|
||||
}
|
||||
|
||||
public static void setExportImportProvider(String exportImportProvider) {
|
||||
System.setProperty(EXPORT_IMPORT_PROVIDER, exportImportProvider);
|
||||
}
|
||||
|
||||
public static String getExportImportDir() {
|
||||
return System.getProperty(EXPORT_IMPORT_DIR);
|
||||
}
|
||||
|
||||
public static void setExportImportDir(String exportImportDir) {
|
||||
System.setProperty(EXPORT_IMPORT_DIR, exportImportDir);
|
||||
}
|
||||
|
||||
public static String getExportImportZipFile() {
|
||||
return System.getProperty(EXPORT_IMPORT_ZIP_FILE);
|
||||
}
|
||||
|
||||
public static void setExportImportZipFile(String exportImportZipFile) {
|
||||
System.setProperty(EXPORT_IMPORT_ZIP_FILE, exportImportZipFile);
|
||||
}
|
||||
|
||||
public static String getExportImportZipPassword() {
|
||||
return System.getProperty(EXPORT_IMPORT_ZIP_PASSWORD);
|
||||
}
|
||||
|
||||
public static void setExportImportZipPassword(String exportImportZipPassword) {
|
||||
System.setProperty(EXPORT_IMPORT_ZIP_PASSWORD, exportImportZipPassword);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,5 +16,7 @@ public interface KeycloakSession {
|
|||
List<RealmModel> getRealms();
|
||||
boolean removeRealm(String id);
|
||||
|
||||
void removeAllData();
|
||||
|
||||
void close();
|
||||
}
|
||||
|
|
|
@ -109,12 +109,18 @@ public interface RealmModel extends RoleContainerModel, RoleMapperModel, ScopeMa
|
|||
|
||||
void updateCredential(UserModel user, UserCredentialModel cred);
|
||||
|
||||
List<UserCredentialValueModel> getCredentialsDirectly(UserModel user);
|
||||
|
||||
void updateCredentialDirectly(UserModel user, UserCredentialValueModel cred);
|
||||
|
||||
UserModel getUser(String name);
|
||||
|
||||
UserModel getUserByEmail(String email);
|
||||
|
||||
UserModel getUserById(String name);
|
||||
|
||||
UserModel addUser(String id, String username);
|
||||
|
||||
UserModel addUser(String username);
|
||||
|
||||
boolean removeUser(String name);
|
||||
|
@ -135,6 +141,8 @@ public interface RealmModel extends RoleContainerModel, RoleMapperModel, ScopeMa
|
|||
|
||||
ApplicationModel addApplication(String name);
|
||||
|
||||
ApplicationModel addApplication(String id, String name);
|
||||
|
||||
boolean removeApplication(String id);
|
||||
|
||||
ApplicationModel getApplicationById(String id);
|
||||
|
@ -160,12 +168,13 @@ public interface RealmModel extends RoleContainerModel, RoleMapperModel, ScopeMa
|
|||
|
||||
void setSocial(boolean social);
|
||||
|
||||
public boolean isUpdateProfileOnInitialSocialLogin();
|
||||
boolean isUpdateProfileOnInitialSocialLogin();
|
||||
|
||||
public void setUpdateProfileOnInitialSocialLogin(boolean updateProfileOnInitialSocialLogin);
|
||||
void setUpdateProfileOnInitialSocialLogin(boolean updateProfileOnInitialSocialLogin);
|
||||
|
||||
public UsernameLoginFailureModel getUserLoginFailure(String username);
|
||||
UsernameLoginFailureModel getUserLoginFailure(String username);
|
||||
UsernameLoginFailureModel addUserLoginFailure(String username);
|
||||
List<UsernameLoginFailureModel> getAllUserLoginFailures();
|
||||
|
||||
List<UserModel> getUsers();
|
||||
|
||||
|
@ -175,6 +184,8 @@ public interface RealmModel extends RoleContainerModel, RoleMapperModel, ScopeMa
|
|||
|
||||
OAuthClientModel addOAuthClient(String name);
|
||||
|
||||
OAuthClientModel addOAuthClient(String id, String name);
|
||||
|
||||
OAuthClientModel getOAuthClient(String name);
|
||||
OAuthClientModel getOAuthClientById(String id);
|
||||
boolean removeOAuthClient(String id);
|
||||
|
|
|
@ -12,6 +12,8 @@ public interface RoleContainerModel {
|
|||
|
||||
RoleModel addRole(String name);
|
||||
|
||||
RoleModel addRole(String id, String name);
|
||||
|
||||
boolean removeRole(RoleModel role);
|
||||
|
||||
Set<RoleModel> getRoles();
|
||||
|
|
|
@ -9,6 +9,8 @@ public class SocialLinkModel {
|
|||
private String socialProvider;
|
||||
private String socialUsername;
|
||||
|
||||
public SocialLinkModel() {};
|
||||
|
||||
public SocialLinkModel(String socialProvider, String socialUserId, String socialUsername) {
|
||||
this.socialUserId = socialUserId;
|
||||
this.socialProvider = socialProvider;
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
package org.keycloak.models;
|
||||
|
||||
/**
|
||||
* Used just in cases when we want to "directly" update or retrieve the hash or salt of user credential (For example during export/import)
|
||||
*
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class UserCredentialValueModel {
|
||||
|
||||
private String type;
|
||||
private String value;
|
||||
private String device;
|
||||
private byte[] salt;
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getDevice() {
|
||||
return device;
|
||||
}
|
||||
|
||||
public void setDevice(String device) {
|
||||
this.device = device;
|
||||
}
|
||||
|
||||
public byte[] getSalt() {
|
||||
return salt;
|
||||
}
|
||||
|
||||
public void setSalt(byte[] salt) {
|
||||
this.salt = salt;
|
||||
}
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
package org.keycloak.models.mongo.api;
|
||||
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
package org.keycloak.models.entities;
|
||||
|
||||
/**
|
||||
* Base for the identifiable entity
|
||||
*
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class AbstractMongoIdentifiableEntity implements MongoIdentifiableEntity {
|
||||
public class AbstractIdentifiableEntity {
|
||||
|
||||
private String id;
|
||||
|
||||
|
@ -17,11 +17,6 @@ public class AbstractMongoIdentifiableEntity implements MongoIdentifiableEntity
|
|||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterRemove(MongoStoreInvocationContext invocationContext) {
|
||||
// Empty by default
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) return true;
|
||||
|
@ -30,7 +25,7 @@ public class AbstractMongoIdentifiableEntity implements MongoIdentifiableEntity
|
|||
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
AbstractMongoIdentifiableEntity that = (AbstractMongoIdentifiableEntity) o;
|
||||
AbstractIdentifiableEntity that = (AbstractIdentifiableEntity) o;
|
||||
|
||||
if (!getId().equals(that.getId())) return false;
|
||||
|
|
@ -1,20 +1,11 @@
|
|||
package org.keycloak.models.mongo.keycloak.entities;
|
||||
package org.keycloak.models.entities;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.QueryBuilder;
|
||||
import org.keycloak.models.mongo.api.MongoCollection;
|
||||
import org.keycloak.models.mongo.api.MongoField;
|
||||
import org.keycloak.models.mongo.api.MongoIndex;
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
@MongoCollection(collectionName = "applications")
|
||||
@MongoIndex(fields = { "realmId", "name" }, unique = true)
|
||||
public class ApplicationEntity extends ClientEntity {
|
||||
|
||||
private boolean surrogateAuthRequired;
|
||||
|
@ -25,7 +16,6 @@ public class ApplicationEntity extends ClientEntity {
|
|||
// We are using names of defaultRoles (not ids)
|
||||
private List<String> defaultRoles = new ArrayList<String>();
|
||||
|
||||
@MongoField
|
||||
public boolean isSurrogateAuthRequired() {
|
||||
return surrogateAuthRequired;
|
||||
}
|
||||
|
@ -34,7 +24,6 @@ public class ApplicationEntity extends ClientEntity {
|
|||
this.surrogateAuthRequired = surrogateAuthRequired;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public String getManagementUrl() {
|
||||
return managementUrl;
|
||||
}
|
||||
|
@ -43,7 +32,6 @@ public class ApplicationEntity extends ClientEntity {
|
|||
this.managementUrl = managementUrl;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public String getBaseUrl() {
|
||||
return baseUrl;
|
||||
}
|
||||
|
@ -52,16 +40,6 @@ public class ApplicationEntity extends ClientEntity {
|
|||
this.baseUrl = baseUrl;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public List<String> getDefaultRoles() {
|
||||
return defaultRoles;
|
||||
}
|
||||
|
||||
public void setDefaultRoles(List<String> defaultRoles) {
|
||||
this.defaultRoles = defaultRoles;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public boolean isBearerOnly() {
|
||||
return bearerOnly;
|
||||
}
|
||||
|
@ -70,12 +48,12 @@ public class ApplicationEntity extends ClientEntity {
|
|||
this.bearerOnly = bearerOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterRemove(MongoStoreInvocationContext context) {
|
||||
// Remove all roles, which belongs to this application
|
||||
DBObject query = new QueryBuilder()
|
||||
.and("applicationId").is(getId())
|
||||
.get();
|
||||
context.getMongoStore().removeEntities(RoleEntity.class, query, context);
|
||||
public List<String> getDefaultRoles() {
|
||||
return defaultRoles;
|
||||
}
|
||||
|
||||
public void setDefaultRoles(List<String> defaultRoles) {
|
||||
this.defaultRoles = defaultRoles;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +1,13 @@
|
|||
package org.keycloak.models.mongo.keycloak.entities;
|
||||
|
||||
import org.keycloak.models.mongo.api.MongoEntity;
|
||||
import org.keycloak.models.mongo.api.MongoField;
|
||||
package org.keycloak.models.entities;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class AuthenticationLinkEntity implements MongoEntity {
|
||||
public class AuthenticationLinkEntity {
|
||||
|
||||
private String authUserId;
|
||||
private String authProvider;
|
||||
|
||||
@MongoField
|
||||
public String getAuthUserId() {
|
||||
return authUserId;
|
||||
}
|
||||
|
@ -20,7 +16,6 @@ public class AuthenticationLinkEntity implements MongoEntity {
|
|||
this.authUserId = authUserId;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public String getAuthProvider() {
|
||||
return authProvider;
|
||||
}
|
|
@ -1,20 +1,16 @@
|
|||
package org.keycloak.models.mongo.keycloak.entities;
|
||||
package org.keycloak.models.entities;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.keycloak.models.mongo.api.MongoEntity;
|
||||
import org.keycloak.models.mongo.api.MongoField;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class AuthenticationProviderEntity implements MongoEntity {
|
||||
public class AuthenticationProviderEntity {
|
||||
|
||||
private String providerName;
|
||||
private boolean passwordUpdateSupported;
|
||||
private Map<String, String> config;
|
||||
|
||||
@MongoField
|
||||
public String getProviderName() {
|
||||
return providerName;
|
||||
}
|
||||
|
@ -23,7 +19,6 @@ public class AuthenticationProviderEntity implements MongoEntity {
|
|||
this.providerName = providerName;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public boolean isPasswordUpdateSupported() {
|
||||
return passwordUpdateSupported;
|
||||
}
|
||||
|
@ -32,7 +27,6 @@ public class AuthenticationProviderEntity implements MongoEntity {
|
|||
this.passwordUpdateSupported = passwordUpdateSupported;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public Map<String, String> getConfig() {
|
||||
return config;
|
||||
}
|
|
@ -1,16 +1,12 @@
|
|||
package org.keycloak.models.mongo.keycloak.entities;
|
||||
package org.keycloak.models.entities;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.keycloak.models.mongo.api.AbstractMongoIdentifiableEntity;
|
||||
import org.keycloak.models.mongo.api.MongoEntity;
|
||||
import org.keycloak.models.mongo.api.MongoField;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class ClientEntity extends AbstractMongoIdentifiableEntity implements MongoEntity {
|
||||
public class ClientEntity extends AbstractIdentifiableEntity {
|
||||
|
||||
private String name;
|
||||
private boolean enabled;
|
||||
|
@ -25,7 +21,6 @@ public class ClientEntity extends AbstractMongoIdentifiableEntity implements Mon
|
|||
private List<String> redirectUris = new ArrayList<String>();
|
||||
private List<String> scopeIds = new ArrayList<String>();
|
||||
|
||||
@MongoField
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
@ -34,7 +29,6 @@ public class ClientEntity extends AbstractMongoIdentifiableEntity implements Mon
|
|||
this.name = name;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
@ -43,7 +37,6 @@ public class ClientEntity extends AbstractMongoIdentifiableEntity implements Mon
|
|||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public String getSecret() {
|
||||
return secret;
|
||||
}
|
||||
|
@ -52,7 +45,6 @@ public class ClientEntity extends AbstractMongoIdentifiableEntity implements Mon
|
|||
this.secret = secret;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public long getAllowedClaimsMask() {
|
||||
return allowedClaimsMask;
|
||||
}
|
||||
|
@ -61,7 +53,6 @@ public class ClientEntity extends AbstractMongoIdentifiableEntity implements Mon
|
|||
this.allowedClaimsMask = allowedClaimsMask;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public int getNotBefore() {
|
||||
return notBefore;
|
||||
}
|
||||
|
@ -70,7 +61,6 @@ public class ClientEntity extends AbstractMongoIdentifiableEntity implements Mon
|
|||
this.notBefore = notBefore;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public boolean isPublicClient() {
|
||||
return publicClient;
|
||||
}
|
||||
|
@ -79,7 +69,6 @@ public class ClientEntity extends AbstractMongoIdentifiableEntity implements Mon
|
|||
this.publicClient = publicClient;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public String getRealmId() {
|
||||
return realmId;
|
||||
}
|
||||
|
@ -88,7 +77,6 @@ public class ClientEntity extends AbstractMongoIdentifiableEntity implements Mon
|
|||
this.realmId = realmId;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public List<String> getWebOrigins() {
|
||||
return webOrigins;
|
||||
}
|
||||
|
@ -97,7 +85,6 @@ public class ClientEntity extends AbstractMongoIdentifiableEntity implements Mon
|
|||
this.webOrigins = webOrigins;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public List<String> getRedirectUris() {
|
||||
return redirectUris;
|
||||
}
|
||||
|
@ -106,7 +93,6 @@ public class ClientEntity extends AbstractMongoIdentifiableEntity implements Mon
|
|||
this.redirectUris = redirectUris;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public List<String> getScopeIds() {
|
||||
return scopeIds;
|
||||
}
|
|
@ -1,19 +1,15 @@
|
|||
package org.keycloak.models.mongo.keycloak.entities;
|
||||
|
||||
import org.keycloak.models.mongo.api.MongoEntity;
|
||||
import org.keycloak.models.mongo.api.MongoField;
|
||||
package org.keycloak.models.entities;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class CredentialEntity implements MongoEntity {
|
||||
public class CredentialEntity {
|
||||
|
||||
private String type;
|
||||
private String value;
|
||||
private String device;
|
||||
private byte[] salt;
|
||||
|
||||
@MongoField
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
@ -22,7 +18,6 @@ public class CredentialEntity implements MongoEntity {
|
|||
this.type = type;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
@ -31,7 +26,6 @@ public class CredentialEntity implements MongoEntity {
|
|||
this.value = value;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public String getDevice() {
|
||||
return device;
|
||||
}
|
||||
|
@ -40,7 +34,6 @@ public class CredentialEntity implements MongoEntity {
|
|||
this.device = device;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public byte[] getSalt() {
|
||||
return salt;
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package org.keycloak.models.entities;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class OAuthClientEntity extends ClientEntity {
|
||||
}
|
|
@ -1,429 +1,357 @@
|
|||
package org.keycloak.models.mongo.keycloak.entities;
|
||||
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.QueryBuilder;
|
||||
import org.keycloak.models.ApplicationModel;
|
||||
import org.keycloak.models.mongo.api.AbstractMongoIdentifiableEntity;
|
||||
import org.keycloak.models.mongo.api.MongoCollection;
|
||||
import org.keycloak.models.mongo.api.MongoEntity;
|
||||
import org.keycloak.models.mongo.api.MongoField;
|
||||
import org.keycloak.models.mongo.api.MongoIndex;
|
||||
import org.keycloak.models.mongo.api.MongoIndexes;
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
@MongoCollection(collectionName = "realms")
|
||||
@MongoIndex(fields = { "name" }, unique = true)
|
||||
public class RealmEntity extends AbstractMongoIdentifiableEntity implements MongoEntity {
|
||||
|
||||
private String name;
|
||||
private boolean enabled;
|
||||
private boolean sslNotRequired;
|
||||
private boolean registrationAllowed;
|
||||
private boolean rememberMe;
|
||||
private boolean verifyEmail;
|
||||
private boolean resetPasswordAllowed;
|
||||
private boolean social;
|
||||
private boolean updateProfileOnInitialSocialLogin;
|
||||
private String passwordPolicy;
|
||||
//--- brute force settings
|
||||
private boolean bruteForceProtected;
|
||||
private int maxFailureWaitSeconds;
|
||||
private int minimumQuickLoginWaitSeconds;
|
||||
private int waitIncrementSeconds;
|
||||
private long quickLoginCheckMilliSeconds;
|
||||
private int maxDeltaTimeSeconds;
|
||||
private int failureFactor;
|
||||
//--- end brute force settings
|
||||
|
||||
private int centralLoginLifespan;
|
||||
private int accessTokenLifespan;
|
||||
private int accessCodeLifespan;
|
||||
private int accessCodeLifespanUserAction;
|
||||
private int refreshTokenLifespan;
|
||||
private int notBefore;
|
||||
|
||||
private String publicKeyPem;
|
||||
private String privateKeyPem;
|
||||
|
||||
private String loginTheme;
|
||||
private String accountTheme;
|
||||
|
||||
// We are using names of defaultRoles (not ids)
|
||||
private List<String> defaultRoles = new ArrayList<String>();
|
||||
|
||||
private List<RequiredCredentialEntity> requiredCredentials = new ArrayList<RequiredCredentialEntity>();
|
||||
private List<AuthenticationProviderEntity> authenticationProviders = new ArrayList<AuthenticationProviderEntity>();
|
||||
|
||||
private Map<String, String> smtpConfig = new HashMap<String, String>();
|
||||
private Map<String, String> socialConfig = new HashMap<String, String>();
|
||||
private Map<String, String> ldapServerConfig;
|
||||
|
||||
private boolean auditEnabled;
|
||||
private long auditExpiration;
|
||||
private List<String> auditListeners = new ArrayList<String>();
|
||||
|
||||
private String adminAppId;
|
||||
|
||||
@MongoField
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String realmName) {
|
||||
this.name = realmName;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public boolean isSslNotRequired() {
|
||||
return sslNotRequired;
|
||||
}
|
||||
|
||||
public void setSslNotRequired(boolean sslNotRequired) {
|
||||
this.sslNotRequired = sslNotRequired;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public boolean isRegistrationAllowed() {
|
||||
return registrationAllowed;
|
||||
}
|
||||
|
||||
public void setRegistrationAllowed(boolean registrationAllowed) {
|
||||
this.registrationAllowed = registrationAllowed;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public boolean isRememberMe() {
|
||||
return rememberMe;
|
||||
}
|
||||
|
||||
public void setRememberMe(boolean rememberMe) {
|
||||
this.rememberMe = rememberMe;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public boolean isVerifyEmail() {
|
||||
return verifyEmail;
|
||||
}
|
||||
|
||||
public void setVerifyEmail(boolean verifyEmail) {
|
||||
this.verifyEmail = verifyEmail;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public boolean isResetPasswordAllowed() {
|
||||
return resetPasswordAllowed;
|
||||
}
|
||||
|
||||
public void setResetPasswordAllowed(boolean resetPasswordAllowed) {
|
||||
this.resetPasswordAllowed = resetPasswordAllowed;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public boolean isSocial() {
|
||||
return social;
|
||||
}
|
||||
|
||||
public void setSocial(boolean social) {
|
||||
this.social = social;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public boolean isUpdateProfileOnInitialSocialLogin() {
|
||||
return updateProfileOnInitialSocialLogin;
|
||||
}
|
||||
|
||||
public void setUpdateProfileOnInitialSocialLogin(boolean updateProfileOnInitialSocialLogin) {
|
||||
this.updateProfileOnInitialSocialLogin = updateProfileOnInitialSocialLogin;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public boolean isBruteForceProtected() {
|
||||
return bruteForceProtected;
|
||||
}
|
||||
|
||||
public void setBruteForceProtected(boolean bruteForceProtected) {
|
||||
this.bruteForceProtected = bruteForceProtected;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public int getMaxFailureWaitSeconds() {
|
||||
return maxFailureWaitSeconds;
|
||||
}
|
||||
|
||||
public void setMaxFailureWaitSeconds(int maxFailureWaitSeconds) {
|
||||
this.maxFailureWaitSeconds = maxFailureWaitSeconds;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public int getMinimumQuickLoginWaitSeconds() {
|
||||
return minimumQuickLoginWaitSeconds;
|
||||
}
|
||||
|
||||
public void setMinimumQuickLoginWaitSeconds(int minimumQuickLoginWaitSeconds) {
|
||||
this.minimumQuickLoginWaitSeconds = minimumQuickLoginWaitSeconds;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public int getWaitIncrementSeconds() {
|
||||
return waitIncrementSeconds;
|
||||
}
|
||||
|
||||
public void setWaitIncrementSeconds(int waitIncrementSeconds) {
|
||||
this.waitIncrementSeconds = waitIncrementSeconds;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public long getQuickLoginCheckMilliSeconds() {
|
||||
return quickLoginCheckMilliSeconds;
|
||||
}
|
||||
|
||||
public void setQuickLoginCheckMilliSeconds(long quickLoginCheckMilliSeconds) {
|
||||
this.quickLoginCheckMilliSeconds = quickLoginCheckMilliSeconds;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public int getMaxDeltaTimeSeconds() {
|
||||
return maxDeltaTimeSeconds;
|
||||
}
|
||||
|
||||
public void setMaxDeltaTimeSeconds(int maxDeltaTimeSeconds) {
|
||||
this.maxDeltaTimeSeconds = maxDeltaTimeSeconds;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public int getFailureFactor() {
|
||||
return failureFactor;
|
||||
}
|
||||
|
||||
public void setFailureFactor(int failureFactor) {
|
||||
this.failureFactor = failureFactor;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public String getPasswordPolicy() {
|
||||
return passwordPolicy;
|
||||
}
|
||||
|
||||
public void setPasswordPolicy(String passwordPolicy) {
|
||||
this.passwordPolicy = passwordPolicy;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public int getNotBefore() {
|
||||
return notBefore;
|
||||
}
|
||||
|
||||
public void setNotBefore(int notBefore) {
|
||||
this.notBefore = notBefore;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public int getCentralLoginLifespan() {
|
||||
return centralLoginLifespan;
|
||||
}
|
||||
|
||||
public void setCentralLoginLifespan(int centralLoginLifespan) {
|
||||
this.centralLoginLifespan = centralLoginLifespan;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public int getAccessTokenLifespan() {
|
||||
return accessTokenLifespan;
|
||||
}
|
||||
|
||||
public void setAccessTokenLifespan(int accessTokenLifespan) {
|
||||
this.accessTokenLifespan = accessTokenLifespan;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public int getRefreshTokenLifespan() {
|
||||
return refreshTokenLifespan;
|
||||
}
|
||||
|
||||
public void setRefreshTokenLifespan(int refreshTokenLifespan) {
|
||||
this.refreshTokenLifespan = refreshTokenLifespan;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public int getAccessCodeLifespan() {
|
||||
return accessCodeLifespan;
|
||||
}
|
||||
|
||||
public void setAccessCodeLifespan(int accessCodeLifespan) {
|
||||
this.accessCodeLifespan = accessCodeLifespan;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public int getAccessCodeLifespanUserAction() {
|
||||
return accessCodeLifespanUserAction;
|
||||
}
|
||||
|
||||
public void setAccessCodeLifespanUserAction(int accessCodeLifespanUserAction) {
|
||||
this.accessCodeLifespanUserAction = accessCodeLifespanUserAction;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public String getPublicKeyPem() {
|
||||
return publicKeyPem;
|
||||
}
|
||||
|
||||
public void setPublicKeyPem(String publicKeyPem) {
|
||||
this.publicKeyPem = publicKeyPem;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public String getPrivateKeyPem() {
|
||||
return privateKeyPem;
|
||||
}
|
||||
|
||||
public void setPrivateKeyPem(String privateKeyPem) {
|
||||
this.privateKeyPem = privateKeyPem;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public String getLoginTheme() {
|
||||
return loginTheme;
|
||||
}
|
||||
|
||||
public void setLoginTheme(String loginTheme) {
|
||||
this.loginTheme = loginTheme;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public String getAccountTheme() {
|
||||
return accountTheme;
|
||||
}
|
||||
|
||||
public void setAccountTheme(String accountTheme) {
|
||||
this.accountTheme = accountTheme;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public List<String> getDefaultRoles() {
|
||||
return defaultRoles;
|
||||
}
|
||||
|
||||
public void setDefaultRoles(List<String> defaultRoles) {
|
||||
this.defaultRoles = defaultRoles;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public List<RequiredCredentialEntity> getRequiredCredentials() {
|
||||
return requiredCredentials;
|
||||
}
|
||||
|
||||
public void setRequiredCredentials(List<RequiredCredentialEntity> requiredCredentials) {
|
||||
this.requiredCredentials = requiredCredentials;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public List<AuthenticationProviderEntity> getAuthenticationProviders() {
|
||||
return authenticationProviders;
|
||||
}
|
||||
|
||||
public void setAuthenticationProviders(List<AuthenticationProviderEntity> authenticationProviders) {
|
||||
this.authenticationProviders = authenticationProviders;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public Map<String, String> getSmtpConfig() {
|
||||
return smtpConfig;
|
||||
}
|
||||
|
||||
public void setSmtpConfig(Map<String, String> smptConfig) {
|
||||
this.smtpConfig = smptConfig;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public Map<String, String> getSocialConfig() {
|
||||
return socialConfig;
|
||||
}
|
||||
|
||||
public void setSocialConfig(Map<String, String> socialConfig) {
|
||||
this.socialConfig = socialConfig;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public Map<String, String> getLdapServerConfig() {
|
||||
return ldapServerConfig;
|
||||
}
|
||||
|
||||
public void setLdapServerConfig(Map<String, String> ldapServerConfig) {
|
||||
this.ldapServerConfig = ldapServerConfig;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public boolean isAuditEnabled() {
|
||||
return auditEnabled;
|
||||
}
|
||||
|
||||
public void setAuditEnabled(boolean auditEnabled) {
|
||||
this.auditEnabled = auditEnabled;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public long getAuditExpiration() {
|
||||
return auditExpiration;
|
||||
}
|
||||
|
||||
public void setAuditExpiration(long auditExpiration) {
|
||||
this.auditExpiration = auditExpiration;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public List<String> getAuditListeners() {
|
||||
return auditListeners;
|
||||
}
|
||||
|
||||
public void setAuditListeners(List<String> auditListeners) {
|
||||
this.auditListeners = auditListeners;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public String getAdminAppId() {
|
||||
return adminAppId;
|
||||
}
|
||||
|
||||
public void setAdminAppId(String adminAppId) {
|
||||
this.adminAppId = adminAppId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterRemove(MongoStoreInvocationContext context) {
|
||||
DBObject query = new QueryBuilder()
|
||||
.and("realmId").is(getId())
|
||||
.get();
|
||||
|
||||
// Remove all users of this realm
|
||||
context.getMongoStore().removeEntities(UserEntity.class, query, context);
|
||||
|
||||
// Remove all roles of this realm
|
||||
context.getMongoStore().removeEntities(RoleEntity.class, query, context);
|
||||
|
||||
// Remove all applications of this realm
|
||||
context.getMongoStore().removeEntities(ApplicationEntity.class, query, context);
|
||||
|
||||
// Remove all clients of this realm
|
||||
context.getMongoStore().removeEntities(OAuthClientEntity.class, query, context);
|
||||
}
|
||||
}
|
||||
package org.keycloak.models.entities;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class RealmEntity extends AbstractIdentifiableEntity {
|
||||
|
||||
private String name;
|
||||
private boolean enabled;
|
||||
private boolean sslNotRequired;
|
||||
private boolean registrationAllowed;
|
||||
private boolean rememberMe;
|
||||
private boolean verifyEmail;
|
||||
private boolean resetPasswordAllowed;
|
||||
private boolean social;
|
||||
private boolean updateProfileOnInitialSocialLogin;
|
||||
private String passwordPolicy;
|
||||
//--- brute force settings
|
||||
private boolean bruteForceProtected;
|
||||
private int maxFailureWaitSeconds;
|
||||
private int minimumQuickLoginWaitSeconds;
|
||||
private int waitIncrementSeconds;
|
||||
private long quickLoginCheckMilliSeconds;
|
||||
private int maxDeltaTimeSeconds;
|
||||
private int failureFactor;
|
||||
//--- end brute force settings
|
||||
|
||||
private int centralLoginLifespan;
|
||||
private int accessTokenLifespan;
|
||||
private int accessCodeLifespan;
|
||||
private int accessCodeLifespanUserAction;
|
||||
private int refreshTokenLifespan;
|
||||
private int notBefore;
|
||||
|
||||
private String publicKeyPem;
|
||||
private String privateKeyPem;
|
||||
|
||||
private String loginTheme;
|
||||
private String accountTheme;
|
||||
|
||||
// We are using names of defaultRoles (not ids)
|
||||
private List<String> defaultRoles = new ArrayList<String>();
|
||||
|
||||
private List<RequiredCredentialEntity> requiredCredentials = new ArrayList<RequiredCredentialEntity>();
|
||||
private List<AuthenticationProviderEntity> authenticationProviders = new ArrayList<AuthenticationProviderEntity>();
|
||||
|
||||
private Map<String, String> smtpConfig = new HashMap<String, String>();
|
||||
private Map<String, String> socialConfig = new HashMap<String, String>();
|
||||
private Map<String, String> ldapServerConfig;
|
||||
|
||||
private boolean auditEnabled;
|
||||
private long auditExpiration;
|
||||
private List<String> auditListeners = new ArrayList<String>();
|
||||
|
||||
private String adminAppId;
|
||||
|
||||
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 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 String getPasswordPolicy() {
|
||||
return passwordPolicy;
|
||||
}
|
||||
|
||||
public void setPasswordPolicy(String passwordPolicy) {
|
||||
this.passwordPolicy = passwordPolicy;
|
||||
}
|
||||
|
||||
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 int getCentralLoginLifespan() {
|
||||
return centralLoginLifespan;
|
||||
}
|
||||
|
||||
public void setCentralLoginLifespan(int centralLoginLifespan) {
|
||||
this.centralLoginLifespan = centralLoginLifespan;
|
||||
}
|
||||
|
||||
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 int getRefreshTokenLifespan() {
|
||||
return refreshTokenLifespan;
|
||||
}
|
||||
|
||||
public void setRefreshTokenLifespan(int refreshTokenLifespan) {
|
||||
this.refreshTokenLifespan = refreshTokenLifespan;
|
||||
}
|
||||
|
||||
public int getNotBefore() {
|
||||
return notBefore;
|
||||
}
|
||||
|
||||
public void setNotBefore(int notBefore) {
|
||||
this.notBefore = notBefore;
|
||||
}
|
||||
|
||||
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 String getLoginTheme() {
|
||||
return loginTheme;
|
||||
}
|
||||
|
||||
public void setLoginTheme(String loginTheme) {
|
||||
this.loginTheme = loginTheme;
|
||||
}
|
||||
|
||||
public String getAccountTheme() {
|
||||
return accountTheme;
|
||||
}
|
||||
|
||||
public void setAccountTheme(String accountTheme) {
|
||||
this.accountTheme = accountTheme;
|
||||
}
|
||||
|
||||
public List<String> getDefaultRoles() {
|
||||
return defaultRoles;
|
||||
}
|
||||
|
||||
public void setDefaultRoles(List<String> defaultRoles) {
|
||||
this.defaultRoles = defaultRoles;
|
||||
}
|
||||
|
||||
public List<RequiredCredentialEntity> getRequiredCredentials() {
|
||||
return requiredCredentials;
|
||||
}
|
||||
|
||||
public void setRequiredCredentials(List<RequiredCredentialEntity> requiredCredentials) {
|
||||
this.requiredCredentials = requiredCredentials;
|
||||
}
|
||||
|
||||
public List<AuthenticationProviderEntity> getAuthenticationProviders() {
|
||||
return authenticationProviders;
|
||||
}
|
||||
|
||||
public void setAuthenticationProviders(List<AuthenticationProviderEntity> authenticationProviders) {
|
||||
this.authenticationProviders = authenticationProviders;
|
||||
}
|
||||
|
||||
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 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 List<String> getAuditListeners() {
|
||||
return auditListeners;
|
||||
}
|
||||
|
||||
public void setAuditListeners(List<String> auditListeners) {
|
||||
this.auditListeners = auditListeners;
|
||||
}
|
||||
|
||||
public String getAdminAppId() {
|
||||
return adminAppId;
|
||||
}
|
||||
|
||||
public void setAdminAppId(String adminAppId) {
|
||||
this.adminAppId = adminAppId;
|
||||
}
|
||||
}
|
|
@ -1,19 +1,15 @@
|
|||
package org.keycloak.models.mongo.keycloak.entities;
|
||||
|
||||
import org.keycloak.models.mongo.api.MongoEntity;
|
||||
import org.keycloak.models.mongo.api.MongoField;
|
||||
package org.keycloak.models.entities;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class RequiredCredentialEntity implements MongoEntity {
|
||||
public class RequiredCredentialEntity {
|
||||
|
||||
private String type;
|
||||
private boolean input;
|
||||
private boolean secret;
|
||||
private String formLabel;
|
||||
|
||||
@MongoField
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
@ -22,7 +18,6 @@ public class RequiredCredentialEntity implements MongoEntity {
|
|||
this.type = type;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public boolean isInput() {
|
||||
return input;
|
||||
}
|
||||
|
@ -31,7 +26,6 @@ public class RequiredCredentialEntity implements MongoEntity {
|
|||
this.input = input;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public boolean isSecret() {
|
||||
return secret;
|
||||
}
|
||||
|
@ -40,7 +34,6 @@ public class RequiredCredentialEntity implements MongoEntity {
|
|||
this.secret = secret;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public String getFormLabel() {
|
||||
return formLabel;
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
package org.keycloak.models.entities;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class RoleEntity extends AbstractIdentifiableEntity {
|
||||
|
||||
private String name;
|
||||
private String description;
|
||||
|
||||
private List<String> compositeRoleIds;
|
||||
|
||||
private String realmId;
|
||||
private String applicationId;
|
||||
|
||||
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 List<String> getCompositeRoleIds() {
|
||||
return compositeRoleIds;
|
||||
}
|
||||
|
||||
public void setCompositeRoleIds(List<String> compositeRoleIds) {
|
||||
this.compositeRoleIds = compositeRoleIds;
|
||||
}
|
||||
|
||||
public String getRealmId() {
|
||||
return realmId;
|
||||
}
|
||||
|
||||
public void setRealmId(String realmId) {
|
||||
this.realmId = realmId;
|
||||
}
|
||||
|
||||
public String getApplicationId() {
|
||||
return applicationId;
|
||||
}
|
||||
|
||||
public void setApplicationId(String applicationId) {
|
||||
this.applicationId = applicationId;
|
||||
}
|
||||
}
|
|
@ -1,18 +1,14 @@
|
|||
package org.keycloak.models.mongo.keycloak.entities;
|
||||
|
||||
import org.keycloak.models.mongo.api.MongoEntity;
|
||||
import org.keycloak.models.mongo.api.MongoField;
|
||||
package org.keycloak.models.entities;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class SocialLinkEntity implements MongoEntity {
|
||||
public class SocialLinkEntity {
|
||||
|
||||
private String socialUserId;
|
||||
private String socialUsername;
|
||||
private String socialProvider;
|
||||
|
||||
@MongoField
|
||||
public String getSocialUserId() {
|
||||
return socialUserId;
|
||||
}
|
||||
|
@ -21,7 +17,6 @@ public class SocialLinkEntity implements MongoEntity {
|
|||
this.socialUserId = socialUserId;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public String getSocialUsername() {
|
||||
return socialUsername;
|
||||
}
|
||||
|
@ -30,7 +25,6 @@ public class SocialLinkEntity implements MongoEntity {
|
|||
this.socialUsername = socialUsername;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public String getSocialProvider() {
|
||||
return socialProvider;
|
||||
}
|
|
@ -1,26 +1,15 @@
|
|||
package org.keycloak.models.mongo.keycloak.entities;
|
||||
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.mongo.api.AbstractMongoIdentifiableEntity;
|
||||
import org.keycloak.models.mongo.api.MongoCollection;
|
||||
import org.keycloak.models.mongo.api.MongoEntity;
|
||||
import org.keycloak.models.mongo.api.MongoField;
|
||||
import org.keycloak.models.mongo.api.MongoIndex;
|
||||
import org.keycloak.models.mongo.api.MongoIndexes;
|
||||
package org.keycloak.models.entities;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.keycloak.models.UserModel;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
@MongoCollection(collectionName = "users")
|
||||
@MongoIndexes({
|
||||
@MongoIndex(fields = { "realmId", "loginName" }, unique = true),
|
||||
@MongoIndex(fields = { "emailIndex" }, unique = true, sparse = true),
|
||||
})
|
||||
public class UserEntity extends AbstractMongoIdentifiableEntity implements MongoEntity {
|
||||
public class UserEntity extends AbstractIdentifiableEntity {
|
||||
|
||||
private String loginName;
|
||||
private String firstName;
|
||||
|
@ -30,11 +19,6 @@ public class UserEntity extends AbstractMongoIdentifiableEntity implements Mongo
|
|||
private boolean totp;
|
||||
private boolean enabled;
|
||||
private int notBefore;
|
||||
private int failedLoginNotBefore;
|
||||
private int numFailures;
|
||||
private long lastFailure;
|
||||
private String lastIPFailure;
|
||||
|
||||
|
||||
private String realmId;
|
||||
|
||||
|
@ -46,7 +30,6 @@ public class UserEntity extends AbstractMongoIdentifiableEntity implements Mongo
|
|||
private List<SocialLinkEntity> socialLinks;
|
||||
private AuthenticationLinkEntity authenticationLink;
|
||||
|
||||
@MongoField
|
||||
public String getLoginName() {
|
||||
return loginName;
|
||||
}
|
||||
|
@ -55,7 +38,6 @@ public class UserEntity extends AbstractMongoIdentifiableEntity implements Mongo
|
|||
this.loginName = loginName;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public String getFirstName() {
|
||||
return firstName;
|
||||
}
|
||||
|
@ -64,7 +46,6 @@ public class UserEntity extends AbstractMongoIdentifiableEntity implements Mongo
|
|||
this.firstName = firstName;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public String getLastName() {
|
||||
return lastName;
|
||||
}
|
||||
|
@ -73,7 +54,6 @@ public class UserEntity extends AbstractMongoIdentifiableEntity implements Mongo
|
|||
this.lastName = lastName;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
@ -82,16 +62,6 @@ public class UserEntity extends AbstractMongoIdentifiableEntity implements Mongo
|
|||
this.email = email;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
// TODO This is required as Mongo doesn't support sparse indexes with compound keys (see https://jira.mongodb.org/browse/SERVER-2193)
|
||||
public String getEmailIndex() {
|
||||
return email != null ? realmId + "//" + email : null;
|
||||
}
|
||||
|
||||
public void setEmailIndex(String ignored) {
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public boolean isEmailVerified() {
|
||||
return emailVerified;
|
||||
}
|
||||
|
@ -100,16 +70,6 @@ public class UserEntity extends AbstractMongoIdentifiableEntity implements Mongo
|
|||
this.emailVerified = emailVerified;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public boolean isTotp() {
|
||||
return totp;
|
||||
}
|
||||
|
@ -118,7 +78,14 @@ public class UserEntity extends AbstractMongoIdentifiableEntity implements Mongo
|
|||
this.totp = totp;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public int getNotBefore() {
|
||||
return notBefore;
|
||||
}
|
||||
|
@ -127,7 +94,6 @@ public class UserEntity extends AbstractMongoIdentifiableEntity implements Mongo
|
|||
this.notBefore = notBefore;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public String getRealmId() {
|
||||
return realmId;
|
||||
}
|
||||
|
@ -136,7 +102,6 @@ public class UserEntity extends AbstractMongoIdentifiableEntity implements Mongo
|
|||
this.realmId = realmId;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public List<String> getRoleIds() {
|
||||
return roleIds;
|
||||
}
|
||||
|
@ -145,8 +110,6 @@ public class UserEntity extends AbstractMongoIdentifiableEntity implements Mongo
|
|||
this.roleIds = roleIds;
|
||||
}
|
||||
|
||||
|
||||
@MongoField
|
||||
public Map<String, String> getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
@ -155,7 +118,6 @@ public class UserEntity extends AbstractMongoIdentifiableEntity implements Mongo
|
|||
this.attributes = attributes;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public List<UserModel.RequiredAction> getRequiredActions() {
|
||||
return requiredActions;
|
||||
}
|
||||
|
@ -164,7 +126,6 @@ public class UserEntity extends AbstractMongoIdentifiableEntity implements Mongo
|
|||
this.requiredActions = requiredActions;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public List<CredentialEntity> getCredentials() {
|
||||
return credentials;
|
||||
}
|
||||
|
@ -173,7 +134,6 @@ public class UserEntity extends AbstractMongoIdentifiableEntity implements Mongo
|
|||
this.credentials = credentials;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public List<SocialLinkEntity> getSocialLinks() {
|
||||
return socialLinks;
|
||||
}
|
||||
|
@ -182,7 +142,6 @@ public class UserEntity extends AbstractMongoIdentifiableEntity implements Mongo
|
|||
this.socialLinks = socialLinks;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public AuthenticationLinkEntity getAuthenticationLink() {
|
||||
return authenticationLink;
|
||||
}
|
||||
|
@ -191,3 +150,4 @@ public class UserEntity extends AbstractMongoIdentifiableEntity implements Mongo
|
|||
this.authenticationLink = authenticationLink;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,76 +1,63 @@
|
|||
package org.keycloak.models.mongo.keycloak.entities;
|
||||
|
||||
import org.keycloak.models.mongo.api.AbstractMongoIdentifiableEntity;
|
||||
import org.keycloak.models.mongo.api.MongoCollection;
|
||||
import org.keycloak.models.mongo.api.MongoEntity;
|
||||
import org.keycloak.models.mongo.api.MongoField;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
@MongoCollection(collectionName = "userFailures")
|
||||
public class UsernameLoginFailureEntity extends AbstractMongoIdentifiableEntity implements MongoEntity {
|
||||
private String username;
|
||||
private int failedLoginNotBefore;
|
||||
private int numFailures;
|
||||
private long lastFailure;
|
||||
private String lastIPFailure;
|
||||
|
||||
|
||||
private String realmId;
|
||||
|
||||
@MongoField
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public int getFailedLoginNotBefore() {
|
||||
return failedLoginNotBefore;
|
||||
}
|
||||
|
||||
public void setFailedLoginNotBefore(int failedLoginNotBefore) {
|
||||
this.failedLoginNotBefore = failedLoginNotBefore;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public int getNumFailures() {
|
||||
return numFailures;
|
||||
}
|
||||
|
||||
public void setNumFailures(int numFailures) {
|
||||
this.numFailures = numFailures;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public long getLastFailure() {
|
||||
return lastFailure;
|
||||
}
|
||||
|
||||
public void setLastFailure(long lastFailure) {
|
||||
this.lastFailure = lastFailure;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public String getLastIPFailure() {
|
||||
return lastIPFailure;
|
||||
}
|
||||
|
||||
public void setLastIPFailure(String lastIPFailure) {
|
||||
this.lastIPFailure = lastIPFailure;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public String getRealmId() {
|
||||
return realmId;
|
||||
}
|
||||
|
||||
public void setRealmId(String realmId) {
|
||||
this.realmId = realmId;
|
||||
}
|
||||
}
|
||||
package org.keycloak.models.entities;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class UsernameLoginFailureEntity extends AbstractIdentifiableEntity {
|
||||
|
||||
private String username;
|
||||
private int failedLoginNotBefore;
|
||||
private int numFailures;
|
||||
private long lastFailure;
|
||||
private String lastIPFailure;
|
||||
|
||||
private String realmId;
|
||||
|
||||
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 String getRealmId() {
|
||||
return realmId;
|
||||
}
|
||||
|
||||
public void setRealmId(String realmId) {
|
||||
this.realmId = realmId;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package org.keycloak.models.utils.reflection;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* A criteria that matches a property based on its annotations
|
||||
*
|
||||
* @see PropertyCriteria
|
||||
*/
|
||||
public class AnnotatedPropertyCriteria implements PropertyCriteria {
|
||||
private final Class<? extends Annotation> annotationClass;
|
||||
|
||||
public AnnotatedPropertyCriteria(Class<? extends Annotation> annotationClass) {
|
||||
this.annotationClass = annotationClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean methodMatches(Method m) {
|
||||
return m.isAnnotationPresent(annotationClass);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package org.keycloak.models.utils.reflection;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public interface MethodProperty<V> extends Property<V> {
|
||||
|
||||
Method getAnnotatedElement();
|
||||
}
|
|
@ -0,0 +1,220 @@
|
|||
package org.keycloak.models.utils.reflection;
|
||||
|
||||
import java.beans.Introspector;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* A bean property based on the value represented by a getter/setter method pair
|
||||
*/
|
||||
class MethodPropertyImpl<V> implements MethodProperty<V> {
|
||||
private static final String GETTER_METHOD_PREFIX = "get";
|
||||
private static final String SETTER_METHOD_PREFIX = "set";
|
||||
private static final String BOOLEAN_GETTER_METHOD_PREFIX = "is";
|
||||
|
||||
private static final int GETTER_METHOD_PREFIX_LENGTH = GETTER_METHOD_PREFIX.length();
|
||||
private static final int SETTER_METHOD_PREFIX_LENGTH = SETTER_METHOD_PREFIX.length();
|
||||
private static final int BOOLEAN_GETTER_METHOD_PREFIX_LENGTH = BOOLEAN_GETTER_METHOD_PREFIX.length();
|
||||
|
||||
private final Method getterMethod;
|
||||
private final String propertyName;
|
||||
private final Method setterMethod;
|
||||
|
||||
public MethodPropertyImpl(Method method) {
|
||||
final String accessorMethodPrefix;
|
||||
final String propertyNameInAccessorMethod;
|
||||
|
||||
if (method.getName().startsWith(GETTER_METHOD_PREFIX)) {
|
||||
if (method.getReturnType() == Void.TYPE) {
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid accessor method, must have return value if starts with 'get'. Method: " + method);
|
||||
} else if (method.getParameterTypes().length > 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid accessor method, must have zero arguments if starts with 'get'. Method: " + method);
|
||||
}
|
||||
propertyNameInAccessorMethod = method.getName().substring(GETTER_METHOD_PREFIX_LENGTH);
|
||||
accessorMethodPrefix = GETTER_METHOD_PREFIX;
|
||||
} else if (method.getName().startsWith(SETTER_METHOD_PREFIX)) {
|
||||
if (method.getReturnType() != Void.TYPE) {
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid accessor method, must not have return value if starts with 'set'. Method: " + method);
|
||||
} else if (method.getParameterTypes().length != 1) {
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid accessor method, must have one argument if starts with 'set'. Method: " + method);
|
||||
}
|
||||
propertyNameInAccessorMethod = method.getName().substring(SETTER_METHOD_PREFIX_LENGTH);
|
||||
accessorMethodPrefix = SETTER_METHOD_PREFIX;
|
||||
} else if (method.getName().startsWith(BOOLEAN_GETTER_METHOD_PREFIX)) {
|
||||
if (method.getReturnType() != Boolean.TYPE || !method.getReturnType().isPrimitive()) {
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid accessor method, must return boolean primitive if starts with 'is'. Method: " +
|
||||
method);
|
||||
}
|
||||
propertyNameInAccessorMethod = method.getName().substring(BOOLEAN_GETTER_METHOD_PREFIX_LENGTH);
|
||||
accessorMethodPrefix = BOOLEAN_GETTER_METHOD_PREFIX;
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid accessor method, must start with 'get', 'set' or 'is'. " + "Method: " + method);
|
||||
}
|
||||
|
||||
if (propertyNameInAccessorMethod.length() == 0 ||
|
||||
!Character.isUpperCase(propertyNameInAccessorMethod.charAt(0))) {
|
||||
throw new IllegalArgumentException("Invalid accessor method, prefix '" + accessorMethodPrefix +
|
||||
"' must be followed a non-empty property name, capitalized. Method: " + method);
|
||||
}
|
||||
|
||||
this.propertyName = Introspector.decapitalize(propertyNameInAccessorMethod);
|
||||
this.getterMethod = getGetterMethod(method.getDeclaringClass(), propertyName);
|
||||
this.setterMethod = getSetterMethod(method.getDeclaringClass(), propertyName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return propertyName;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Class<V> getJavaClass() {
|
||||
return (Class<V>) getterMethod.getReturnType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getBaseType() {
|
||||
return getterMethod.getGenericReturnType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Method getAnnotatedElement() {
|
||||
return getterMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Member getMember() {
|
||||
return getterMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V getValue(Object instance) {
|
||||
if (getterMethod == null) {
|
||||
throw new UnsupportedOperationException("Property " +
|
||||
this.setterMethod.getDeclaringClass() + "." + propertyName +
|
||||
" cannot be read, as there is no getter method.");
|
||||
}
|
||||
return Reflections.cast(Reflections.invokeMethod(getterMethod, instance));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(Object instance, V value) {
|
||||
if (setterMethod == null) {
|
||||
// if the setter method is null may be because the declaring type is an interface which does not declare
|
||||
// a setter method. We just check if the instance is assignable from the property declaring class and
|
||||
// try to find a overridden method.
|
||||
if (getDeclaringClass().isAssignableFrom(instance.getClass())) {
|
||||
Method instanceSetterMethod = getSetterMethod(instance.getClass(), getName());
|
||||
|
||||
if (instanceSetterMethod != null) {
|
||||
Reflections.invokeMethod(instanceSetterMethod, instance, value);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException("Property " +
|
||||
this.getterMethod.getDeclaringClass() + "." + propertyName +
|
||||
" is read only, as there is no setter method.");
|
||||
}
|
||||
Reflections.invokeMethod(setterMethod, instance, value);
|
||||
}
|
||||
|
||||
private static Method getSetterMethod(Class<?> clazz, String name) {
|
||||
Method[] methods = clazz.getMethods();
|
||||
for (Method method : methods) {
|
||||
String methodName = method.getName();
|
||||
if (methodName.startsWith(SETTER_METHOD_PREFIX) && method.getParameterTypes().length == 1) {
|
||||
if (Introspector.decapitalize(methodName.substring(SETTER_METHOD_PREFIX_LENGTH)).equals(name)) {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Method getGetterMethod(Class<?> clazz, String name) {
|
||||
for (Method method : clazz.getDeclaredMethods()) {
|
||||
String methodName = method.getName();
|
||||
if (method.getParameterTypes().length == 0) {
|
||||
if (methodName.startsWith(GETTER_METHOD_PREFIX)) {
|
||||
if (Introspector.decapitalize(methodName.substring(GETTER_METHOD_PREFIX_LENGTH)).equals(name)) {
|
||||
return method;
|
||||
}
|
||||
} else if (methodName.startsWith(BOOLEAN_GETTER_METHOD_PREFIX)) {
|
||||
if (Introspector.decapitalize(
|
||||
methodName.substring(BOOLEAN_GETTER_METHOD_PREFIX_LENGTH)).equals(name)) {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("no such getter method: " + clazz.getName() + '.' + name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getDeclaringClass() {
|
||||
return getterMethod.getDeclaringClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReadOnly() {
|
||||
return setterMethod == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAccessible() {
|
||||
if (setterMethod != null) {
|
||||
Reflections.setAccessible(setterMethod);
|
||||
}
|
||||
if (getterMethod != null) {
|
||||
Reflections.setAccessible(getterMethod);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAnnotationPresent(final Class<? extends Annotation> annotation) {
|
||||
return getAnnotatedElement() != null && getAnnotatedElement().isAnnotationPresent(annotation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
if (isReadOnly()) {
|
||||
builder.append("read-only ").append(setterMethod.toString()).append("; ");
|
||||
}
|
||||
builder.append(getterMethod.toString());
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 1;
|
||||
hash = hash * 31 + (setterMethod == null ? 0 : setterMethod.hashCode());
|
||||
hash = hash * 31 + getterMethod.hashCode();
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof MethodPropertyImpl<?>) {
|
||||
MethodPropertyImpl<?> that = (MethodPropertyImpl<?>) obj;
|
||||
if (this.setterMethod == null) {
|
||||
return that.setterMethod == null && this.getterMethod.equals(that.getterMethod);
|
||||
} else {
|
||||
return this.setterMethod.equals(that.setterMethod) && this.getterMethod.equals(that.getterMethod);
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package org.keycloak.models.utils.reflection;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Utility class for working with JavaBean style properties
|
||||
*
|
||||
* @see Property
|
||||
*/
|
||||
public class Properties {
|
||||
|
||||
private Properties() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a JavaBean style property from the specified method
|
||||
*
|
||||
* @param <V>
|
||||
* @param method
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws IllegalArgumentException if the method does not match JavaBean conventions
|
||||
* @see http://www.oracle.com/technetwork/java/javase/documentation/spec-136004.html
|
||||
*/
|
||||
public static <V> MethodProperty<V> createProperty(Method method) {
|
||||
return new MethodPropertyImpl<V>(method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this method is a valid property method.
|
||||
*/
|
||||
public static <V> boolean isProperty(Method method) {
|
||||
try {
|
||||
new MethodPropertyImpl<V>(method);
|
||||
return true;
|
||||
} catch (IllegalArgumentException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
package org.keycloak.models.utils.reflection;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* A representation of a JavaBean style property
|
||||
*
|
||||
* @param <V> the type of the properties value
|
||||
*
|
||||
* @see Properties
|
||||
*/
|
||||
public interface Property<V> {
|
||||
|
||||
/**
|
||||
* Returns the name of the property. If the property is a field, then the field name is returned. Otherwise, if the
|
||||
* property is a method, then the name that is returned is the getter method name without the "get" or "is" prefix,
|
||||
* and a lower case first letter.
|
||||
*
|
||||
* @return The name of the property
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Returns the property type
|
||||
*
|
||||
* @return The property type
|
||||
*/
|
||||
Type getBaseType();
|
||||
|
||||
/**
|
||||
* Returns the property type
|
||||
*
|
||||
* @return The property type
|
||||
*/
|
||||
Class<V> getJavaClass();
|
||||
|
||||
/**
|
||||
* Get the element responsible for retrieving the property value
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
AnnotatedElement getAnnotatedElement();
|
||||
|
||||
/**
|
||||
* Get the member responsible for retrieving the property value
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Member getMember();
|
||||
|
||||
/**
|
||||
* Returns the property value for the specified bean. The property to be returned is either a field or getter
|
||||
* method.
|
||||
*
|
||||
* @param bean The bean to read the property from
|
||||
*
|
||||
* @return The property value
|
||||
*
|
||||
* @throws ClassCastException if the value is not of the type V
|
||||
*/
|
||||
V getValue(Object instance);
|
||||
|
||||
/**
|
||||
* This method sets the property value for a specified bean to the specified value. The property to be set is either
|
||||
* a field or setter method.
|
||||
*
|
||||
* @param bean The bean containing the property to set
|
||||
* @param value The new property value
|
||||
*/
|
||||
void setValue(Object instance, V value);
|
||||
|
||||
/**
|
||||
* Returns the class that declares the property
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Class<?> getDeclaringClass();
|
||||
|
||||
/**
|
||||
* Indicates whether this is a read-only property
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean isReadOnly();
|
||||
|
||||
/**
|
||||
* Calls the setAccessible method on the underlying member(s).
|
||||
* <p/>
|
||||
* The operation should be performed within a {@link PrivilegedAction}
|
||||
*/
|
||||
void setAccessible();
|
||||
|
||||
/**
|
||||
* Indicates whether the given <code>annotation</code> is defined for this property. This method will consider
|
||||
* the annotations present in both field and accessor method.
|
||||
*
|
||||
* @param annotation The Annotation to check.
|
||||
*
|
||||
* @return True if the annotation is defined. Otherwise is false.
|
||||
*/
|
||||
boolean isAnnotationPresent(Class<? extends Annotation> annotation);
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package org.keycloak.models.utils.reflection;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* <p> A property criteria can be used to filter the properties found by a {@link PropertyQuery} </p> <p/> <p>
|
||||
* DeltaSpike provides a number of property queries ( {@link TypedPropertyCriteria}, {@link NamedPropertyCriteria} and
|
||||
* {@link AnnotatedPropertyCriteria}), or you can create a custom query by implementing this interface. </p>
|
||||
*
|
||||
* @see PropertyQuery#addCriteria(PropertyCriteria)
|
||||
* @see PropertyQueries
|
||||
* @see TypedPropertyCriteria
|
||||
* @see AnnotatedPropertyCriteria
|
||||
* @see NamedPropertyCriteria
|
||||
*/
|
||||
public interface PropertyCriteria {
|
||||
|
||||
/**
|
||||
* Tests whether the specified method matches the criteria
|
||||
*
|
||||
* @param m
|
||||
*
|
||||
* @return true if the method matches
|
||||
*/
|
||||
boolean methodMatches(Method m);
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package org.keycloak.models.utils.reflection;
|
||||
|
||||
/**
|
||||
* Utilities for working with property queries
|
||||
*
|
||||
* @see PropertyQuery
|
||||
*/
|
||||
public class PropertyQueries {
|
||||
|
||||
private PropertyQueries() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link PropertyQuery}
|
||||
*
|
||||
* @param <V>
|
||||
* @param targetClass
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static <V> PropertyQuery<V> createQuery(Class<?> targetClass) {
|
||||
return new PropertyQuery<V>(targetClass);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,162 @@
|
|||
package org.keycloak.models.utils.reflection;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <p> Queries a target class for properties that match certain criteria. A property may either be a private or public
|
||||
* field, declared by the target class or inherited from a superclass, or a public method declared by the target class
|
||||
* or inherited from any of its superclasses. For properties that are exposed via a method, the property must be a
|
||||
* JavaBean style property, i.e. it must provide both an accessor and mutator method according to the JavaBean
|
||||
* specification. </p> <p/> <p> This class is not thread-safe, however the result returned by the getResultList() method
|
||||
* is. </p>
|
||||
*
|
||||
* @see PropertyQueries
|
||||
* @see PropertyCriteria
|
||||
*/
|
||||
public class PropertyQuery<V> {
|
||||
private final Class<?> targetClass;
|
||||
private final List<PropertyCriteria> criteria;
|
||||
|
||||
PropertyQuery(Class<?> targetClass) {
|
||||
if (targetClass == null) {
|
||||
throw new IllegalArgumentException("targetClass parameter may not be null");
|
||||
}
|
||||
|
||||
this.targetClass = targetClass;
|
||||
this.criteria = new ArrayList<PropertyCriteria>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a criteria to query
|
||||
*
|
||||
* @param criteria the criteria to add
|
||||
*/
|
||||
public PropertyQuery<V> addCriteria(PropertyCriteria criteria) {
|
||||
this.criteria.add(criteria);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first result from the query, causing the query to be run.
|
||||
*
|
||||
* @return the first result, or null if there are no results
|
||||
*/
|
||||
public Property<V> getFirstResult() {
|
||||
Map<String, Property<V>> results = getResultList();
|
||||
return results.isEmpty() ? null : results.values().iterator().next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first result from the query that is not marked as read only, causing the query to be run.
|
||||
*
|
||||
* @return the first writable result, or null if there are no results
|
||||
*/
|
||||
public Property<V> getFirstWritableResult() {
|
||||
Map<String, Property<V>> results = getWritableResultList();
|
||||
return results.isEmpty() ? null : results.values().iterator().next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single result from the query, causing the query to be run. An exception is thrown if the query does not
|
||||
* return exactly one result.
|
||||
*
|
||||
* @return the single result
|
||||
*
|
||||
* @throws RuntimeException if the query does not return exactly one result
|
||||
*/
|
||||
public Property<V> getSingleResult() {
|
||||
Map<String, Property<V>> results = getResultList();
|
||||
if (results.size() == 1) {
|
||||
return results.values().iterator().next();
|
||||
} else if (results.isEmpty()) {
|
||||
throw new RuntimeException(
|
||||
"Expected one property match, but the criteria did not match any properties on " +
|
||||
targetClass.getName());
|
||||
} else {
|
||||
throw new RuntimeException("Expected one property match, but the criteria matched " + results.size() +
|
||||
" properties on " + targetClass.getName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single result from the query that is not marked as read only, causing the query to be run. An exception is
|
||||
* thrown if the query does not return exactly one result.
|
||||
*
|
||||
* @return the single writable result
|
||||
*
|
||||
* @throws RuntimeException if the query does not return exactly one result
|
||||
*/
|
||||
public Property<V> getWritableSingleResult() {
|
||||
Map<String, Property<V>> results = getWritableResultList();
|
||||
if (results.size() == 1) {
|
||||
return results.values().iterator().next();
|
||||
} else if (results.isEmpty()) {
|
||||
throw new RuntimeException(
|
||||
"Expected one property match, but the criteria did not match any properties on " +
|
||||
targetClass.getName());
|
||||
} else {
|
||||
throw new RuntimeException("Expected one property match, but the criteria matched " +
|
||||
results.size() + " properties on " + targetClass.getName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the result from the query, causing the query to be run.
|
||||
*
|
||||
* @return the results, or an empty list if there are no results
|
||||
*/
|
||||
public Map<String, Property<V>> getResultList() {
|
||||
return getResultList(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the non read only results from the query, causing the query to be run.
|
||||
*
|
||||
* @return the results, or an empty list if there are no results
|
||||
*/
|
||||
public Map<String, Property<V>> getWritableResultList() {
|
||||
return getResultList(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the result from the query, causing the query to be run.
|
||||
*
|
||||
* @param writable if this query should only return properties that are not read only
|
||||
*
|
||||
* @return the results, or an empty list if there are no results
|
||||
*/
|
||||
private Map<String, Property<V>> getResultList(boolean writable) {
|
||||
Map<String, Property<V>> properties = new HashMap<String, Property<V>>();
|
||||
|
||||
// First check public accessor methods (we ignore private methods)
|
||||
for (Method method : targetClass.getMethods()) {
|
||||
if (!(method.getName().startsWith("is") || method.getName().startsWith("get"))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean match = true;
|
||||
for (PropertyCriteria c : criteria) {
|
||||
if (!c.methodMatches(method)) {
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (match) {
|
||||
MethodProperty<V> property = Properties.<V>createProperty(method);
|
||||
|
||||
if (!writable || !property.isReadOnly()) {
|
||||
properties.put(property.getName(), property);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Collections.unmodifiableMap(properties);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,980 @@
|
|||
package org.keycloak.models.utils.reflection;
|
||||
|
||||
import java.beans.Introspector;
|
||||
import java.io.Serializable;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AccessibleObject;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.lang.reflect.TypeVariable;
|
||||
import java.lang.reflect.WildcardType;
|
||||
import java.security.AccessController;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Utility class for working with JDK Reflection and also CDI's {link Annotated} metadata.
|
||||
*/
|
||||
public class Reflections {
|
||||
/**
|
||||
* An empty array of type {@link java.lang.annotation.Annotation}, useful converting lists to arrays.
|
||||
*/
|
||||
public static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0];
|
||||
|
||||
/**
|
||||
* An empty array of type {@link Object}, useful for converting lists to arrays.
|
||||
*/
|
||||
public static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
|
||||
|
||||
public static final Type[] EMPTY_TYPES = {};
|
||||
|
||||
public static final Class<?>[] EMPTY_CLASSES = new Class<?>[0];
|
||||
|
||||
private Reflections() {
|
||||
}
|
||||
|
||||
/**
|
||||
* <p> Perform a runtime cast. Similar to {@link Class#cast(Object)}, but useful when you do not have a {@link
|
||||
* Class} object for type you wish to cast to. </p> <p/> <p> {@link Class#cast(Object)} should be used if possible
|
||||
* </p>
|
||||
*
|
||||
* @param <T> the type to cast to
|
||||
* @param obj the object to perform the cast on
|
||||
*
|
||||
* @return the casted object
|
||||
*
|
||||
* @throws ClassCastException if the type T is not a subtype of the object
|
||||
* @see Class#cast(Object)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T cast(Object obj) {
|
||||
return (T) obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the declared fields on the class hierarchy. This <b>will</b> return overridden fields.
|
||||
*
|
||||
* @param clazz The class to search
|
||||
*
|
||||
* @return the set of all declared fields or an empty set if there are none
|
||||
*/
|
||||
public static Set<Field> getAllDeclaredFields(Class<?> clazz) {
|
||||
HashSet<Field> fields = new HashSet<Field>();
|
||||
for (Class<?> c = clazz; c != null && c != Object.class; c = c.getSuperclass()) {
|
||||
for (Field a : c.getDeclaredFields()) {
|
||||
fields.add(a);
|
||||
}
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search the class hierarchy for a field with the given name. Will return the nearest match, starting with the
|
||||
* class specified and searching up the hierarchy.
|
||||
*
|
||||
* @param clazz The class to search
|
||||
* @param name The name of the field to search for
|
||||
*
|
||||
* @return The field found, or null if no field is found
|
||||
*/
|
||||
public static Field findDeclaredField(Class<?> clazz, String name) {
|
||||
for (Class<?> c = clazz; c != null && c != Object.class; c = c.getSuperclass()) {
|
||||
try {
|
||||
return c.getDeclaredField(name);
|
||||
} catch (NoSuchFieldException e) {
|
||||
// No-op, we continue looking up the class hierarchy
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for annotations with the specified meta annotation type
|
||||
*
|
||||
* @param annotations The annotation set to search
|
||||
* @param metaAnnotationType The type of the meta annotation to search for
|
||||
*
|
||||
* @return The set of annotations with the specified meta annotation, or an empty set if none are found
|
||||
*/
|
||||
public static Set<Annotation> getAnnotationsWithMetaAnnotation(
|
||||
Set<Annotation> annotations, Class<? extends Annotation> metaAnnotationType) {
|
||||
Set<Annotation> set = new HashSet<Annotation>();
|
||||
for (Annotation annotation : annotations) {
|
||||
if (annotation.annotationType().isAnnotationPresent(metaAnnotationType)) {
|
||||
set.add(annotation);
|
||||
}
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a method exists in a specified class hierarchy
|
||||
*
|
||||
* @param clazz The class to search
|
||||
* @param name The name of the method
|
||||
*
|
||||
* @return true if a method is found, otherwise false
|
||||
*/
|
||||
public static boolean methodExists(Class<?> clazz, String name) {
|
||||
for (Class<?> c = clazz; c != null && c != Object.class; c = c.getSuperclass()) {
|
||||
for (Method m : c.getDeclaredMethods()) {
|
||||
if (m.getName().equals(name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the declared methods on the class hierarchy. This <b>will</b> return overridden methods.
|
||||
*
|
||||
* @param clazz The class to search
|
||||
*
|
||||
* @return the set of all declared methods or an empty set if there are none
|
||||
*/
|
||||
public static Set<Method> getAllDeclaredMethods(Class<?> clazz) {
|
||||
HashSet<Method> methods = new HashSet<Method>();
|
||||
for (Class<?> c = clazz; c != null && c != Object.class; c = c.getSuperclass()) {
|
||||
for (Method a : c.getDeclaredMethods()) {
|
||||
methods.add(a);
|
||||
}
|
||||
}
|
||||
return methods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search the class hierarchy for a method with the given name and arguments. Will return the nearest match,
|
||||
* starting with the class specified and searching up the hierarchy.
|
||||
*
|
||||
* @param clazz The class to search
|
||||
* @param name The name of the method to search for
|
||||
* @param args The arguments of the method to search for
|
||||
*
|
||||
* @return The method found, or null if no method is found
|
||||
*/
|
||||
public static Method findDeclaredMethod(Class<?> clazz, String name, Class<?>... args) {
|
||||
for (Class<?> c = clazz; c != null && c != Object.class; c = c.getSuperclass()) {
|
||||
try {
|
||||
return c.getDeclaredMethod(name, args);
|
||||
} catch (NoSuchMethodException e) {
|
||||
// No-op, continue the search
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search the class hierarchy for a constructor with the given arguments. Will return the nearest match, starting
|
||||
* with the class specified and searching up the hierarchy.
|
||||
*
|
||||
* @param clazz The class to search
|
||||
* @param args The arguments of the constructor to search for
|
||||
*
|
||||
* @return The constructor found, or null if no constructor is found
|
||||
*/
|
||||
public static Constructor<?> findDeclaredConstructor(Class<?> clazz, Class<?>... args) {
|
||||
for (Class<?> c = clazz; c != null && c != Object.class; c = c.getSuperclass()) {
|
||||
try {
|
||||
return c.getDeclaredConstructor(args);
|
||||
} catch (NoSuchMethodException e) {
|
||||
// No-op, continue the search
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the declared constructors on the class hierarchy. This <b>will</b> return overridden constructors.
|
||||
*
|
||||
* @param clazz The class to search
|
||||
*
|
||||
* @return the set of all declared constructors or an empty set if there are none
|
||||
*/
|
||||
public static Set<Constructor<?>> getAllDeclaredConstructors(Class<?> clazz) {
|
||||
HashSet<Constructor<?>> constructors = new HashSet<Constructor<?>>();
|
||||
for (Class<?> c = clazz; c != null && c != Object.class; c = c.getSuperclass()) {
|
||||
for (Constructor<?> constructor : c.getDeclaredConstructors()) {
|
||||
constructors.add(constructor);
|
||||
}
|
||||
}
|
||||
return constructors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type of the member
|
||||
*
|
||||
* @param member The member
|
||||
*
|
||||
* @return The type of the member
|
||||
*
|
||||
* @throws UnsupportedOperationException if the member is not a field, method, or constructor
|
||||
*/
|
||||
public static Class<?> getMemberType(Member member) {
|
||||
if (member instanceof Field) {
|
||||
return ((Field) member).getType();
|
||||
} else if (member instanceof Method) {
|
||||
return ((Method) member).getReturnType();
|
||||
} else if (member instanceof Constructor<?>) {
|
||||
return ((Constructor<?>) member).getDeclaringClass();
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Cannot operate on a member of type " + member.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p> Loads and initializes a class for the given name. </p> <p/> <p> If the Thread Context Class Loader is
|
||||
* available, it will be used, otherwise the classloader used to load {@link Reflections} will be used </p> <p/> <p>
|
||||
* It is also possible to specify additional classloaders to attempt to load the class with. If the first attempt
|
||||
* fails, then these additional loaders are tried in order. </p>
|
||||
*
|
||||
* @param name the name of the class to load
|
||||
* @param loaders additional classloaders to use to attempt to load the class
|
||||
*
|
||||
* @return the class object
|
||||
*
|
||||
* @throws ClassNotFoundException if the class cannot be found
|
||||
*/
|
||||
public static <T> Class<T> classForName(String name, ClassLoader... loaders) throws ClassNotFoundException {
|
||||
try {
|
||||
if (Thread.currentThread().getContextClassLoader() != null) {
|
||||
return (Class<T>) Class.forName(name, true, Thread.currentThread().getContextClassLoader());
|
||||
} else {
|
||||
return (Class<T>) Class.forName(name);
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
for (ClassLoader l : loaders) {
|
||||
try {
|
||||
return (Class<T>) Class.forName(name, true, l);
|
||||
} catch (ClassNotFoundException ex) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Thread.currentThread().getContextClassLoader() != null) {
|
||||
throw new ClassNotFoundException("Could not load class " + name +
|
||||
" with the context class loader " + Thread.currentThread().getContextClassLoader().toString() +
|
||||
" or any of the additional ClassLoaders: " + Arrays.toString(loaders));
|
||||
} else {
|
||||
throw new ClassNotFoundException("Could not load class " + name +
|
||||
" using Class.forName or using any of the additional ClassLoaders: " +
|
||||
Arrays.toString(loaders));
|
||||
}
|
||||
}
|
||||
|
||||
private static String buildInvokeMethodErrorMessage(Method method, Object obj, Object... args) {
|
||||
StringBuilder message = new StringBuilder(
|
||||
String.format("Exception invoking method [%s] on object [%s], using arguments [",
|
||||
method.getName(), obj));
|
||||
if (args != null) {
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
message.append((i > 0 ? "," : "") + args[i]);
|
||||
}
|
||||
}
|
||||
message.append("]");
|
||||
return message.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p> Invoke the specified method on the provided instance, passing any additional arguments included in this
|
||||
* method as arguments to the specified method. </p> <p/> <p>This method provides the same functionality and throws
|
||||
* the same exceptions as {@link Reflections#invokeMethod(boolean, Method, Class, Object, Object...)}, with the
|
||||
* expected return type set to {@link Object} and no change to the method's accessibility.</p>
|
||||
*
|
||||
* @see Reflections#invokeMethod(boolean, Method, Class, Object, Object...)
|
||||
* @see Method#invoke(Object, Object...)
|
||||
*/
|
||||
public static Object invokeMethod(Method method, Object instance, Object... args) {
|
||||
return invokeMethod(false, method, Object.class, instance, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p> Invoke the specified method on the provided instance, passing any additional arguments included in this
|
||||
* method as arguments to the specified method. </p> <p/> <p> This method attempts to set the accessible flag of the
|
||||
* method in a {link PrivilegedAction} before invoking the method if the first argument is true. </p> <p/> <p>This
|
||||
* method provides the same functionality and throws the same exceptions as {@link Reflections#invokeMethod(boolean,
|
||||
* Method, Class, Object, Object...)}, with the expected return type set to {@link Object}.</p>
|
||||
*
|
||||
* @see Reflections#invokeMethod(boolean, Method, Class, Object, Object...)
|
||||
* @see Method#invoke(Object, Object...)
|
||||
*/
|
||||
public static Object invokeMethod(boolean setAccessible, Method method, Object instance, Object... args) {
|
||||
return invokeMethod(setAccessible, method, Object.class, instance, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p> Invoke the specified method on the provided instance, passing any additional arguments included in this
|
||||
* method as arguments to the specified method. </p> <p/> <p>This method provides the same functionality and throws
|
||||
* the same exceptions as {@link Reflections#invokeMethod(boolean, Method, Class, Object, Object...)}, with the
|
||||
* expected return type set to {@link Object} and honoring the accessibility of the method.</p>
|
||||
*
|
||||
* @see Reflections#invokeMethod(boolean, Method, Class, Object, Object...)
|
||||
* @see Method#invoke(Object, Object...)
|
||||
*/
|
||||
public static <T> T invokeMethod(Method method, Class<T> expectedReturnType, Object instance, Object... args) {
|
||||
return invokeMethod(false, method, expectedReturnType, instance, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p> Invoke the method on the instance, with any arguments specified, casting the result of invoking the method to
|
||||
* the expected return type. </p> <p/> <p> This method wraps {@link Method#invoke(Object, Object...)}, converting
|
||||
* the checked exceptions that {@link Method#invoke(Object, Object...)} specifies to runtime exceptions. </p> <p/>
|
||||
* <p> If instructed, this method attempts to set the accessible flag of the method in a {link PrivilegedAction}
|
||||
* before invoking the method. </p>
|
||||
*
|
||||
* @param setAccessible flag indicating whether method should first be set as accessible
|
||||
* @param method the method to invoke
|
||||
* @param instance the instance to invoke the method
|
||||
* @param args the arguments to the method
|
||||
*
|
||||
* @return the result of invoking the method, or null if the method's return type is void
|
||||
*
|
||||
* @throws RuntimeException if this <code>Method</code> object enforces Java language access control and the
|
||||
* underlying method is inaccessible or if the underlying method throws an exception or if the initialization
|
||||
* provoked by this method fails.
|
||||
* @throws IllegalArgumentException if the method is an instance method and the specified <code>instance</code>
|
||||
* argument is not an instance of the class or interface declaring the underlying method (or of a subclass or
|
||||
* implementor thereof); if the number of actual and formal parameters differ; if an unwrapping conversion for
|
||||
* primitive arguments fails; or if, after possible unwrapping, a parameter value cannot be converted to the
|
||||
* corresponding formal parameter type by a method invocation conversion.
|
||||
* @throws NullPointerException if the specified <code>instance</code> is null and the method is an instance
|
||||
* method.
|
||||
* @throws ClassCastException if the result of invoking the method cannot be cast to the expectedReturnType
|
||||
* @throws ExceptionInInitializerError if the initialization provoked by this method fails.
|
||||
* @see Method#invoke(Object, Object...)
|
||||
*/
|
||||
public static <T> T invokeMethod(boolean setAccessible, Method method,
|
||||
Class<T> expectedReturnType, Object instance, Object... args) {
|
||||
if (setAccessible && !method.isAccessible()) {
|
||||
setAccessible(method);
|
||||
}
|
||||
|
||||
try {
|
||||
return expectedReturnType.cast(method.invoke(instance, args));
|
||||
} catch (IllegalAccessException ex) {
|
||||
throw new RuntimeException(buildInvokeMethodErrorMessage(method, instance, args), ex);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
throw new IllegalArgumentException(buildInvokeMethodErrorMessage(method, instance, args), ex);
|
||||
} catch (InvocationTargetException ex) {
|
||||
throw new RuntimeException(buildInvokeMethodErrorMessage(method, instance, args), ex.getCause());
|
||||
} catch (NullPointerException ex) {
|
||||
NullPointerException ex2 = new NullPointerException(buildInvokeMethodErrorMessage(method, instance, args));
|
||||
ex2.initCause(ex.getCause());
|
||||
throw ex2;
|
||||
} catch (ExceptionInInitializerError e) {
|
||||
ExceptionInInitializerError e2 = new ExceptionInInitializerError(
|
||||
buildInvokeMethodErrorMessage(method, instance, args));
|
||||
e2.initCause(e.getCause());
|
||||
throw e2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the accessibility flag on the {@link AccessibleObject} as described in {@link
|
||||
* AccessibleObject#setAccessible(boolean)} within the context of a {link PrivilegedAction}.
|
||||
*
|
||||
* @param <A> member the accessible object type
|
||||
* @param member the accessible object
|
||||
*
|
||||
* @return the accessible object after the accessible flag has been altered
|
||||
*/
|
||||
public static <A extends AccessibleObject> A setAccessible(A member) {
|
||||
AccessController.doPrivileged(new SetAccessiblePrivilegedAction(member));
|
||||
return member;
|
||||
}
|
||||
|
||||
private static String buildSetFieldValueErrorMessage(Field field, Object obj, Object value) {
|
||||
return String.format("Exception setting [%s] field on object [%s] to value [%s]", field.getName(), obj, value);
|
||||
}
|
||||
|
||||
private static String buildGetFieldValueErrorMessage(Field field, Object obj) {
|
||||
return String.format("Exception reading [%s] field from object [%s].", field.getName(), obj);
|
||||
}
|
||||
|
||||
public static Object getFieldValue(Field field, Object instance) {
|
||||
return getFieldValue(field, instance, Object.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p> Get the value of the field, on the specified instance, casting the value of the field to the expected type.
|
||||
* </p> <p/> <p> This method wraps {@link Field#get(Object)}, converting the checked exceptions that {@link
|
||||
* Field#get(Object)} specifies to runtime exceptions. </p>
|
||||
*
|
||||
* @param <T> the type of the field's value
|
||||
* @param field the field to operate on
|
||||
* @param instance the instance from which to retrieve the value
|
||||
* @param expectedType the expected type of the field's value
|
||||
*
|
||||
* @return the value of the field
|
||||
*
|
||||
* @throws RuntimeException if the underlying field is inaccessible.
|
||||
* @throws IllegalArgumentException if the specified <code>instance</code> is not an instance of the class or
|
||||
* interface declaring the underlying field (or a subclass or implementor thereof).
|
||||
* @throws NullPointerException if the specified <code>instance</code> is null and the field is an instance field.
|
||||
* @throws ExceptionInInitializerError if the initialization provoked by this method fails.
|
||||
*/
|
||||
public static <T> T getFieldValue(Field field, Object instance, Class<T> expectedType) {
|
||||
try {
|
||||
return Reflections.cast(field.get(instance));
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(buildGetFieldValueErrorMessage(field, instance), e);
|
||||
} catch (NullPointerException ex) {
|
||||
NullPointerException ex2 = new NullPointerException(buildGetFieldValueErrorMessage(field, instance));
|
||||
ex2.initCause(ex.getCause());
|
||||
throw ex2;
|
||||
} catch (ExceptionInInitializerError e) {
|
||||
ExceptionInInitializerError e2 = new ExceptionInInitializerError(
|
||||
buildGetFieldValueErrorMessage(field, instance));
|
||||
e2.initCause(e.getCause());
|
||||
throw e2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the raw type, given a type.
|
||||
*
|
||||
* @param <T> the type
|
||||
* @param type the type to extract the raw type from
|
||||
*
|
||||
* @return the raw type, or null if the raw type cannot be determined.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Class<T> getRawType(Type type) {
|
||||
if (type instanceof Class<?>) {
|
||||
return (Class<T>) type;
|
||||
} else if (type instanceof ParameterizedType) {
|
||||
if (((ParameterizedType) type).getRawType() instanceof Class<?>) {
|
||||
return (Class<T>) ((ParameterizedType) type).getRawType();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a class is serializable.
|
||||
*
|
||||
* @param clazz The class to check
|
||||
*
|
||||
* @return true if the class implements serializable or is a primitive
|
||||
*/
|
||||
public static boolean isSerializable(Class<?> clazz) {
|
||||
return clazz.isPrimitive() || Serializable.class.isAssignableFrom(clazz);
|
||||
}
|
||||
|
||||
|
||||
public static Map<Class<?>, Type> buildTypeMap(Set<Type> types) {
|
||||
Map<Class<?>, Type> map = new HashMap<Class<?>, Type>();
|
||||
for (Type type : types) {
|
||||
if (type instanceof Class<?>) {
|
||||
map.put((Class<?>) type, type);
|
||||
} else if (type instanceof ParameterizedType) {
|
||||
if (((ParameterizedType) type).getRawType() instanceof Class<?>) {
|
||||
map.put((Class<?>) ((ParameterizedType) type).getRawType(), type);
|
||||
}
|
||||
} else if (type instanceof TypeVariable<?>) {
|
||||
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
public static boolean isCacheable(Set<Annotation> annotations) {
|
||||
for (Annotation qualifier : annotations) {
|
||||
Class<?> clazz = qualifier.getClass();
|
||||
if (clazz.isAnonymousClass() || (clazz.isMemberClass() && isStatic(clazz))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean isCacheable(Annotation[] annotations) {
|
||||
for (Annotation qualifier : annotations) {
|
||||
Class<?> clazz = qualifier.getClass();
|
||||
if (clazz.isAnonymousClass() || (clazz.isMemberClass() && isStatic(clazz))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the property name from a getter method.
|
||||
* <p/>
|
||||
* We extend JavaBean conventions, allowing the getter method to have parameters
|
||||
*
|
||||
* @param method The getter method
|
||||
*
|
||||
* @return The name of the property. Returns null if method wasn't JavaBean getter-styled
|
||||
*/
|
||||
public static String getPropertyName(Method method) {
|
||||
String methodName = method.getName();
|
||||
if (methodName.matches("^(get).*")) {
|
||||
return Introspector.decapitalize(methodName.substring(3));
|
||||
} else if (methodName.matches("^(is).*")) {
|
||||
return Introspector.decapitalize(methodName.substring(2));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if class is final
|
||||
*
|
||||
* @param clazz The class to check
|
||||
*
|
||||
* @return True if final, false otherwise
|
||||
*/
|
||||
public static boolean isFinal(Class<?> clazz) {
|
||||
return Modifier.isFinal(clazz.getModifiers());
|
||||
}
|
||||
|
||||
public static int getNesting(Class<?> clazz) {
|
||||
if (clazz.isMemberClass() && !isStatic(clazz)) {
|
||||
return 1 + getNesting(clazz.getDeclaringClass());
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if member is final
|
||||
*
|
||||
* @param member The member to check
|
||||
*
|
||||
* @return True if final, false otherwise
|
||||
*/
|
||||
public static boolean isFinal(Member member) {
|
||||
return Modifier.isFinal(member.getModifiers());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if member is private
|
||||
*
|
||||
* @param member The member to check
|
||||
*
|
||||
* @return True if final, false otherwise
|
||||
*/
|
||||
public static boolean isPrivate(Member member) {
|
||||
return Modifier.isPrivate(member.getModifiers());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if type or member is final
|
||||
*
|
||||
* @param type Type or member
|
||||
*
|
||||
* @return True if final, false otherwise
|
||||
*/
|
||||
public static boolean isTypeOrAnyMethodFinal(Class<?> type) {
|
||||
return getNonPrivateFinalMethodOrType(type) != null;
|
||||
}
|
||||
|
||||
public static Object getNonPrivateFinalMethodOrType(Class<?> type) {
|
||||
if (isFinal(type)) {
|
||||
return type;
|
||||
}
|
||||
for (Method method : type.getDeclaredMethods()) {
|
||||
if (isFinal(method) && !isPrivate(method)) {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean isPackagePrivate(int mod) {
|
||||
return !(Modifier.isPrivate(mod) || Modifier.isProtected(mod) || Modifier.isPublic(mod));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if type is static
|
||||
*
|
||||
* @param type Type to check
|
||||
*
|
||||
* @return True if static, false otherwise
|
||||
*/
|
||||
public static boolean isStatic(Class<?> type) {
|
||||
return Modifier.isStatic(type.getModifiers());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if member is static
|
||||
*
|
||||
* @param member Member to check
|
||||
*
|
||||
* @return True if static, false otherwise
|
||||
*/
|
||||
public static boolean isStatic(Member member) {
|
||||
return Modifier.isStatic(member.getModifiers());
|
||||
}
|
||||
|
||||
public static boolean isTransient(Member member) {
|
||||
return Modifier.isTransient(member.getModifiers());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a method is abstract
|
||||
*
|
||||
* @param method
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static boolean isAbstract(Method method) {
|
||||
return Modifier.isAbstract(method.getModifiers());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if raw type is array type
|
||||
*
|
||||
* @param rawType The raw type to check
|
||||
*
|
||||
* @return True if array, false otherwise
|
||||
*/
|
||||
public static boolean isArrayType(Class<?> rawType) {
|
||||
return rawType.isArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if type is parameterized type
|
||||
*
|
||||
* @param type The type to check
|
||||
*
|
||||
* @return True if parameterized, false otherwise
|
||||
*/
|
||||
public static boolean isParameterizedType(Class<?> type) {
|
||||
return type.getTypeParameters().length > 0;
|
||||
}
|
||||
|
||||
public static boolean isParamerterizedTypeWithWildcard(Class<?> type) {
|
||||
if (isParameterizedType(type)) {
|
||||
return containsWildcards(type.getTypeParameters());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean containsWildcards(Type[] types) {
|
||||
for (Type type : types) {
|
||||
if (type instanceof WildcardType) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the assignability of one type to another, taking into account the actual type arguements
|
||||
*
|
||||
* @param rawType1 the raw type of the class to check
|
||||
* @param actualTypeArguments1 the actual type arguements to check, or an empty array if not a parameterized type
|
||||
* @param rawType2 the raw type of the class to check
|
||||
* @param actualTypeArguments2 the actual type arguements to check, or an empty array if not a parameterized type
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static boolean isAssignableFrom(Class<?> rawType1, Type[] actualTypeArguments1,
|
||||
Class<?> rawType2, Type[] actualTypeArguments2) {
|
||||
return Types.boxedClass(rawType1).isAssignableFrom(Types.boxedClass(rawType2)) &&
|
||||
isAssignableFrom(actualTypeArguments1, actualTypeArguments2);
|
||||
}
|
||||
|
||||
public static boolean matches(Class<?> rawType1, Type[] actualTypeArguments1,
|
||||
Class<?> rawType2, Type[] actualTypeArguments2) {
|
||||
return Types.boxedClass(rawType1).equals(Types.boxedClass(rawType2)) &&
|
||||
isAssignableFrom(actualTypeArguments1, actualTypeArguments2);
|
||||
}
|
||||
|
||||
public static boolean isAssignableFrom(Type[] actualTypeArguments1, Type[] actualTypeArguments2) {
|
||||
for (int i = 0; i < actualTypeArguments1.length; i++) {
|
||||
Type type1 = actualTypeArguments1[i];
|
||||
Type type2 = Object.class;
|
||||
if (actualTypeArguments2.length > i) {
|
||||
type2 = actualTypeArguments2[i];
|
||||
}
|
||||
if (!isAssignableFrom(type1, type2)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean isAssignableFrom(Type type1, Set<? extends Type> types2) {
|
||||
for (Type type2 : types2) {
|
||||
if (isAssignableFrom(type1, type2)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean matches(Type type1, Set<? extends Type> types2) {
|
||||
for (Type type2 : types2) {
|
||||
if (matches(type1, type2)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isAssignableFrom(Type type1, Type[] types2) {
|
||||
for (Type type2 : types2) {
|
||||
if (isAssignableFrom(type1, type2)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isAssignableFrom(Type type1, Type type2) {
|
||||
if (type1 instanceof Class<?>) {
|
||||
Class<?> clazz = (Class<?>) type1;
|
||||
if (isAssignableFrom(clazz, EMPTY_TYPES, type2)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (type1 instanceof ParameterizedType) {
|
||||
ParameterizedType parameterizedType1 = (ParameterizedType) type1;
|
||||
if (parameterizedType1.getRawType() instanceof Class<?>) {
|
||||
if (isAssignableFrom((Class<?>) parameterizedType1.getRawType(),
|
||||
parameterizedType1.getActualTypeArguments(), type2)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (type1 instanceof WildcardType) {
|
||||
WildcardType wildcardType = (WildcardType) type1;
|
||||
if (isTypeBounded(type2, wildcardType.getLowerBounds(), wildcardType.getUpperBounds())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (type2 instanceof WildcardType) {
|
||||
WildcardType wildcardType = (WildcardType) type2;
|
||||
if (isTypeBounded(type1, wildcardType.getUpperBounds(), wildcardType.getLowerBounds())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (type1 instanceof TypeVariable<?>) {
|
||||
TypeVariable<?> typeVariable = (TypeVariable<?>) type1;
|
||||
if (isTypeBounded(type2, EMPTY_TYPES, typeVariable.getBounds())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (type2 instanceof TypeVariable<?>) {
|
||||
TypeVariable<?> typeVariable = (TypeVariable<?>) type2;
|
||||
if (isTypeBounded(type1, typeVariable.getBounds(), EMPTY_TYPES)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean matches(Type type1, Type type2) {
|
||||
if (type1 instanceof Class<?>) {
|
||||
Class<?> clazz = (Class<?>) type1;
|
||||
if (matches(clazz, EMPTY_TYPES, type2)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (type1 instanceof ParameterizedType) {
|
||||
ParameterizedType parameterizedType1 = (ParameterizedType) type1;
|
||||
if (parameterizedType1.getRawType() instanceof Class<?>) {
|
||||
if (matches((Class<?>) parameterizedType1.getRawType(),
|
||||
parameterizedType1.getActualTypeArguments(), type2)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (type1 instanceof WildcardType) {
|
||||
WildcardType wildcardType = (WildcardType) type1;
|
||||
if (isTypeBounded(type2, wildcardType.getLowerBounds(), wildcardType.getUpperBounds())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (type2 instanceof WildcardType) {
|
||||
WildcardType wildcardType = (WildcardType) type2;
|
||||
if (isTypeBounded(type1, wildcardType.getUpperBounds(), wildcardType.getLowerBounds())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (type1 instanceof TypeVariable<?>) {
|
||||
TypeVariable<?> typeVariable = (TypeVariable<?>) type1;
|
||||
if (isTypeBounded(type2, EMPTY_TYPES, typeVariable.getBounds())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (type2 instanceof TypeVariable<?>) {
|
||||
TypeVariable<?> typeVariable = (TypeVariable<?>) type2;
|
||||
if (isTypeBounded(type1, typeVariable.getBounds(), EMPTY_TYPES)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isTypeBounded(Type type, Type[] lowerBounds, Type[] upperBounds) {
|
||||
if (lowerBounds.length > 0) {
|
||||
if (!isAssignableFrom(type, lowerBounds)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (upperBounds.length > 0) {
|
||||
if (!isAssignableFrom(upperBounds, type)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean isAssignableFrom(Class<?> rawType1, Type[] actualTypeArguments1, Type type2) {
|
||||
if (type2 instanceof ParameterizedType) {
|
||||
ParameterizedType parameterizedType = (ParameterizedType) type2;
|
||||
if (parameterizedType.getRawType() instanceof Class<?>) {
|
||||
if (isAssignableFrom(rawType1, actualTypeArguments1, (Class<?>) parameterizedType.getRawType(),
|
||||
parameterizedType.getActualTypeArguments())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (type2 instanceof Class<?>) {
|
||||
Class<?> clazz = (Class<?>) type2;
|
||||
if (isAssignableFrom(rawType1, actualTypeArguments1, clazz, EMPTY_TYPES)) {
|
||||
return true;
|
||||
}
|
||||
} else if (type2 instanceof TypeVariable<?>) {
|
||||
TypeVariable<?> typeVariable = (TypeVariable<?>) type2;
|
||||
if (isTypeBounded(rawType1, actualTypeArguments1, typeVariable.getBounds())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean matches(Class<?> rawType1, Type[] actualTypeArguments1, Type type2) {
|
||||
if (type2 instanceof ParameterizedType) {
|
||||
ParameterizedType parameterizedType = (ParameterizedType) type2;
|
||||
if (parameterizedType.getRawType() instanceof Class<?>) {
|
||||
if (matches(rawType1, actualTypeArguments1, (Class<?>) parameterizedType.getRawType(),
|
||||
parameterizedType.getActualTypeArguments())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (type2 instanceof Class<?>) {
|
||||
Class<?> clazz = (Class<?>) type2;
|
||||
if (matches(rawType1, actualTypeArguments1, clazz, EMPTY_TYPES)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the assiginability of a set of <b>flattened</b> types. This algorithm will check whether any of the types1
|
||||
* matches a type in types2
|
||||
*
|
||||
* @param types1
|
||||
* @param types2
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static boolean isAssignableFrom(Set<Type> types1, Set<Type> types2) {
|
||||
for (Type type : types1) {
|
||||
if (isAssignableFrom(type, types2)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether whether any of the types1 matches a type in types2
|
||||
*
|
||||
* @param types1
|
||||
* @param types2
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static boolean matches(Set<Type> types1, Set<Type> types2) {
|
||||
for (Type type : types1) {
|
||||
if (matches(type, types2)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the assignability of a set of <b>flattened</b> types. This algorithm will check whether any of the types1
|
||||
* matches a type in types2
|
||||
*
|
||||
* @param types1
|
||||
* @param type2
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static boolean isAssignableFrom(Set<Type> types1, Type type2) {
|
||||
for (Type type : types1) {
|
||||
if (isAssignableFrom(type, type2)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isAssignableFrom(Type[] types1, Type type2) {
|
||||
for (Type type : types1) {
|
||||
if (isAssignableFrom(type, type2)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isPrimitive(Type type) {
|
||||
Class<?> rawType = getRawType(type);
|
||||
return rawType == null ? false : rawType.isPrimitive();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates a new instance of a class.</p>
|
||||
*
|
||||
* <p>This method will use the same class loader of the given class to create the new instance.</p>
|
||||
*
|
||||
* @param fromClass The class from where the instance should be created.
|
||||
*
|
||||
* @return A newly allocated instance of the class.
|
||||
*
|
||||
* @throws ClassNotFoundException
|
||||
* @throws IllegalAccessException
|
||||
* @throws InstantiationException
|
||||
*/
|
||||
public static <T> T newInstance(final Class<T> fromClass) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
|
||||
return newInstance(fromClass, fromClass.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates a new instance of a class given its <code>fullQualifiedName</code>.</p>
|
||||
*
|
||||
* <p>This method will use the same class loader of <code>type</code> to create the new instance.</p>
|
||||
*
|
||||
* @param type The class that will be used to get the class loader from.
|
||||
* @param fullQualifiedName The full qualified name of the class from which the instance will be created.
|
||||
*
|
||||
* @return A newly allocated instance of the class.
|
||||
*
|
||||
* @throws ClassNotFoundException
|
||||
* @throws IllegalAccessException
|
||||
* @throws InstantiationException
|
||||
*/
|
||||
public static <T> T newInstance(final Class<?> type, final String fullQualifiedName) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
|
||||
return (T) classForName(fullQualifiedName, type.getClassLoader()).newInstance();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package org.keycloak.models.utils.reflection;
|
||||
|
||||
import java.lang.reflect.AccessibleObject;
|
||||
import java.security.PrivilegedAction;
|
||||
|
||||
/**
|
||||
* A {@link java.security.PrivilegedAction} that calls {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)}
|
||||
*/
|
||||
public class SetAccessiblePrivilegedAction implements PrivilegedAction<Void> {
|
||||
|
||||
private final AccessibleObject member;
|
||||
|
||||
public SetAccessiblePrivilegedAction(AccessibleObject member) {
|
||||
this.member = member;
|
||||
}
|
||||
|
||||
public Void run() {
|
||||
member.setAccessible(true);
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
package org.keycloak.models.utils.reflection;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* Utility class for Types
|
||||
*/
|
||||
public class Types {
|
||||
private Types() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the boxed type of a class
|
||||
*
|
||||
* @param type The type
|
||||
*
|
||||
* @return The boxed type
|
||||
*/
|
||||
public static Type boxedType(Type type) {
|
||||
if (type instanceof Class<?>) {
|
||||
return boxedClass((Class<?>) type);
|
||||
} else {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
public static Class<?> boxedClass(Class<?> type) {
|
||||
if (!type.isPrimitive()) {
|
||||
return type;
|
||||
} else if (type.equals(Boolean.TYPE)) {
|
||||
return Boolean.class;
|
||||
} else if (type.equals(Character.TYPE)) {
|
||||
return Character.class;
|
||||
} else if (type.equals(Byte.TYPE)) {
|
||||
return Byte.class;
|
||||
} else if (type.equals(Short.TYPE)) {
|
||||
return Short.class;
|
||||
} else if (type.equals(Integer.TYPE)) {
|
||||
return Integer.class;
|
||||
} else if (type.equals(Long.TYPE)) {
|
||||
return Long.class;
|
||||
} else if (type.equals(Float.TYPE)) {
|
||||
return Float.class;
|
||||
} else if (type.equals(Double.TYPE)) {
|
||||
return Double.class;
|
||||
} else if (type.equals(Void.TYPE)) {
|
||||
return Void.class;
|
||||
} else {
|
||||
// Vagaries of if/else statement, can't be reached ;-)
|
||||
return type;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -115,6 +115,21 @@
|
|||
</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>
|
||||
|
|
|
@ -7,6 +7,7 @@ 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;
|
||||
|
@ -99,7 +100,13 @@ public class ApplicationAdapter extends ClientAdapter implements ApplicationMode
|
|||
|
||||
@Override
|
||||
public RoleModel addRole(String name) {
|
||||
return this.addRole(KeycloakModelUtils.generateId(), name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleModel addRole(String id, String name) {
|
||||
ApplicationRoleEntity roleEntity = new ApplicationRoleEntity();
|
||||
roleEntity.setId(id);
|
||||
roleEntity.setName(name);
|
||||
roleEntity.setApplication(applicationEntity);
|
||||
em.persist(roleEntity);
|
||||
|
|
|
@ -102,4 +102,13 @@ public class JpaKeycloakSession implements KeycloakSession {
|
|||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import org.keycloak.models.AuthenticationLinkModel;
|
|||
import org.keycloak.models.AuthenticationProviderModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.RoleContainerModel;
|
||||
import org.keycloak.models.UserCredentialValueModel;
|
||||
import org.keycloak.models.UsernameLoginFailureModel;
|
||||
import org.keycloak.models.jpa.entities.ApplicationEntity;
|
||||
import org.keycloak.models.jpa.entities.ApplicationRoleEntity;
|
||||
|
@ -434,6 +435,17 @@ public class RealmAdapter implements RealmModel {
|
|||
return new UsernameLoginFailureAdapter(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UsernameLoginFailureModel> getAllUserLoginFailures() {
|
||||
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 UserModel getUserByEmail(String email) {
|
||||
TypedQuery<UserEntity> query = em.createNamedQuery("getRealmUserByEmail", UserEntity.class);
|
||||
|
@ -454,7 +466,13 @@ public class RealmAdapter implements RealmModel {
|
|||
|
||||
@Override
|
||||
public UserModel addUser(String username) {
|
||||
return this.addUser(KeycloakModelUtils.generateId(), username);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel addUser(String id, String username) {
|
||||
UserEntity entity = new UserEntity();
|
||||
entity.setId(id);
|
||||
entity.setLoginName(username);
|
||||
entity.setRealm(realm);
|
||||
em.persist(entity);
|
||||
|
@ -580,7 +598,13 @@ public class RealmAdapter implements RealmModel {
|
|||
|
||||
@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);
|
||||
|
@ -805,7 +829,13 @@ public class RealmAdapter implements RealmModel {
|
|||
|
||||
@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);
|
||||
|
@ -949,7 +979,13 @@ public class RealmAdapter implements RealmModel {
|
|||
|
||||
@Override
|
||||
public RoleModel addRole(String name) {
|
||||
return this.addRole(KeycloakModelUtils.generateId(), name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleModel addRole(String id, String name) {
|
||||
RealmRoleEntity entity = new RealmRoleEntity();
|
||||
entity.setId(id);
|
||||
entity.setName(name);
|
||||
entity.setRealm(realm);
|
||||
realm.getRoles().add(entity);
|
||||
|
@ -1170,13 +1206,9 @@ public class RealmAdapter implements RealmModel {
|
|||
|
||||
@Override
|
||||
public void updateCredential(UserModel user, UserCredentialModel cred) {
|
||||
CredentialEntity credentialEntity = null;
|
||||
UserEntity userEntity = ((UserAdapter) user).getUser();
|
||||
for (CredentialEntity entity : userEntity.getCredentials()) {
|
||||
if (entity.getType().equals(cred.getType())) {
|
||||
credentialEntity = entity;
|
||||
}
|
||||
}
|
||||
CredentialEntity credentialEntity = getCredentialEntity(userEntity, cred.getType());
|
||||
|
||||
if (credentialEntity == null) {
|
||||
credentialEntity = new CredentialEntity();
|
||||
credentialEntity.setType(cred.getType());
|
||||
|
@ -1196,6 +1228,57 @@ public class RealmAdapter implements RealmModel {
|
|||
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(UserModel user) {
|
||||
UserEntity userEntity = ((UserAdapter) user).getUser();
|
||||
List<CredentialEntity> credentials = new ArrayList<CredentialEntity>(userEntity.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());
|
||||
|
||||
result.add(credModel);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateCredentialDirectly(UserModel user, UserCredentialValueModel credModel) {
|
||||
UserEntity userEntity = ((UserAdapter) user).getUser();
|
||||
CredentialEntity credentialEntity = getCredentialEntity(userEntity, credModel.getType());
|
||||
|
||||
if (credentialEntity == null) {
|
||||
credentialEntity = new CredentialEntity();
|
||||
credentialEntity.setType(credModel.getType());
|
||||
credentialEntity.setUser(userEntity);
|
||||
em.persist(credentialEntity);
|
||||
userEntity.getCredentials().add(credentialEntity);
|
||||
}
|
||||
|
||||
credentialEntity.setValue(credModel.getValue());
|
||||
credentialEntity.setSalt(credModel.getSalt());
|
||||
credentialEntity.setDevice(credModel.getDevice());
|
||||
|
||||
em.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PasswordPolicy getPasswordPolicy() {
|
||||
if (passwordPolicy == null) {
|
||||
|
|
|
@ -29,8 +29,6 @@ import java.util.Set;
|
|||
@Table(uniqueConstraints = {@UniqueConstraint(columnNames = {"realm", "name"})})
|
||||
public abstract class ClientEntity {
|
||||
@Id
|
||||
@GenericGenerator(name="keycloak_generator", strategy="org.keycloak.models.jpa.utils.JpaIdGenerator")
|
||||
@GeneratedValue(generator = "keycloak_generator")
|
||||
private String id;
|
||||
@Column(name = "name")
|
||||
private String name;
|
||||
|
@ -63,6 +61,10 @@ public abstract class ClientEntity {
|
|||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
|
|
@ -28,8 +28,6 @@ import org.hibernate.annotations.GenericGenerator;
|
|||
})
|
||||
public abstract class RoleEntity {
|
||||
@Id
|
||||
@GenericGenerator(name="keycloak_generator", strategy="org.keycloak.models.jpa.utils.JpaIdGenerator")
|
||||
@GeneratedValue(generator = "keycloak_generator")
|
||||
private String id;
|
||||
|
||||
private String description;
|
||||
|
|
|
@ -44,8 +44,6 @@ import java.util.Set;
|
|||
})
|
||||
public class UserEntity {
|
||||
@Id
|
||||
@GenericGenerator(name="uuid_generator", strategy="org.keycloak.models.jpa.utils.JpaIdGenerator")
|
||||
@GeneratedValue(generator = "uuid_generator")
|
||||
protected String id;
|
||||
|
||||
protected String loginName;
|
||||
|
|
|
@ -3,12 +3,17 @@ package org.keycloak.models.jpa.entities;
|
|||
import javax.persistence.Entity;
|
||||
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
|
||||
|
|
|
@ -61,11 +61,6 @@
|
|||
<artifactId>jboss-logging</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.picketlink</groupId>
|
||||
<artifactId>picketlink-common</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mongodb</groupId>
|
||||
<artifactId>mongo-java-driver</artifactId>
|
||||
|
|
|
@ -40,4 +40,9 @@ public interface MongoStore {
|
|||
<S> boolean pushItemToList(MongoIdentifiableEntity entity, String listPropertyName, S itemToPush, boolean skipIfAlreadyPresent, MongoStoreInvocationContext context);
|
||||
|
||||
<S> boolean pullItemFromList(MongoIdentifiableEntity entity, String listPropertyName, S itemToPull, MongoStoreInvocationContext context);
|
||||
|
||||
/**
|
||||
* Completely remove all data from DB
|
||||
*/
|
||||
void removeAllEntities();
|
||||
}
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
package org.keycloak.models.mongo.impl;
|
||||
|
||||
import org.keycloak.models.mongo.api.MongoEntity;
|
||||
import org.picketlink.common.properties.Property;
|
||||
import org.keycloak.models.utils.reflection.Property;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
|
@ -14,24 +11,19 @@ import java.util.Map;
|
|||
*/
|
||||
public class EntityInfo {
|
||||
|
||||
private final Class<? extends MongoEntity> entityClass;
|
||||
private final Class<?> entityClass;
|
||||
|
||||
private final String dbCollectionName;
|
||||
|
||||
private final Map<String, Property<Object>> properties;
|
||||
|
||||
public EntityInfo(Class<? extends MongoEntity> entityClass, String dbCollectionName, List<Property<Object>> properties) {
|
||||
public EntityInfo(Class<?> entityClass, String dbCollectionName, Map<String, Property<Object>> properties) {
|
||||
this.entityClass = entityClass;
|
||||
this.dbCollectionName = dbCollectionName;
|
||||
|
||||
Map<String, Property<Object>> props= new HashMap<String, Property<Object>>();
|
||||
for (Property<Object> property : properties) {
|
||||
props.put(property.getName(), property);
|
||||
}
|
||||
this.properties = Collections.unmodifiableMap(props);
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
public Class<? extends MongoEntity> getEntityClass() {
|
||||
public Class<?> getEntityClass() {
|
||||
return entityClass;
|
||||
}
|
||||
|
||||
|
|
|
@ -32,15 +32,16 @@ import org.keycloak.models.mongo.impl.types.MongoEntityMapper;
|
|||
import org.keycloak.models.mongo.impl.types.SimpleMapper;
|
||||
import org.keycloak.models.mongo.impl.types.StringToEnumMapper;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.picketlink.common.properties.Property;
|
||||
import org.picketlink.common.properties.query.AnnotatedPropertyCriteria;
|
||||
import org.picketlink.common.properties.query.PropertyQueries;
|
||||
import org.keycloak.models.utils.reflection.AnnotatedPropertyCriteria;
|
||||
import org.keycloak.models.utils.reflection.Property;
|
||||
import org.keycloak.models.utils.reflection.PropertyQueries;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
|
@ -55,19 +56,19 @@ public class MongoStoreImpl implements MongoStore {
|
|||
private static final Logger logger = Logger.getLogger(MongoStoreImpl.class);
|
||||
|
||||
private final MapperRegistry mapperRegistry;
|
||||
private ConcurrentMap<Class<? extends MongoEntity>, EntityInfo> entityInfoCache =
|
||||
new ConcurrentHashMap<Class<? extends MongoEntity>, EntityInfo>();
|
||||
private ConcurrentMap<Class<?>, EntityInfo> entityInfoCache =
|
||||
new ConcurrentHashMap<Class<?>, EntityInfo>();
|
||||
|
||||
|
||||
public MongoStoreImpl(DB database, boolean clearCollectionsOnStartup, Class<? extends MongoEntity>[] managedEntityTypes) {
|
||||
public MongoStoreImpl(DB database, boolean clearCollectionsOnStartup, Class<?>[] managedEntityTypes) {
|
||||
this.database = database;
|
||||
|
||||
mapperRegistry = new MapperRegistry();
|
||||
|
||||
for (Class<?> simpleConverterClass : SIMPLE_TYPES) {
|
||||
SimpleMapper converter = new SimpleMapper(simpleConverterClass);
|
||||
mapperRegistry.addAppObjectMapper(converter);
|
||||
mapperRegistry.addDBObjectMapper(converter);
|
||||
for (Class<?> simpleMapperClass : SIMPLE_TYPES) {
|
||||
SimpleMapper mapper = new SimpleMapper(simpleMapperClass);
|
||||
mapperRegistry.addAppObjectMapper(mapper);
|
||||
mapperRegistry.addDBObjectMapper(mapper);
|
||||
}
|
||||
|
||||
// Specific converter for ArrayList is added just for performance purposes to avoid recursive converter lookup (most of list idm will be ArrayList)
|
||||
|
@ -83,7 +84,7 @@ public class MongoStoreImpl implements MongoStore {
|
|||
mapperRegistry.addAppObjectMapper(new EnumToStringMapper());
|
||||
mapperRegistry.addDBObjectMapper(new StringToEnumMapper());
|
||||
|
||||
for (Class<? extends MongoEntity> type : managedEntityTypes) {
|
||||
for (Class<?> type : managedEntityTypes) {
|
||||
getEntityInfo(type);
|
||||
mapperRegistry.addAppObjectMapper(new MongoEntityMapper(this, mapperRegistry, type));
|
||||
mapperRegistry.addDBObjectMapper(new BasicDBObjectMapper(this, mapperRegistry, type));
|
||||
|
@ -102,9 +103,9 @@ public class MongoStoreImpl implements MongoStore {
|
|||
logger.info("Database " + this.database.getName() + " dropped in MongoDB");
|
||||
}
|
||||
|
||||
// Don't drop database, but just clear all data in managed collections (useful for development)
|
||||
protected void clearManagedCollections(Class<? extends MongoEntity>[] managedEntityTypes) {
|
||||
for (Class<? extends MongoEntity> clazz : managedEntityTypes) {
|
||||
// Don't drop database, but just clear all data in managed collections (useful for export/import or during development)
|
||||
protected void clearManagedCollections(Class<?>[] managedEntityTypes) {
|
||||
for (Class<?> clazz : managedEntityTypes) {
|
||||
DBCollection dbCollection = getDBCollectionForType(clazz);
|
||||
if (dbCollection != null) {
|
||||
dbCollection.remove(new BasicDBObject());
|
||||
|
@ -113,8 +114,8 @@ public class MongoStoreImpl implements MongoStore {
|
|||
}
|
||||
}
|
||||
|
||||
protected void initManagedCollections(Class<? extends MongoEntity>[] managedEntityTypes) {
|
||||
for (Class<? extends MongoEntity> clazz : managedEntityTypes) {
|
||||
protected void initManagedCollections(Class<?>[] managedEntityTypes) {
|
||||
for (Class<?> clazz : managedEntityTypes) {
|
||||
EntityInfo entityInfo = getEntityInfo(clazz);
|
||||
String dbCollectionName = entityInfo.getDbCollectionName();
|
||||
if (dbCollectionName != null && !database.collectionExists(dbCollectionName)) {
|
||||
|
@ -416,6 +417,13 @@ public class MongoStoreImpl implements MongoStore {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllEntities() {
|
||||
Set<Class<?>> managedTypes = this.entityInfoCache.keySet();
|
||||
Class<? extends MongoEntity>[] arrayTemplate = (Class<? extends MongoEntity>[])new Class<?>[0];
|
||||
this.clearManagedCollections(managedTypes.toArray(arrayTemplate));
|
||||
}
|
||||
|
||||
// Possibility to add user-defined mappers
|
||||
public void addAppObjectConverter(Mapper<?, ?> mapper) {
|
||||
mapperRegistry.addAppObjectMapper(mapper);
|
||||
|
@ -425,10 +433,10 @@ public class MongoStoreImpl implements MongoStore {
|
|||
mapperRegistry.addDBObjectMapper(mapper);
|
||||
}
|
||||
|
||||
public EntityInfo getEntityInfo(Class<? extends MongoEntity> entityClass) {
|
||||
public EntityInfo getEntityInfo(Class<?> entityClass) {
|
||||
EntityInfo entityInfo = entityInfoCache.get(entityClass);
|
||||
if (entityInfo == null) {
|
||||
List<Property<Object>> properties = PropertyQueries.createQuery(entityClass).addCriteria(new AnnotatedPropertyCriteria(MongoField.class)).getResultList();
|
||||
Map<String, Property<Object>> properties = PropertyQueries.createQuery(entityClass).getWritableResultList();
|
||||
|
||||
MongoCollection classAnnotation = entityClass.getAnnotation(MongoCollection.class);
|
||||
|
||||
|
@ -473,7 +481,7 @@ public class MongoStoreImpl implements MongoStore {
|
|||
return object;
|
||||
}
|
||||
|
||||
protected DBCollection getDBCollectionForType(Class<? extends MongoEntity> type) {
|
||||
protected DBCollection getDBCollectionForType(Class<?> type) {
|
||||
EntityInfo entityInfo = getEntityInfo(type);
|
||||
String dbCollectionName = entityInfo.getDbCollectionName();
|
||||
return dbCollectionName==null ? null : database.getCollection(dbCollectionName);
|
||||
|
|
|
@ -14,13 +14,13 @@ import org.keycloak.models.mongo.api.types.MapperContext;
|
|||
import org.keycloak.models.mongo.api.types.MapperRegistry;
|
||||
import org.keycloak.models.mongo.impl.MongoStoreImpl;
|
||||
import org.keycloak.models.mongo.impl.EntityInfo;
|
||||
import org.picketlink.common.properties.Property;
|
||||
import org.picketlink.common.reflection.Types;
|
||||
import org.keycloak.models.utils.reflection.Property;
|
||||
import org.keycloak.models.utils.reflection.Types;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class BasicDBObjectMapper<S extends MongoEntity> implements Mapper<BasicDBObject, S> {
|
||||
public class BasicDBObjectMapper<S> implements Mapper<BasicDBObject, S> {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(BasicDBObjectMapper.class);
|
||||
|
||||
|
@ -73,7 +73,7 @@ public class BasicDBObjectMapper<S extends MongoEntity> implements Mapper<BasicD
|
|||
return entity;
|
||||
}
|
||||
|
||||
private void setPropertyValue(MongoEntity entity, Object valueFromDB, Property property) {
|
||||
private void setPropertyValue(Object entity, Object valueFromDB, Property property) {
|
||||
if (valueFromDB == null) {
|
||||
property.setValue(entity, null);
|
||||
return;
|
||||
|
|
|
@ -1,20 +1,19 @@
|
|||
package org.keycloak.models.mongo.impl.types;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import org.keycloak.models.mongo.api.MongoEntity;
|
||||
import org.keycloak.models.mongo.api.types.Mapper;
|
||||
import org.keycloak.models.mongo.api.types.MapperContext;
|
||||
import org.keycloak.models.mongo.api.types.MapperRegistry;
|
||||
import org.keycloak.models.mongo.impl.MongoStoreImpl;
|
||||
import org.keycloak.models.mongo.impl.EntityInfo;
|
||||
import org.picketlink.common.properties.Property;
|
||||
import org.keycloak.models.utils.reflection.Property;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class MongoEntityMapper<T extends MongoEntity> implements Mapper<T, BasicDBObject> {
|
||||
public class MongoEntityMapper<T> implements Mapper<T, BasicDBObject> {
|
||||
|
||||
private final MongoStoreImpl mongoStoreImpl;
|
||||
private final MapperRegistry mapperRegistry;
|
||||
|
@ -37,11 +36,14 @@ public class MongoEntityMapper<T extends MongoEntity> implements Mapper<T, Basic
|
|||
Collection<Property<Object>> props = entityInfo.getProperties();
|
||||
for (Property<Object> property : props) {
|
||||
String propName = property.getName();
|
||||
Object propValue = property.getValue(applicationObject);
|
||||
|
||||
if (propValue != null) {
|
||||
Object dbValue = mapperRegistry.convertApplicationObjectToDBObject(propValue, Object.class);
|
||||
dbObject.put(propName, dbValue);
|
||||
// Ignore "id" property
|
||||
if (!"id".equals(propName)) {
|
||||
Object propValue = property.getValue(applicationObject);
|
||||
if (propValue != null) {
|
||||
Object dbValue = propValue == null ? null : mapperRegistry.convertApplicationObjectToDBObject(propValue, Object.class);
|
||||
dbObject.put(propName, dbValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
package org.keycloak.models.mongo.keycloak.adapters;
|
||||
|
||||
import org.keycloak.models.mongo.api.AbstractMongoIdentifiableEntity;
|
||||
import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
|
||||
import org.keycloak.models.mongo.api.MongoStore;
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public abstract class AbstractMongoAdapter<T extends AbstractMongoIdentifiableEntity> {
|
||||
public abstract class AbstractMongoAdapter<T extends MongoIdentifiableEntity> {
|
||||
|
||||
protected final MongoStoreInvocationContext invocationContext;
|
||||
|
||||
|
|
|
@ -8,8 +8,8 @@ import org.keycloak.models.RealmModel;
|
|||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.models.mongo.keycloak.entities.ApplicationEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.RoleEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoApplicationEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity;
|
||||
import org.keycloak.models.mongo.utils.MongoModelUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -20,9 +20,9 @@ import java.util.Set;
|
|||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class ApplicationAdapter extends ClientAdapter<ApplicationEntity> implements ApplicationModel {
|
||||
public class ApplicationAdapter extends ClientAdapter<MongoApplicationEntity> implements ApplicationModel {
|
||||
|
||||
public ApplicationAdapter(RealmModel realm, ApplicationEntity applicationEntity, MongoStoreInvocationContext invContext) {
|
||||
public ApplicationAdapter(RealmModel realm, MongoApplicationEntity applicationEntity, MongoStoreInvocationContext invContext) {
|
||||
super(realm, applicationEntity, invContext);
|
||||
}
|
||||
|
||||
|
@ -103,7 +103,7 @@ public class ApplicationAdapter extends ClientAdapter<ApplicationEntity> impleme
|
|||
.and("name").is(name)
|
||||
.and("applicationId").is(getId())
|
||||
.get();
|
||||
RoleEntity role = getMongoStore().loadSingleEntity(RoleEntity.class, query, invocationContext);
|
||||
MongoRoleEntity role = getMongoStore().loadSingleEntity(MongoRoleEntity.class, query, invocationContext);
|
||||
if (role == null) {
|
||||
return null;
|
||||
} else {
|
||||
|
@ -113,7 +113,13 @@ public class ApplicationAdapter extends ClientAdapter<ApplicationEntity> impleme
|
|||
|
||||
@Override
|
||||
public RoleAdapter addRole(String name) {
|
||||
RoleEntity roleEntity = new RoleEntity();
|
||||
return this.addRole(null, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleAdapter addRole(String id, String name) {
|
||||
MongoRoleEntity roleEntity = new MongoRoleEntity();
|
||||
roleEntity.setId(id);
|
||||
roleEntity.setName(name);
|
||||
roleEntity.setApplicationId(getId());
|
||||
|
||||
|
@ -124,7 +130,7 @@ public class ApplicationAdapter extends ClientAdapter<ApplicationEntity> impleme
|
|||
|
||||
@Override
|
||||
public boolean removeRole(RoleModel role) {
|
||||
return getMongoStore().removeEntity(RoleEntity.class, role.getId(), invocationContext);
|
||||
return getMongoStore().removeEntity(MongoRoleEntity.class, role.getId(), invocationContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -132,10 +138,10 @@ public class ApplicationAdapter extends ClientAdapter<ApplicationEntity> impleme
|
|||
DBObject query = new QueryBuilder()
|
||||
.and("applicationId").is(getId())
|
||||
.get();
|
||||
List<RoleEntity> roles = getMongoStore().loadEntities(RoleEntity.class, query, invocationContext);
|
||||
List<MongoRoleEntity> roles = getMongoStore().loadEntities(MongoRoleEntity.class, query, invocationContext);
|
||||
|
||||
Set<RoleModel> result = new HashSet<RoleModel>();
|
||||
for (RoleEntity role : roles) {
|
||||
for (MongoRoleEntity role : roles) {
|
||||
result.add(new RoleAdapter(getRealm(), role, this, invocationContext));
|
||||
}
|
||||
|
||||
|
@ -145,9 +151,9 @@ public class ApplicationAdapter extends ClientAdapter<ApplicationEntity> impleme
|
|||
@Override
|
||||
public Set<RoleModel> getApplicationRoleMappings(UserModel user) {
|
||||
Set<RoleModel> result = new HashSet<RoleModel>();
|
||||
List<RoleEntity> roles = MongoModelUtils.getAllRolesOfUser(user, invocationContext);
|
||||
List<MongoRoleEntity> roles = MongoModelUtils.getAllRolesOfUser(user, invocationContext);
|
||||
|
||||
for (RoleEntity role : roles) {
|
||||
for (MongoRoleEntity role : roles) {
|
||||
if (getId().equals(role.getApplicationId())) {
|
||||
result.add(new RoleAdapter(getRealm(), role, this, invocationContext));
|
||||
}
|
||||
|
@ -158,9 +164,9 @@ public class ApplicationAdapter extends ClientAdapter<ApplicationEntity> impleme
|
|||
@Override
|
||||
public Set<RoleModel> getApplicationScopeMappings(ClientModel client) {
|
||||
Set<RoleModel> result = new HashSet<RoleModel>();
|
||||
List<RoleEntity> roles = MongoModelUtils.getAllScopesOfClient(client, invocationContext);
|
||||
List<MongoRoleEntity> roles = MongoModelUtils.getAllScopesOfClient(client, invocationContext);
|
||||
|
||||
for (RoleEntity role : roles) {
|
||||
for (MongoRoleEntity role : roles) {
|
||||
if (getId().equals(role.getApplicationId())) {
|
||||
result.add(new RoleAdapter(getRealm(), role, this, invocationContext));
|
||||
}
|
||||
|
|
|
@ -7,13 +7,14 @@ import java.util.Set;
|
|||
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.entities.ClientEntity;
|
||||
import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.models.mongo.keycloak.entities.ClientEntity;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class ClientAdapter<T extends ClientEntity> extends AbstractMongoAdapter<T> implements ClientModel {
|
||||
public class ClientAdapter<T extends MongoIdentifiableEntity> extends AbstractMongoAdapter<T> implements ClientModel {
|
||||
|
||||
protected final T clientEntity;
|
||||
private final RealmModel realm;
|
||||
|
@ -29,6 +30,11 @@ public class ClientAdapter<T extends ClientEntity> extends AbstractMongoAdapter<
|
|||
return clientEntity;
|
||||
}
|
||||
|
||||
// ClientEntity doesn't extend MongoIdentifiableEntity
|
||||
public ClientEntity getMongoEntityAsClient() {
|
||||
return (ClientEntity)getMongoEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return getMongoEntity().getId();
|
||||
|
@ -36,25 +42,25 @@ public class ClientAdapter<T extends ClientEntity> extends AbstractMongoAdapter<
|
|||
|
||||
@Override
|
||||
public String getClientId() {
|
||||
return getMongoEntity().getName();
|
||||
return getMongoEntityAsClient().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getAllowedClaimsMask() {
|
||||
return getMongoEntity().getAllowedClaimsMask();
|
||||
return getMongoEntityAsClient().getAllowedClaimsMask();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAllowedClaimsMask(long mask) {
|
||||
getMongoEntity().setAllowedClaimsMask(mask);
|
||||
getMongoEntityAsClient().setAllowedClaimsMask(mask);
|
||||
updateMongoEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getWebOrigins() {
|
||||
Set<String> result = new HashSet<String>();
|
||||
if (getMongoEntity().getWebOrigins() != null) {
|
||||
result.addAll(clientEntity.getWebOrigins());
|
||||
if (getMongoEntityAsClient().getWebOrigins() != null) {
|
||||
result.addAll(getMongoEntityAsClient().getWebOrigins());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -63,7 +69,7 @@ public class ClientAdapter<T extends ClientEntity> extends AbstractMongoAdapter<
|
|||
public void setWebOrigins(Set<String> webOrigins) {
|
||||
List<String> result = new ArrayList<String>();
|
||||
result.addAll(webOrigins);
|
||||
clientEntity.setWebOrigins(result);
|
||||
getMongoEntityAsClient().setWebOrigins(result);
|
||||
updateMongoEntity();
|
||||
}
|
||||
|
||||
|
@ -80,8 +86,8 @@ public class ClientAdapter<T extends ClientEntity> extends AbstractMongoAdapter<
|
|||
@Override
|
||||
public Set<String> getRedirectUris() {
|
||||
Set<String> result = new HashSet<String>();
|
||||
if (clientEntity.getRedirectUris() != null) {
|
||||
result.addAll(clientEntity.getRedirectUris());
|
||||
if (getMongoEntityAsClient().getRedirectUris() != null) {
|
||||
result.addAll(getMongoEntityAsClient().getRedirectUris());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -90,7 +96,7 @@ public class ClientAdapter<T extends ClientEntity> extends AbstractMongoAdapter<
|
|||
public void setRedirectUris(Set<String> redirectUris) {
|
||||
List<String> result = new ArrayList<String>();
|
||||
result.addAll(redirectUris);
|
||||
clientEntity.setRedirectUris(result);
|
||||
getMongoEntityAsClient().setRedirectUris(result);
|
||||
updateMongoEntity();
|
||||
}
|
||||
|
||||
|
@ -106,39 +112,39 @@ public class ClientAdapter<T extends ClientEntity> extends AbstractMongoAdapter<
|
|||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return clientEntity.isEnabled();
|
||||
return getMongoEntityAsClient().isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
clientEntity.setEnabled(enabled);
|
||||
getMongoEntityAsClient().setEnabled(enabled);
|
||||
updateMongoEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validateSecret(String secret) {
|
||||
return secret.equals(clientEntity.getSecret());
|
||||
return secret.equals(getMongoEntityAsClient().getSecret());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSecret() {
|
||||
return clientEntity.getSecret();
|
||||
return getMongoEntityAsClient().getSecret();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSecret(String secret) {
|
||||
clientEntity.setSecret(secret);
|
||||
getMongoEntityAsClient().setSecret(secret);
|
||||
updateMongoEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPublicClient() {
|
||||
return clientEntity.isPublicClient();
|
||||
return getMongoEntityAsClient().isPublicClient();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPublicClient(boolean flag) {
|
||||
clientEntity.setPublicClient(flag);
|
||||
getMongoEntityAsClient().setPublicClient(flag);
|
||||
updateMongoEntity();
|
||||
}
|
||||
|
||||
|
@ -149,12 +155,12 @@ public class ClientAdapter<T extends ClientEntity> extends AbstractMongoAdapter<
|
|||
|
||||
@Override
|
||||
public int getNotBefore() {
|
||||
return clientEntity.getNotBefore();
|
||||
return getMongoEntityAsClient().getNotBefore();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNotBefore(int notBefore) {
|
||||
clientEntity.setNotBefore(notBefore);
|
||||
getMongoEntityAsClient().setNotBefore(notBefore);
|
||||
updateMongoEntity();
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ import org.keycloak.models.RealmModel;
|
|||
import org.keycloak.models.mongo.api.MongoStore;
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.models.mongo.impl.context.TransactionMongoStoreInvocationContext;
|
||||
import org.keycloak.models.mongo.keycloak.entities.RealmEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoRealmEntity;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -39,6 +39,11 @@ public class MongoKeycloakSession implements KeycloakSession {
|
|||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllData() {
|
||||
getMongoStore().removeAllEntities();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmModel createRealm(String name) {
|
||||
return createRealm(KeycloakModelUtils.generateId(), name);
|
||||
|
@ -46,7 +51,7 @@ public class MongoKeycloakSession implements KeycloakSession {
|
|||
|
||||
@Override
|
||||
public RealmModel createRealm(String id, String name) {
|
||||
RealmEntity newRealm = new RealmEntity();
|
||||
MongoRealmEntity newRealm = new MongoRealmEntity();
|
||||
newRealm.setId(id);
|
||||
newRealm.setName(name);
|
||||
|
||||
|
@ -57,17 +62,17 @@ public class MongoKeycloakSession implements KeycloakSession {
|
|||
|
||||
@Override
|
||||
public RealmModel getRealm(String id) {
|
||||
RealmEntity realmEntity = getMongoStore().loadEntity(RealmEntity.class, id, invocationContext);
|
||||
MongoRealmEntity realmEntity = getMongoStore().loadEntity(MongoRealmEntity.class, id, invocationContext);
|
||||
return realmEntity != null ? new RealmAdapter(realmEntity, invocationContext) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RealmModel> getRealms() {
|
||||
DBObject query = new BasicDBObject();
|
||||
List<RealmEntity> realms = getMongoStore().loadEntities(RealmEntity.class, query, invocationContext);
|
||||
List<MongoRealmEntity> realms = getMongoStore().loadEntities(MongoRealmEntity.class, query, invocationContext);
|
||||
|
||||
List<RealmModel> results = new ArrayList<RealmModel>();
|
||||
for (RealmEntity realmEntity : realms) {
|
||||
for (MongoRealmEntity realmEntity : realms) {
|
||||
results.add(new RealmAdapter(realmEntity, invocationContext));
|
||||
}
|
||||
return results;
|
||||
|
@ -78,7 +83,7 @@ public class MongoKeycloakSession implements KeycloakSession {
|
|||
DBObject query = new QueryBuilder()
|
||||
.and("name").is(name)
|
||||
.get();
|
||||
RealmEntity realm = getMongoStore().loadSingleEntity(RealmEntity.class, query, invocationContext);
|
||||
MongoRealmEntity realm = getMongoStore().loadSingleEntity(MongoRealmEntity.class, query, invocationContext);
|
||||
|
||||
if (realm == null) return null;
|
||||
return new RealmAdapter(realm, invocationContext);
|
||||
|
@ -86,7 +91,7 @@ public class MongoKeycloakSession implements KeycloakSession {
|
|||
|
||||
@Override
|
||||
public boolean removeRealm(String id) {
|
||||
return getMongoStore().removeEntity(RealmEntity.class, id, invocationContext);
|
||||
return getMongoStore().removeEntity(MongoRealmEntity.class, id, invocationContext);
|
||||
}
|
||||
|
||||
protected MongoStore getMongoStore() {
|
||||
|
|
|
@ -3,20 +3,20 @@ package org.keycloak.models.mongo.keycloak.adapters;
|
|||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.mongo.api.MongoEntity;
|
||||
import org.keycloak.models.entities.AuthenticationLinkEntity;
|
||||
import org.keycloak.models.entities.AuthenticationProviderEntity;
|
||||
import org.keycloak.models.entities.CredentialEntity;
|
||||
import org.keycloak.models.entities.RequiredCredentialEntity;
|
||||
import org.keycloak.models.entities.SocialLinkEntity;
|
||||
import org.keycloak.models.mongo.api.MongoStore;
|
||||
import org.keycloak.models.mongo.impl.MongoStoreImpl;
|
||||
import org.keycloak.models.mongo.keycloak.config.MongoClientProvider;
|
||||
import org.keycloak.models.mongo.keycloak.entities.ApplicationEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.AuthenticationLinkEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.AuthenticationProviderEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.CredentialEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.OAuthClientEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.RealmEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.RequiredCredentialEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.RoleEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.SocialLinkEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.UserEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoApplicationEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoOAuthClientEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoRealmEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoUserEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoUsernameLoginFailureEntity;
|
||||
|
||||
/**
|
||||
* KeycloakSessionFactory implementation based on MongoDB
|
||||
|
@ -26,17 +26,18 @@ import org.keycloak.models.mongo.keycloak.entities.UserEntity;
|
|||
public class MongoKeycloakSessionFactory implements KeycloakSessionFactory {
|
||||
protected static final Logger logger = Logger.getLogger(MongoKeycloakSessionFactory.class);
|
||||
|
||||
private static final Class<? extends MongoEntity>[] MANAGED_ENTITY_TYPES = (Class<? extends MongoEntity>[])new Class<?>[] {
|
||||
RealmEntity.class,
|
||||
UserEntity.class,
|
||||
RoleEntity.class,
|
||||
private static final Class<?>[] MANAGED_ENTITY_TYPES = (Class<?>[])new Class<?>[] {
|
||||
MongoRealmEntity.class,
|
||||
MongoUserEntity.class,
|
||||
MongoRoleEntity.class,
|
||||
RequiredCredentialEntity.class,
|
||||
AuthenticationProviderEntity.class,
|
||||
CredentialEntity.class,
|
||||
SocialLinkEntity.class,
|
||||
AuthenticationLinkEntity.class,
|
||||
ApplicationEntity.class,
|
||||
OAuthClientEntity.class
|
||||
MongoApplicationEntity.class,
|
||||
MongoOAuthClientEntity.class,
|
||||
MongoUsernameLoginFailureEntity.class
|
||||
};
|
||||
|
||||
private final MongoClientProvider mongoClientProvider;
|
||||
|
|
|
@ -3,14 +3,14 @@ package org.keycloak.models.mongo.keycloak.adapters;
|
|||
import org.keycloak.models.OAuthClientModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.models.mongo.keycloak.entities.OAuthClientEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoOAuthClientEntity;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class OAuthClientAdapter extends ClientAdapter<OAuthClientEntity> implements OAuthClientModel {
|
||||
public class OAuthClientAdapter extends ClientAdapter<MongoOAuthClientEntity> implements OAuthClientModel {
|
||||
|
||||
public OAuthClientAdapter(RealmModel realm, OAuthClientEntity oauthClientEntity, MongoStoreInvocationContext invContext) {
|
||||
public OAuthClientAdapter(RealmModel realm, MongoOAuthClientEntity oauthClientEntity, MongoStoreInvocationContext invContext) {
|
||||
super(realm, oauthClientEntity, invContext);
|
||||
}
|
||||
|
||||
|
|
|
@ -14,19 +14,21 @@ import org.keycloak.models.RequiredCredentialModel;
|
|||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.SocialLinkModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserCredentialValueModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UsernameLoginFailureModel;
|
||||
import org.keycloak.models.entities.AuthenticationLinkEntity;
|
||||
import org.keycloak.models.entities.AuthenticationProviderEntity;
|
||||
import org.keycloak.models.entities.CredentialEntity;
|
||||
import org.keycloak.models.entities.RequiredCredentialEntity;
|
||||
import org.keycloak.models.entities.SocialLinkEntity;
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.models.mongo.keycloak.entities.ApplicationEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.AuthenticationLinkEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.AuthenticationProviderEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.CredentialEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.OAuthClientEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.RealmEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.RequiredCredentialEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.RoleEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.SocialLinkEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.UserEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.UsernameLoginFailureEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoApplicationEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoOAuthClientEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoRealmEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoUserEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoUsernameLoginFailureEntity;
|
||||
import org.keycloak.models.mongo.utils.MongoModelUtils;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.models.utils.Pbkdf2PasswordEncoder;
|
||||
|
@ -39,7 +41,6 @@ import java.util.Collection;
|
|||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -48,18 +49,18 @@ import java.util.regex.Pattern;
|
|||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements RealmModel {
|
||||
public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> implements RealmModel {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(RealmAdapter.class);
|
||||
|
||||
private final RealmEntity realm;
|
||||
private final MongoRealmEntity realm;
|
||||
|
||||
protected volatile transient PublicKey publicKey;
|
||||
protected volatile transient PrivateKey privateKey;
|
||||
|
||||
private volatile transient PasswordPolicy passwordPolicy;
|
||||
|
||||
public RealmAdapter(RealmEntity realmEntity, MongoStoreInvocationContext invocationContext) {
|
||||
public RealmAdapter(MongoRealmEntity realmEntity, MongoStoreInvocationContext invocationContext) {
|
||||
super(invocationContext);
|
||||
this.realm = realmEntity;
|
||||
}
|
||||
|
@ -132,6 +133,7 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
|
|||
@Override
|
||||
public void setBruteForceProtected(boolean value) {
|
||||
realm.setBruteForceProtected(value);
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -142,6 +144,7 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
|
|||
@Override
|
||||
public void setMaxFailureWaitSeconds(int val) {
|
||||
realm.setMaxFailureWaitSeconds(val);
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -152,6 +155,7 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
|
|||
@Override
|
||||
public void setWaitIncrementSeconds(int val) {
|
||||
realm.setWaitIncrementSeconds(val);
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -162,6 +166,7 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
|
|||
@Override
|
||||
public void setQuickLoginCheckMilliSeconds(long val) {
|
||||
realm.setQuickLoginCheckMilliSeconds(val);
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -172,6 +177,7 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
|
|||
@Override
|
||||
public void setMinimumQuickLoginWaitSeconds(int val) {
|
||||
realm.setMinimumQuickLoginWaitSeconds(val);
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
|
||||
|
@ -183,6 +189,7 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
|
|||
@Override
|
||||
public void setMaxDeltaTimeSeconds(int val) {
|
||||
realm.setMaxDeltaTimeSeconds(val);
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -193,6 +200,7 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
|
|||
@Override
|
||||
public void setFailureFactor(int failureFactor) {
|
||||
realm.setFailureFactor(failureFactor);
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
|
||||
|
@ -403,7 +411,7 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
|
|||
.and("loginName").is(name)
|
||||
.and("realmId").is(getId())
|
||||
.get();
|
||||
UserEntity user = getMongoStore().loadSingleEntity(UserEntity.class, query, invocationContext);
|
||||
MongoUserEntity user = getMongoStore().loadSingleEntity(MongoUserEntity.class, query, invocationContext);
|
||||
|
||||
if (user == null) {
|
||||
return null;
|
||||
|
@ -418,7 +426,7 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
|
|||
.and("username").is(name)
|
||||
.and("realmId").is(getId())
|
||||
.get();
|
||||
UsernameLoginFailureEntity user = getMongoStore().loadSingleEntity(UsernameLoginFailureEntity.class, query, invocationContext);
|
||||
MongoUsernameLoginFailureEntity user = getMongoStore().loadSingleEntity(MongoUsernameLoginFailureEntity.class, query, invocationContext);
|
||||
|
||||
if (user == null) {
|
||||
return null;
|
||||
|
@ -434,23 +442,38 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
|
|||
return userLoginFailure;
|
||||
}
|
||||
|
||||
UsernameLoginFailureEntity userEntity = new UsernameLoginFailureEntity();
|
||||
MongoUsernameLoginFailureEntity userEntity = new MongoUsernameLoginFailureEntity();
|
||||
userEntity.setUsername(username);
|
||||
// Compatibility with JPA model, which has user disabled by default
|
||||
// userEntity.setEnabled(true);
|
||||
userEntity.setRealmId(getId());
|
||||
|
||||
getMongoStore().insertEntity(userEntity, invocationContext);
|
||||
return new UsernameLoginFailureAdapter(invocationContext, userEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UsernameLoginFailureModel> getAllUserLoginFailures() {
|
||||
DBObject query = new QueryBuilder()
|
||||
.and("realmId").is(getId())
|
||||
.get();
|
||||
List<MongoUsernameLoginFailureEntity> failures = getMongoStore().loadEntities(MongoUsernameLoginFailureEntity.class, query, invocationContext);
|
||||
|
||||
List<UsernameLoginFailureModel> result = new ArrayList<UsernameLoginFailureModel>();
|
||||
|
||||
if (failures == null) return result;
|
||||
for (MongoUsernameLoginFailureEntity failure : failures) {
|
||||
result.add(new UsernameLoginFailureAdapter(invocationContext, failure));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel getUserByEmail(String email) {
|
||||
DBObject query = new QueryBuilder()
|
||||
.and("email").is(email)
|
||||
.and("realmId").is(getId())
|
||||
.get();
|
||||
UserEntity user = getMongoStore().loadSingleEntity(UserEntity.class, query, invocationContext);
|
||||
MongoUserEntity user = getMongoStore().loadSingleEntity(MongoUserEntity.class, query, invocationContext);
|
||||
|
||||
if (user == null) {
|
||||
return null;
|
||||
|
@ -461,7 +484,7 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
|
|||
|
||||
@Override
|
||||
public UserModel getUserById(String id) {
|
||||
UserEntity user = getMongoStore().loadEntity(UserEntity.class, id, invocationContext);
|
||||
MongoUserEntity user = getMongoStore().loadEntity(MongoUserEntity.class, id, invocationContext);
|
||||
|
||||
// Check that it's user from this realm
|
||||
if (user == null || !getId().equals(user.getRealmId())) {
|
||||
|
@ -473,7 +496,12 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
|
|||
|
||||
@Override
|
||||
public UserAdapter addUser(String username) {
|
||||
UserAdapter userModel = addUserEntity(username);
|
||||
return this.addUser(null, username);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserAdapter addUser(String id, String username) {
|
||||
UserAdapter userModel = addUserEntity(id, username);
|
||||
|
||||
for (String r : getDefaultRoles()) {
|
||||
grantRole(userModel, getRole(r));
|
||||
|
@ -488,9 +516,9 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
|
|||
return userModel;
|
||||
}
|
||||
|
||||
// Add just user entity without defaultRoles
|
||||
protected UserAdapter addUserEntity(String username) {
|
||||
UserEntity userEntity = new UserEntity();
|
||||
protected UserAdapter addUserEntity(String id, String username) {
|
||||
MongoUserEntity userEntity = new MongoUserEntity();
|
||||
userEntity.setId(id);
|
||||
userEntity.setLoginName(username);
|
||||
// Compatibility with JPA model, which has user disabled by default
|
||||
// userEntity.setEnabled(true);
|
||||
|
@ -506,7 +534,7 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
|
|||
.and("loginName").is(name)
|
||||
.and("realmId").is(getId())
|
||||
.get();
|
||||
return getMongoStore().removeEntities(UserEntity.class, query, invocationContext);
|
||||
return getMongoStore().removeEntities(MongoUserEntity.class, query, invocationContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -515,7 +543,7 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
|
|||
.and("name").is(name)
|
||||
.and("realmId").is(getId())
|
||||
.get();
|
||||
RoleEntity role = getMongoStore().loadSingleEntity(RoleEntity.class, query, invocationContext);
|
||||
MongoRoleEntity role = getMongoStore().loadSingleEntity(MongoRoleEntity.class, query, invocationContext);
|
||||
if (role == null) {
|
||||
return null;
|
||||
} else {
|
||||
|
@ -525,7 +553,13 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
|
|||
|
||||
@Override
|
||||
public RoleModel addRole(String name) {
|
||||
RoleEntity roleEntity = new RoleEntity();
|
||||
return this.addRole(null, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleModel addRole(String id, String name) {
|
||||
MongoRoleEntity roleEntity = new MongoRoleEntity();
|
||||
roleEntity.setId(id);
|
||||
roleEntity.setName(name);
|
||||
roleEntity.setRealmId(getId());
|
||||
|
||||
|
@ -541,7 +575,7 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
|
|||
|
||||
@Override
|
||||
public boolean removeRoleById(String id) {
|
||||
return getMongoStore().removeEntity(RoleEntity.class, id, invocationContext);
|
||||
return getMongoStore().removeEntity(MongoRoleEntity.class, id, invocationContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -549,12 +583,12 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
|
|||
DBObject query = new QueryBuilder()
|
||||
.and("realmId").is(getId())
|
||||
.get();
|
||||
List<RoleEntity> roles = getMongoStore().loadEntities(RoleEntity.class, query, invocationContext);
|
||||
List<MongoRoleEntity> roles = getMongoStore().loadEntities(MongoRoleEntity.class, query, invocationContext);
|
||||
|
||||
Set<RoleModel> result = new HashSet<RoleModel>();
|
||||
|
||||
if (roles == null) return result;
|
||||
for (RoleEntity role : roles) {
|
||||
for (MongoRoleEntity role : roles) {
|
||||
result.add(new RoleAdapter(this, role, this, invocationContext));
|
||||
}
|
||||
|
||||
|
@ -563,7 +597,7 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
|
|||
|
||||
@Override
|
||||
public RoleModel getRoleById(String id) {
|
||||
RoleEntity role = getMongoStore().loadEntity(RoleEntity.class, id, invocationContext);
|
||||
MongoRoleEntity role = getMongoStore().loadEntity(MongoRoleEntity.class, id, invocationContext);
|
||||
if (role == null) return null;
|
||||
if (role.getRealmId() != null) {
|
||||
if (!role.getRealmId().equals(this.getId())) return null;
|
||||
|
@ -615,7 +649,7 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
|
|||
|
||||
@Override
|
||||
public ApplicationModel getApplicationById(String id) {
|
||||
ApplicationEntity appData = getMongoStore().loadEntity(ApplicationEntity.class, id, invocationContext);
|
||||
MongoApplicationEntity appData = getMongoStore().loadEntity(MongoApplicationEntity.class, id, invocationContext);
|
||||
|
||||
// Check if application belongs to this realm
|
||||
if (appData == null || !getId().equals(appData.getRealmId())) {
|
||||
|
@ -631,7 +665,7 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
|
|||
.and("realmId").is(getId())
|
||||
.and("name").is(name)
|
||||
.get();
|
||||
ApplicationEntity appEntity = getMongoStore().loadSingleEntity(ApplicationEntity.class, query, invocationContext);
|
||||
MongoApplicationEntity appEntity = getMongoStore().loadSingleEntity(MongoApplicationEntity.class, query, invocationContext);
|
||||
return appEntity == null ? null : new ApplicationAdapter(this, appEntity, invocationContext);
|
||||
}
|
||||
|
||||
|
@ -649,10 +683,10 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
|
|||
DBObject query = new QueryBuilder()
|
||||
.and("realmId").is(getId())
|
||||
.get();
|
||||
List<ApplicationEntity> appDatas = getMongoStore().loadEntities(ApplicationEntity.class, query, invocationContext);
|
||||
List<MongoApplicationEntity> appDatas = getMongoStore().loadEntities(MongoApplicationEntity.class, query, invocationContext);
|
||||
|
||||
List<ApplicationModel> result = new ArrayList<ApplicationModel>();
|
||||
for (ApplicationEntity appData : appDatas) {
|
||||
for (MongoApplicationEntity appData : appDatas) {
|
||||
result.add(new ApplicationAdapter(this, appData, invocationContext));
|
||||
}
|
||||
return result;
|
||||
|
@ -660,7 +694,13 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
|
|||
|
||||
@Override
|
||||
public ApplicationModel addApplication(String name) {
|
||||
ApplicationEntity appData = new ApplicationEntity();
|
||||
return this.addApplication(null, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplicationModel addApplication(String id, String name) {
|
||||
MongoApplicationEntity appData = new MongoApplicationEntity();
|
||||
appData.setId(id);
|
||||
appData.setName(name);
|
||||
appData.setRealmId(getId());
|
||||
appData.setEnabled(true);
|
||||
|
@ -671,7 +711,7 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
|
|||
|
||||
@Override
|
||||
public boolean removeApplication(String id) {
|
||||
return getMongoStore().removeEntity(ApplicationEntity.class, id, invocationContext);
|
||||
return getMongoStore().removeEntity(MongoApplicationEntity.class, id, invocationContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -687,16 +727,16 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
|
|||
|
||||
@Override
|
||||
public void grantRole(UserModel user, RoleModel role) {
|
||||
UserEntity userEntity = ((UserAdapter) user).getUser();
|
||||
MongoUserEntity userEntity = ((UserAdapter) user).getUser();
|
||||
getMongoStore().pushItemToList(userEntity, "roleIds", role.getId(), true, invocationContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<RoleModel> getRoleMappings(UserModel user) {
|
||||
Set<RoleModel> result = new HashSet<RoleModel>();
|
||||
List<RoleEntity> roles = MongoModelUtils.getAllRolesOfUser(user, invocationContext);
|
||||
List<MongoRoleEntity> roles = MongoModelUtils.getAllRolesOfUser(user, invocationContext);
|
||||
|
||||
for (RoleEntity role : roles) {
|
||||
for (MongoRoleEntity role : roles) {
|
||||
if (getId().equals(role.getRealmId())) {
|
||||
result.add(new RoleAdapter(this, role, this, invocationContext));
|
||||
} else {
|
||||
|
@ -714,7 +754,7 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
|
|||
// Filter to retrieve just realm roles TODO: Maybe improve to avoid filter programmatically... Maybe have separate fields for realmRoles and appRoles on user?
|
||||
Set<RoleModel> realmRoles = new HashSet<RoleModel>();
|
||||
for (RoleModel role : allRoles) {
|
||||
RoleEntity roleEntity = ((RoleAdapter) role).getRole();
|
||||
MongoRoleEntity roleEntity = ((RoleAdapter) role).getRole();
|
||||
|
||||
if (getId().equals(roleEntity.getRealmId())) {
|
||||
realmRoles.add(role);
|
||||
|
@ -727,16 +767,16 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
|
|||
public void deleteRoleMapping(UserModel user, RoleModel role) {
|
||||
if (user == null || role == null) return;
|
||||
|
||||
UserEntity userEntity = ((UserAdapter) user).getUser();
|
||||
MongoUserEntity userEntity = ((UserAdapter) user).getUser();
|
||||
getMongoStore().pullItemFromList(userEntity, "roleIds", role.getId(), invocationContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<RoleModel> getScopeMappings(ClientModel client) {
|
||||
Set<RoleModel> result = new HashSet<RoleModel>();
|
||||
List<RoleEntity> roles = MongoModelUtils.getAllScopesOfClient(client, invocationContext);
|
||||
List<MongoRoleEntity> roles = MongoModelUtils.getAllScopesOfClient(client, invocationContext);
|
||||
|
||||
for (RoleEntity role : roles) {
|
||||
for (MongoRoleEntity role : roles) {
|
||||
if (getId().equals(role.getRealmId())) {
|
||||
result.add(new RoleAdapter(this, role, this, invocationContext));
|
||||
} else {
|
||||
|
@ -754,7 +794,7 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
|
|||
// Filter to retrieve just realm roles TODO: Maybe improve to avoid filter programmatically... Maybe have separate fields for realmRoles and appRoles on user?
|
||||
Set<RoleModel> realmRoles = new HashSet<RoleModel>();
|
||||
for (RoleModel role : allScopes) {
|
||||
RoleEntity roleEntity = ((RoleAdapter) role).getRole();
|
||||
MongoRoleEntity roleEntity = ((RoleAdapter) role).getRole();
|
||||
|
||||
if (getId().equals(roleEntity.getRealmId())) {
|
||||
realmRoles.add(role);
|
||||
|
@ -787,7 +827,13 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
|
|||
|
||||
@Override
|
||||
public OAuthClientModel addOAuthClient(String name) {
|
||||
OAuthClientEntity oauthClient = new OAuthClientEntity();
|
||||
return this.addOAuthClient(null, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuthClientModel addOAuthClient(String id, String name) {
|
||||
MongoOAuthClientEntity oauthClient = new MongoOAuthClientEntity();
|
||||
oauthClient.setId(id);
|
||||
oauthClient.setRealmId(getId());
|
||||
oauthClient.setName(name);
|
||||
getMongoStore().insertEntity(oauthClient, invocationContext);
|
||||
|
@ -797,7 +843,7 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
|
|||
|
||||
@Override
|
||||
public boolean removeOAuthClient(String id) {
|
||||
return getMongoStore().removeEntity(OAuthClientEntity.class, id, invocationContext);
|
||||
return getMongoStore().removeEntity(MongoOAuthClientEntity.class, id, invocationContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -806,13 +852,13 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
|
|||
.and("realmId").is(getId())
|
||||
.and("name").is(name)
|
||||
.get();
|
||||
OAuthClientEntity oauthClient = getMongoStore().loadSingleEntity(OAuthClientEntity.class, query, invocationContext);
|
||||
MongoOAuthClientEntity oauthClient = getMongoStore().loadSingleEntity(MongoOAuthClientEntity.class, query, invocationContext);
|
||||
return oauthClient == null ? null : new OAuthClientAdapter(this, oauthClient, invocationContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuthClientModel getOAuthClientById(String id) {
|
||||
OAuthClientEntity clientEntity = getMongoStore().loadEntity(OAuthClientEntity.class, id, invocationContext);
|
||||
MongoOAuthClientEntity clientEntity = getMongoStore().loadEntity(MongoOAuthClientEntity.class, id, invocationContext);
|
||||
|
||||
// Check if client belongs to this realm
|
||||
if (clientEntity == null || !getId().equals(clientEntity.getRealmId())) return null;
|
||||
|
@ -825,9 +871,9 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
|
|||
DBObject query = new QueryBuilder()
|
||||
.and("realmId").is(getId())
|
||||
.get();
|
||||
List<OAuthClientEntity> results = getMongoStore().loadEntities(OAuthClientEntity.class, query, invocationContext);
|
||||
List<MongoOAuthClientEntity> results = getMongoStore().loadEntities(MongoOAuthClientEntity.class, query, invocationContext);
|
||||
List<OAuthClientModel> list = new ArrayList<OAuthClientModel>();
|
||||
for (OAuthClientEntity data : results) {
|
||||
for (MongoOAuthClientEntity data : results) {
|
||||
list.add(new OAuthClientAdapter(this, data, invocationContext));
|
||||
}
|
||||
return list;
|
||||
|
@ -923,13 +969,8 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
|
|||
|
||||
@Override
|
||||
public void updateCredential(UserModel user, UserCredentialModel cred) {
|
||||
CredentialEntity credentialEntity = null;
|
||||
UserEntity userEntity = ((UserAdapter) user).getUser();
|
||||
for (CredentialEntity entity : userEntity.getCredentials()) {
|
||||
if (entity.getType().equals(cred.getType())) {
|
||||
credentialEntity = entity;
|
||||
}
|
||||
}
|
||||
MongoUserEntity userEntity = ((UserAdapter) user).getUser();
|
||||
CredentialEntity credentialEntity = getCredentialEntity(userEntity, cred.getType());
|
||||
|
||||
if (credentialEntity == null) {
|
||||
credentialEntity = new CredentialEntity();
|
||||
|
@ -949,6 +990,52 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
|
|||
getMongoStore().updateEntity(userEntity, invocationContext);
|
||||
}
|
||||
|
||||
private CredentialEntity getCredentialEntity(MongoUserEntity userEntity, String credType) {
|
||||
for (CredentialEntity entity : userEntity.getCredentials()) {
|
||||
if (entity.getType().equals(credType)) {
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserCredentialValueModel> getCredentialsDirectly(UserModel user) {
|
||||
MongoUserEntity userEntity = ((UserAdapter) user).getUser();
|
||||
List<CredentialEntity> credentials = userEntity.getCredentials();
|
||||
List<UserCredentialValueModel> result = new ArrayList<UserCredentialValueModel>();
|
||||
for (CredentialEntity credEntity : credentials) {
|
||||
UserCredentialValueModel credModel = new UserCredentialValueModel();
|
||||
credModel.setType(credEntity.getType());
|
||||
credModel.setDevice(credEntity.getDevice());
|
||||
credModel.setValue(credEntity.getValue());
|
||||
credModel.setSalt(credEntity.getSalt());
|
||||
|
||||
result.add(credModel);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateCredentialDirectly(UserModel user, UserCredentialValueModel credModel) {
|
||||
MongoUserEntity userEntity = ((UserAdapter) user).getUser();
|
||||
CredentialEntity credentialEntity = getCredentialEntity(userEntity, credModel.getType());
|
||||
|
||||
if (credentialEntity == null) {
|
||||
credentialEntity = new CredentialEntity();
|
||||
credentialEntity.setType(credModel.getType());
|
||||
userEntity.getCredentials().add(credentialEntity);
|
||||
}
|
||||
|
||||
credentialEntity.setValue(credModel.getValue());
|
||||
credentialEntity.setSalt(credModel.getSalt());
|
||||
credentialEntity.setDevice(credModel.getDevice());
|
||||
|
||||
getMongoStore().updateEntity(userEntity, invocationContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel getUserBySocialLink(SocialLinkModel socialLink) {
|
||||
DBObject query = new QueryBuilder()
|
||||
|
@ -956,13 +1043,13 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
|
|||
.and("socialLinks.socialUserId").is(socialLink.getSocialUserId())
|
||||
.and("realmId").is(getId())
|
||||
.get();
|
||||
UserEntity userEntity = getMongoStore().loadSingleEntity(UserEntity.class, query, invocationContext);
|
||||
MongoUserEntity userEntity = getMongoStore().loadSingleEntity(MongoUserEntity.class, query, invocationContext);
|
||||
return userEntity == null ? null : new UserAdapter(userEntity, invocationContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<SocialLinkModel> getSocialLinks(UserModel user) {
|
||||
UserEntity userEntity = ((UserAdapter) user).getUser();
|
||||
MongoUserEntity userEntity = ((UserAdapter) user).getUser();
|
||||
List<SocialLinkEntity> linkEntities = userEntity.getSocialLinks();
|
||||
|
||||
if (linkEntities == null) {
|
||||
|
@ -985,7 +1072,7 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
|
|||
|
||||
@Override
|
||||
public void addSocialLink(UserModel user, SocialLinkModel socialLink) {
|
||||
UserEntity userEntity = ((UserAdapter) user).getUser();
|
||||
MongoUserEntity userEntity = ((UserAdapter) user).getUser();
|
||||
SocialLinkEntity socialLinkEntity = new SocialLinkEntity();
|
||||
socialLinkEntity.setSocialProvider(socialLink.getSocialProvider());
|
||||
socialLinkEntity.setSocialUserId(socialLink.getSocialUserId());
|
||||
|
@ -1000,13 +1087,13 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
|
|||
if (socialLinkEntity == null) {
|
||||
return false;
|
||||
}
|
||||
UserEntity userEntity = ((UserAdapter) user).getUser();
|
||||
MongoUserEntity userEntity = ((UserAdapter) user).getUser();
|
||||
|
||||
return getMongoStore().pullItemFromList(userEntity, "socialLinks", socialLinkEntity, invocationContext);
|
||||
}
|
||||
|
||||
private SocialLinkEntity findSocialLink(UserModel user, String socialProvider) {
|
||||
UserEntity userEntity = ((UserAdapter) user).getUser();
|
||||
MongoUserEntity userEntity = ((UserAdapter) user).getUser();
|
||||
List<SocialLinkEntity> linkEntities = userEntity.getSocialLinks();
|
||||
if (linkEntities == null) {
|
||||
return null;
|
||||
|
@ -1022,7 +1109,7 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
|
|||
|
||||
@Override
|
||||
public AuthenticationLinkModel getAuthenticationLink(UserModel user) {
|
||||
UserEntity userEntity = ((UserAdapter) user).getUser();
|
||||
MongoUserEntity userEntity = ((UserAdapter) user).getUser();
|
||||
AuthenticationLinkEntity authLinkEntity = userEntity.getAuthenticationLink();
|
||||
|
||||
if (authLinkEntity == null) {
|
||||
|
@ -1034,7 +1121,7 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
|
|||
|
||||
@Override
|
||||
public void setAuthenticationLink(UserModel user, AuthenticationLinkModel authenticationLink) {
|
||||
UserEntity userEntity = ((UserAdapter) user).getUser();
|
||||
MongoUserEntity userEntity = ((UserAdapter) user).getUser();
|
||||
AuthenticationLinkEntity authLinkEntity = new AuthenticationLinkEntity();
|
||||
authLinkEntity.setAuthProvider(authenticationLink.getAuthProvider());
|
||||
authLinkEntity.setAuthUserId(authenticationLink.getAuthUserId());
|
||||
|
@ -1060,7 +1147,7 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
|
|||
DBObject query = new QueryBuilder()
|
||||
.and("realmId").is(getId())
|
||||
.get();
|
||||
List<UserEntity> users = getMongoStore().loadEntities(UserEntity.class, query, invocationContext);
|
||||
List<MongoUserEntity> users = getMongoStore().loadEntities(MongoUserEntity.class, query, invocationContext);
|
||||
return convertUserEntities(users);
|
||||
}
|
||||
|
||||
|
@ -1100,7 +1187,7 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
|
|||
).get()
|
||||
);
|
||||
|
||||
List<UserEntity> users = getMongoStore().loadEntities(UserEntity.class, builder.get(), invocationContext);
|
||||
List<MongoUserEntity> users = getMongoStore().loadEntities(MongoUserEntity.class, builder.get(), invocationContext);
|
||||
return convertUserEntities(users);
|
||||
}
|
||||
|
||||
|
@ -1122,13 +1209,13 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
|
|||
queryBuilder.and(UserModel.EMAIL).regex(Pattern.compile("(?i:" + entry.getValue() + "$)"));
|
||||
}
|
||||
}
|
||||
List<UserEntity> users = getMongoStore().loadEntities(UserEntity.class, queryBuilder.get(), invocationContext);
|
||||
List<MongoUserEntity> users = getMongoStore().loadEntities(MongoUserEntity.class, queryBuilder.get(), invocationContext);
|
||||
return convertUserEntities(users);
|
||||
}
|
||||
|
||||
protected List<UserModel> convertUserEntities(List<UserEntity> userEntities) {
|
||||
protected List<UserModel> convertUserEntities(List<MongoUserEntity> userEntities) {
|
||||
List<UserModel> userModels = new ArrayList<UserModel>();
|
||||
for (UserEntity user : userEntities) {
|
||||
for (MongoUserEntity user : userEntities) {
|
||||
userModels.add(new UserAdapter(user, invocationContext));
|
||||
}
|
||||
return userModels;
|
||||
|
@ -1232,17 +1319,18 @@ public class RealmAdapter extends AbstractMongoAdapter<RealmEntity> implements R
|
|||
|
||||
@Override
|
||||
public ApplicationModel getAdminApp() {
|
||||
ApplicationEntity appData = getMongoStore().loadEntity(ApplicationEntity.class, realm.getAdminAppId(), invocationContext);
|
||||
return new ApplicationAdapter(this, appData, invocationContext);
|
||||
MongoApplicationEntity appData = getMongoStore().loadEntity(MongoApplicationEntity.class, realm.getAdminAppId(), invocationContext);
|
||||
return appData != null ? new ApplicationAdapter(this, appData, invocationContext) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAdminApp(ApplicationModel app) {
|
||||
realm.setAdminAppId(app.getId());
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmEntity getMongoEntity() {
|
||||
public MongoRealmEntity getMongoEntity() {
|
||||
return realm;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,11 +10,10 @@ import com.mongodb.QueryBuilder;
|
|||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleContainerModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.mongo.api.AbstractMongoIdentifiableEntity;
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.models.mongo.keycloak.entities.ApplicationEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.RealmEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.RoleEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoApplicationEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoRealmEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
|
||||
/**
|
||||
|
@ -22,17 +21,17 @@ import org.keycloak.models.utils.KeycloakModelUtils;
|
|||
*
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class RoleAdapter extends AbstractMongoAdapter<RoleEntity> implements RoleModel {
|
||||
public class RoleAdapter extends AbstractMongoAdapter<MongoRoleEntity> implements RoleModel {
|
||||
|
||||
private final RoleEntity role;
|
||||
private final MongoRoleEntity role;
|
||||
private RoleContainerModel roleContainer;
|
||||
private RealmModel realm;
|
||||
|
||||
public RoleAdapter(RealmModel realm, RoleEntity roleEntity, MongoStoreInvocationContext invContext) {
|
||||
public RoleAdapter(RealmModel realm, MongoRoleEntity roleEntity, MongoStoreInvocationContext invContext) {
|
||||
this(realm, roleEntity, null, invContext);
|
||||
}
|
||||
|
||||
public RoleAdapter(RealmModel realm, RoleEntity roleEntity, RoleContainerModel roleContainer, MongoStoreInvocationContext invContext) {
|
||||
public RoleAdapter(RealmModel realm, MongoRoleEntity roleEntity, RoleContainerModel roleContainer, MongoStoreInvocationContext invContext) {
|
||||
super(invContext);
|
||||
this.role = roleEntity;
|
||||
this.roleContainer = roleContainer;
|
||||
|
@ -94,10 +93,10 @@ public class RoleAdapter extends AbstractMongoAdapter<RoleEntity> implements Rol
|
|||
DBObject query = new QueryBuilder()
|
||||
.and("_id").in(role.getCompositeRoleIds())
|
||||
.get();
|
||||
List<RoleEntity> childRoles = getMongoStore().loadEntities(RoleEntity.class, query, invocationContext);
|
||||
List<MongoRoleEntity> childRoles = getMongoStore().loadEntities(MongoRoleEntity.class, query, invocationContext);
|
||||
|
||||
Set<RoleModel> set = new HashSet<RoleModel>();
|
||||
for (RoleEntity childRole : childRoles) {
|
||||
for (MongoRoleEntity childRole : childRoles) {
|
||||
set.add(new RoleAdapter(realm, childRole, invocationContext));
|
||||
}
|
||||
return set;
|
||||
|
@ -108,13 +107,13 @@ public class RoleAdapter extends AbstractMongoAdapter<RoleEntity> implements Rol
|
|||
if (roleContainer == null) {
|
||||
// Compute it
|
||||
if (role.getRealmId() != null) {
|
||||
RealmEntity realm = getMongoStore().loadEntity(RealmEntity.class, role.getRealmId(), invocationContext);
|
||||
MongoRealmEntity realm = getMongoStore().loadEntity(MongoRealmEntity.class, role.getRealmId(), invocationContext);
|
||||
if (realm == null) {
|
||||
throw new IllegalStateException("Realm with id: " + role.getRealmId() + " doesn't exists");
|
||||
}
|
||||
roleContainer = new RealmAdapter(realm, invocationContext);
|
||||
} else if (role.getApplicationId() != null) {
|
||||
ApplicationEntity appEntity = getMongoStore().loadEntity(ApplicationEntity.class, role.getApplicationId(), invocationContext);
|
||||
MongoApplicationEntity appEntity = getMongoStore().loadEntity(MongoApplicationEntity.class, role.getApplicationId(), invocationContext);
|
||||
if (appEntity == null) {
|
||||
throw new IllegalStateException("Application with id: " + role.getApplicationId() + " doesn't exists");
|
||||
}
|
||||
|
@ -135,12 +134,12 @@ public class RoleAdapter extends AbstractMongoAdapter<RoleEntity> implements Rol
|
|||
return KeycloakModelUtils.searchFor(role, this, visited);
|
||||
}
|
||||
|
||||
public RoleEntity getRole() {
|
||||
public MongoRoleEntity getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleEntity getMongoEntity() {
|
||||
public MongoRoleEntity getMongoEntity() {
|
||||
return role;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ package org.keycloak.models.mongo.keycloak.adapters;
|
|||
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.models.mongo.keycloak.entities.UserEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoUserEntity;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
@ -15,11 +15,11 @@ import java.util.Set;
|
|||
*
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class UserAdapter extends AbstractMongoAdapter<UserEntity> implements UserModel {
|
||||
public class UserAdapter extends AbstractMongoAdapter<MongoUserEntity> implements UserModel {
|
||||
|
||||
private final UserEntity user;
|
||||
private final MongoUserEntity user;
|
||||
|
||||
public UserAdapter(UserEntity userEntity, MongoStoreInvocationContext invContext) {
|
||||
public UserAdapter(MongoUserEntity userEntity, MongoStoreInvocationContext invContext) {
|
||||
super(invContext);
|
||||
this.user = userEntity;
|
||||
}
|
||||
|
@ -133,7 +133,7 @@ public class UserAdapter extends AbstractMongoAdapter<UserEntity> implements Use
|
|||
return user.getAttributes()==null ? Collections.EMPTY_MAP : Collections.unmodifiableMap(user.getAttributes());
|
||||
}
|
||||
|
||||
public UserEntity getUser() {
|
||||
public MongoUserEntity getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
|
@ -173,7 +173,7 @@ public class UserAdapter extends AbstractMongoAdapter<UserEntity> implements Use
|
|||
}
|
||||
|
||||
@Override
|
||||
public UserEntity getMongoEntity() {
|
||||
public MongoUserEntity getMongoEntity() {
|
||||
return user;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,23 +2,22 @@ package org.keycloak.models.mongo.keycloak.adapters;
|
|||
|
||||
import org.keycloak.models.UsernameLoginFailureModel;
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.models.mongo.keycloak.entities.UserEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.UsernameLoginFailureEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoUsernameLoginFailureEntity;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class UsernameLoginFailureAdapter extends AbstractMongoAdapter<UsernameLoginFailureEntity> implements UsernameLoginFailureModel {
|
||||
protected UsernameLoginFailureEntity user;
|
||||
public class UsernameLoginFailureAdapter extends AbstractMongoAdapter<MongoUsernameLoginFailureEntity> implements UsernameLoginFailureModel {
|
||||
protected MongoUsernameLoginFailureEntity user;
|
||||
|
||||
public UsernameLoginFailureAdapter(MongoStoreInvocationContext invocationContext, UsernameLoginFailureEntity user) {
|
||||
public UsernameLoginFailureAdapter(MongoStoreInvocationContext invocationContext, MongoUsernameLoginFailureEntity user) {
|
||||
super(invocationContext);
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UsernameLoginFailureEntity getMongoEntity() {
|
||||
protected MongoUsernameLoginFailureEntity getMongoEntity() {
|
||||
return user;
|
||||
}
|
||||
|
||||
|
@ -35,6 +34,7 @@ public class UsernameLoginFailureAdapter extends AbstractMongoAdapter<UsernameL
|
|||
@Override
|
||||
public void setFailedLoginNotBefore(int notBefore) {
|
||||
user.setFailedLoginNotBefore(notBefore);
|
||||
updateMongoEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -45,11 +45,13 @@ public class UsernameLoginFailureAdapter extends AbstractMongoAdapter<UsernameL
|
|||
@Override
|
||||
public void incrementFailures() {
|
||||
user.setNumFailures(getNumFailures() + 1);
|
||||
updateMongoEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearFailures() {
|
||||
user.setNumFailures(0);
|
||||
updateMongoEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -60,6 +62,7 @@ public class UsernameLoginFailureAdapter extends AbstractMongoAdapter<UsernameL
|
|||
@Override
|
||||
public void setLastFailure(long lastFailure) {
|
||||
user.setLastFailure(lastFailure);
|
||||
updateMongoEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -70,4 +73,5 @@ public class UsernameLoginFailureAdapter extends AbstractMongoAdapter<UsernameL
|
|||
@Override
|
||||
public void setLastIPFailure(String ip) {
|
||||
user.setLastIPFailure(ip);
|
||||
updateMongoEntity();
|
||||
}}
|
||||
|
|
|
@ -15,12 +15,12 @@ public class SystemPropertiesMongoClientProvider implements MongoClientProvider
|
|||
|
||||
protected static final Logger logger = Logger.getLogger(SystemPropertiesMongoClientProvider.class);
|
||||
|
||||
private static final String MONGO_HOST = "keycloak.mongo.host";
|
||||
private static final String MONGO_PORT = "keycloak.mongo.port";
|
||||
private static final String MONGO_DB_NAME = "keycloak.mongo.db";
|
||||
private static final String MONGO_CLEAR_ON_STARTUP = "keycloak.mongo.clearOnStartup";
|
||||
public static final String MONGO_HOST = "keycloak.mongo.host";
|
||||
public static final String MONGO_PORT = "keycloak.mongo.port";
|
||||
public static final String MONGO_DB_NAME = "keycloak.mongo.db";
|
||||
public static final String MONGO_CLEAR_ON_STARTUP = "keycloak.mongo.clearOnStartup";
|
||||
|
||||
// Property names from Liveoak . Those are used as fallback in case that original value is not available
|
||||
// Property names from Liveoak . Those are used as fallback
|
||||
private static final String MONGO_HOST_2 = "mongo.host";
|
||||
private static final String MONGO_PORT_2 = "mongo.port";
|
||||
private static final String MONGO_DB_NAME_2 = "mongo.db";
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
package org.keycloak.models.mongo.keycloak.entities;
|
||||
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.QueryBuilder;
|
||||
import org.keycloak.models.entities.ApplicationEntity;
|
||||
import org.keycloak.models.mongo.api.MongoCollection;
|
||||
import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
|
||||
import org.keycloak.models.mongo.api.MongoIndex;
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
@MongoCollection(collectionName = "applications")
|
||||
@MongoIndex(fields = { "realmId", "name" }, unique = true)
|
||||
public class MongoApplicationEntity extends ApplicationEntity implements MongoIdentifiableEntity {
|
||||
|
||||
@Override
|
||||
public void afterRemove(MongoStoreInvocationContext context) {
|
||||
// Remove all roles, which belongs to this application
|
||||
DBObject query = new QueryBuilder()
|
||||
.and("applicationId").is(getId())
|
||||
.get();
|
||||
context.getMongoStore().removeEntities(MongoRoleEntity.class, query, context);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package org.keycloak.models.mongo.keycloak.entities;
|
||||
|
||||
import org.keycloak.models.entities.OAuthClientEntity;
|
||||
import org.keycloak.models.mongo.api.MongoCollection;
|
||||
import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
|
||||
import org.keycloak.models.mongo.api.MongoIndex;
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
@MongoCollection(collectionName = "oauthClients")
|
||||
@MongoIndex(fields = { "realmId", "name" }, unique = true)
|
||||
public class MongoOAuthClientEntity extends OAuthClientEntity implements MongoIdentifiableEntity {
|
||||
|
||||
@Override
|
||||
public void afterRemove(MongoStoreInvocationContext invocationContext) {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package org.keycloak.models.mongo.keycloak.entities;
|
||||
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.QueryBuilder;
|
||||
import org.keycloak.models.entities.RealmEntity;
|
||||
import org.keycloak.models.mongo.api.MongoCollection;
|
||||
import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
|
||||
import org.keycloak.models.mongo.api.MongoIndex;
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
@MongoCollection(collectionName = "realms")
|
||||
@MongoIndex(fields = { "name" }, unique = true)
|
||||
public class MongoRealmEntity extends RealmEntity implements MongoIdentifiableEntity {
|
||||
|
||||
@Override
|
||||
public void afterRemove(MongoStoreInvocationContext context) {
|
||||
DBObject query = new QueryBuilder()
|
||||
.and("realmId").is(getId())
|
||||
.get();
|
||||
|
||||
// Remove all users of this realm
|
||||
context.getMongoStore().removeEntities(MongoUserEntity.class, query, context);
|
||||
|
||||
// Remove all roles of this realm
|
||||
context.getMongoStore().removeEntities(MongoRoleEntity.class, query, context);
|
||||
|
||||
// Remove all applications of this realm
|
||||
context.getMongoStore().removeEntities(MongoApplicationEntity.class, query, context);
|
||||
|
||||
// Remove all clients of this realm
|
||||
context.getMongoStore().removeEntities(MongoOAuthClientEntity.class, query, context);
|
||||
}
|
||||
}
|
|
@ -3,10 +3,10 @@ package org.keycloak.models.mongo.keycloak.entities;
|
|||
import com.mongodb.DBObject;
|
||||
import com.mongodb.QueryBuilder;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.models.mongo.api.AbstractMongoIdentifiableEntity;
|
||||
import org.keycloak.models.entities.RoleEntity;
|
||||
import org.keycloak.models.mongo.api.MongoCollection;
|
||||
import org.keycloak.models.mongo.api.MongoEntity;
|
||||
import org.keycloak.models.mongo.api.MongoField;
|
||||
import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
|
||||
import org.keycloak.models.mongo.api.MongoIndex;
|
||||
import org.keycloak.models.mongo.api.MongoStore;
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
|
@ -18,21 +18,17 @@ import java.util.List;
|
|||
*/
|
||||
@MongoCollection(collectionName = "roles")
|
||||
@MongoIndex(fields = "nameIndex", unique = true)
|
||||
public class RoleEntity extends AbstractMongoIdentifiableEntity implements MongoEntity {
|
||||
public class MongoRoleEntity extends RoleEntity implements MongoIdentifiableEntity {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(RoleEntity.class);
|
||||
|
||||
private String name;
|
||||
private String description;
|
||||
|
||||
private List<String> compositeRoleIds;
|
||||
|
||||
private String realmId;
|
||||
private String applicationId;
|
||||
private static final Logger logger = Logger.getLogger(MongoRoleEntity.class);
|
||||
|
||||
@MongoField
|
||||
// TODO This is required as Mongo doesn't support sparse indexes with compound keys (see https://jira.mongodb.org/browse/SERVER-2193)
|
||||
public String getNameIndex() {
|
||||
String realmId = getRealmId();
|
||||
String applicationId = getApplicationId();
|
||||
String name = getName();
|
||||
|
||||
if (realmId != null) {
|
||||
return realmId + "//" + name;
|
||||
} else {
|
||||
|
@ -43,51 +39,6 @@ public class RoleEntity extends AbstractMongoIdentifiableEntity implements Mongo
|
|||
public void setNameIndex(String ignored) {
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public List<String> getCompositeRoleIds() {
|
||||
return compositeRoleIds;
|
||||
}
|
||||
|
||||
public void setCompositeRoleIds(List<String> compositeRoleIds) {
|
||||
this.compositeRoleIds = compositeRoleIds;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public String getRealmId() {
|
||||
return realmId;
|
||||
}
|
||||
|
||||
public void setRealmId(String realmId) {
|
||||
this.realmId = realmId;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public String getApplicationId() {
|
||||
return applicationId;
|
||||
}
|
||||
|
||||
public void setApplicationId(String applicationId) {
|
||||
this.applicationId = applicationId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterRemove(MongoStoreInvocationContext invContext) {
|
||||
MongoStore mongoStore = invContext.getMongoStore();
|
||||
|
@ -97,8 +48,8 @@ public class RoleEntity extends AbstractMongoIdentifiableEntity implements Mongo
|
|||
.and("roleIds").is(getId())
|
||||
.get();
|
||||
|
||||
List<UserEntity> users = mongoStore.loadEntities(UserEntity.class, query, invContext);
|
||||
for (UserEntity user : users) {
|
||||
List<MongoUserEntity> users = mongoStore.loadEntities(MongoUserEntity.class, query, invContext);
|
||||
for (MongoUserEntity user : users) {
|
||||
logger.info("Removing role " + getName() + " from user " + user.getLoginName());
|
||||
mongoStore.pullItemFromList(user, "roleIds", getId(), invContext);
|
||||
}
|
||||
|
@ -108,15 +59,15 @@ public class RoleEntity extends AbstractMongoIdentifiableEntity implements Mongo
|
|||
.and("scopeIds").is(getId())
|
||||
.get();
|
||||
|
||||
users = mongoStore.loadEntities(UserEntity.class, query, invContext);
|
||||
for (UserEntity user : users) {
|
||||
users = mongoStore.loadEntities(MongoUserEntity.class, query, invContext);
|
||||
for (MongoUserEntity user : users) {
|
||||
logger.info("Removing scope " + getName() + " from user " + user.getLoginName());
|
||||
mongoStore.pullItemFromList(user, "scopeIds", getId(), invContext);
|
||||
}
|
||||
|
||||
// Remove defaultRoles from realm
|
||||
if (realmId != null) {
|
||||
RealmEntity realmEntity = mongoStore.loadEntity(RealmEntity.class, realmId, invContext);
|
||||
if (getRealmId() != null) {
|
||||
MongoRealmEntity realmEntity = mongoStore.loadEntity(MongoRealmEntity.class, getRealmId(), invContext);
|
||||
|
||||
// Realm might be already removed at this point
|
||||
if (realmEntity != null) {
|
||||
|
@ -125,8 +76,8 @@ public class RoleEntity extends AbstractMongoIdentifiableEntity implements Mongo
|
|||
}
|
||||
|
||||
// Remove defaultRoles from application
|
||||
if (applicationId != null) {
|
||||
ApplicationEntity appEntity = mongoStore.loadEntity(ApplicationEntity.class, applicationId, invContext);
|
||||
if (getApplicationId() != null) {
|
||||
MongoApplicationEntity appEntity = mongoStore.loadEntity(MongoApplicationEntity.class, getApplicationId(), invContext);
|
||||
|
||||
// Application might be already removed at this point
|
||||
if (appEntity != null) {
|
||||
|
@ -138,8 +89,8 @@ public class RoleEntity extends AbstractMongoIdentifiableEntity implements Mongo
|
|||
query = new QueryBuilder()
|
||||
.and("compositeRoleIds").is(getId())
|
||||
.get();
|
||||
List<RoleEntity> parentRoles = mongoStore.loadEntities(RoleEntity.class, query, invContext);
|
||||
for (RoleEntity role : parentRoles) {
|
||||
List<MongoRoleEntity> parentRoles = mongoStore.loadEntities(MongoRoleEntity.class, query, invContext);
|
||||
for (MongoRoleEntity role : parentRoles) {
|
||||
mongoStore.pullItemFromList(role, "compositeRoleIds", getId(), invContext);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package org.keycloak.models.mongo.keycloak.entities;
|
||||
|
||||
import org.keycloak.models.entities.UserEntity;
|
||||
import org.keycloak.models.mongo.api.MongoCollection;
|
||||
import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
|
||||
import org.keycloak.models.mongo.api.MongoIndex;
|
||||
import org.keycloak.models.mongo.api.MongoIndexes;
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
@MongoCollection(collectionName = "users")
|
||||
@MongoIndexes({
|
||||
@MongoIndex(fields = { "realmId", "loginName" }, unique = true),
|
||||
@MongoIndex(fields = { "emailIndex" }, unique = true, sparse = true),
|
||||
})
|
||||
public class MongoUserEntity extends UserEntity implements MongoIdentifiableEntity {
|
||||
|
||||
|
||||
public String getEmailIndex() {
|
||||
return getEmail() != null ? getRealmId() + "//" + getEmail() : null;
|
||||
}
|
||||
|
||||
public void setEmailIndex(String ignored) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterRemove(MongoStoreInvocationContext invocationContext) {
|
||||
//To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package org.keycloak.models.mongo.keycloak.entities;
|
||||
|
||||
import org.keycloak.models.entities.UsernameLoginFailureEntity;
|
||||
import org.keycloak.models.mongo.api.MongoCollection;
|
||||
import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
@MongoCollection(collectionName = "userFailures")
|
||||
public class MongoUsernameLoginFailureEntity extends UsernameLoginFailureEntity implements MongoIdentifiableEntity {
|
||||
|
||||
@Override
|
||||
public void afterRemove(MongoStoreInvocationContext invocationContext) {
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
package org.keycloak.models.mongo.keycloak.entities;
|
||||
|
||||
import org.keycloak.models.mongo.api.MongoCollection;
|
||||
import org.keycloak.models.mongo.api.MongoIndex;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
@MongoCollection(collectionName = "oauthClients")
|
||||
@MongoIndex(fields = { "realmId", "name" }, unique = true)
|
||||
public class OAuthClientEntity extends ClientEntity {
|
||||
|
||||
}
|
|
@ -7,12 +7,12 @@ import com.mongodb.DBObject;
|
|||
import com.mongodb.QueryBuilder;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.entities.ClientEntity;
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.models.mongo.keycloak.adapters.ClientAdapter;
|
||||
import org.keycloak.models.mongo.keycloak.adapters.UserAdapter;
|
||||
import org.keycloak.models.mongo.keycloak.entities.ClientEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.RoleEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.UserEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoUserEntity;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
|
@ -20,8 +20,8 @@ import org.keycloak.models.mongo.keycloak.entities.UserEntity;
|
|||
public class MongoModelUtils {
|
||||
|
||||
// Get everything including both application and realm roles
|
||||
public static List<RoleEntity> getAllRolesOfUser(UserModel user, MongoStoreInvocationContext invContext) {
|
||||
UserEntity userEntity = ((UserAdapter)user).getUser();
|
||||
public static List<MongoRoleEntity> getAllRolesOfUser(UserModel user, MongoStoreInvocationContext invContext) {
|
||||
MongoUserEntity userEntity = ((UserAdapter)user).getUser();
|
||||
List<String> roleIds = userEntity.getRoleIds();
|
||||
|
||||
if (roleIds == null || roleIds.isEmpty()) {
|
||||
|
@ -31,12 +31,12 @@ public class MongoModelUtils {
|
|||
DBObject query = new QueryBuilder()
|
||||
.and("_id").in(roleIds)
|
||||
.get();
|
||||
return invContext.getMongoStore().loadEntities(RoleEntity.class, query, invContext);
|
||||
return invContext.getMongoStore().loadEntities(MongoRoleEntity.class, query, invContext);
|
||||
}
|
||||
|
||||
// Get everything including both application and realm scopes
|
||||
public static List<RoleEntity> getAllScopesOfClient(ClientModel client, MongoStoreInvocationContext invContext) {
|
||||
ClientEntity scopedEntity = ((ClientAdapter)client).getMongoEntity();
|
||||
public static List<MongoRoleEntity> getAllScopesOfClient(ClientModel client, MongoStoreInvocationContext invContext) {
|
||||
ClientEntity scopedEntity = ((ClientAdapter)client).getMongoEntityAsClient();
|
||||
List<String> scopeIds = scopedEntity.getScopeIds();
|
||||
|
||||
if (scopeIds == null || scopeIds.isEmpty()) {
|
||||
|
@ -46,6 +46,6 @@ public class MongoModelUtils {
|
|||
DBObject query = new QueryBuilder()
|
||||
.and("_id").in(scopeIds)
|
||||
.get();
|
||||
return invContext.getMongoStore().loadEntities(RoleEntity.class, query, invContext);
|
||||
return invContext.getMongoStore().loadEntities(MongoRoleEntity.class, query, invContext);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,12 +8,11 @@ import java.util.List;
|
|||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class Address implements MongoEntity {
|
||||
public class Address {
|
||||
|
||||
private String street;
|
||||
private int number;
|
||||
|
||||
@MongoField
|
||||
public String getStreet() {
|
||||
return street;
|
||||
}
|
||||
|
@ -22,7 +21,6 @@ public class Address implements MongoEntity {
|
|||
this.street = street;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public int getNumber() {
|
||||
return number;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ public class AddressWithFlats extends Address {
|
|||
|
||||
private List<String> flatNumbers;
|
||||
|
||||
@MongoField
|
||||
public List<String> getFlatNumbers() {
|
||||
return flatNumbers;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ import org.junit.After;
|
|||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.models.mongo.api.MongoEntity;
|
||||
import org.keycloak.models.mongo.api.MongoStore;
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.models.mongo.impl.MongoStoreImpl;
|
||||
|
@ -22,7 +21,7 @@ import java.util.List;
|
|||
*/
|
||||
public class MongoStoreTest {
|
||||
|
||||
private static final Class<? extends MongoEntity>[] MANAGED_DATA_TYPES = (Class<? extends MongoEntity>[])new Class<?>[] {
|
||||
private static final Class<?>[] MANAGED_DATA_TYPES = (Class<?>[])new Class<?>[] {
|
||||
Person.class,
|
||||
Address.class,
|
||||
AddressWithFlats.class
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package org.keycloak.models.mongo.test;
|
||||
|
||||
import org.keycloak.models.mongo.api.AbstractMongoIdentifiableEntity;
|
||||
import org.keycloak.models.mongo.api.MongoCollection;
|
||||
import org.keycloak.models.mongo.api.MongoField;
|
||||
import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
|
||||
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
@ -12,8 +12,9 @@ import java.util.Map;
|
|||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
@MongoCollection(collectionName = "persons")
|
||||
public class Person extends AbstractMongoIdentifiableEntity {
|
||||
public class Person implements MongoIdentifiableEntity {
|
||||
|
||||
private String id;
|
||||
private String firstName;
|
||||
private int age;
|
||||
private List<String> kids;
|
||||
|
@ -23,7 +24,14 @@ public class Person extends AbstractMongoIdentifiableEntity {
|
|||
private List<Gender> genders;
|
||||
private Map<String, String> attributes = new HashMap<String, String>();
|
||||
|
||||
@MongoField
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getFirstName() {
|
||||
return firstName;
|
||||
}
|
||||
|
@ -32,7 +40,6 @@ public class Person extends AbstractMongoIdentifiableEntity {
|
|||
this.firstName = firstName;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public int getAge() {
|
||||
return age;
|
||||
}
|
||||
|
@ -41,7 +48,6 @@ public class Person extends AbstractMongoIdentifiableEntity {
|
|||
this.age = age;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public Gender getGender() {
|
||||
return gender;
|
||||
}
|
||||
|
@ -50,7 +56,6 @@ public class Person extends AbstractMongoIdentifiableEntity {
|
|||
this.gender = gender;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public List<Gender> getGenders() {
|
||||
return genders;
|
||||
}
|
||||
|
@ -59,7 +64,6 @@ public class Person extends AbstractMongoIdentifiableEntity {
|
|||
this.genders = genders;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public List<String> getKids() {
|
||||
return kids;
|
||||
}
|
||||
|
@ -68,7 +72,6 @@ public class Person extends AbstractMongoIdentifiableEntity {
|
|||
this.kids = kids;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public List<AddressWithFlats> getAddresses() {
|
||||
return addresses;
|
||||
}
|
||||
|
@ -77,7 +80,6 @@ public class Person extends AbstractMongoIdentifiableEntity {
|
|||
this.addresses = addresses;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public Address getMainAddress() {
|
||||
return mainAddress;
|
||||
}
|
||||
|
@ -86,7 +88,6 @@ public class Person extends AbstractMongoIdentifiableEntity {
|
|||
this.mainAddress = mainAddress;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public Map<String, String> getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
@ -103,6 +104,10 @@ public class Person extends AbstractMongoIdentifiableEntity {
|
|||
attributes.remove(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterRemove(MongoStoreInvocationContext invocationContext) {
|
||||
}
|
||||
|
||||
public static enum Gender {
|
||||
MALE, FEMALE
|
||||
}
|
||||
|
|
|
@ -164,6 +164,9 @@ public class AdapterTest extends AbstractModelTest {
|
|||
cred.setValue("password");
|
||||
realmModel.updateCredential(user, cred);
|
||||
|
||||
commit();
|
||||
|
||||
realmModel = identitySession.getRealm("JUGGLER");
|
||||
Assert.assertTrue(realmModel.removeUser("bburke"));
|
||||
Assert.assertFalse(realmModel.removeUser("bburke"));
|
||||
Assert.assertNull(realmModel.getUser("bburke"));
|
||||
|
@ -218,6 +221,9 @@ public class AdapterTest extends AbstractModelTest {
|
|||
|
||||
realmModel.addScopeMapping(app, realmRole);
|
||||
|
||||
commit();
|
||||
realmModel = identitySession.getRealm("JUGGLER");
|
||||
|
||||
Assert.assertTrue(realmManager.removeRealm(realmModel));
|
||||
Assert.assertFalse(realmManager.removeRealm(realmModel));
|
||||
Assert.assertNull(realmManager.getRealm(realmModel.getId()));
|
||||
|
@ -241,6 +247,10 @@ public class AdapterTest extends AbstractModelTest {
|
|||
RoleModel realmRole = realmModel.addRole("test");
|
||||
realmModel.addScopeMapping(app, realmRole);
|
||||
|
||||
commit();
|
||||
realmModel = identitySession.getRealm("JUGGLER");
|
||||
app = realmModel.getApplicationByName("test-app");
|
||||
|
||||
Assert.assertTrue(realmModel.removeRoleById(realmRole.getId()));
|
||||
Assert.assertFalse(realmModel.removeRoleById(realmRole.getId()));
|
||||
Assert.assertNull(realmModel.getRole(realmRole.getName()));
|
||||
|
@ -521,7 +531,7 @@ public class AdapterTest extends AbstractModelTest {
|
|||
}
|
||||
commit(true);
|
||||
|
||||
// Ty to rename realm to duplicate name
|
||||
// Try to rename realm to duplicate name
|
||||
realmManager.createRealm("JUGGLER2");
|
||||
commit();
|
||||
try {
|
||||
|
|
|
@ -64,6 +64,15 @@ public class ApplicationModelTest extends AbstractModelTest {
|
|||
assertEquals(application, copy);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddApplicationWithId() {
|
||||
application = realm.addApplication("app-123", "application2");
|
||||
commit();
|
||||
application = realmManager.getRealm(realm.getId()).getApplicationById("app-123");
|
||||
Assert.assertNotNull(application);
|
||||
}
|
||||
|
||||
|
||||
public static void assertEquals(ApplicationModel expected, ApplicationModel actual) {
|
||||
Assert.assertEquals(expected.getName(), actual.getName());
|
||||
Assert.assertEquals(expected.getBaseUrl(), actual.getBaseUrl());
|
||||
|
|
|
@ -50,6 +50,16 @@ public class ImportTest extends AbstractModelTest {
|
|||
commit();
|
||||
|
||||
realm = realmManager.getRealm("demo");
|
||||
assertDataImportedInRealm(realm);
|
||||
|
||||
commit();
|
||||
|
||||
realm = realmManager.getRealm("demo");
|
||||
realmManager.removeRealm(realm);
|
||||
}
|
||||
|
||||
// Moved to static method, so it's possible to test this from other places too (for example export-import tests)
|
||||
public static void assertDataImportedInRealm(RealmModel realm) {
|
||||
Assert.assertTrue(realm.isVerifyEmail());
|
||||
|
||||
Assert.assertFalse(realm.isUpdateProfileOnInitialSocialLogin());
|
||||
|
@ -211,13 +221,6 @@ public class ImportTest extends AbstractModelTest {
|
|||
AuthenticationLinkModel authLink = realm.getAuthenticationLink(socialUser);
|
||||
Assert.assertEquals(AuthProviderConstants.PROVIDER_NAME_PICKETLINK, authLink.getAuthProvider());
|
||||
Assert.assertEquals("myUser1", authLink.getAuthUserId());
|
||||
|
||||
commit();
|
||||
|
||||
realm = realmManager.getRealm("demo");
|
||||
realmManager.removeRealm(realm);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -48,6 +48,10 @@ public class MultipleRealmsTest extends AbstractModelTest {
|
|||
// Test searching
|
||||
Assert.assertEquals(2, realm1.searchForUser("user").size());
|
||||
|
||||
commit();
|
||||
realm1 = identitySession.getRealm("id1");
|
||||
realm2 = identitySession.getRealm("id2");
|
||||
|
||||
realm1.removeUser("user1");
|
||||
realm1.removeUser("user2");
|
||||
Assert.assertEquals(0, realm1.searchForUser("user").size());
|
||||
|
|
8
pom.xml
8
pom.xml
|
@ -102,6 +102,7 @@
|
|||
<module>timer</module>
|
||||
<module>bundled-war-example</module>
|
||||
<module>project-integrations</module>
|
||||
<module>export-import</module>
|
||||
</modules>
|
||||
|
||||
<dependencyManagement>
|
||||
|
@ -323,6 +324,13 @@
|
|||
<version>1.3.1b</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Encrypted ZIP -->
|
||||
<dependency>
|
||||
<groupId>de.idyl</groupId>
|
||||
<artifactId>winzipaes</artifactId>
|
||||
<version>1.0.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Selenium -->
|
||||
<dependency>
|
||||
<groupId>org.seleniumhq.selenium</groupId>
|
||||
|
|
|
@ -225,6 +225,21 @@
|
|||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- export/import -->
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-export-import-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-export-import-impl</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.idyl</groupId>
|
||||
<artifactId>winzipaes</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
|
|
@ -85,6 +85,12 @@
|
|||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-export-import-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-picketlink-api</artifactId>
|
||||
|
|
|
@ -10,6 +10,7 @@ import org.keycloak.audit.AuditProvider;
|
|||
import org.keycloak.audit.AuditProviderFactory;
|
||||
import org.keycloak.authentication.AuthenticationProvider;
|
||||
import org.keycloak.authentication.AuthenticationProviderFactory;
|
||||
import org.keycloak.exportimport.ExportImportProvider;
|
||||
import org.keycloak.models.Config;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
|
@ -31,6 +32,7 @@ import org.keycloak.models.utils.ModelProviderUtils;
|
|||
import org.keycloak.timer.TimerProvider;
|
||||
import org.keycloak.timer.TimerProviderFactory;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
import org.keycloak.util.ProviderLoader;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.ws.rs.core.Application;
|
||||
|
@ -43,6 +45,7 @@ import java.io.InputStream;
|
|||
import java.net.URI;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
|
@ -92,6 +95,8 @@ public class KeycloakApplication extends Application {
|
|||
|
||||
setupScheduledTasks(providerSessionFactory, factory);
|
||||
importRealms(context);
|
||||
|
||||
checkExportImportProvider();
|
||||
}
|
||||
|
||||
public String getContextPath() {
|
||||
|
@ -267,5 +272,16 @@ public class KeycloakApplication extends Application {
|
|||
}
|
||||
}
|
||||
|
||||
protected void checkExportImportProvider() {
|
||||
Iterator<ExportImportProvider> providers = ProviderLoader.load(ExportImportProvider.class).iterator();
|
||||
|
||||
if (providers.hasNext()) {
|
||||
ExportImportProvider exportImport = providers.next();
|
||||
exportImport.checkExportImport(factory);
|
||||
} else {
|
||||
log.warn("No ExportImportProvider found!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -233,6 +233,17 @@
|
|||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-export-import-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-export-import-impl</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
|
@ -346,6 +357,12 @@
|
|||
<artifactId>picketlink-common</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Encrypted ZIP -->
|
||||
<dependency>
|
||||
<groupId>de.idyl</groupId>
|
||||
<artifactId>winzipaes</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- This adds couple of other dependencies (like picketlink) -->
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
|
|
Loading…
Reference in a new issue