org.glite.ce.cream.delegationmanagement.cmdexecutor.DelegationExecutor.java Source code

Java tutorial

Introduction

Here is the source code for org.glite.ce.cream.delegationmanagement.cmdexecutor.DelegationExecutor.java

Source

/*
 * Copyright (c) Members of the EGEE Collaboration. 2004. 
 * See http://www.eu-egee.org/partners/ for details on the copyright
 * holders.  
 *
 * 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.
 */

/*
 * 
 * Authors: Luigi Zangrando <zangrando@pd.infn.it>
 *
 */

package org.glite.ce.cream.delegationmanagement.cmdexecutor;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.io.StringWriter;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.text.FieldPosition;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Random;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;

import javax.sql.DataSource;

import org.apache.log4j.Logger;
import org.bouncycastle.jce.PKCS10CertificationRequest;
import org.bouncycastle.openssl.PEMReader;
import org.bouncycastle.openssl.PEMWriter;
import org.bouncycastle.util.encoders.Hex;
import org.glite.ce.commonj.authz.VOMSResultCollector;
import org.glite.ce.commonj.authz.axis2.AuthorizationModule;
import org.glite.ce.commonj.db.DatasourceManager;
import org.glite.ce.commonj.utils.CEUtils;
import org.glite.ce.cream.cmdmanagement.CommandManager;
import org.glite.ce.cream.configuration.ServiceConfig;
import org.glite.ce.cream.delegationmanagement.DelegationManager;
import org.glite.ce.cream.delegationmanagement.DelegationPurger;
import org.glite.ce.creamapi.cmdmanagement.AbstractCommandExecutor;
import org.glite.ce.creamapi.cmdmanagement.Command;
import org.glite.ce.creamapi.cmdmanagement.CommandException;
import org.glite.ce.creamapi.cmdmanagement.CommandExecutorException;
import org.glite.ce.creamapi.delegationmanagement.Delegation;
import org.glite.ce.creamapi.delegationmanagement.DelegationCommand;
import org.glite.ce.creamapi.delegationmanagement.DelegationManagerInterface;
import org.glite.ce.creamapi.delegationmanagement.DelegationRequest;
import org.glite.ce.creamapi.jobmanagement.db.DBInfoManager;
import org.italiangrid.voms.VOMSAttribute;
import org.italiangrid.voms.VOMSValidators;
import org.italiangrid.voms.ac.VOMSACValidator;
import org.italiangrid.voms.util.CredentialsUtils;

import eu.emi.security.authn.x509.ValidationError;
import eu.emi.security.authn.x509.ValidationResult;
import eu.emi.security.authn.x509.helpers.proxy.ProxyHelper;
import eu.emi.security.authn.x509.impl.CertificateUtils;
import eu.emi.security.authn.x509.proxy.ProxyCSR;
import eu.emi.security.authn.x509.proxy.ProxyCSRGenerator;
import eu.emi.security.authn.x509.proxy.ProxyCertificate;
import eu.emi.security.authn.x509.proxy.ProxyCertificateOptions;
import eu.emi.security.authn.x509.proxy.ProxyChainInfo;
import eu.emi.security.authn.x509.proxy.ProxyChainType;
import eu.emi.security.authn.x509.proxy.ProxyGenerator;
import eu.emi.security.authn.x509.proxy.ProxyType;
import eu.emi.security.authn.x509.proxy.ProxyUtils;

public class DelegationExecutor extends AbstractCommandExecutor {
    private static final Logger logger = Logger.getLogger(DelegationExecutor.class.getName());

    private static final Random delegationIdGenerator = new Random();

    private static String delegationSuffix = null;

    private static MessageDigest sDigester = null;

    private boolean initialized = false;

    /** Key size being used. */
    private int keySize = 2048;

    public static final String DELEGATION_PURGE_RATE = "DELEGATION_PURGE_RATE";

    public static final String CREAM_SANDBOX_DIR = "CREAM_SANDBOX_DIR";

    public static final String CREAM_COPY_PROXY_TO_SANDBOX_BIN_PATH = "CREAM_COPY_PROXY_TO_SANDBOX_BIN_PATH";

    public static final String CREAM_PURGE_PROXY_FROM_SANDBOX_BIN_PATH = "CREAM_PURGE_PROXY_FROM_SANDBOX_BIN_PATH";

    public DelegationExecutor() throws CommandExecutorException {
        super("DelegationExecutor", DelegationCommand.DELEGATION_MANAGEMENT);

        final List<String> commands = new ArrayList<String>(9);
        commands.add(DelegationCommand.DESTROY_DELEGATION);
        commands.add(DelegationCommand.GET_DATABASE_VERSION);
        commands.add(DelegationCommand.GET_DELEGATION);
        commands.add(DelegationCommand.GET_NEW_DELEGATION_REQUEST);
        commands.add(DelegationCommand.GET_DELEGATION_REQUEST);
        commands.add(DelegationCommand.GET_SERVICE_MEDATADA);
        commands.add(DelegationCommand.GET_TERMINATION_TIME);
        commands.add(DelegationCommand.PUT_DELEGATION);
        commands.add(DelegationCommand.RENEW_DELEGATION_REQUEST);

        setCommands(commands);

        addParameter(CREAM_SANDBOX_DIR, "/var/cream_sandbox");
        addParameter(CREAM_COPY_PROXY_TO_SANDBOX_BIN_PATH, "/usr/bin/glite-cream-copyProxyToSandboxDir.sh");
        addParameter(CREAM_PURGE_PROXY_FROM_SANDBOX_BIN_PATH, "/usr/bin/glite-ce-cream-purge-proxy");
        addParameter(DELEGATION_PURGE_RATE, "720");

        dataSourceName = DelegationManagerInterface.DELEGATION_DATASOURCE_NAME;
    }

