org.gvnix.addon.gva.security.providers.aplusu.AplusuSecurityProvider.java Source code

Java tutorial

Introduction

Here is the source code for org.gvnix.addon.gva.security.providers.aplusu.AplusuSecurityProvider.java

Source

/*
 * gvNIX. Spring Roo based RAD tool for Conselleria d'Infraestructures i
 * Transport - Generalitat Valenciana Copyright (C) 2010 CIT - Generalitat
 * Valenciana
 * 
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 */
package org.gvnix.addon.gva.security.providers.aplusu;

import java.io.*;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.gvnix.addon.gva.security.providers.SecurityProvider;
import org.springframework.roo.addon.security.SecurityOperations;
import org.springframework.roo.metadata.MetadataService;
import org.springframework.roo.model.JavaPackage;
import org.springframework.roo.process.manager.FileManager;
import org.springframework.roo.process.manager.MutableFile;
import org.springframework.roo.project.*;
import org.springframework.roo.support.util.FileUtils;
import org.springframework.roo.support.util.XmlUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
 * <b>APLUSU</b> Security Provider
 * 
 * @author Jose Manuel Viv ( jmvivo at disid dot com ) at <a
 *         href="http://www.disid.com">DiSiD Technologies S.L.</a> made for <a
 *         href="http://www.cit.gva.es">Conselleria d'Infraestructures i
 *         Transport</a>
 * @author Juan Carlos Garca ( jcgarcia at disid dot com ) at <a
 *         href="http://www.disid.com">DiSiD Technologies S.L.</a> made for <a
 *         href="http://www.cit.gva.es">Conselleria d'Infraestructures i
 *         Transport</a>
 */
@Component
@Service
public class AplusuSecurityProvider implements SecurityProvider {

    private static final String DESCRIPTION = "Security APLUSU Provider";

    private static final String NAME = "APLUSU";

    /**
     * Installed classes package
     */

    public String aplusuPackage = ".security.authentication.aplusu";

    static final Integer DEFAUL_SESSION_TIMEOUT = 45;

    /**
     * Spring Security authentication provider to use
     */
    private static final String PROVIDER_CLASS_FILENAME = "AplusuSecurityProvider.java";

    /**
     * Classes with Spring Security integration
     */
    private static final String[] JAVA_CLASS_FILENAMES = new String[] { PROVIDER_CLASS_FILENAME,
            "AplusuSecurityProvider.java", "AplusuUserAuthority.java", "AplusuUser.java", };

    /**
     * Classes with Web-service client
     */
    private static final String[] JAVA_WS_CLASS_FILENAMES = new String[] { "ServerWSAuthBindingStub.java",
            "ServerWSAuthPort.java", "ServerWSAuthPortProxy.java", "ServerWSAuthService.java",
            "ServerWSAuthServiceLocator.java" };

    /**
     * Classes with service data structures
     */
    private static final String[] JAVA_XSD_CLASS_FILENAMES = new String[] { "CredencialAplusu.java",
            "ModuloStruct.java", "ValidaStruct.java", "StructUtil.java" };

    /**
     * Test classes
     */
    private static final String[] JAVA_TEST_CLASS_FILENAMES = new String[] { "ServerWSAuthBindingStubTest.java",
            "AplusuSecurityProviderTest.java" };

    private static final String WSAUTH_PROPERTIES_NAME = "aplusu.properties";

    private static final Dependency AXIS_DEPENDENCY = new Dependency("axis", "axis", "1.4");

    private static final Dependency MOCK_DEPENDENCY = new Dependency("org.mockito", "mockito-all", "1.9.5",
            DependencyType.JAR, DependencyScope.TEST);

    private static Logger logger = Logger.getLogger(AplusuSecurityProvider.class.getName());

    @Reference
    private FileManager fileManager;

    @Reference
    private PathResolver pathResolver;

    @Reference
    private MetadataService metadataService;

    @Reference
    private ProjectOperations projectOperations;

    @Reference
    private SecurityOperations securityOperations;

    /**
     * Get java package of APLUSU security classes into destination project.
     * 
     * @return APLUSU security classes java package
     */
    protected String getClassesPackage() {
        return aplusuPackage;
    }

    /**
     * Get folder path of APLUSU security files into destination project.
     * 
     * @return APLUSU security files folder path
     */
    protected String getClassesPath() {

        return getClassesPackage().replace(".", File.separator);
    }

