001/*
002 * (C) Copyright 2015 Nuxeo SA (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl-2.1.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     François Maturel
016 */
017
018package org.nuxeo.ecm.platform.ui.web.keycloak;
019
020import java.io.IOException;
021import java.io.InputStream;
022import java.lang.reflect.Constructor;
023import java.lang.reflect.InvocationTargetException;
024import java.lang.reflect.Method;
025
026import org.codehaus.jackson.map.ObjectMapper;
027import org.keycloak.adapters.KeycloakDeployment;
028import org.keycloak.adapters.KeycloakDeploymentBuilder;
029import org.keycloak.representations.adapters.config.AdapterConfig;
030import org.keycloak.util.SystemPropertiesJsonParserFactory;
031
032/**
033 * This class is developed to overcome a Jackson version problem between Nuxeo and Keycloak.<br>
034 * Nuxeo uses Jackson version 1.8.x where Keycloak uses 1.9.x<br>
035 * Sadly the {@link ObjectMapper#setSerializationInclusion} method is not in 1.8.x<br>
036 * Then {@link KeycloakNuxeoDeployment} is the same class as {@link KeycloakDeploymentBuilder}, rewriting static method
037 * {@link KeycloakDeploymentBuilder#loadAdapterConfig} to avoid the use of
038 * {@link ObjectMapper#setSerializationInclusion}
039 *
040 * @since 7.4
041 */
042public class KeycloakNuxeoDeployment {
043
044    /**
045     * Invokes KeycloakDeploymentBuilder.internalBuild with reflection to avoid rewriting source code
046     *
047     * @param is the configuration file {@link InputStream}
048     * @return the {@link KeycloakDeployment} corresponding to the configuration file
049     */
050    public static KeycloakDeployment build(InputStream is) {
051        AdapterConfig adapterConfig = loadAdapterConfig(is);
052
053        try {
054            Constructor<KeycloakDeploymentBuilder> constructor = KeycloakDeploymentBuilder.class.getDeclaredConstructor();
055            constructor.setAccessible(true);
056            KeycloakDeploymentBuilder builder = constructor.newInstance();
057
058            Method method = KeycloakDeploymentBuilder.class.getDeclaredMethod("internalBuild", AdapterConfig.class);
059            method.setAccessible(true);
060            return (KeycloakDeployment) method.invoke(builder, adapterConfig);
061        } catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) {
062            throw new RuntimeException(e);
063        }
064    }
065
066    public static AdapterConfig loadAdapterConfig(InputStream is) {
067        ObjectMapper mapper = new ObjectMapper(new SystemPropertiesJsonParserFactory());
068        AdapterConfig adapterConfig;
069        try {
070            adapterConfig = mapper.readValue(is, AdapterConfig.class);
071        } catch (IOException e) {
072            throw new RuntimeException(e);
073        }
074        return adapterConfig;
075    }
076
077}