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();
}
}