    /**
     * Get file path of APLUSU security provider file into destination project.
     * 
     * @return APLUSU security provider file path
     */
    protected String getProviderTargetClassFileName() {
        return getClassesPath() + File.separator + PROVIDER_CLASS_FILENAME;
    }

    public boolean isSetupAvailable() {
        // Si no esta configurada la seguriad pero se puede configurar
        // ya lo haremos nosotros
        if (securityOperations.isSecurityInstallationPossible()) {
            return true;
        }

        // Si no se puede configurar la seguridad comprobamos y no esta
        // configurada
        // no estara disponible el comando
        String appSecurityXMLPath = pathResolver.getIdentifier(LogicalPath.getInstance(Path.SPRING_CONFIG_ROOT, ""),
                "applicationContext-security.xml");
        if (!fileManager.exists(appSecurityXMLPath)) {
            return false;
        }

        // Esta configurada la seguridad, pero si ya esta ejecutado
        // este comando no estamos disponibles

        return !checkIsAlredyInstalled();
    }

    /**
     * Check if services is already installed on target project
     * 
     * @return
     */
    public boolean checkIsAlredyInstalled() {
        // Si no existe la ruta de nuestas clases no estan instaladas: estamos
        // disponibles
        String folderPath = pathResolver.getIdentifier(LogicalPath.getInstance(Path.SRC_MAIN_JAVA, ""),
                getClassesPath());
        if (!fileManager.exists(folderPath)) {
            return false;
        }

        // si no existe la clase provider no estamos instalados: estamos
        // disonible
        String filePath = pathResolver.getIdentifier(LogicalPath.getInstance(Path.SRC_MAIN_JAVA, ""),
                getProviderTargetClassFileName());
        return fileManager.exists(filePath);
    }

    /**
     * {@inheritDoc}
     * <p>
     * Do not permit ad reports unless they have a web project with Spring MVC
     * Tiles.
     */
    public boolean isSpringMvcTilesProject() {
        return fileManager.exists(getSpringMvcConfigFile()) && fileManager.exists(getTilesLayoutsFile());
    }

    /**
     * Get the absolute path for {@code webmvc-config.xml}.
     * 
     * @return the absolute path to file (never null)
     */
    private String getSpringMvcConfigFile() {
        // resolve path for webmvc-config.xml if it hasn't been resolved yet
        return projectOperations.getPathResolver().getIdentifier(LogicalPath.getInstance(Path.SRC_MAIN_WEBAPP, ""),
                "WEB-INF/spring/webmvc-config.xml");
    }

    /**
     * Get the absolute path for {@code layouts.xml}.
     * <p>
     * Note that this file is required for any Tiles project.
     * 
     * @return the absolute path to file (never null)
     */
    private String getTilesLayoutsFile() {
        // resolve absolute path for layouts.xml if it hasn't been resolved yet
        return projectOperations.getPathResolver().getIdentifier(LogicalPath.getInstance(Path.SRC_MAIN_WEBAPP, ""),
                "/WEB-INF/layouts/layouts.xml");
    }

    public void setup(String url, String login, String password, String appName) {
        // Si no esta configurada la seguriad pero se puede configurar
        // ya lo haremos nosotros
        if (securityOperations.isSecurityInstallationPossible()) {
            securityOperations.installSecurity();
            // Forced write of web.xml modified by previous command because we
            // need to modify it too in following lines
            fileManager.commit();

            /*
             * TODO: Hay un bug en ROO-1.1.2-RELEASE que cuando crea login.jspx
             * el path del action del formulario apunta a
             * /resources/j_spring_security_check/ en lugar de a
             * /static/j_spring_security_check. De momento modificamos
             * login.jspx a mano. Redmine #4886. Relacionado con
             * https://jira.springsource.org/browse/ROO-2173
             */
        }

        // Copiamos las clases necesarias para el servicio
        copyJavaFiles();

        // Copiar los archivos de configuracion que necesitamos
        copyConfigFiles(url, login, password, appName);

        // Actualizamos la configuracion de seguriada
        updateSecurityConfig();

        // Aadimos dependencias
        addDependencies();

    }