    private String createAndStoreCertificateRequest(X509Certificate parentCert, String delegationId, String dn,
            String localUser, List<VOMSAttribute> vomsAttributes) throws CommandException {
        logger.debug("BEGIN createAndStoreCertificateRequest");

        ProxyCertificateOptions prOpts = new ProxyCertificateOptions(new X509Certificate[] { parentCert });
        prOpts.setKeyLength(keySize);

        ProxyCSR pCSRContainer = null;
        String privateKey = null;
        PublicKey publicKey = null;
        String certificateRequest = null;
        try {

            pCSRContainer = ProxyCSRGenerator.generate(prOpts);
            PKCS10CertificationRequest pRequest = pCSRContainer.getCSR();
            publicKey = pRequest.getPublicKey();

            ByteArrayOutputStream outStream = new ByteArrayOutputStream();
            CertificateUtils.savePrivateKey(outStream, pCSRContainer.getPrivateKey(), CertificateUtils.Encoding.PEM,
                    null, null);
            outStream.close();
            privateKey = outStream.toString();

            StringWriter strWriter = new StringWriter();
            PEMWriter pemWriter = new PEMWriter(strWriter);
            pemWriter.writeObject(pRequest);
            pemWriter.close();
            certificateRequest = strWriter.toString();

            logger.debug("Public key is: " + publicKey.toString());
            logger.debug("Private key is: " + privateKey);
            logger.debug("Certificate request is: " + certificateRequest);

        } catch (Exception ex) {
            throw new CommandException("Error while generating the certificate request [delegId=" + delegationId
                    + "; dn=" + dn + "; localUser=" + localUser + "]: " + ex.getMessage());
        }

        String reqId = null;
        try {
            reqId = delegationId + '+' + this.generateSessionID(publicKey);
            logger.debug("DelegationRequestId (delegationId + sessionId): " + reqId);
        } catch (GeneralSecurityException e) {
            throw new CommandException("Error while generating the sessionId [delegId=" + delegationId + "; dn="
                    + dn + "; localUser=" + localUser + "]: " + e.getMessage());
        }

        ArrayList<String> vomsAttributeList = new ArrayList<String>(0);

        for (VOMSAttribute vomsAttr : vomsAttributes) {
            vomsAttributeList.addAll(vomsAttr.getFQANs());
        }

        DelegationRequest delegationRequest = null;
        try {
            boolean found = true;
            // Search for an existing entry in storage for this delegation ID
            delegationRequest = DelegationManager.getInstance().getDelegationRequest(reqId, dn, localUser);

            if (delegationRequest == null) {
                delegationRequest = new DelegationRequest(reqId);
                found = false;
            }

            delegationRequest.setDN(dn);
            delegationRequest.setVOMSAttributes(vomsAttributeList);
            delegationRequest.setCertificateRequest(certificateRequest);
            delegationRequest.setPublicKey(publicKey.toString());
            delegationRequest.setPrivateKey(privateKey);
            delegationRequest.setLocalUser(localUser);

            if (found) {
                DelegationManager.getInstance().update(delegationRequest);
            } else {
                DelegationManager.getInstance().insert(delegationRequest);
            }
        } catch (Exception e) {
            throw new CommandException("Failure on storage interaction [delegId=" + delegationId + "; dn=" + dn
                    + "; localUser=" + localUser + "]: " + e.getMessage());
        }

        logger.debug("END createAndStoreCertificateRequest");
        return certificateRequest;
    }

    public void destroy() {
        logger.info("destroy invoked!");

        super.destroy();

        try {
            DelegationManager.getInstance().terminate();
        } catch (Throwable t) {
            logger.error("cannot get instance of DelegationManager: " + t.getMessage());
        }

        logger.info("destroyed!");
    }

