/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* 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 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;
import org.keycloak.services.ErrorResponse;
import org.keycloak.services.ServicesLogger;
import javax.ws.rs.core.Response;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 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) 2016 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 void removeOverwrites(RealmModel realm, KeycloakSession session) {
deleteClientRoleOverwrites(realm);
deleteRealmRoleOverwrites(realm, session);
}
@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
removeRealmRoleSkips(results, rep, realm, session);
removeClientRoleSkips(results, rep, realm);
if (rep.hasRealmRoles()) setUniqueIds(rep.getRoles().getRealm());
if (rep.hasClientRoles()) setUniqueIds(rep.getRoles().getClient());
try {
RepresentationToModel.importRoles(rep.getRoles(), realm);
} catch (Exception e) {
ServicesLogger.LOGGER.roleImportError(e);
throw new ErrorResponseException(ErrorResponse.error(e.getMessage(), Response.Status.INTERNAL_SERVER_ERROR));
}
// add "add" results for new roles created
realmRoleAdds(results, rep, realm, session);
clientRoleAdds(results, rep, realm);
// add "overwritten" results for roles overwritten
addResultsForOverwrittenRealmRoles(results, realm, session);
addResultsForOverwrittenClientRoles(results, 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(RealmModel realm, KeycloakSession session) {
if (isEmpty(realmRolesToOverwrite)) return;
for (RoleRepresentation roleRep : realmRolesToOverwrite) {
realmRolesPI.remove(realm, session, roleRep);
}
}
private void addResultsForOverwrittenRealmRoles(PartialImportResults results, RealmModel realm, KeycloakSession session) {
if (isEmpty(realmRolesToOverwrite)) return;
for (RoleRepresentation roleRep : realmRolesToOverwrite) {
String modelId = realmRolesPI.getModelId(realm, session, roleRep);
results.addResult(realmRolesPI.overwritten(modelId, roleRep));
}
}
private void deleteClientRoleOverwrites(RealmModel realm) {
if (isEmpty(clientRolesToOverwrite)) return;
for (String clientId : clientRolesToOverwrite.keySet()) {
for (RoleRepresentation roleRep : clientRolesToOverwrite.get(clientId)) {
clientRolesPI.deleteRole(realm, clientId, roleRep);
}
}
}
private void addResultsForOverwrittenClientRoles(PartialImportResults results, RealmModel realm) {
if (isEmpty(clientRolesToOverwrite)) return;
for (String clientId : clientRolesToOverwrite.keySet()) {
for (RoleRepresentation roleRep : clientRolesToOverwrite.get(clientId)) {
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;
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;
String modelId = clientRolesPI.getModelId(realm, clientId);
results.addResult(clientRolesPI.added(clientId, modelId, roleRep));
}
}
}
}