    /**
     * Add all dependencies needed on target project (if they aren't installed
     * yet)
     */
    private void addDependencies() {
        if (!isInstalled(AXIS_DEPENDENCY)) {
            projectOperations.addDependency(projectOperations.getFocusedModuleName(), AXIS_DEPENDENCY);
        }
        if (!isInstalled(MOCK_DEPENDENCY)) {
            projectOperations.addDependency(projectOperations.getFocusedModuleName(), MOCK_DEPENDENCY);
        }
    }

    /**
     * Check if a dependency is already installed on tarject project
     * 
     * @param dependency
     * @return
     */
    private boolean isInstalled(Dependency dependency) {
        Set<Dependency> dependencies = projectOperations.getFocusedModule()
                .getDependenciesExcludingVersion(dependency);

        if (dependencies.isEmpty()) {
            return false;
        }
        Validate.isTrue(dependencies.size() == 1, "Library dependecy: ".concat(dependency.toString()));
        Dependency current = dependencies.iterator().next();
        String[] currentVersion = current.getVersion().split("[.]");
        if (currentVersion.length > 1) {
            String[] requiredVersion = dependency.getVersion().split("[.]");
            int cur, req;
            for (int i = 0; i < currentVersion.length && i < requiredVersion.length; i++) {
                try {
                    cur = Integer.parseInt(currentVersion[i]);
                    req = Integer.parseInt(requiredVersion[i]);
                } catch (NumberFormatException e) {
                    return false;
                }
                if (cur > req) {
                    // La dependencia actual una versin ms nueva:
                    // no cambiamos
                    return true;
                }
            }
            // la version es inferior instalamos
            return false;
        } else {
            // no sabemos la versin asumimos que es buena
            return true;
        }

    }

    /**
     * Copia los archivos de configuracin prpios de nuestra herramienta
     * 
     * @param url
     * @param login
     * @param password
     * @param appName
     */
    private void copyConfigFiles(String url, String login, String password, String appName) {

        String properties = pathResolver.getIdentifier(LogicalPath.getInstance(Path.SPRING_CONFIG_ROOT, ""),
                WSAUTH_PROPERTIES_NAME);
        if (fileManager.exists(properties)) {
            return;
        }
        InputStream templateInputStream = null;
        try {
            templateInputStream = FileUtils.getInputStream(getClass(), WSAUTH_PROPERTIES_NAME);
            String template = IOUtils.toString(new InputStreamReader(templateInputStream));
            template = StringUtils.replace(template, "__URL__", url);
            template = StringUtils.replace(template, "__LOGIN__", login);
            template = StringUtils.replace(template, "__PASSWORD__", password);
            template = StringUtils.replace(template, "__APPNAME__", appName);

            InputStream inputStream = null;
            OutputStreamWriter outputStream = null;
            try {
                inputStream = IOUtils.toInputStream(template);
                outputStream = new OutputStreamWriter(fileManager.createFile(properties).getOutputStream());
                IOUtils.copy(inputStream, outputStream);
            } finally {
                IOUtils.closeQuietly(inputStream);
                IOUtils.closeQuietly(outputStream);
            }

        } catch (IOException e) {
            throw new IllegalStateException("Unable to create '" + WSAUTH_PROPERTIES_NAME + "'", e);
        } finally {
            if (templateInputStream != null) {
                try {
                    templateInputStream.close();
                } catch (IOException e) {
                    logger.throwing(getClass().getName(), "copyConfigFiles", e);
                }
            }
        }

    }

    private void updateSecurityConfig() {

        String secTemplate = "applicationContext-security-template.xml";
        String secXmlPath = pathResolver.getIdentifier(LogicalPath.getInstance(Path.SPRING_CONFIG_ROOT, ""),
                "applicationContext-security.xml");

        Document secXmlDoc;
        MutableFile mutableFile;
        if (fileManager.exists(secXmlPath)) {
            // File exists, update file
            mutableFile = fileManager.updateFile(secXmlPath);
        } else {
            // Create file
            mutableFile = fileManager.createFile(secXmlPath);
        }

        InputStream templateInputStream = FileUtils.getInputStream(getClass(), secTemplate);
        try {
            secXmlDoc = XmlUtils.getDocumentBuilder().parse(templateInputStream);

            Element root = secXmlDoc.getDocumentElement();
            Element bean = XmlUtils.findFirstElement("/beans/bean[@id='aplusuSecurityProvider']", root);
            String clazz = bean.getAttribute("class");
            bean.setAttribute("class", clazz.replace("__TARGET_PACKAGE__", getClassesPackage()));

            bean = XmlUtils.findFirstElement("/beans/bean[@id='serverWSAuthPortProxy']", root);
            clazz = bean.getAttribute("class");
            bean.setAttribute("class", clazz.replace("__TARGET_PACKAGE__", getClassesPackage()));

        } catch (Exception ex) {
            throw new IllegalStateException(ex);
        }

        // Write file
        XmlUtils.writeXml(mutableFile.getOutputStream(), secXmlDoc);

        updateSessionTimeout(DEFAUL_SESSION_TIMEOUT);

        fileManager.scan();
    }