    /**
     * @see org.glite.security.delegation.service.Delegation#destroy(java.lang.String)
     */
    private void destroyDelegation(Command command) throws CommandException {
        logger.debug("BEGIN destroy");

        Delegation delegation = getDelegation(command);

        if (delegation == null) {
            throw new CommandException("delegation [delegId="
                    + getParameterValueAsString(command, DelegationCommand.DELEGATION_ID) + "; dn="
                    + getParameterValueAsString(command, DelegationCommand.USER_DN_RFC2253) + "; localUser="
                    + getParameterValueAsString(command, DelegationCommand.LOCAL_USER) + "] not found!");
        }

        // String delegationId = delegation.getId();
        // String dn = delegation.getDN();
        // String userId = delegation.getUserId();
        String localUser = delegation.getLocalUser();
        // String localUserGroup = delegation.getLocalUserGroup();

        logger.debug("removing the delegation from sandbox " + delegation.toString());

        Process proc = null;
        String[] cmd = new String[] { "sudo", "-S", "-n", "-u", localUser,
                getParameterValueAsString(CREAM_PURGE_PROXY_FROM_SANDBOX_BIN_PATH), delegation.getFullPath() };

        try {
            proc = Runtime.getRuntime().exec(cmd);

            logger.debug("removed the delegation from sandbox " + delegation.toString());
        } catch (Throwable e) {
            if (proc != null) {
                proc.destroy();
            }
        } finally {
            if (proc != null) {
                try {
                    proc.waitFor();
                } catch (InterruptedException ioe) {
                    throw new CommandException(ioe.getMessage());
                }

                StringBuffer errorMessage = null;

                if (proc.exitValue() != 0) {
                    BufferedReader readErr = new BufferedReader(new InputStreamReader(proc.getErrorStream()));

                    errorMessage = new StringBuffer();
                    String inputLine = null;

                    try {
                        while ((inputLine = readErr.readLine()) != null) {
                            errorMessage.append(inputLine);
                        }
                    } catch (IOException ioe) {
                        logger.error(ioe.getMessage());
                    } finally {
                        try {
                            readErr.close();
                        } catch (IOException e) {
                        }
                    }

                    if (errorMessage.length() > 0) {
                        errorMessage.append("\n");
                    }
                }

                try {
                    proc.getInputStream().close();
                } catch (IOException ioe) {
                }

                try {
                    proc.getErrorStream().close();
                } catch (IOException ioe) {
                }

                try {
                    proc.getOutputStream().close();
                } catch (IOException ioe) {
                }

                if (errorMessage != null && errorMessage.length() > 0) {
                    logger.warn("failure on removing the delegation " + delegation.toString() + " from sandbox: "
                            + errorMessage.toString());
                    // throw new CommandException(errorMessage.toString());
                }
            }
        }

        try {
            DelegationManager.getInstance().delete(delegation);
        } catch (Throwable t) {
            throw new CommandException(
                    "Failure on deleting the delegation " + delegation.toString() + ": " + t.getMessage());
        }

        try {
            logger.info("cancelling all related active jobs");

            Command jobCancelCmd = new Command("JOB_CANCEL", "JOB_MANAGEMENT");
            jobCancelCmd.setCommandGroupId("COMPOUND");
            jobCancelCmd.setAsynchronous(true);
            jobCancelCmd.setUserId(delegation.getUserId());
            jobCancelCmd.setDescription("job cancelled because the related delegation has expired");
            jobCancelCmd.addParameter("USER_DN", delegation.getDN());
            jobCancelCmd.addParameter("USER_FQAN", delegation.getFQAN());
            jobCancelCmd.addParameter("LOCAL_USER", delegation.getLocalUser());
            jobCancelCmd.addParameter("LOCAL_USER_GROUP", delegation.getLocalUserGroup());
            jobCancelCmd.addParameter("DELEGATION_PROXY_ID", delegation.getId());

            CommandManager.getInstance().execute(jobCancelCmd);

            logger.info("cancelled all related active jobs");
        } catch (Throwable t) {
            logger.error("failure on invoking the jobCancel: " + t.getMessage());
        }

        logger.debug("END destroy");
    }

    public void execute(Command command) throws CommandExecutorException, CommandException {
        logger.debug("BEGIN execute");

        if (!initialized) {
            throw new CommandExecutorException(getName() + " not initialized!");
        }

        if (command == null) {
            throw new IllegalArgumentException("command not defined!");
        }

        if (!command.getCategory().equalsIgnoreCase(getCategory())) {
            throw new CommandException("command category mismatch: found \"" + command.getCategory()
                    + "\" required \"" + getCategory() + "\"");
        }

        if (command.containsParameterKey(DelegationCommand.USER_DN_RFC2253)) {
            command.addParameter(DelegationCommand.USER_DN_RFC2253,
                    normalize(command.getParameterAsString(DelegationCommand.USER_DN_RFC2253)));
        }

        try {
            if (command.getName().equalsIgnoreCase(DelegationCommand.DESTROY_DELEGATION)) {
                destroyDelegation(command);

            } else if (command.getName().equalsIgnoreCase(DelegationCommand.PUT_DELEGATION)) {
                putDelegation(command);

            } else if (command.getName().equalsIgnoreCase(DelegationCommand.GET_DELEGATION)) {
                Delegation delegation = getDelegation(command, true);
                if (delegation == null) {
                    throw new CommandException("delegation [delegId="
                            + getParameterValueAsString(command, DelegationCommand.DELEGATION_ID) + "; dn="
                            + getParameterValueAsString(command, DelegationCommand.USER_DN_RFC2253) + "; localUser="
                            + getParameterValueAsString(command, DelegationCommand.LOCAL_USER) + "] not found!");
                }
            } else if (command.getName().equalsIgnoreCase(DelegationCommand.GET_DELEGATION_REQUEST)) {
                getDelegationRequest(command);

            } else if (command.getName().equalsIgnoreCase(DelegationCommand.GET_NEW_DELEGATION_REQUEST)) {
                getNewDelegationRequest(command);

            } else if (command.getName().equalsIgnoreCase(DelegationCommand.RENEW_DELEGATION_REQUEST)) {
                renewDelegationRequest(command);

            } else if (command.getName().equalsIgnoreCase(DelegationCommand.GET_TERMINATION_TIME)) {
                getTerminationTime(command);

            } else if (command.getName().equalsIgnoreCase(DelegationCommand.GET_DATABASE_VERSION)) {
                getDatabaseVersion(command);

            } else if (command.getName().equalsIgnoreCase(DelegationCommand.GET_SERVICE_MEDATADA)) {

            } else {
                logger.error("command \"" + command.getName() + "\" not found!");
                throw new CommandExecutorException("command \"" + command.getName() + "\" not found!");
            }
        } catch (CommandException ex) {
            logger.error("Execution of the command \"" + command.getName() + "\" failed: " + ex.getMessage());
            throw ex;
        }

        logger.debug("END execute");
    }

    public void execute(List<Command> commandList) throws CommandExecutorException, CommandException {
        if (commandList == null) {
            return;
        }

        for (Command command : commandList) {
            execute(command);
        }
    }

    private String getDatabaseVersion(Command command) throws CommandException {
        logger.debug("BEGIN getDatabaseVersion");

        String version = "N/A";

        try {
            version = DBInfoManager.getDBVersion(dataSourceName);
        } catch (Exception e) {
            throw new CommandException("Failure on storage interaction: " + e.getMessage());
        }

        command.getResult().addParameter("DATABASE_VERSION", version);
        logger.debug("END getDatabaseVersion");

        return version;
    }

    private Delegation getDelegation(Command command) throws CommandException {
        return getDelegation(command, false);
    }

