keycloak-memoizeit

For overwrite, delete then create. Do all prepares to check

1/5/2016 6:20:23 PM

Details

diff --git a/core/src/main/java/org/keycloak/representations/idm/PartialImportRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/PartialImportRepresentation.java
index 1beeaad..c27000d 100644
--- a/core/src/main/java/org/keycloak/representations/idm/PartialImportRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/PartialImportRepresentation.java
@@ -48,6 +48,14 @@ public class PartialImportRepresentation {
         return (identityProviders != null) && !identityProviders.isEmpty();
     }
 
+    public boolean hasRealmRoles() {
+        return (roles.getRealm() != null) && (!roles.getRealm().isEmpty());
+    }
+
+    public boolean hasClientRoles() {
+        return (roles.getClient() != null) && (!roles.getClient().isEmpty());
+    }
+
     public String getIfResourceExists() {
         return ifResourceExists;
     }
diff --git a/services/src/main/java/org/keycloak/partialimport/AbstractPartialImport.java b/services/src/main/java/org/keycloak/partialimport/AbstractPartialImport.java
index 74392a2..ad4c203 100644
--- a/services/src/main/java/org/keycloak/partialimport/AbstractPartialImport.java
+++ b/services/src/main/java/org/keycloak/partialimport/AbstractPartialImport.java
@@ -30,7 +30,10 @@ import org.keycloak.services.ErrorResponse;
  *
  * @author Stan Silvert ssilvert@redhat.com (C) 2015 Red Hat Inc.
  */