    /**
     * Set session-timeout with value given as parameter
     * 
     * @param sessionTimeout
     */
    private void updateSessionTimeout(Integer sessionTimeout) {
        Validate.notNull(sessionTimeout, "Session timeout must not be null");

        String webXmlPath = pathResolver.getIdentifier(LogicalPath.getInstance(Path.SRC_MAIN_WEBAPP, ""),
                "WEB-INF/web.xml");

        Document webXmlDoc;
        MutableFile mutableFile;
        try {
            if (fileManager.exists(webXmlPath)) {
                // File exists, update file
                mutableFile = fileManager.updateFile(webXmlPath);
                webXmlDoc = XmlUtils.getDocumentBuilder().parse(mutableFile.getInputStream());
            } else {
                throw new IllegalStateException("Could not acquire ".concat(webXmlPath));
            }
            Element root = webXmlDoc.getDocumentElement();
            Element sessionTimeoutElement = XmlUtils.findFirstElement("/web-app/session-config/session-timeout",
                    root);
            sessionTimeoutElement.setTextContent(String.valueOf(sessionTimeout));
        } catch (Exception e) {
            throw new IllegalStateException(e);
        }

        // Write file
        XmlUtils.writeXml(mutableFile.getOutputStream(), webXmlDoc);

    }

    private void copyJavaFiles() {

        String prjId = ProjectMetadata.getProjectIdentifier(projectOperations.getFocusedModuleName());
        ProjectMetadata projectMetadata = (ProjectMetadata) metadataService.get(prjId);

        // Copiamos los ficheros del cliente del servicio WSAuth
        for (String className : JAVA_WS_CLASS_FILENAMES) {
            installTemplate("java-src-templates", className, getClassesPackage(), projectMetadata, null, false,
                    false);
        }

        // Copiamos los ficheros del Provider, usuarios y el cliente del
        // servicio WSAuth
        for (String className : JAVA_CLASS_FILENAMES) {
            installTemplate("java-src-templates", className, getClassesPackage(), projectMetadata, null, false,
                    false);
        }

        // Copiamos los ficheros de los xsd del servicio WSAuth
        for (String className : JAVA_XSD_CLASS_FILENAMES) {
            installTemplate("java-src-templates", className, getClassesPackage(), projectMetadata, null, false,
                    false);
        }

        // Copiamos los ficheros de los test
        for (String className : JAVA_TEST_CLASS_FILENAMES) {
            installTemplate("java-src-templates", className, getClassesPackage(), projectMetadata, null, false,
                    true);
        }
    }