    private Delegation getDelegation(Command command, boolean includeCertificate) throws CommandException {
        logger.debug("BEGIN getDelegation");

        String delegationId = getParameterValueAsString(command, DelegationCommand.DELEGATION_ID);
        String userDN = getParameterValueAsString(command, DelegationCommand.USER_DN_RFC2253);
        String localUser = getParameterValueAsString(command, DelegationCommand.LOCAL_USER);

        Delegation delegation = null;

        try {
            // Search for an existing entry in storage for this delegation ID
            delegation = DelegationManager.getInstance().getDelegation(delegationId, userDN, localUser,
                    includeCertificate);
        } catch (Exception e) {
            throw new CommandException("Failure on storage interaction [delegId=" + delegationId + "; dn=" + userDN
                    + "; localUser=" + localUser + "]: " + e.getMessage());
        }

        if (delegation != null) {
            delegation.setFileName(makeDelegationFileName(delegationId));
            delegation.setPath(makeDelegationPath(delegation));
            command.getResult().addParameter(DelegationCommand.DELEGATION, delegation);
        }

        logger.debug("END getDelegation");

        return delegation;
    }

    private void getNewDelegationRequest(Command command) throws CommandException {
        logger.debug("BEGIN getNewDelegationRequest");

        String userDN = getParameterValueAsString(command, DelegationCommand.USER_DN_RFC2253);
        String originalString = userDN;
        @SuppressWarnings("unchecked")
        List<VOMSAttribute> vomsAttributes = (List<VOMSAttribute>) getParameterValue(command,
                DelegationCommand.VOMS_ATTRIBUTES);

        // Generate a delegation id from the client DN and VOMS attributes
        for (VOMSAttribute vomsAttr : vomsAttributes) {
            for (String attr : vomsAttr.getFQANs()) {
                originalString += attr;
            }
        }

        byte[] hashDigest = null;

        synchronized (sDigester) {
            hashDigest = sDigester.digest(originalString.getBytes());
        }

        byte[] resultDigest = new byte[20];
        // Returns 'n' most significant bytes of byte array
        for (int i = 0; i < 20; ++i) {
            resultDigest[i] = hashDigest[i];
        }

        GregorianCalendar now = new GregorianCalendar();

        command.addParameter(DelegationCommand.DELEGATION_ID,
                new String(Hex.encode(resultDigest)) + now.getTimeInMillis());

        getDelegationRequest(command);

        logger.debug("END getNewDelegationRequest");
    }

    private Object getParameterValue(Command command, String key) throws CommandException {
        if (command == null) {
            throw new CommandException("command not specified!");
        }

        if (key == null) {
            throw new CommandException("paramenter key not specified!");
        }

        Object value = command.getParameter(key);

        // Check for a null Delegation
        if (value == null) {
            throw new CommandException("parameter \"" + key + "\" not specified!");
        }

        return value;
    }

    private String getParameterValueAsString(Command command, String key) throws CommandException {
        if (command == null) {
            throw new CommandException("command not specified!");
        }

        if (key == null) {
            throw new CommandException("paramenter key not specified!");
        }

        Object value = command.getParameter(key);

        // Check for a null Delegation
        if (value == null) {
            throw new CommandException("parameter \"" + key + "\" not specified!");
        }

        if (!(value instanceof String)) {
            throw new CommandException(
                    "the value of the parameter \"" + key + "\" is not an instance of the String type!");
        }

        return (String) value;
    }

    private String getDelegationRequest(Command command) throws CommandException {
        logger.debug("BEGIN getDelegationRequest");

        Delegation delegation = null;
        String delegationId = null;

        if (command.containsParameterKey(DelegationCommand.DELEGATION_ID)) {
            delegationId = command.getParameterAsString(DelegationCommand.DELEGATION_ID);
            delegation = getDelegation(command);

            // Throw error in case there was already a credential with the given
            // id
            if (delegation != null) {
                throw new CommandException("delegation [delegId=" + delegation.getId() + "; dn="
                        + delegation.getDN() + "; localUser=" + delegation.getLocalUser()
                        + "] already exists! please invoke renewDelegationReq()");
            }
        } else {
            delegationId = "";

            synchronized (delegationIdGenerator) {
                delegationId += delegationIdGenerator.nextDouble();
                delegationId = delegationId.substring(2);
                // delegationId = delegationId.substring(delegationId.length() -
                // 15);
            }
        }

        String userDN = getParameterValueAsString(command, DelegationCommand.USER_DN_RFC2253);
        String localUser = getParameterValueAsString(command, DelegationCommand.LOCAL_USER);
        X509Certificate userCertificate = (X509Certificate) getParameterValue(command,
                DelegationCommand.USER_CERTIFICATE);
        @SuppressWarnings("unchecked")
        List<VOMSAttribute> vomsAttributes = (List<VOMSAttribute>) getParameterValue(command,
                DelegationCommand.VOMS_ATTRIBUTES);
        String certificateRequest = createAndStoreCertificateRequest(userCertificate, delegationId, userDN,
                localUser, vomsAttributes);

        command.getResult().addParameter(DelegationCommand.CERTIFICATE_REQUEST, certificateRequest);
        command.getResult().addParameter(DelegationCommand.DELEGATION_ID, delegationId);

        logger.debug("END getDelegationRequest");

        return certificateRequest;
    }

    private Calendar getTerminationTime(Command command) throws CommandException {
        logger.debug("BEGIN getTerminationTime");

        Delegation delegation = getDelegation(command);

        if (delegation == null) {
            throw new CommandException("delegation [delegId="
                    + getParameterValueAsString(command, DelegationCommand.DELEGATION_ID) + "; dn="
                    + getParameterValueAsString(command, DelegationCommand.USER_DN_RFC2253) + "; localUser="
                    + getParameterValueAsString(command, DelegationCommand.LOCAL_USER) + "] not found!");
        }

        // Build a calendar object with the proper time
        Calendar time = Calendar.getInstance();
        time.setTime(delegation.getExpirationTime());

        command.getResult().addParameter(DelegationCommand.TERMINATION_TIME, time);

        logger.debug("END getTerminationTime");

        return time;
    }

