/*
* 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.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;
import javax.ws.rs.core.Response;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Partial Import handler for Client Roles.
*
* @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
*/
public class ClientRolesPartialImport {
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;
return partialImportRep.getRoles().getClient();
}
public String getName(RoleRepresentation roleRep) {
if (roleRep.getName() == null)
throw new IllegalStateException("Client role to import does not have a name");
return roleRep.getName();
}
public String getCombinedName(String clientId, RoleRepresentation roleRep) {
return clientId + "-->" + getName(roleRep);
}
public boolean exists(RealmModel realm, KeycloakSession session, String clientId, RoleRepresentation roleRep) {
ClientModel client = realm.getClientByClientId(clientId);
if (client == null) return false;
for (RoleModel role : client.getRoles()) {
if (getName(roleRep).equals(role.getName())) return true;
}
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.";
}
public ResourceType getResourceType() {
return ResourceType.CLIENT_ROLE;
}
public void deleteRole(RealmModel realm, String clientId, RoleRepresentation roleRep) {
ClientModel client = realm.getClientByClientId(clientId);
if (client == null) {
// client might have been removed as part of this partial import
return;
}
RoleModel role = client.getRole(getName(roleRep));
if (role == null) {
// role might not exist if client was just created as part of the
// partial import
return;
}
client.removeRole(role);
}
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()) {
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:
toSkip.get(clientId).add(roleRep);
break;
case OVERWRITE:
toOverwrite.get(clientId).add(roleRep);
break;
default:
throw exists(existsMessage(clientId, roleRep));
}
}
}
}
}
protected ErrorResponseException exists(String message) {
Response error = ErrorResponse.exists(message);
return new ErrorResponseException(error);
}
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);
}
public PartialImportResult skipped(String clientId, String modelId, RoleRepresentation roleRep) {
return PartialImportResult.skipped(getResourceType(), getCombinedName(clientId, roleRep), modelId, roleRep);
}
public PartialImportResult added(String clientId, String modelId, RoleRepresentation roleRep) {
return PartialImportResult.added(getResourceType(), getCombinedName(clientId, roleRep), modelId, roleRep);
}
public String getModelId(RealmModel realm, String clientId) {
return realm.getClientByClientId(clientId).getId();
}
}