-public abstract class AbstractPartialImport<T> implements PartialImport {
+public abstract class AbstractPartialImport<T> implements PartialImport<T> {
+
+    protected final Set<T> toOverwrite = new HashSet<>();
+    protected final Set<T> toSkip = new HashSet<>();
 
     public abstract List<T> getRepList(PartialImportRepresentation partialImportRep);
     public abstract String getName(T resourceRep);
@@ -41,36 +44,38 @@ public abstract class AbstractPartialImport<T> implements PartialImport {
     public abstract void overwrite(RealmModel realm, KeycloakSession session, T resourceRep);
     public abstract void create(RealmModel realm, KeycloakSession session, T resourceRep);
 
-    protected void prepare(PartialImportRepresentation partialImportRep,
+    @Override
+    public void prepare(PartialImportRepresentation partialImportRep,
                          RealmModel realm,
-                         KeycloakSession session,
-                         Set<T> resourcesToOverwrite,
-                         Set<T> resourcesToSkip) throws ErrorResponseException {
+                         KeycloakSession session) throws ErrorResponseException {
+        List<T> repList = getRepList(partialImportRep);
+        if ((repList == null) || repList.isEmpty()) return;
+
         for (T resourceRep : getRepList(partialImportRep)) {
             if (exists(realm, session, resourceRep)) {
                 switch (partialImportRep.getPolicy()) {
-                    case SKIP: resourcesToSkip.add(resourceRep); break;
-                    case OVERWRITE: resourcesToOverwrite.add(resourceRep); break;
-                    default: throw exists(existsMessage(resourceRep));
+                    case SKIP: toSkip.add(resourceRep); break;
+                    case OVERWRITE: toOverwrite.add(resourceRep); break;
+                    default: throw existsError(existsMessage(resourceRep));
                 }
             }
         }
     }
 
-    protected ErrorResponseException exists(String message) {
+    protected ErrorResponseException existsError(String message) {
         Response error = ErrorResponse.exists(message);
         return new ErrorResponseException(error);
     }
 
-    protected PartialImportResult overwritten(String modelId, T resourceRep){
+    public PartialImportResult overwritten(String modelId, T resourceRep){
         return PartialImportResult.overwritten(getResourceType(), getName(resourceRep), modelId, resourceRep);
     }
 
-    protected PartialImportResult skipped(String modelId, T resourceRep) {
+    public PartialImportResult skipped(String modelId, T resourceRep) {
         return PartialImportResult.skipped(getResourceType(), getName(resourceRep), modelId, resourceRep);
     }
 
-    protected PartialImportResult added(String modelId, T resourceRep) {
+    public PartialImportResult added(String modelId, T resourceRep) {
         return PartialImportResult.added(getResourceType(), getName(resourceRep), modelId, resourceRep);
     }
 
@@ -80,12 +85,8 @@ public abstract class AbstractPartialImport<T> implements PartialImport {
         List<T> repList = getRepList(partialImportRep);
         if ((repList == null) || repList.isEmpty()) return results;
 
-        final Set<T> toOverwrite = new HashSet<>();
-        final Set<T> toSkip = new HashSet<>();
-        prepare(partialImportRep, realm, session, toOverwrite, toSkip);
-
         for (T resourceRep: toOverwrite) {
-            System.out.println("overwriting " + getResourceType() + " " + getName(resourceRep));
+            //System.out.println("overwriting " + getResourceType() + " " + getName(resourceRep));
             try {
                 overwrite(realm, session, resourceRep);
             } catch (Exception e) {
@@ -97,7 +98,7 @@ public abstract class AbstractPartialImport<T> implements PartialImport {
         }
 
         for (T resourceRep : toSkip) {
-            System.out.println("skipping " + getResourceType() + " " + getName(resourceRep));
+            //System.out.println("skipping " + getResourceType() + " " + getName(resourceRep));
             String modelId = getModelId(realm, session, resourceRep);
             results.addResult(skipped(modelId, resourceRep));
         }
@@ -107,7 +108,7 @@ public abstract class AbstractPartialImport<T> implements PartialImport {
             if (toSkip.contains(resourceRep)) continue;
 
             try {
-                System.out.println("adding " + getResourceType() + " " + getName(resourceRep));
+                //System.out.println("adding " + getResourceType() + " " + getName(resourceRep));
                 create(realm, session, resourceRep);
                 String modelId = getModelId(realm, session, resourceRep);
                 results.addResult(added(modelId, resourceRep));
diff --git a/services/src/main/java/org/keycloak/partialimport/ClientRolesPartialImport.java b/services/src/main/java/org/keycloak/partialimport/ClientRolesPartialImport.java
index fc524f2..00a6c25 100644
--- a/services/src/main/java/org/keycloak/partialimport/ClientRolesPartialImport.java
+++ b/services/src/main/java/org/keycloak/partialimport/ClientRolesPartialImport.java
@@ -26,6 +26,7 @@ import org.keycloak.models.ClientModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.RoleModel;
+import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.representations.idm.PartialImportRepresentation;
 import org.keycloak.representations.idm.RoleRepresentation;
 import org.keycloak.services.ErrorResponse;
@@ -34,7 +35,17 @@ import org.keycloak.services.ErrorResponse;
  *
  * @author Stan Silvert ssilvert@redhat.com (C) 2015 Red Hat Inc.
  */
-public class ClientRolesPartialImport implements PartialImport {
+public class ClientRolesPartialImport implements PartialImport<RoleRepresentation> {
+    private final Map<String, Set<RoleRepresentation>> toOverwrite = new HashMap<>();
+    private final Map<String, Set<RoleRepresentation>> toSkip = new HashMap<>();
+
+    public Map<String, Set<RoleRepresentation>> getToOverwrite() {
+        return this.toOverwrite;
+    }
+
+    public Map<String, Set<RoleRepresentation>> getToSkip() {
+        return this.toSkip;
+    }
 
     public Map<String, List<RoleRepresentation>> getRepList(PartialImportRepresentation partialImportRep) {
         if (partialImportRep.getRoles() == null) return null;
@@ -52,12 +63,9 @@ public class ClientRolesPartialImport implements PartialImport {
     }
 
     public boolean exists(RealmModel realm, KeycloakSession session, String clientId, RoleRepresentation roleRep) {
-        System.out.println("**** exists *****");
-        System.out.println("clientId =" + clientId);
         ClientModel client = realm.getClientByClientId(clientId);
         if (client == null) return false;
 
-        System.out.println("client=" + client);
         for (RoleModel role : client.getRoles()) {
             if (getName(roleRep).equals(role.getName())) return true;
         }
@@ -65,6 +73,19 @@ public class ClientRolesPartialImport implements PartialImport {
         return false;
     }
 
+    // check if client currently exists or will exists as a result of this partial import
+    private boolean clientExists(PartialImportRepresentation partialImportRep, RealmModel realm, String clientId) {
+        if (realm.getClientByClientId(clientId) != null) return true;
+
+        if (partialImportRep.getClients() == null) return false;
+
+        for (ClientRepresentation client : partialImportRep.getClients()) {
+            if (clientId.equals(client.getClientId())) return true;
+        }
+
+        return false;
+    }
+
     public String existsMessage(String clientId, RoleRepresentation roleRep) {
         return "Client role '" + getName(roleRep) + "' for client '" + clientId + "' already exists.";
     }
@@ -79,7 +100,13 @@ public class ClientRolesPartialImport implements PartialImport {
         RoleModel role = client.getRole(getName(roleRep));
         checkForOverwriteComposite(role);
         RealmRolesPartialImport.RoleHelper helper = new RealmRolesPartialImport.RoleHelper(realm);
-        helper.updateRole(roleRep, role);
+//        helper.updateRole(roleRep, role);
+    }
+
+    public void deleteRole(RealmModel realm, String clientId, RoleRepresentation roleRep) {
+        ClientModel client = realm.getClientByClientId(clientId);
+        RoleModel role = client.getRole(getName(roleRep));
+        client.removeRole(role);
     }
 
     private void checkForComposite(RoleRepresentation roleRep) {
@@ -104,23 +131,26 @@ public class ClientRolesPartialImport implements PartialImport {
         overwrite(realm, session, clientId, roleRep);
     }
 
-    protected void prepare(PartialImportRepresentation partialImportRep,
-            RealmModel realm,
-            KeycloakSession session,
-            Map<String, Set<RoleRepresentation>> resourcesToOverwrite,
-            Map<String, Set<RoleRepresentation>> resourcesToSkip) throws ErrorResponseException {
+    @Override
+    public void prepare(PartialImportRepresentation partialImportRep, RealmModel realm, KeycloakSession session) throws ErrorResponseException {
         Map<String, List<RoleRepresentation>> repList = getRepList(partialImportRep);
+        if (repList == null || repList.isEmpty()) return;
+
         for (String clientId : repList.keySet()) {
-            resourcesToOverwrite.put(clientId, new HashSet<RoleRepresentation>());
-            resourcesToSkip.put(clientId, new HashSet<RoleRepresentation>());
+            if (!clientExists(partialImportRep, realm, clientId)) {
+                throw noClientFound(clientId);
+            }
+
+            toOverwrite.put(clientId, new HashSet<RoleRepresentation>());
+            toSkip.put(clientId, new HashSet<RoleRepresentation>());
             for (RoleRepresentation roleRep : repList.get(clientId)) {
                 if (exists(realm, session, clientId, roleRep)) {
                     switch (partialImportRep.getPolicy()) {
                         case SKIP:
-                            resourcesToSkip.get(clientId).add(roleRep);
+                            toSkip.get(clientId).add(roleRep);
                             break;
                         case OVERWRITE:
-                            resourcesToOverwrite.get(clientId).add(roleRep);
+                            toOverwrite.get(clientId).add(roleRep);
                             break;
                         default:
                             throw exists(existsMessage(clientId, roleRep));
@@ -135,15 +165,21 @@ public class ClientRolesPartialImport implements PartialImport {
         return new ErrorResponseException(error);
     }
 
-    protected PartialImportResult overwritten(String clientId, String modelId, RoleRepresentation roleRep) {
+    protected ErrorResponseException noClientFound(String clientId) {
+        String message = "Can not import client roles for nonexistent client named " + clientId;
+        Response error = ErrorResponse.error(message, Response.Status.PRECONDITION_FAILED);
+        return new ErrorResponseException(error);
+    }
+
+    public PartialImportResult overwritten(String clientId, String modelId, RoleRepresentation roleRep) {
         return PartialImportResult.overwritten(getResourceType(), getCombinedName(clientId, roleRep), modelId, roleRep);
     }
 
-    protected PartialImportResult skipped(String clientId, String modelId, RoleRepresentation roleRep) {
+    public PartialImportResult skipped(String clientId, String modelId, RoleRepresentation roleRep) {
         return PartialImportResult.skipped(getResourceType(), getCombinedName(clientId, roleRep), modelId, roleRep);
     }
 
-    protected PartialImportResult added(String clientId, String modelId, RoleRepresentation roleRep) {
+    public PartialImportResult added(String clientId, String modelId, RoleRepresentation roleRep) {
         return PartialImportResult.added(getResourceType(), getCombinedName(clientId, roleRep), modelId, roleRep);
     }
 
@@ -153,10 +189,6 @@ public class ClientRolesPartialImport implements PartialImport {
         Map<String, List<RoleRepresentation>> repList = getRepList(partialImportRep);
         if ((repList == null) || repList.isEmpty()) return results;
 
-        final Map<String, Set<RoleRepresentation>> toOverwrite = new HashMap<>();
-        final Map<String, Set<RoleRepresentation>> toSkip = new HashMap<>();
-        prepare(partialImportRep, realm, session, toOverwrite, toSkip);
-
         for (String clientId : toOverwrite.keySet()) {
             for (RoleRepresentation roleRep : toOverwrite.get(clientId)) {
                 System.out.println("overwriting " + getResourceType() + " " + getCombinedName(clientId, roleRep));
@@ -199,7 +231,7 @@ public class ClientRolesPartialImport implements PartialImport {
         return results;
     }
 
-    private String getModelId(RealmModel realm, String clientId) {
+    public String getModelId(RealmModel realm, String clientId) {
         return realm.getClientByClientId(clientId).getId();
     }
 }
diff --git a/services/src/main/java/org/keycloak/partialimport/ClientsPartialImport.java b/services/src/main/java/org/keycloak/partialimport/ClientsPartialImport.java
index ad633cc..940dbe3 100644
--- a/services/src/main/java/org/keycloak/partialimport/ClientsPartialImport.java
+++ b/services/src/main/java/org/keycloak/partialimport/ClientsPartialImport.java
@@ -21,10 +21,13 @@ import java.util.List;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.models.utils.RepresentationToModel;
 import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.representations.idm.PartialImportRepresentation;
-import org.keycloak.services.resources.admin.ClientResource;
+import org.keycloak.representations.idm.ProtocolMapperRepresentation;
+import org.keycloak.services.managers.ClientManager;
+import org.keycloak.services.managers.RealmManager;
 
 /**
  *
@@ -64,16 +67,27 @@ public class ClientsPartialImport extends AbstractPartialImport<ClientRepresenta
 
     @Override
     public void overwrite(RealmModel realm, KeycloakSession session, ClientRepresentation clientRep) {
+        remove(realm, session, clientRep);
+        create(realm, session, clientRep);
+    }
+
+    protected void remove(RealmModel realm, KeycloakSession session, ClientRepresentation clientRep) {
         ClientModel clientModel = realm.getClientByClientId(getName(clientRep));
-        ClientResource.updateClientFromRep(clientRep, clientModel, session);
+        new ClientManager(new RealmManager(session)).removeClient(realm, clientModel);
     }
 
     @Override
     public void create(RealmModel realm, KeycloakSession session, ClientRepresentation clientRep) {
-        clientRep.setId(null);
-        RepresentationToModel.createClient(session, realm, clientRep, true);
-    }
+        clientRep.setId(KeycloakModelUtils.generateId());
 
+        List<ProtocolMapperRepresentation> mappers = clientRep.getProtocolMappers();
+        if (mappers != null) {
+            for (ProtocolMapperRepresentation mapper : mappers) {
+                mapper.setId(KeycloakModelUtils.generateId());
+            }
+        }
 
+        RepresentationToModel.createClient(session, realm, clientRep, true);
+    }
 
 }
diff --git a/services/src/main/java/org/keycloak/partialimport/IdentityProvidersPartialImport.java b/services/src/main/java/org/keycloak/partialimport/IdentityProvidersPartialImport.java
index d1abdb7..c92ede0 100644
--- a/services/src/main/java/org/keycloak/partialimport/IdentityProvidersPartialImport.java
+++ b/services/src/main/java/org/keycloak/partialimport/IdentityProvidersPartialImport.java
@@ -18,9 +18,12 @@
 package org.keycloak.partialimport;
 
 import java.util.List;
+import org.jboss.resteasy.spi.NotFoundException;
+import org.keycloak.models.IdentityProviderMapperModel;
 import org.keycloak.models.IdentityProviderModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.models.utils.RepresentationToModel;
 import org.keycloak.representations.idm.IdentityProviderRepresentation;
 import org.keycloak.representations.idm.PartialImportRepresentation;
@@ -64,11 +67,17 @@ public class IdentityProvidersPartialImport extends AbstractPartialImport<Identi
 
     @Override
     public void overwrite(RealmModel realm, KeycloakSession session, IdentityProviderRepresentation idpRep) {
-        IdentityProviderResource.updateIdpFromRep(idpRep, realm, session);
+        remove(realm, idpRep);
+        create(realm, session, idpRep);
+    }
+
+    protected void remove(RealmModel realm, IdentityProviderRepresentation idpRep) {
+        realm.removeIdentityProviderByAlias(getName(idpRep));
     }
 
     @Override
     public void create(RealmModel realm, KeycloakSession session, IdentityProviderRepresentation idpRep) {
+        idpRep.setInternalId(KeycloakModelUtils.generateId());
         IdentityProviderModel identityProvider = RepresentationToModel.toModel(realm, idpRep);
         realm.addIdentityProvider(identityProvider);
     }
diff --git a/services/src/main/java/org/keycloak/partialimport/PartialImport.java b/services/src/main/java/org/keycloak/partialimport/PartialImport.java
index 0ee487b..e80368f 100644
--- a/services/src/main/java/org/keycloak/partialimport/PartialImport.java
+++ b/services/src/main/java/org/keycloak/partialimport/PartialImport.java
@@ -25,7 +25,11 @@ import org.keycloak.representations.idm.PartialImportRepresentation;
  *
  * @author Stan Silvert ssilvert@redhat.com (C) 2015 Red Hat Inc.
  */
-public interface PartialImport {
+public interface PartialImport<T> {
+
+    public void prepare(PartialImportRepresentation rep,
+                         RealmModel realm,
+                         KeycloakSession session) throws ErrorResponseException;
 
     /**
      * @param rep
@@ -34,5 +38,7 @@ public interface PartialImport {
      * @return
      * @throws ErrorResponseException if an error was detected trying to doImport a resource.
      */
-    public PartialImportResults doImport(PartialImportRepresentation rep, RealmModel realm, KeycloakSession session) throws ErrorResponseException;
+    public PartialImportResults doImport(PartialImportRepresentation rep,
+                                         RealmModel realm,
+                                         KeycloakSession session) throws ErrorResponseException;
 }
diff --git a/services/src/main/java/org/keycloak/partialimport/PartialImportManager.java b/services/src/main/java/org/keycloak/partialimport/PartialImportManager.java
index c722ec2..43f4eb0 100644
--- a/services/src/main/java/org/keycloak/partialimport/PartialImportManager.java
+++ b/services/src/main/java/org/keycloak/partialimport/PartialImportManager.java
@@ -52,30 +52,32 @@ public class PartialImportManager {
     private final PartialImportRepresentation rep;
     private final KeycloakSession session;
     private final RealmModel realm;
-    private final UriInfo uriInfo;
+    //private final UriInfo uriInfo;
     private final AdminEventBuilder adminEvent;
 
-    private final Set<UserRepresentation> usersToOverwrite = new HashSet<>();
-    private final Set<ClientRepresentation> clientsToOverwrite = new HashSet<>();
-    private final Set<IdentityProviderRepresentation> idpsToOverwrite = new HashSet<>();
+    //private final Set<UserRepresentation> usersToOverwrite = new HashSet<>();
+    //private final Set<ClientRepresentation> clientsToOverwrite = new HashSet<>();
+    //private final Set<IdentityProviderRepresentation> idpsToOverwrite = new HashSet<>();
 
-    private int added = 0;
-    private int skipped = 0;
-    private int overwritten = 0;
+    //private int added = 0;
+    //private int skipped = 0;
+    //private int overwritten = 0;
 
-    public PartialImportManager(PartialImportRepresentation rep, KeycloakSession session, RealmModel realm,
-                         UriInfo uriInfo, AdminEventBuilder adminEvent) {
+    public PartialImportManager(PartialImportRepresentation rep, KeycloakSession session,
+                                RealmModel realm, AdminEventBuilder adminEvent) {
         this.rep = rep;
         this.session = session;
         this.realm = realm;
-        this.uriInfo = uriInfo;
+        //this.uriInfo = uriInfo;
         this.adminEvent = adminEvent;
 
-        partialImports.add(new UsersPartialImport());
+        // Do not change the order of these!!!
         partialImports.add(new ClientsPartialImport());
+       // partialImports.add(new RealmRolesPartialImport());
+       // partialImports.add(new ClientRolesPartialImport());
+        partialImports.add(new RolesPartialImport());
+        partialImports.add(new UsersPartialImport());
         partialImports.add(new IdentityProvidersPartialImport());
-        partialImports.add(new RealmRolesPartialImport());
-        partialImports.add(new ClientRolesPartialImport());
     }
 
     public Response saveResources() {
@@ -84,6 +86,15 @@ public class PartialImportManager {
 
         for (PartialImport partialImport : partialImports) {
             try {
+                partialImport.prepare(rep, realm, session);
+            } catch (ErrorResponseException error) {
+                if (session.getTransaction().isActive()) session.getTransaction().setRollbackOnly();
+                return error.getResponse();
+            }
+        }
+
+        for (PartialImport partialImport : partialImports) {
+            try {
                 results.addAllResults(partialImport.doImport(rep, realm, session));
             } catch (ErrorResponseException error) {
                 if (session.getTransaction().isActive()) session.getTransaction().setRollbackOnly();
diff --git a/services/src/main/java/org/keycloak/partialimport/PartialImportResults.java b/services/src/main/java/org/keycloak/partialimport/PartialImportResults.java
index f4d01cb..9ff95c4 100644
--- a/services/src/main/java/org/keycloak/partialimport/PartialImportResults.java
+++ b/services/src/main/java/org/keycloak/partialimport/PartialImportResults.java
@@ -19,9 +19,6 @@ package org.keycloak.partialimport;
 
 import java.util.HashSet;
 import java.util.Set;
-import javax.ws.rs.core.UriInfo;
-import org.keycloak.events.admin.OperationType;
-import org.keycloak.services.resources.admin.AdminEventBuilder;
 
 /**
  *
@@ -32,7 +29,7 @@ public class PartialImportResults {
     private final Set<PartialImportResult> importResults = new HashSet<>();
 
     public void addResult(PartialImportResult result) {
-        System.out.println("PartialImportResults: add " + result.getResourceName() + " action=" + result.getAction());
+        //System.out.println("PartialImportResults: add " + result.getResourceName() + " action=" + result.getAction());
         importResults.add(result);
     }
 
@@ -52,7 +49,7 @@ public class PartialImportResults {
     public int getOverwritten() {
         int overwritten = 0;
         for (PartialImportResult result : importResults) {
-            System.out.println("action=" + result.getAction());
+            //System.out.println("action=" + result.getAction());
             if (result.getAction() == Action.OVERWRITTEN) overwritten++;
         }
 
diff --git a/services/src/main/java/org/keycloak/partialimport/RealmRolesPartialImport.java b/services/src/main/java/org/keycloak/partialimport/RealmRolesPartialImport.java
index b2213e4..1c335a9 100644
--- a/services/src/main/java/org/keycloak/partialimport/RealmRolesPartialImport.java
+++ b/services/src/main/java/org/keycloak/partialimport/RealmRolesPartialImport.java
@@ -17,6 +17,7 @@
 package org.keycloak.partialimport;
 
 import java.util.List;
+import java.util.Set;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.RoleModel;
@@ -30,6 +31,14 @@ import org.keycloak.services.resources.admin.RoleResource;
  */
 public class RealmRolesPartialImport extends AbstractPartialImport<RoleRepresentation> {
 
+    public Set<RoleRepresentation> getToOverwrite() {
+        return this.toOverwrite;
+    }
+
+    public Set<RoleRepresentation> getToSkip() {
+        return this.toSkip;
+    }
+
     @Override
     public List<RoleRepresentation> getRepList(PartialImportRepresentation partialImportRep) {
         if (partialImportRep.getRoles() == null) return null;
@@ -73,13 +82,17 @@ public class RealmRolesPartialImport extends AbstractPartialImport<RoleRepresent
 
     @Override
     public void overwrite(RealmModel realm, KeycloakSession session, RoleRepresentation roleRep) {
-        checkForComposite(roleRep);
+        //checkForComposite(roleRep);
+        deleteRole(realm, roleRep);
+        create(realm, session, roleRep);
+    }
+
+    public void deleteRole(RealmModel realm, RoleRepresentation roleRep) {
         RoleModel role = realm.getRole(getName(roleRep));
-        checkForOverwriteComposite(role);
         RoleHelper helper = new RoleHelper(realm);
-        helper.updateRole(roleRep, role);
+        helper.deleteRole(role);
     }
-
+/*
     private void checkForComposite(RoleRepresentation roleRep) {
         if (roleRep.isComposite()) {
             throw new IllegalArgumentException("Composite role '" + getName(roleRep) + "' can not be partially imported");
@@ -91,12 +104,12 @@ public class RealmRolesPartialImport extends AbstractPartialImport<RoleRepresent
             throw new IllegalArgumentException("Composite role '" + role.getName() + "' can not be overwritten.");
         }
     }
-
+*/
     @Override
     public void create(RealmModel realm, KeycloakSession session, RoleRepresentation roleRep) {
-        checkForComposite(roleRep);
+        //checkForComposite(roleRep);
         realm.addRole(getName(roleRep));
-        overwrite(realm, session, roleRep);
+        //overwrite(realm, session, roleRep);
     }
 
     public static class RoleHelper extends RoleResource {
@@ -105,8 +118,8 @@ public class RealmRolesPartialImport extends AbstractPartialImport<RoleRepresent
         }
 
         @Override
-        protected void updateRole(RoleRepresentation rep, RoleModel role) {
-            super.updateRole(rep, role);
+        protected void deleteRole(RoleModel role) {
+            super.deleteRole(role);
         }
     }
 }
diff --git a/services/src/main/java/org/keycloak/partialimport/RolesPartialImport.java b/services/src/main/java/org/keycloak/partialimport/RolesPartialImport.java
new file mode 100644
index 0000000..c9c4fdb
--- /dev/null
+++ b/services/src/main/java/org/keycloak/partialimport/RolesPartialImport.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.keycloak.partialimport;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.models.utils.RepresentationToModel;
+import org.keycloak.representations.idm.PartialImportRepresentation;
+import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.representations.idm.RolesRepresentation;
+
+/**
+ * This class handles both realm roles and client roles.  It delegates to
+ * RealmRolesPartialImport and ClientRolesPartialImport, which are no longer used
+ * directly by the PartialImportManager.
+ *
+ * The strategy is to utilize RepresentationToModel.importRoles().  That way,
+ * the complex code for bulk creation of roles is kept in one place.  To do this, the
+ * logic for skip needs to remove the roles that are going to be skipped so that
+ * importRoles() doesn't know about them.  The logic for overwrite needs to delete
+ * the overwritten roles before importRoles() is called.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2015 Red Hat Inc.
+ */
+public class RolesPartialImport implements PartialImport<RolesRepresentation> {
+
+    private Set<RoleRepresentation> realmRolesToOverwrite;
+    private Set<RoleRepresentation> realmRolesToSkip;
+
+    private Map<String, Set<RoleRepresentation>> clientRolesToOverwrite;
+    private Map<String, Set<RoleRepresentation>> clientRolesToSkip;
+
+    private final RealmRolesPartialImport realmRolesPI = new RealmRolesPartialImport();
+    private final ClientRolesPartialImport clientRolesPI = new ClientRolesPartialImport();
+
+    @Override
+    public void prepare(PartialImportRepresentation rep, RealmModel realm, KeycloakSession session) throws ErrorResponseException {
+        prepareRealmRoles(rep, realm, session);
+        prepareClientRoles(rep, realm, session);
+    }
+
+    private void prepareRealmRoles(PartialImportRepresentation rep, RealmModel realm, KeycloakSession session) throws ErrorResponseException {
+        if (!rep.hasRealmRoles()) return;
+
+        realmRolesPI.prepare(rep, realm, session);
+        this.realmRolesToOverwrite = realmRolesPI.getToOverwrite();
+        this.realmRolesToSkip = realmRolesPI.getToSkip();
+    }
+
+    private void prepareClientRoles(PartialImportRepresentation rep, RealmModel realm, KeycloakSession session) throws ErrorResponseException {
+        if (!rep.hasClientRoles()) return;
+
+        clientRolesPI.prepare(rep, realm, session);
+        this.clientRolesToOverwrite = clientRolesPI.getToOverwrite();
+        this.clientRolesToSkip = clientRolesPI.getToSkip();
+    }
+
+    @Override
+    public PartialImportResults doImport(PartialImportRepresentation rep, RealmModel realm, KeycloakSession session) throws ErrorResponseException {
+        PartialImportResults results = new PartialImportResults();
+        if (!rep.hasRealmRoles() && !rep.hasClientRoles()) return results;
+
+        // finalize preparation and add results for skips and overwrites
+        removeRealmRoleSkips(results, rep, realm, session);
+        removeClientRoleSkips(results, rep, realm);
+        deleteRealmRoleOverwrites(results, realm, session);
+        deleteClientRoleOverwrites(results, realm);
+        if (rep.hasRealmRoles()) setUniqueIds(rep.getRoles().getRealm());
+        if (rep.hasClientRoles()) setUniqueIds(rep.getRoles().getClient());
+
+        RepresentationToModel.importRoles(rep.getRoles(), realm);
+
+        // add "add" results for new roles created
+        realmRoleAdds(results, rep, realm, session);
+        clientRoleAdds(results, rep, realm);
+
+        return results;
+    }
+
+    private void setUniqueIds(List<RoleRepresentation> realmRoles) {
+        for (RoleRepresentation realmRole : realmRoles) {
+            realmRole.setId(KeycloakModelUtils.generateId());
+        }
+    }
+
+    private void setUniqueIds(Map<String, List<RoleRepresentation>> clientRoles) {
+        for (String clientId : clientRoles.keySet()) {
+            for (RoleRepresentation clientRole : clientRoles.get(clientId)) {
+                clientRole.setId(KeycloakModelUtils.generateId());
+            }
+        }
+    }
+
+    private void removeRealmRoleSkips(PartialImportResults results,
+                                      PartialImportRepresentation rep,
+                                      RealmModel realm,
+                                      KeycloakSession session) {
+        if (isEmpty(realmRolesToSkip)) return;
+
+        for (RoleRepresentation roleRep : realmRolesToSkip) {
+            rep.getRoles().getRealm().remove(roleRep);
+            String modelId = realmRolesPI.getModelId(realm, session, roleRep);
+            results.addResult(realmRolesPI.skipped(modelId, roleRep));
+        }
+    }
+
+    private void removeClientRoleSkips(PartialImportResults results,
+                                       PartialImportRepresentation rep,
+                                       RealmModel realm) {
+        if (isEmpty(clientRolesToSkip)) return;
+
+        for (String clientId : clientRolesToSkip.keySet()) {
+            for (RoleRepresentation roleRep : clientRolesToSkip.get(clientId)) {
+                rep.getRoles().getClient().get(clientId).remove(roleRep);
+                String modelId = clientRolesPI.getModelId(realm, clientId);
+                results.addResult(clientRolesPI.skipped(clientId, modelId, roleRep));
+            }
+        }
+    }
+
+    private void deleteRealmRoleOverwrites(PartialImportResults results, RealmModel realm, KeycloakSession session) {
+        if (isEmpty(realmRolesToOverwrite)) return;
+
+        for (RoleRepresentation roleRep : realmRolesToOverwrite) {
+            realmRolesPI.deleteRole(realm, roleRep);
+            String modelId = realmRolesPI.getModelId(realm, session, roleRep);
+            results.addResult(realmRolesPI.overwritten(modelId, roleRep));
+        }
+    }
+
+    private void deleteClientRoleOverwrites(PartialImportResults results, RealmModel realm) {
+        if (isEmpty(clientRolesToOverwrite)) return;
+
+        for (String clientId : clientRolesToOverwrite.keySet()) {
+            for (RoleRepresentation roleRep : clientRolesToOverwrite.get(clientId)) {
+                clientRolesPI.deleteRole(realm, clientId, roleRep);
+                String modelId = clientRolesPI.getModelId(realm, clientId);
+                results.addResult(clientRolesPI.overwritten(clientId, modelId, roleRep));
+            }
+        }
+    }
+
+    private boolean isEmpty(Set set) {
+        return (set == null) || (set.isEmpty());
+    }
+
+    private boolean isEmpty(Map map) {
+        return (map == null) || (map.isEmpty());
+    }
+
+    private void realmRoleAdds(PartialImportResults results,
+                               PartialImportRepresentation rep,
+                               RealmModel realm,
+                               KeycloakSession session) {
+        if (!rep.hasRealmRoles()) return;
+
+        for (RoleRepresentation roleRep : rep.getRoles().getRealm()) {
+            if (realmRolesToOverwrite.contains(roleRep)) continue;
+            if (realmRolesToSkip.contains(roleRep)) continue;
+
+            //System.out.println("adding " + realmRolesPI.getResourceType() + " " + realmRolesPI.getName(roleRep));
+            String modelId = realmRolesPI.getModelId(realm, session, roleRep);
+            results.addResult(realmRolesPI.added(modelId, roleRep));
+        }
+    }
+
+    private void clientRoleAdds(PartialImportResults results,
+                                PartialImportRepresentation rep,
+                                RealmModel realm) {
+        if (!rep.hasClientRoles()) return;
+
+        Map<String, List<RoleRepresentation>> repList = clientRolesPI.getRepList(rep);
+        for (String clientId : repList.keySet()) {
+            for (RoleRepresentation roleRep : repList.get(clientId)) {
+                if (clientRolesToOverwrite.get(clientId).contains(roleRep)) continue;
+                if (clientRolesToSkip.get(clientId).contains(roleRep)) continue;
+
+                //System.out.println("adding " + clientRolesPI.getResourceType() + " " + clientRolesPI.getCombinedName(clientId, roleRep));
+                String modelId = clientRolesPI.getModelId(realm, clientId);
+                results.addResult(clientRolesPI.added(clientId, modelId, roleRep));
+            }
+        }
+    }
+}
diff --git a/services/src/main/java/org/keycloak/partialimport/UsersPartialImport.java b/services/src/main/java/org/keycloak/partialimport/UsersPartialImport.java
index 49df8a5..68d6b0a 100644
--- a/services/src/main/java/org/keycloak/partialimport/UsersPartialImport.java
+++ b/services/src/main/java/org/keycloak/partialimport/UsersPartialImport.java
@@ -17,16 +17,18 @@
 
 package org.keycloak.partialimport;
 
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.models.utils.RepresentationToModel;
 import org.keycloak.representations.idm.PartialImportRepresentation;
 import org.keycloak.representations.idm.UserRepresentation;
-import org.keycloak.services.resources.admin.UsersResource;
+import org.keycloak.services.managers.UserManager;
 
 /**
  *
@@ -34,6 +36,10 @@ import org.keycloak.services.resources.admin.UsersResource;
  */
 public class UsersPartialImport extends AbstractPartialImport<UserRepresentation> {
 
+    // Sometimes session.users().getUserByUsername() doesn't work right after create,
+    // so we cache the created id here.
+    private final Map<String, String> createdIds = new HashMap<>();
+
     @Override
     public List<UserRepresentation> getRepList(PartialImportRepresentation partialImportRep) {
         return partialImportRep.getUsers();
@@ -41,11 +47,15 @@ public class UsersPartialImport extends AbstractPartialImport<UserRepresentation
 
     @Override
     public String getName(UserRepresentation user) {
-        return user.getUsername();
+        if (user.getUsername() != null) return user.getUsername();
+
+        return user.getEmail();
     }
 
     @Override
     public String getModelId(RealmModel realm, KeycloakSession session, UserRepresentation user) {
+        if (createdIds.containsKey(getName(user))) return createdIds.get(getName(user));
+
         String userName = user.getUsername();
         if (userName != null) {
             return session.users().getUserByUsername(userName, realm).getId();
@@ -85,15 +95,27 @@ public class UsersPartialImport extends AbstractPartialImport<UserRepresentation
 
     @Override
     public void overwrite(RealmModel realm, KeycloakSession session, UserRepresentation user) {
+        remove(realm, session, user);
+        create(realm, session, user);
+    }
+
+    protected void remove(RealmModel realm, KeycloakSession session, UserRepresentation user) {
         UserModel userModel = session.users().getUserByUsername(user.getUsername(), realm);
-        UsersResource.updateUserFromRep(userModel, user, null, realm, session);
+        if (userModel == null) {
+            userModel = session.users().getUserByEmail(user.getEmail(), realm);
+        }
+
+        boolean success = new UserManager(session).removeUser(realm, userModel);
+        if (!success) throw new RuntimeException("Unable to overwrite user " + getName(user));
     }
 
     @Override
     public void create(RealmModel realm, KeycloakSession session, UserRepresentation user) {
         Map<String, ClientModel> apps = realm.getClientNameMap();
-        user.setId(null);
-        RepresentationToModel.createUser(session, realm, user, apps);
+        user.setId(KeycloakModelUtils.generateId());
+        UserModel userModel = RepresentationToModel.createUser(session, realm, user, apps);
+        if (userModel == null) throw new RuntimeException("Unable to create user " + getName(user));
+        createdIds.put(getName(user), userModel.getId());
     }
 
 }
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
index b21649f..1cee1f2 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
@@ -714,16 +714,15 @@ public class RealmAdminResource {
     /**
      * Partial import from a JSON file to an existing realm.
      *
-     * @param uriInfo
      * @param rep
      * @return
      */
     @Path("partialImport")
     @POST
     @Consumes(MediaType.APPLICATION_JSON)
-    public Response partialImport(final @Context UriInfo uriInfo, PartialImportRepresentation rep) {
+    public Response partialImport(PartialImportRepresentation rep) {
         auth.requireManage();
-        PartialImportManager partialImport = new PartialImportManager(rep, session, realm, uriInfo, adminEvent);
+        PartialImportManager partialImport = new PartialImportManager(rep, session, realm, adminEvent);
         return partialImport.saveResources();
     }
 }