    public void initExecutor() throws CommandExecutorException {
        logger.debug("BEGIN initExecutor");

        if (!initialized) {
            logger.info("initalizing the " + getName() + " executor...");

            ServiceConfig serviceConfig = ServiceConfig.getConfiguration();
            if (serviceConfig == null) {
                throw new CommandExecutorException("Configuration error: cannot initialize the ServiceConfig");
            }

            HashMap<String, DataSource> dataSources = serviceConfig.getDataSources();
            if (dataSources == null) {
                throw new CommandExecutorException("Datasource is empty!");
            }

            if (dataSources.containsKey(dataSourceName)) {
                if (DatasourceManager.addDataSource(dataSourceName, dataSources.get(dataSourceName))) {
                    logger.info("new dataSource \"" + dataSourceName + "\" added to the DatasourceManager");
                } else {
                    logger.info("the dataSource \"" + dataSourceName + "\" already exist!");
                }
            } else {
                throw new CommandExecutorException("Datasource \"" + dataSourceName + "\" not found!");
            }

            try {
                DelegationManager.getInstance();
            } catch (Throwable t) {
                throw new CommandExecutorException(
                        "initialization error: cannot get instance of DelegationManager: " + t.getMessage());
            }

            try {
                sDigester = MessageDigest.getInstance("SHA-1");
            } catch (Throwable t) {
                throw new CommandExecutorException(
                        "initialization error: message digester implementation not found: " + t.getMessage());
            }

            try {
                delegationSuffix = DelegationManager.getInstance().getDelegationSuffix();
            } catch (Throwable t) {
                throw new CommandExecutorException("cannot get instance of DelegationManager: " + t.getMessage());
            }

            if (delegationSuffix == null || delegationSuffix.equals("")) {
                throw new CommandExecutorException("delegationSuffix not defined!");
            }

            logger.info("delegationSuffix = " + delegationSuffix);

            if (!containsParameterKey(CREAM_SANDBOX_DIR)) {
                throw new CommandExecutorException("parameter CREAM_SANDBOX_DIR not defined!");
            }

            if (!containsParameterKey(CREAM_COPY_PROXY_TO_SANDBOX_BIN_PATH)) {
                throw new CommandExecutorException("parameter CREAM_COPY_PROXY_TO_SANDBOX_BIN_PATH not defined!");
            }

            if (!containsParameterKey(CREAM_PURGE_PROXY_FROM_SANDBOX_BIN_PATH)) {
                throw new CommandExecutorException(
                        "parameter CREAM_PURGE_PROXY_FROM_SANDBOX_BIN_PATH not defined!");
            }

            if (containsParameterKey(DELEGATION_PURGE_RATE)) {
                int purgeRateInMinutes = 720;

                try {
                    purgeRateInMinutes = Integer.parseInt(getParameterValueAsString(DELEGATION_PURGE_RATE));
                    DelegationPurger.getInstance().setRate(purgeRateInMinutes);
                    logger.debug("found new value for DELEGATION_PURGE_RATE: " + purgeRateInMinutes + " min.");
                } catch (Throwable t) {
                    logger.warn(
                            "Configuration warning: wrong value for DELEGATION_PURGE_RATE parameter => using default "
                                    + purgeRateInMinutes);
                }
            }

            initialized = true;
            logger.info(getName() + " executor initialized!");
        }

        logger.debug("END initExecutor");
    }

    private String makeDelegationFileName(String delegationId) throws CommandException {
        if (delegationId == null) {
            throw new CommandException("delegationId not specified!");
        }

        return normalize(delegationId + "_" + delegationSuffix);
    }

    private String makeDelegationPath(Delegation delegation) throws CommandException {
        if (delegation == null) {
            throw new CommandException("delegation not specified!");
        }

        String cream_sandbox_dir = getParameterValueAsString(CREAM_SANDBOX_DIR);
        if (cream_sandbox_dir == null) {
            throw new CommandException("parameter CREAM_SANDBOX_DIR not defined!");
        }

        return cream_sandbox_dir + File.separator + delegation.getLocalUserGroup() + File.separator
                + delegation.getUserId() + "_" + delegation.getLocalUser() + File.separator + "proxy"
                + File.separator;
    }

    private String normalize(String s) {
        if (s != null) {
            return s.replaceAll("\\W", "_");
        }
        return null;
    }

