More cleanup and removed export-import-impl module

This commit is contained in:
mposolda 2014-07-15 16:43:55 +02:00
parent c14840a4c0
commit 726ce08700
26 changed files with 78 additions and 1599 deletions

View file

@ -1,236 +0,0 @@
<?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-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.0-beta-4-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-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.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-invalidation-cache-model</artifactId>
<version>${project.version}</version>
<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.keycloak</groupId>
<artifactId>keycloak-model-tests</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.model.mongo.host>localhost</keycloak.model.mongo.host>
<keycloak.model.mongo.port>27018</keycloak.model.mongo.port>
<keycloak.model.mongo.db>keycloak</keycloak.model.mongo.db>
<keycloak.model.mongo.clearOnStartup>true</keycloak.model.mongo.clearOnStartup>
<keycloak.model.mongo.bindIp>127.0.0.1</keycloak.model.mongo.bindIp>
</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.model.mongo.host>${keycloak.model.mongo.host}</keycloak.model.mongo.host>-->
<!--<keycloak.model.mongo.port>${keycloak.model.mongo.port}</keycloak.model.mongo.port>-->
<!--<keycloak.model.mongo.db>${keycloak.model.mongo.db}</keycloak.model.mongo.db>-->
<!--<keycloak.model.mongo.clearOnStartup>${keycloak.model.mongo.clearOnStartup}</keycloak.model.mongo.clearOnStartup>-->
<!--<keycloak.model.mongo.bindIp>${keycloak.model.mongo.bindIp}</keycloak.model.mongo.bindIp>-->
<!--</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.model.mongo.port}</port>-->
<!--<logging>file</logging>-->
<!--<logFile>${project.build.directory}/mongodb.log</logFile>-->
<!--<bindIp>${keycloak.model.mongo.bindIp}</bindIp>-->
<!--</configuration>-->
<!--</execution>-->
<!--<execution>-->
<!--<id>stop-mongodb</id>-->
<!--<phase>post-integration-test</phase>-->
<!--<goals>-->
<!--<goal>stop</goal>-->
<!--</goals>-->
<!--</execution>-->
<!--</executions>-->
<!--</plugin>-->
</plugins>
</build>
</project>

View file

@ -1,152 +0,0 @@
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", "getAuthenticationLink");
@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;
}
}
}

View file

