/* * 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.subsystem.server.extension; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.jboss.as.controller.PathAddress; import org.jboss.as.controller.operations.common.Util; import org.jboss.dmr.ModelNode; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.WRITE_ATTRIBUTE_OPERATION; import static org.keycloak.subsystem.server.extension.KeycloakSubsystemDefinition.MASTER_REALM_NAME; import static org.keycloak.subsystem.server.extension.KeycloakSubsystemDefinition.PROVIDERS; import static org.keycloak.subsystem.server.extension.KeycloakSubsystemDefinition.SCHEDULED_TASK_INTERVAL; import static org.keycloak.subsystem.server.extension.ThemeResourceDefinition.CACHE_TEMPLATES; import static org.keycloak.subsystem.server.extension.ThemeResourceDefinition.CACHE_THEMES; import static org.keycloak.subsystem.server.extension.ThemeResourceDefinition.DEFAULT; import static org.keycloak.subsystem.server.extension.ThemeResourceDefinition.DIR; import static org.keycloak.subsystem.server.extension.ThemeResourceDefinition.MODULES; import static org.keycloak.subsystem.server.extension.ThemeResourceDefinition.STATIC_MAX_AGE; import static org.keycloak.subsystem.server.extension.ThemeResourceDefinition.WELCOME_THEME; /** * Converts json representation of Keycloak config to DMR operations. * * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc. */ public class JsonConfigConverter { private static final List<String> NON_SPI_LIST = new ArrayList<>(); static { NON_SPI_LIST.add("providers"); NON_SPI_LIST.add("admin"); NON_SPI_LIST.add("theme"); NON_SPI_LIST.add("scheduled"); } /** * Convert keycloak-server.json to DMR operations that write to standalone.xml * or domain.xml. * * @param json The json representation of the config. * @param subsysAddress The management model address of the keycloak-server subsystem. * @return A list of DMR operations. * @throws IOException If the json can not be parsed. */ public static List<ModelNode> convertJsonConfig(String json, PathAddress subsysAddress) throws IOException { List<ModelNode> list = new ArrayList<>(); JsonNode root = new ObjectMapper().readTree(json); list.add(masterRealmName(root, subsysAddress)); list.add(scheduledTaskInterval(root, subsysAddress)); list.add(providers(root, subsysAddress)); list.add(theme(root, subsysAddress.append(ThemeResourceDefinition.TAG_NAME, ThemeResourceDefinition.RESOURCE_NAME))); list.addAll(spis(root, subsysAddress)); return list; } private static ModelNode masterRealmName(JsonNode root, PathAddress addr) { JsonNode targetNode = getNode(root, "admin", "realm"); String value = MASTER_REALM_NAME.getDefaultValue().asString(); if (targetNode != null) value = targetNode.asText(value); ModelNode op = Util.createOperation(WRITE_ATTRIBUTE_OPERATION, addr); op.get("name").set(MASTER_REALM_NAME.getName()); op.get("value").set(value); return op; } private static ModelNode scheduledTaskInterval(JsonNode root, PathAddress addr) { JsonNode targetNode = getNode(root, "scheduled", "interval"); Long value = SCHEDULED_TASK_INTERVAL.getDefaultValue().asLong(); if (targetNode != null) value = targetNode.asLong(value); ModelNode op = Util.createOperation(WRITE_ATTRIBUTE_OPERATION, addr); op.get("name").set(SCHEDULED_TASK_INTERVAL.getName()); op.get("value").set(value); return op; } private static ModelNode providers(JsonNode root, PathAddress addr) { JsonNode targetNode = getNode(root, "providers"); ModelNode value = PROVIDERS.getDefaultValue(); if (targetNode != null && targetNode.isArray()) { value = new ModelNode(); for (JsonNode node : targetNode) { value.add(node.asText()); } } ModelNode op = Util.createOperation(WRITE_ATTRIBUTE_OPERATION, addr); op.get("name").set(PROVIDERS.getName()); op.get("value").set(value); return op; } private static ModelNode theme(JsonNode root, PathAddress addr) { JsonNode themeNode = getNode(root, "theme"); ModelNode op = Util.createAddOperation(addr); JsonNode targetNode = getNode(themeNode, "staticMaxAge"); Long lValue = STATIC_MAX_AGE.getDefaultValue().asLong(); if (targetNode != null) lValue = targetNode.asLong(lValue); op.get(STATIC_MAX_AGE.getName()).set(lValue); targetNode = getNode(themeNode, "cacheTemplates"); Boolean bValue = CACHE_TEMPLATES.getDefaultValue().asBoolean(); if (targetNode != null) bValue = targetNode.asBoolean(bValue); op.get(CACHE_TEMPLATES.getName()).set(bValue); targetNode = getNode(themeNode, "cacheThemes"); bValue = CACHE_THEMES.getDefaultValue().asBoolean(); if (targetNode != null) bValue = targetNode.asBoolean(bValue); op.get(CACHE_THEMES.getName()).set(bValue); targetNode = getNode(themeNode, "folder", "dir"); String sValue = DIR.getDefaultValue().asString(); if (targetNode != null) sValue = targetNode.asText(sValue); op.get(DIR.getName()).set(sValue); targetNode = getNode(themeNode, "welcomeTheme"); if (targetNode != null) op.get(WELCOME_THEME.getName()).set(targetNode.asText()); targetNode = getNode(themeNode, "default"); if (targetNode != null) op.get(DEFAULT.getName()).set(targetNode.asText()); targetNode = getNode(themeNode, "module", "modules"); if (targetNode != null && targetNode.isArray()) { op.get(MODULES.getName()).set(themeModules(targetNode)); } return op; } private static ModelNode themeModules(JsonNode modulesNode) { ModelNode modules = new ModelNode(); for (JsonNode node : modulesNode) { modules.add(node.asText()); } return modules; } private static Collection<ModelNode> spis(JsonNode root, PathAddress addr) { List<ModelNode> spis = new ArrayList<>(); Iterator<String> spiIterator = root.fieldNames(); while (spiIterator.hasNext()) { String spiName = spiIterator.next(); if (NON_SPI_LIST.contains(spiName)) continue; PathAddress spiAddr = addr.append("spi", spiName); spis.addAll(spi(root, spiAddr, spiName)); } return spis; } private static List<ModelNode> spi(JsonNode root, PathAddress spiAddr, String spiName) { List<ModelNode> spiAndProviders = new ArrayList<>(); ModelNode op = Util.createAddOperation(spiAddr); spiAndProviders.add(op); Iterator<String> providerIterator = root.get(spiName).fieldNames(); while (providerIterator.hasNext()) { String providerName = providerIterator.next(); if ("provider".equals(providerName)) { op.get(SpiResourceDefinition.DEFAULT_PROVIDER.getName()).set(getNode(root, spiName, "provider").asText()); } else { PathAddress providerAddr = spiAddr.append("provider", providerName); spiAndProviders.add(spiProvider(getNode(root, spiName, providerName), providerAddr)); } } return spiAndProviders; } private static ModelNode spiProvider(JsonNode providerNode, PathAddress providerAddr) { ModelNode op = Util.createAddOperation(providerAddr); ModelNode properties = new ModelNode(); Iterator<String> propNames = providerNode.fieldNames(); while (propNames.hasNext()) { String propName = propNames.next(); if ("enabled".equals(propName)) { op.get(ProviderResourceDefinition.ENABLED.getName()).set(providerNode.get(propName).asBoolean()); } else { if (providerNode.get(propName).isArray()) { properties.get(propName).set(makeArrayText(providerNode.get(propName))); } else { properties.get(propName).set(providerNode.get(propName).asText()); } } } if (properties.isDefined() && !properties.asPropertyList().isEmpty()) { op.get("properties").set(properties); } if (!op.hasDefined(ProviderResourceDefinition.ENABLED.getName())) { op.get(ProviderResourceDefinition.ENABLED.getName()).set(ProviderResourceDefinition.ENABLED.getDefaultValue()); } return op; } private static String makeArrayText(JsonNode arrayNode) { StringBuilder builder = new StringBuilder("["); Iterator<JsonNode> nodes = arrayNode.iterator(); while (nodes.hasNext()) { JsonNode node = nodes.next(); builder.append("\""); builder.append(node.asText()); builder.append("\""); if (nodes.hasNext()) builder.append(","); } builder.append("]"); return builder.toString(); } private static JsonNode getNode(JsonNode root, String... path) { if (root == null) { return null; } JsonNode n = root; for (String p : path) { n = n.get(p); if (n == null) { return null; } } return n; } }