    private void putDelegation(Command command) throws CommandException {

        String delegationId = getParameterValueAsString(command, DelegationCommand.DELEGATION_ID);
        String deleg = getParameterValueAsString(command, DelegationCommand.DELEGATION);
        String userDN = getParameterValueAsString(command, DelegationCommand.USER_DN_RFC2253);
        String localUser = getParameterValueAsString(command, DelegationCommand.LOCAL_USER);
        String localUserGroup = getParameterValueAsString(command, DelegationCommand.LOCAL_USER_GROUP);

        String delegInfoStr = "[delegId=" + delegationId + "; dn=" + userDN + "; localUser=" + localUser + "]";

        logger.debug("BEGIN putDelegation" + delegInfoStr);

        X509Certificate[] certChain = null;
        try {

            BufferedInputStream pemStream = new BufferedInputStream(new ByteArrayInputStream(deleg.getBytes()));
            certChain = CertificateUtils.loadCertificateChain(pemStream, CertificateUtils.Encoding.PEM);

            if (certChain.length == 0) {
                throw new IOException("Chain has size 0");
            } else {
                logger.debug("Given proxy certificate loaded successfully.");
            }

        } catch (IOException ex) {
            throw new CommandException("Failed to load certificate chain " + delegInfoStr + ": " + ex.getMessage());
        }

        ValidationResult vRes = AuthorizationModule.validator.validate(certChain);
        if (!vRes.isValid()) {

            StringBuffer buff = new StringBuffer("Proxy certificate is not valid\n");
            for (ValidationError vErr : vRes.getErrors()) {
                buff.append(vErr.getMessage()).append("\n");
            }

            String tmps = buff.toString();
            throw new CommandException("Validation failed " + delegInfoStr + ": " + tmps);

        }

        ProxyChainInfo pChainInfo = null;
        boolean isRFCproxy = false;

        try {

            pChainInfo = new ProxyChainInfo(certChain);

            isRFCproxy = pChainInfo.getProxyType().equals(ProxyChainType.RFC3820);

        } catch (CertificateException ex) {
            throw new CommandException("Proxy parsing error " + delegInfoStr + ": " + ex.getMessage());
        }

        String subjectDN = certChain[0].getIssuerX500Principal().getName();
        String issuerDN = certChain[0].getSubjectX500Principal().getName();

        if (logger.isDebugEnabled()) {
            logger.debug("subject DN: " + subjectDN);
            logger.debug("issuer DN: " + issuerDN);
            logger.debug("chain length is: " + certChain.length);
            logger.debug("last cert is:" + certChain[certChain.length - 1]);

            for (int n = 0; n < certChain.length; n++) {
                logger.debug("cert [" + n + "] is from " + certChain[n].getSubjectX500Principal().getName());
            }
        }

        if (subjectDN == null || issuerDN == null) {
            throw new CommandException(
                    "Failed to get DN (subject or issuer) out of delegation " + delegInfoStr + ": it came null");
        }

        String clientDN;
        try {
            X509Certificate eeCert = ProxyUtils.getEndUserCertificate(certChain);
            clientDN = eeCert.getSubjectX500Principal().getName();
        } catch (Exception ex) {
            throw new CommandException(
                    "No user certificate found in the delegation chain " + delegInfoStr + ": " + ex.getMessage());
        }

        if (clientDN == null) {
            throw new CommandException("Failed to get client DN " + delegInfoStr + ": it came null");
        }

        String authDN = CEUtils.getUserDN_RFC2253();
        if (!clientDN.equals(authDN)) {
            throw new CommandException("Invalid delegation issuer: '" + clientDN + "' " + authDN);
        }

        String reqId = delegationId;
        try {
            reqId = delegationId + '+' + this.generateSessionID(certChain[0].getPublicKey());
            logger.debug("reqId (delegationId + sessionId): " + reqId);
        } catch (GeneralSecurityException e) {
            throw new CommandException("Failed to generate the session ID " + delegInfoStr + ": " + e.getMessage());
        }

        DelegationRequest delegationRequest = null;
        try {
            // Search for an existing entry in storage for this delegation ID
            delegationRequest = DelegationManager.getInstance().getDelegationRequest(reqId, userDN, localUser);
        } catch (Exception e) {
            throw new CommandException("Failure on storage interaction " + delegInfoStr + ": " + e.getMessage());
        }

        // Check if the delegation request existed
        if (delegationRequest == null) {
            throw new CommandException("Delegation request not found! " + delegInfoStr);
        }
        logger.debug("Got delegation request from cache " + delegInfoStr);

        PrivateKey delegPKey = null;
        try {
            ByteArrayInputStream bIn = new ByteArrayInputStream(delegationRequest.getPrivateKey().getBytes());
            delegPKey = CertificateUtils.loadPEMPrivateKey(bIn, null);
        } catch (Exception ex) {
            logger.error(ex.getMessage(), ex);
            throw new CommandException("Cannot load delegated private key");
        }

        // the public key of the cached certificate request has to
        // match the public key of the proxy certificate, otherwise
        // this is an answer to a different request
        PublicKey publicKey = null;
        PEMReader pemReader = new PEMReader(new StringReader(delegationRequest.getCertificateRequest()));
        try {
            PKCS10CertificationRequest req = (PKCS10CertificationRequest) pemReader.readObject();
            publicKey = req.getPublicKey();
        } catch (IOException e1) {
            throw new CommandException("Could not load the original certificate request from cache " + delegInfoStr
                    + ": " + e1.getMessage());
        } catch (Throwable th) {
            throw new CommandException("cannot get the public key " + delegInfoStr + ": " + th.getMessage());
        }

        if (!publicKey.equals(certChain[0].getPublicKey())) {
            logger.error(
                    "The delegation and the original request's public key do not match [delegation public key: '"
                            + certChain[0].getPublicKey() + "'; request public key: '" + publicKey + "']");
            throw new CommandException(
                    "The delegation and the original request's public key do not match " + delegInfoStr);
        }

        SimpleDateFormat dateFormat = new SimpleDateFormat();
        dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
        FieldPosition fp = new FieldPosition(SimpleDateFormat.YEAR_FIELD);

        StringBuffer buff = new StringBuffer("[ isRFC=\"");
        buff.append(isRFCproxy);
        buff.append("\"; valid from=\"");
        dateFormat.format(certChain[0].getNotBefore(), buff, fp);
        buff.append(" (GMT)\"; valid to=\"");
        dateFormat.format(certChain[0].getNotAfter(), buff, fp);
        buff.append(" (GMT)\"; holder DN=\"").append(clientDN);
        buff.append("\"; holder AC issuer=\"").append(issuerDN);

        HashMap<String, List<String>> proxyVOAttrs = new HashMap<String, List<String>>(0);
        List<String> vomsAttrituteList = new ArrayList<String>(0);

        VOMSResultCollector collector = new VOMSResultCollector();
        VOMSACValidator validator = VOMSValidators.newValidator(AuthorizationModule.vomsStore,
                AuthorizationModule.validator, collector);
        List<VOMSAttribute> vomsList = validator.validate(certChain);

        if (collector.size() > 0) {
            StringBuffer eBuff = new StringBuffer("Cannot validate proxy ");
            eBuff.append(delegationId).append("\n").append(collector.toString());
            throw new CommandException(eBuff.toString());
        }

        for (VOMSAttribute vomsAttr : vomsList) {
            buff.append("\"; VO=\"").append(vomsAttr.getVO());
            buff.append("\"; AC issuer=\"").append(vomsAttr.getIssuer());
            buff.append("\"; VOMS attributes={ ");

            for (String attr : vomsAttr.getFQANs()) {
                buff.append(attr).append(", ");
                vomsAttrituteList.add(attr);
            }

            buff.replace(buff.length() - 2, buff.length() - 1, " }");

            proxyVOAttrs.put(vomsAttr.getVO(), vomsAttr.getFQANs());
        }

        buff.append("]");

        // Save the delegation into the storage (copying the rest from the info
        // taken from the cache)
        Delegation delegation = null;

        try {
            delegation = DelegationManager.getInstance().getDelegation(delegationId, userDN, localUser, false);
        } catch (Exception e) {
            throw new CommandException("Failure on storage interaction " + delegInfoStr + ": " + e.getMessage());
        }

        boolean found = true;

        if (delegation == null) {
            delegation = new Delegation(delegationId);
            found = false;
        }

        if (!proxyVOAttrs.values().isEmpty()) {
            List<String> fqanList = proxyVOAttrs.values().iterator().next();
            if (fqanList != null && fqanList.size() > 0) {
                delegation.setFQAN(fqanList.get(0).toString());
            }
        }

        if (!proxyVOAttrs.keySet().isEmpty()) {
            delegation.setVO(proxyVOAttrs.keySet().iterator().next());
        }

        if (!vomsAttrituteList.isEmpty()) {
            delegation.setVOMSAttributes(vomsAttrituteList);
        }

        delegation.setRFC(isRFCproxy);
        delegation.setDN(userDN);
        delegation.setCertificate(insertKeyIntoChain(delegationRequest.getPrivateKey(), deleg));
        delegation.setStartTime(certChain[0].getNotBefore());
        delegation.setExpirationTime(certChain[0].getNotAfter());
        delegation.setLastUpdateTime(Calendar.getInstance().getTime());
        delegation.setLocalUser(localUser);
        delegation.setLocalUserGroup(localUserGroup);
        delegation.setInfo(buff.toString());
        delegation.setFileName(makeDelegationFileName(delegationId));
        delegation.setPath(makeDelegationPath(delegation));

        try {
            storeLimitedDelegationProxy(delegation, pChainInfo, certChain, delegPKey);

            logger.info("New delegation created " + delegation.toString());
        } catch (CommandException e) {
            logger.error(
                    "Cannot store the limited delegation locally " + delegation.toString() + ": " + e.getMessage());
            throw e;
        }

        try {
            if (found) {
                DelegationManager.getInstance().update(delegation);
            } else {
                DelegationManager.getInstance().insert(delegation);
            }
        } catch (Throwable t) {
            logger.error(t.getMessage());
            throw new CommandException(
                    "Failure on storage interaction " + delegation.toString() + ": " + t.getMessage());
        }

        logger.debug("Delegation finished successfully.");

        // Remove the credential from storage cache
        try {
            DelegationManager.getInstance().delete(delegationRequest);
        } catch (Exception e) {
            logger.warn(
                    "Failed to remove credential from storage " + delegation.toString() + ": " + e.getMessage());
        }

        command.getResult().addParameter(DelegationCommand.DELEGATION, delegation);
        logger.debug("END putDelegation" + delegInfoStr);
    }