@ -1,82 +0,0 @@
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.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 {
private static final Logger logger = Logger.getLogger(ExportImportProviderImpl.class);
public static final String ACTION_EXPORT = "export";
public static final String ACTION_IMPORT = "import";
public void checkExportImport(KeycloakSessionFactory sessionFactory) {
String exportImportAction = ExportImportConfig.getAction();
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 = sessionFactory.create();
KeycloakTransaction transaction = session.getTransaction();
try {
transaction.begin();
if (export) {
ExportWriter exportWriter = getProvider().getExportWriter();
new ModelExporter().exportModel(session.model(), exportWriter);
logger.infof("Export finished successfully");
} else {
ImportReader importReader = getProvider().getImportReader();
new ModelImporter().importModel(session.model(), 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 = ExportImportConfig.getProvider();
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");
}
}

View file

@ -1,335 +0,0 @@
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.ModelProvider;
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(ModelProvider model, 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(model, "realms.json");
exportApplications(model, "applications.json");
exportOAuthClients(model, "oauthClients.json");
exportRoles(model, "roles.json");
exportUsers(model, "users.json");
// exportUserFailures(model, "userFailures.json");
this.exportWriter.closeExportWriter();
}
protected void exportRealms(ModelProvider model, String fileName) {
List<RealmModel> realms = model.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.getMasterAdminApp();
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(ModelProvider model, String fileName) {
List<ApplicationModel> allApplications = getAllApplications(model);
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(ModelProvider model, String fileName) {
List<RealmModel> realms = model.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(ModelProvider model, String fileName) {
List<RoleModel> allRoles = getAllRoles(model);
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(ModelProvider model, String fileName) {
List<RealmModel> realms = model.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.setUsername(userModel.getUsername());
userEntity.setRealmId(realm.getId());
// authentication links
AuthenticationLinkModel authLink = userModel.getAuthenticationLink();
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 = userModel.getRoleMappings();
List<String> roleIds = new ArrayList<String>();
for (RoleModel role : roles) {
roleIds.add(role.getId());
}
userEntity.setRoleIds(roleIds);
// credentials
List<UserCredentialValueModel> credentials = userModel.getCredentialsDirectly();
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(ModelProvider model, String fileName) {
// List<RealmModel> realms = model.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.getScopeMappings();
List<String> scopeIds = new ArrayList<String>();
for (RoleModel role : allScopes) {
scopeIds.add(role.getId());
}
return scopeIds;
}
private List<ApplicationModel> getAllApplications(ModelProvider model) {
List<RealmModel> realms = model.getRealms();
List<ApplicationModel> allApplications = new ArrayList<ApplicationModel>();
for (RealmModel realmModel : realms) {
allApplications.addAll(realmModel.getApplications());
}
return allApplications;
}
private List<RoleModel> getAllRoles(ModelProvider model) {
List<RoleModel> allRoles = new ArrayList<RoleModel>();
List<RealmModel> realms = model.getRealms();
for (RealmModel realmModel : realms) {
allRoles.addAll(realmModel.getRoles());
}
List<ApplicationModel> allApplications = getAllApplications(model);
for (ApplicationModel appModel : allApplications) {
allRoles.addAll(appModel.getRoles());
}
return allRoles;
}
}

View file

@ -1,327 +0,0 @@
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.Config;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelProvider;
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(ModelProvider model, ImportReader importReader) {
// Initialize needed objects
this.importReader = importReader;
this.propertiesManager = new ExportImportPropertiesManager();
importRealms(model, "realms.json");
importApplications(model, "applications.json");
importRoles(model, "roles.json");
// Now we have all realms,applications and roles filled. So fill other objects (default roles, scopes etc)
importRealmsStep2(model, "realms.json");
importApplicationsStep2(model, "applications.json");
importOAuthClients(model, "oauthClients.json");
importUsers(model, "users.json");
// importUserFailures(model, "userFailures.json");
this.importReader.closeImportReader();
}
protected void importRealms(ModelProvider model, String fileName) {
List<RealmEntity> realms = this.importReader.readEntities(fileName, RealmEntity.class);
for (RealmEntity realmEntity : realms) {
RealmModel realm = model.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(ModelProvider model, String fileName) {
List<ApplicationEntity> apps = this.importReader.readEntities(fileName, ApplicationEntity.class);
for (ApplicationEntity appEntity : apps) {
RealmModel realm = model.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(ModelProvider model, 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 = model.getRealm(roleEntity.getRealmId());
role = realm.addRole(roleEntity.getId(), roleEntity.getName());
} else if (roleEntity.getApplicationId() != null) {
ApplicationModel app = findApplicationById(model, 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 : model.getRealms()) {
// realm roles
fillCompositeRoles(rolesMap, realm, realm);
// app roles
for (ApplicationModel app : realm.getApplications()) {
fillCompositeRoles(rolesMap, app, realm);
}
}
logger.infof("%d roles imported: ", roles.size());
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(ModelProvider model, String fileName) {
List<RealmEntity> realms = this.importReader.readEntities(fileName, RealmEntity.class);
RealmModel adminRealm = model.getRealm(Config.getAdminRealm());
for (RealmEntity realmEntity : realms) {
RealmModel realm = model.getRealm(realmEntity.getId());
// admin app
String adminAppId = realmEntity.getAdminAppId();
if (adminAppId != null) {
realm.setMasterAdminApp(adminRealm.getApplicationById(adminAppId));
}
// Default roles
realm.updateDefaultRoles(realmEntity.getDefaultRoles().toArray(new String[] {}));
}
}
protected void importApplicationsStep2(ModelProvider model, String fileName) {
List<ApplicationEntity> apps = this.importReader.readEntities(fileName, ApplicationEntity.class);
for (ApplicationEntity appEntity : apps) {
RealmModel realm = model.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);
client.addScopeMapping(scope);
}
}
protected void importOAuthClients(ModelProvider model, String fileName) {
List<OAuthClientEntity> clients = this.importReader.readEntities(fileName, OAuthClientEntity.class);
for (OAuthClientEntity clientEntity : clients) {
RealmModel realm = model.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.info("OAuth clients imported: " + clients);
}
protected ApplicationModel findApplicationById(ModelProvider model, String applicationId) {
for (RealmModel realm : model.getRealms()) {
ApplicationModel appModel = realm.getApplicationById(applicationId);
if (appModel != null) {
return appModel;
}
}
return null;
}
public void importUsers(ModelProvider model, String fileName) {
List<UserEntity> users = this.importReader.readEntities(fileName, UserEntity.class);
for (UserEntity userEntity : users) {
RealmModel realm = model.getRealm(userEntity.getRealmId());
UserModel user = realm.addUser(userEntity.getId(), userEntity.getUsername(), false);
// 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 : user.getRoleMappings()) {
user.deleteRoleMapping(role);
}
this.propertiesManager.setBasicPropertiesToModel(user, userEntity);
// authentication links
AuthenticationLinkEntity authLinkEntity = userEntity.getAuthenticationLink();
if (authLinkEntity != null) {
AuthenticationLinkModel authLinkModel = new AuthenticationLinkModel();
this.propertiesManager.setBasicPropertiesToModel(authLinkModel, authLinkEntity);
user.setAuthenticationLink(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);
user.grantRole(role);
}
}
// credentials
List<CredentialEntity> credentials = userEntity.getCredentials();
if (credentials != null) {
for (CredentialEntity credEntity : credentials) {
UserCredentialValueModel credModel = new UserCredentialValueModel();
this.propertiesManager.setBasicPropertiesToModel(credModel, credEntity);
user.updateCredentialDirectly(credModel);
}
}
}
logger.infof("%d users imported: ", users.size());
if (logger.isDebugEnabled()) {
logger.debug("Imported users: " + users);
}
}
// public void importUserFailures(ModelProvider model, String fileName) {
// List<UsernameLoginFailureEntity> userFailures = this.importReader.readEntities(fileName, UsernameLoginFailureEntity.class);
// for (UsernameLoginFailureEntity entity : userFailures) {
// RealmModel realm = model.getRealm(entity.getRealmId());
// UsernameLoginFailureModel loginFailureModel = realm.addUserLoginFailure(entity.getUsername());
//
// this.propertiesManager.setBasicPropertiesToModel(loginFailureModel , entity);
//
// for (int i=0 ; i<entity.getNumFailures() ; i++) {
// loginFailureModel.incrementFailures();
// }
// }
// }
}

View file

@ -1,14 +0,0 @@
package org.keycloak.exportimport.io;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public interface ExportImportIOProvider {
String getId();
ExportWriter getExportWriter();
ImportReader getImportReader();
}

View file

@ -1,13 +0,0 @@
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();
}

View file

@ -1,13 +0,0 @@
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();
}

View file

@ -1,36 +0,0 @@
package org.keycloak.exportimport.io.directory;
import org.keycloak.exportimport.ExportImportConfig;
import org.keycloak.exportimport.io.ExportImportIOProvider;
import org.keycloak.exportimport.io.ExportWriter;
import org.keycloak.exportimport.io.ImportReader;
import java.io.File;
/**
* 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 = ExportImportConfig.getDir();
return dir!=null ? new TmpDirExportWriter(new File(dir)) : new TmpDirExportWriter();
}
@Override
public ImportReader getImportReader() {
String dir = ExportImportConfig.getDir();
return dir!=null ? new TmpDirImportReader(new File(dir)) : new TmpDirImportReader();
}
@Override
public String getId() {
return PROVIDER_ID;
}
}

View file

@ -1,82 +0,0 @@
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.
}
}

View file

@ -1,67 +0,0 @@
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?
}
}

View file

@ -1,66 +0,0 @@
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);
}
}
}

View file

@ -1,48 +0,0 @@
package org.keycloak.exportimport.io.zip;
import org.jboss.logging.Logger;
import org.keycloak.exportimport.ExportImportConfig;
import org.keycloak.exportimport.io.ExportImportIOProvider;
import org.keycloak.exportimport.io.ExportWriter;
import org.keycloak.exportimport.io.ImportReader;
/**
* @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 = ExportImportConfig.getZipFile();
String zipPassword = ExportImportConfig.getZipPassword();
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 = ExportImportConfig.getZipFile();
String zipPassword = ExportImportConfig.getZipPassword();
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);
}
}

View file

@ -1,70 +0,0 @@
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);
}
}
}

View file

@ -1 +0,0 @@
org.keycloak.exportimport.ExportImportProviderImpl

View file

@ -1,2 +0,0 @@
org.keycloak.exportimport.io.directory.TmpDirExportImportIOProvider
org.keycloak.exportimport.io.zip.EncryptedZIPIOProvider

View file

@ -1,14 +0,0 @@
package org.keycloak.exportimport;
import org.junit.Ignore;
import org.keycloak.exportimport.io.directory.TmpDirExportImportIOProvider;
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>
*/
@Ignore
public class JPAToMongoExportImportTest {
}

View file

@ -1,17 +0,0 @@
package org.keycloak.exportimport;
import org.junit.Assert;
import org.junit.Ignore;
import org.keycloak.exportimport.io.zip.EncryptedZIPIOProvider;
import org.keycloak.models.KeycloakSessionFactory;
import java.io.File;
/**
* Test for full export of data from Mongo and import them to JPA. Using export into encrypted ZIP and import from it
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
@Ignore
public class MongoToJPAExportImportTest {
}

View file

@ -16,7 +16,6 @@
<modules>
<module>export-import-api</module>
<module>export-import-impl</module>
<module>export-import-dir</module>
<module>export-import-single-file</module>
<module>export-import-zip</module>

View file

@ -266,9 +266,23 @@ public class DefaultCacheModelProvider implements CacheModelProvider {
@Override
public boolean removeRealm(String id) {
cache.invalidateCachedRealmById(id);
RealmModel realm = getDelegate().getRealm(id);
Set<RoleModel> realmRoles = null;
if (realm != null) {
realmRoles = realm.getRoles();
}
boolean didIt = getDelegate().removeRealm(id);
realmInvalidations.add(id);
// TODO: Temporary workaround to invalidate cached realm roles
if (didIt && realmRoles != null) {
for (RoleModel role : realmRoles) {
roleInvalidations.add(role.getId());
}
}
return didIt;
}

View file

@ -167,7 +167,12 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-export-import-impl</artifactId>
<artifactId>keycloak-export-import-dir</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-export-import-single-file</artifactId>
<version>${project.version}</version>
</dependency>

View file

@ -284,7 +284,17 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-export-import-impl</artifactId>
<artifactId>keycloak-export-import-dir</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-export-import-zip</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-export-import-single-file</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>

View file

@ -5,8 +5,6 @@ import java.util.Properties;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.FixMethodOrder;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExternalResource;
import org.junit.rules.RuleChain;
@ -25,7 +23,6 @@ import org.keycloak.models.UserModel;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.testsuite.rule.KeycloakRule;
import org.keycloak.testutils.KeycloakServer;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@ -39,7 +36,7 @@ public class ExportImportTest {
@Override
protected void before() throws Throwable {
if (System.getProperty("hibernate.connection.url") == null) {
if (System.getProperty("hibernate.connection.url") == null) {
String baseExportImportDir = getExportImportTestDirectory();
File oldDBFile = new File(baseExportImportDir, "keycloakDB.h2.db");
@ -64,6 +61,29 @@ public class ExportImportTest {
}
};
// We want data to be persisted among server restarts
private static ExternalResource mongoRule = new ExternalResource() {
private static final String MONGO_CLEAR_ON_STARTUP_PROP_NAME = "keycloak.model.mongo.clearOnStartup";
private String previousMongoClearOnStartup;
@Override
protected void before() throws Throwable {
previousMongoClearOnStartup = System.getProperty(MONGO_CLEAR_ON_STARTUP_PROP_NAME);
System.setProperty(MONGO_CLEAR_ON_STARTUP_PROP_NAME, "false");
}
@Override
protected void after() {
if (previousMongoClearOnStartup != null) {
System.setProperty(MONGO_CLEAR_ON_STARTUP_PROP_NAME, "false");
} else {
System.getProperties().remove(MONGO_CLEAR_ON_STARTUP_PROP_NAME);
}
}
};
private static KeycloakRule keycloakRule = new KeycloakRule( new KeycloakRule.KeycloakSetup() {
@Override
@ -79,9 +99,10 @@ public class ExportImportTest {
@ClassRule
public static TestRule chain = RuleChain
.outerRule(hibernateSetupRule)
.around(mongoRule)
.around(keycloakRule);
//@Test
@Test
public void testDirFullExportImport() throws Throwable {
ExportImportConfig.setProvider(DirExportProviderFactory.PROVIDER_ID);
String targetDirPath = getExportImportTestDirectory() + File.separator + "dirExport";
@ -95,7 +116,7 @@ public class ExportImportTest {
Assert.assertEquals(4, new File(targetDirPath).listFiles().length);
}
//@Test
@Test
public void testDirRealmExportImport() throws Throwable {
ExportImportConfig.setProvider(DirExportProviderFactory.PROVIDER_ID);
String targetDirPath = getExportImportTestDirectory() + File.separator + "dirRealmExport";
@ -118,7 +139,7 @@ public class ExportImportTest {
testFullExportImport();
}
//@Test
@Test
public void testSingleFileRealmExportImport() throws Throwable {
ExportImportConfig.setProvider(SingleFileExportProviderFactory.PROVIDER_ID);
String targetFilePath = getExportImportTestDirectory() + File.separator + "singleFile-realm.json";
@ -127,7 +148,7 @@ public class ExportImportTest {
testRealmExportImport();
}
//@Test
@Test
public void testZipFullExportImport() throws Throwable {
ExportImportConfig.setProvider(ZipExportProviderFactory.PROVIDER_ID);
String zipFilePath = getExportImportTestDirectory() + File.separator + "export-full.zip";
@ -139,7 +160,7 @@ public class ExportImportTest {
testFullExportImport();
}
//@Test
@Test
public void testZipRealmExportImport() throws Throwable {
ExportImportConfig.setProvider(ZipExportProviderFactory.PROVIDER_ID);
String zipFilePath = getExportImportTestDirectory() + File.separator + "export-realm.zip";

View file

@ -6,6 +6,7 @@ import org.apache.log.Logger;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.utils.RepresentationToModel;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.services.managers.RealmManager;
@ -77,9 +78,9 @@ public class CreateRealmsWorker implements Worker {
// Add required credentials
if (createRequiredCredentials) {
realmManager.addRequiredCredential(realm, CredentialRepresentation.PASSWORD);
realmManager.addRequiredCredential(realm, CredentialRepresentation.TOTP);
realmManager.addRequiredCredential(realm, CredentialRepresentation.CLIENT_CERT);
RepresentationToModel.addRequiredCredential(realm, CredentialRepresentation.PASSWORD);
RepresentationToModel.addRequiredCredential(realm, CredentialRepresentation.TOTP);
RepresentationToModel.addRequiredCredential(realm, CredentialRepresentation.CLIENT_CERT);
}
log.info("Finished creation of realm " + realmName);

View file

@ -283,7 +283,17 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-export-import-impl</artifactId>
<artifactId>keycloak-export-import-dir</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-export-import-zip</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-export-import-single-file</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>

View file

@ -1,6 +1,7 @@
package org.keycloak.test.tools;
import org.keycloak.exportimport.ExportImportConfig;
import org.keycloak.exportimport.ExportImportManager;
import org.keycloak.exportimport.ExportProvider;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
@ -231,14 +232,7 @@ public class PerfTools {
ExportImportConfig.setProvider("dir");
ExportImportConfig.setDir(dir);
Iterator<ExportProvider> providers = ProviderLoader.load(ExportProvider.class).iterator();
if (providers.hasNext()) {
ExportProvider exportImport = providers.next();
exportImport.checkExportImport(sessionFactory);
} else {
throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
}
new ExportImportManager().checkExportImport(sessionFactory);
}
public static class JobRepresentation {