    /***
     * <p>
     * Mtodo de utilida que genera un fichero <code>targetFilename</code>
     * basado en un <i>template</i>.
     * </p>
     * <p>
     * Los <i>template</i> se buscan en el paquete de la clase actual compuestos
     * por <code>targetFilename</code> con la extensin terminada en
     * <code>-template</code>.
     * </p>
     * <p>
     * Antes de escribir el fichero destino, se reemplazan las siguientes
     * cadenas:
     * </p>
     * <ul>
     * <li><code>__TOP_LEVEL_PACKAGE__</code> --> Paquete raz de la aplicacin</li>
     * <li><code>__TARGET_PACKAGE__</code> --> Paquete de destino del fichero
     * especificado en <code>targetPackage</code></li>
     * <li>Por cada elemento de <code>parameters</code>: <code>__</code>
     * <i>clave</i><code>__</code> --> <i>valor<i></li>
     * </ul>
     * 
     * @param sourceFolder path relativo a esta clase para buscar el template,
     *        si es null usa la la ruta de la clase actual
     * @param targetFilename nombre del fichero final
     * @param targetPackage paquete donde se generar el fichero (admite '~'
     *        como comodn del paquete base)
     * @param projectMetadata metadatos del proyecto
     * @param parameters valores adicionales a reemplazar (puede ser
     *        <code>null</code> si no se necesita)
     * @param override especifica si sobreescribir el archivo si ya existe
     * @param isTest especifica si la clase es de test o del fuente
     */
    private void installTemplate(String sourceFolder, String targetFilename, String targetPackage,
            ProjectMetadata projectMetadata, Map<String, String> parameters, boolean override, boolean isTest) {
        // default package
        String packagePath = projectOperations.getTopLevelPackage(projectOperations.getFocusedModuleName())
                .getFullyQualifiedPackageName().replace('.', '/');

        // setting targetPackage change default package
        String finalTargetPackage = null;
        if (targetPackage == null) {
            finalTargetPackage = getClass().getPackage().getName();
        } else {
            if (targetPackage.startsWith("~")) {
                finalTargetPackage = targetPackage.replace("~",
                        projectOperations.getTopLevelPackage(projectOperations.getFocusedModuleName())
                                .getFullyQualifiedPackageName());
            } else {
                finalTargetPackage = targetPackage;
            }
        }
        packagePath = finalTargetPackage.replace('.', '/');

        Path basePath = Path.SRC_MAIN_JAVA;
        if (isTest) {
            basePath = Path.SRC_TEST_JAVA;
        }

        String destinationFile = projectOperations.getPathResolver()
                .getIdentifier(LogicalPath.getInstance(basePath, ""), packagePath + "/" + targetFilename);

        if ((!fileManager.exists(destinationFile)) || override) {
            InputStream templateInputStream;
            if (sourceFolder == null) {
                templateInputStream = FileUtils.getInputStream(getClass(), targetFilename + "-template");
            } else {
                templateInputStream = FileUtils.getInputStream(getClass(),
                        sourceFolder + "/" + targetFilename + "-template");
            }
            try {
                // Read template and insert the user's package
                String input = IOUtils.toString(new InputStreamReader(templateInputStream));
                input = input.replace("__TOP_LEVEL_PACKAGE__", aplusuPackage);

                input = input.replace("__TARGET_PACKAGE__", aplusuPackage);

                if (parameters != null) {
                    for (Entry<String, String> entry : parameters.entrySet()) {
                        input = input.replace("__" + entry.getKey() + "__", entry.getValue());
                    }
                }

                // Output the file for the user
                MutableFile mutableFile = fileManager.createFile(destinationFile);

                InputStream inputStream = null;
                OutputStream outputStream = null;
                try {
                    inputStream = IOUtils.toInputStream(input);
                    outputStream = mutableFile.getOutputStream();
                    IOUtils.copy(inputStream, outputStream);
                } finally {
                    IOUtils.closeQuietly(inputStream);
                    IOUtils.closeQuietly(outputStream);
                }

            } catch (IOException ioe) {
                throw new IllegalStateException("Unable to create '" + targetFilename + "'", ioe);
            }
        }
    }

    @Override
    public String getName() {
        return NAME;
    }

    @Override
    public String getDescription() {
        return DESCRIPTION;
    }

    @Override
    public void install(JavaPackage targetPackage) {
        // Changing default package with the developer package
        aplusuPackage = targetPackage.getFullyQualifiedPackageName();
        setup("", "", "", "");
        // Showing next steps to do
        showNextSteps();
    }

    @Override
    public Boolean isInstalled() {
        // TODO Auto-generated method stub
        return null;
    }

    /**
     * This method shows the next steps to configure the application correctly
     * to use this provider
     * 
     */
    public void showNextSteps() {
        logger.log(Level.INFO, "");
        logger.log(Level.INFO, "");
        logger.log(Level.INFO, "*** Before execute your application you must to configure the follow"
                + " APLUSU Client Properties:");
        logger.log(Level.INFO, "--------------------------------------------------------------------");
        logger.log(Level.INFO, "    - wsauth.loggin");
        logger.log(Level.INFO, "    - wsauth.password");
        logger.log(Level.INFO, "    - wsauth.appName");
        logger.log(Level.INFO, "    - wsauth.url");
        logger.log(Level.INFO, "");
        logger.log(Level.INFO, "*** Use the configuration commands to set this parameters");
        logger.log(Level.INFO, "");
        logger.log(Level.INFO, "");
    }

}