    private String renewDelegationRequest(Command command) throws CommandException {
        logger.debug("BEGIN renewDelegationRequest");

        Delegation delegation = getDelegation(command);

        if (delegation == null) {
            throw new CommandException("delegation [delegId="
                    + getParameterValueAsString(command, DelegationCommand.DELEGATION_ID) + "; dn="
                    + getParameterValueAsString(command, DelegationCommand.USER_DN_RFC2253) + "; localUser="
                    + getParameterValueAsString(command, DelegationCommand.LOCAL_USER) + "] not found!");
        }

        X509Certificate userCertificate = (X509Certificate) getParameterValue(command,
                DelegationCommand.USER_CERTIFICATE);
        @SuppressWarnings("unchecked")
        List<VOMSAttribute> vomsAttributes = (List<VOMSAttribute>) getParameterValue(command,
                DelegationCommand.VOMS_ATTRIBUTES);

        String certificateRequest = createAndStoreCertificateRequest(userCertificate, delegation.getId(),
                delegation.getDN(), delegation.getLocalUser(), vomsAttributes);

        command.getResult().addParameter(DelegationCommand.CERTIFICATE_REQUEST, certificateRequest);
        command.getResult().addParameter(DelegationCommand.DELEGATION_ID, delegation.getId());

        logger.debug("END renewDelegationRequest");

        return certificateRequest;
    }

    private String storeLimitedDelegationProxy(Delegation delegation, ProxyChainInfo pChainInfo,
            X509Certificate[] certChain, PrivateKey delegKey) throws CommandException {
        logger.debug("BEGIN storeLimitedDelegationProxy " + delegation.toString());

        String[] cmd = new String[] { "sudo", "-S", "-n", "-u", delegation.getLocalUser(),
                getParameterValueAsString(CREAM_COPY_PROXY_TO_SANDBOX_BIN_PATH), delegation.getFileName(),
                delegation.getPath(), delegation.isRFC() ? "1" : "0" };

        Process proc = null;
        BufferedOutputStream outStr = null;
        BufferedReader errReader = null;
        String errorMessage = null;

        try {

            int remainPathLimit = this.getRemainingPathLimit(certChain, pChainInfo);

            if (!pChainInfo.isLimited() && remainPathLimit <= 0) {
                throw new CommandException("Cannot limit proxy: wrong path limit");
            }

            ProcessBuilder procBuilder = new ProcessBuilder(cmd);
            procBuilder.redirectErrorStream(true);
            proc = procBuilder.start();
            outStr = new BufferedOutputStream(proc.getOutputStream());
            errReader = new BufferedReader(new InputStreamReader(proc.getInputStream()));

            if (pChainInfo.isLimited()) {

                logger.debug("Proxy already limited for " + delegation.getDN());
                outStr.write(delegation.getCertificate().getBytes());

            } else {
                /*
                 * See method createProxy in
                 * org.italiangrid.voms.clients.impl.DefaultVOMSProxyInitBehaviour
                 */
                logger.debug("Proxy limitation for " + delegation.getDN());

                long lifeTime = certChain[0].getNotAfter().getTime();
                lifeTime -= System.currentTimeMillis();

                ProxyCertificateOptions proxyOptions = new ProxyCertificateOptions(certChain);
                proxyOptions.setProxyPathLimit(remainPathLimit);
                proxyOptions.setLimited(true);
                proxyOptions.setLifetime(lifeTime, TimeUnit.MILLISECONDS);
                proxyOptions.setKeyLength(keySize);
                if (pChainInfo.getProxyType() == ProxyChainType.MIXED) {
                    /*
                     * TODO raise an exception
                     */
                    proxyOptions.setType(ProxyType.LEGACY);
                } else {
                    proxyOptions.setType(pChainInfo.getProxyType().toProxyType());
                }

                logger.debug("Creating proxy for " + delegation.getDN());
                ProxyCertificate proxy = ProxyGenerator.generate(proxyOptions, delegKey);
                CredentialsUtils.saveProxyCredentials(outStr, proxy.getCredential(),
                        CredentialsUtils.DEFAULT_ENCONDING);
            }

            outStr.flush();
            outStr.close();
            outStr = null;

            if (proc.waitFor() != 0) {
                logger.error("proc.exitValue() != 0");
                StringBuffer tmpbuff = new StringBuffer();
                String inputLine = null;

                try {
                    while ((inputLine = errReader.readLine()) != null) {
                        tmpbuff.append(inputLine);
                    }
                } catch (IOException ioe) {
                    logger.error(ioe.getMessage());
                }

                if (tmpbuff.length() > 0) {
                    tmpbuff.append("\n");
                }

                errorMessage = tmpbuff.toString();
            }

        } catch (IOException e) {

            logger.error("IOException caught: " + e.getMessage());
            errorMessage = e.getMessage();

        } catch (Throwable e) {

            logger.error(e.getMessage(), e);
            errorMessage = e.getMessage();

        } finally {

            if (proc != null) {
                proc.destroy();
            }

            if (outStr != null) {
                try {
                    outStr.close();
                } catch (IOException ioEx) {
                    logger.error(ioEx.getMessage(), ioEx);
                }
            }

            if (errReader != null) {
                try {
                    errReader.close();
                } catch (IOException ioEx) {
                    logger.error(ioEx.getMessage(), ioEx);
                }
            }
        }

        if (errorMessage != null && errorMessage.length() > 0) {
            throw new CommandException("Cannot store proxy " + delegation.toString() + ": " + errorMessage);
        }

        logger.debug("END storeLimitedDelegationProxy " + delegation.toString());

        return delegation.getFullPath();
    }

    /*
     * This code has been extracted from
     * org.glite.security.delegation.GrDPX509Util
     */
    private String generateSessionID(PublicKey pk) throws java.security.NoSuchAlgorithmException {

        MessageDigest digester = MessageDigest.getInstance("SHA-256");
        byte[] oldDigest = digester.digest(pk.getEncoded());
        byte[] newDigest = new byte[20];
        for (int i = 0; i < 20; ++i) {
            newDigest[i] = oldDigest[i];
        }
        return new String(Hex.encode(newDigest));
    }

    /*
     * This workaround fixes the remaining path length in case of unlimited
     * proxy chains
     */
    private int getRemainingPathLimit(X509Certificate[] certChain, ProxyChainInfo pChainInfo) throws IOException {

        int remainingLen = Integer.MAX_VALUE;
        for (int i = pChainInfo.getFirstProxyPosition(); i >= 0; i--) {
            int lenRestriction = ProxyHelper.getProxyPathLimit(certChain[i]);

            if (lenRestriction == remainingLen && remainingLen == Integer.MAX_VALUE) {
                continue;
            }

            if (lenRestriction < remainingLen)
                remainingLen = lenRestriction;
            else
                remainingLen--;
        }
        return remainingLen;
    }

    private String insertKeyIntoChain(String pKey, String certChain) throws CommandException {

        String delimit = "-----BEGIN CERTIFICATE-----";
        String lineSep = System.getProperty("line.separator", "\n");

        int idx = certChain.indexOf(delimit);
        if (idx < 0) {
            throw new CommandException("Corrupted certChain: missing certificate");
        }

        idx = certChain.indexOf(delimit, idx + 27);
        if (idx < 0) {
            if (certChain.endsWith(lineSep)) {
                return certChain + pKey;
            }
            return certChain + lineSep + pKey;
        }

        if (!pKey.endsWith(lineSep)) {
            pKey += lineSep;
        }

        return certChain.substring(0, idx) + pKey + certChain.substring(idx);
    }

}