org.italiangrid.voms.request.impl.DefaultVOMSACService.java Source code

Java tutorial

Introduction

Here is the source code for org.italiangrid.voms.request.impl.DefaultVOMSACService.java

Source

/**
 * Copyright (c) Istituto Nazionale di Fisica Nucleare, 2006-2014.
 *
 * 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.italiangrid.voms.request.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.x509.AttributeCertificate;
import org.italiangrid.voms.VOMSError;
import org.italiangrid.voms.request.VOMSACRequest;
import org.italiangrid.voms.request.VOMSACService;
import org.italiangrid.voms.request.VOMSESLookupStrategy;
import org.italiangrid.voms.request.VOMSProtocol;
import org.italiangrid.voms.request.VOMSProtocolError;
import org.italiangrid.voms.request.VOMSProtocolListener;
import org.italiangrid.voms.request.VOMSRequestListener;
import org.italiangrid.voms.request.VOMSResponse;
import org.italiangrid.voms.request.VOMSServerInfo;
import org.italiangrid.voms.request.VOMSServerInfoStore;
import org.italiangrid.voms.request.VOMSServerInfoStoreListener;
import org.italiangrid.voms.util.NullListener;

import eu.emi.security.authn.x509.X509CertChainValidatorExt;
import eu.emi.security.authn.x509.X509Credential;

/**
 * The default implementation of the {@link VOMSACService}.
 * 
 * 
 * @author Valerio Venturi
 * @author Andrea Ceccanti
 * 
 */
public class DefaultVOMSACService implements VOMSACService {

    /**
     * The listener that will be informed about request events
     */
    protected VOMSRequestListener requestListener;

    /**
     * The listener that will be informed about low-level protocol details
     */
    protected VOMSProtocolListener protocolListener;

    /**
     * The validator used for the SSL handshake
     */
    protected X509CertChainValidatorExt validator;

    /**
     * The store used to keep VOMS server contact information.
     */
    protected VOMSServerInfoStore serverInfoStore;

    /**
     * The http protocol implementation
     */
    protected VOMSProtocol httpProtocol;

    /**
     * The voms legacy protocol implementation
     */
    protected VOMSProtocol legacyProtocol;

    /**
     * Constructor which builds a {@link DefaultVOMSACService} from a
     * {@link Builder}
     * 
     * @param builder
     *          the builder object that provides the settings for this
     *          {@link VOMSACService}
     */
    protected DefaultVOMSACService(Builder builder) {

        this.validator = builder.validator;
        this.requestListener = builder.requestListener;
        this.protocolListener = builder.protocolListener;
        this.serverInfoStore = builder.serverInfoStore;
        this.httpProtocol = builder.httpProtocol;
        this.legacyProtocol = builder.legacyProtocol;
    }

    /**
     * Extracts an AC from a VOMS response
     * 
     * @param request
     *          the request
     * @param response
     *          the received response
     * @return a possibly <code>null</code> {@link AttributeCertificate} object
     */
    protected AttributeCertificate getACFromResponse(VOMSACRequest request, VOMSResponse response) {

        byte[] acBytes = response.getAC();

        if (acBytes == null)
            return null;

        ASN1InputStream asn1InputStream = new ASN1InputStream(acBytes);

        AttributeCertificate attributeCertificate = null;

        try {

            attributeCertificate = AttributeCertificate.getInstance(asn1InputStream.readObject());

            asn1InputStream.close();
            return attributeCertificate;

        } catch (Throwable e) {

            requestListener.notifyVOMSRequestFailure(request, null,
                    new VOMSError("Error unmarshalling VOMS AC. Cause: " + e.getMessage(), e));

            return null;
        }
    }

    private VOMSResponse doRequest(VOMSProtocol protocol, VOMSServerInfo endpoint, X509Credential cred,
            VOMSACRequest req) {

        VOMSResponse response = null;

        try {

            response = protocol.doRequest(endpoint, cred, req);

        } catch (VOMSProtocolError e) {
            requestListener.notifyVOMSRequestFailure(req, endpoint, e);
        }

        return response;

    }

    /**
     * Handles errors included in the VOMS response
     * 
     * @param request
     *          the request
     * @param si
     *          the VOMS server endpoint information
     * @param response
     *          the received {@link VOMSResponse}
     */
    protected void handleErrorsInResponse(VOMSACRequest request, VOMSServerInfo si, VOMSResponse response) {

        if (response.hasErrors())
            requestListener.notifyErrorsInVOMSReponse(request, si, response.errorMessages());

    }

    /**
     * Handles warnings included in the VOMS response
     * 
     * @param request
     *          the request
     * @param si
     *          the VOMS server endpoint information
     * @param response
     *          the received {@link VOMSResponse}
     */
    protected void handleWarningsInResponse(VOMSACRequest request, VOMSServerInfo si, VOMSResponse response) {

        if (response.hasWarnings())
            requestListener.notifyWarningsInVOMSResponse(request, si, response.warningMessages());
    }

    public AttributeCertificate getVOMSAttributeCertificate(X509Credential credential, VOMSACRequest request) {

        List<VOMSServerInfo> vomsServerInfos = getVOMSServerInfos(request);

        if (vomsServerInfos.isEmpty())
            throw new VOMSError("VOMS server for VO " + request.getVoName() + " "
                    + "is not known! Check your vomses configuration.");

        VOMSResponse response = null;

        AttributeCertificate vomsAC = null;

        for (VOMSServerInfo vomsServerInfo : vomsServerInfos) {

            requestListener.notifyVOMSRequestStart(request, vomsServerInfo);

            // Try HTTP request first
            response = doRequest(httpProtocol, vomsServerInfo, credential, request);

            // If failed, try legacy request
            if (response == null) {
                response = doRequest(legacyProtocol, vomsServerInfo, credential, request);
            }

            // We had failures with both requests
            if (response == null) {
                requestListener.notifyVOMSRequestFailure(request, vomsServerInfo,
                        new VOMSError("REST and legacy VOMS endpoints failed."));

                // continue to next server
                continue;
            }

            // Notify that the server was contacted successfully
            requestListener.notifyVOMSRequestSuccess(request, vomsServerInfo);

            // Notify errors
            handleErrorsInResponse(request, vomsServerInfo, response);

            // Notify warnings
            handleWarningsInResponse(request, vomsServerInfo, response);

            vomsAC = getACFromResponse(request, response);

            // Exit the loop only when succesfully get an AC
            // out of the VOMS server
            if (!response.hasErrors() && vomsAC != null) {
                return vomsAC;
            }

        }

        // if we reach this point we had failures in contacting
        // all known voms server for the VO
        requestListener.notifyVOMSRequestFailure(request, null, null);
        return null;

    }

    /**
     * Get VOMS server endpoint information that matches with the
     * {@link VOMSACRequest} passed as argument.
     * 
     * This method returns a random shuffle of the {@link VOMSServerInfo} objects
     * that match the input request.
     * 
     * @param request
     *          the request
     * @return a possibly empty {@link List} of {@link VOMSServerInfo} objects
     */
    protected List<VOMSServerInfo> getVOMSServerInfos(VOMSACRequest request) {

        List<VOMSServerInfo> vomsServerInfos = new ArrayList<VOMSServerInfo>(
                serverInfoStore.getVOMSServerInfo(request.getVoName()));

        if (!vomsServerInfos.isEmpty()) {
            Collections.shuffle(vomsServerInfos);
        }
        return vomsServerInfos;
    }

    /**
     * Creates a {@link DefaultVOMSACService} object. The
     * {@link DefaultVOMSACService} parameters can be set with the appropriate
     * methods. Example:
     * 
     * <pre>
     * 
     * 
     * 
     * 
     * 
     * 
     * 
     * 
     * 
     * 
     * {
     *   &#064;code
     *   VOMSACService acService = new DefaultVOMSACService.Builder(certChainValidator)
     *     .requestListener(requestListener)
     *     .serverInfoStoreListener(serverInfoStoreListener)
     *     .protocolListener(protocolListener).build();
     * }
     * </pre>
     * 
     * 
     */
    public static class Builder {

        /**
         * The listener that will be informed about request events
         */
        private VOMSRequestListener requestListener = NullListener.INSTANCE;

        /**
         * The listener that will be informed about low-level protocol details
         */
        private VOMSProtocolListener protocolListener = NullListener.INSTANCE;

        /**
         * The listener that will be informed about server info store events
         */
        private VOMSServerInfoStoreListener storeListener = NullListener.INSTANCE;

        /**
         * The validator used for the SSL handshake
         */
        private X509CertChainValidatorExt validator;

        /**
         * The store used to keep VOMS server contact information.
         */
        private VOMSServerInfoStore serverInfoStore;

        /**
         * The provided strategy to lookup vomses information.
         */
        private VOMSESLookupStrategy vomsesLookupStrategy;

        /**
         * A list of paths where vomses information will be looked for, used to
         * create the server info store.
         */
        private List<String> vomsesLocations;

        /**
         * The connect timeout value
         */
        private int connectTimeout = AbstractVOMSProtocol.DEFAULT_CONNECT_TIMEOUT;

        /**
         * The read timeout used
         */
        private int readTimeout = AbstractVOMSProtocol.DEFAULT_READ_TIMEOUT;

        /**
         * Whether the client should skip hostname checking
         */
        private boolean skipHostnameChecks = true;

        /**
         * The http protocol implementation
         */
        protected VOMSProtocol httpProtocol;

        /**
         * The voms legacy protocol implementation
         */
        protected VOMSProtocol legacyProtocol;

        /**
         * Creates a Builder for a {@link DefaultVOMSACService}.
         * 
         * @param certChainValidator
         *          the validator to use to setup the SSL connection and validate
         *          the certificates
         */
        public Builder(X509CertChainValidatorExt certChainValidator) {

            if (certChainValidator == null)
                throw new NullPointerException("Please provide a non-null certificate chain validator");

            this.validator = certChainValidator;
        }

        /**
         * Sets the request listener for the {@link DefaultVOMSACService} that this
         * builder is creating
         * 
         * @param l
         *          the request listener that will receive notifications about
         *          request events
         * @return this {@link Builder} instance
         */
        public Builder requestListener(VOMSRequestListener l) {

            this.requestListener = l;
            return this;
        }

        /**
         * Sets the {@link VOMSServerInfoStoreListener} for the
         * {@link DefaultVOMSACService} that this builder is creating
         * 
         * @param sl
         *          the store listener that will receive notifications about store
         *          events
         * @return this {@link Builder} instance
         */
        public Builder serverInfoStoreListener(VOMSServerInfoStoreListener sl) {

            this.storeListener = sl;
            return this;
        }

        /**
         * Sets the {@link VOMSServerInfoStore} for the {@link DefaultVOMSACService}
         * that this builder is creating
         * 
         * @param sis
         *          a {@link VOMSServerInfoStore} object
         * @return this {@link Builder} instance
         */
        public Builder serverInfoStore(VOMSServerInfoStore sis) {

            this.serverInfoStore = sis;
            return this;
        }

        /**
         * Sets the {@link VOMSProtocolListener} for the
         * {@link DefaultVOMSACService} that this builder is creating
         * 
         * @param pl
         *          the {@link VOMSProtocolListener} that will receive notifications
         *          about protocol events
         * @return this {@link Builder} instance
         */
        public Builder protocolListener(VOMSProtocolListener pl) {

            this.protocolListener = pl;
            return this;
        }

        /**
         * Sets the connect timeout (in millisecods) for the
         * {@link DefaultVOMSACService} that this builder is creating
         * 
         * @param timeout
         *          the timeout value in milliseconds
         * @return this {@link Builder} instance
         */
        public Builder connectTimeout(int timeout) {

            this.connectTimeout = timeout;
            return this;
        }

        /**
         * Sets the read timeout (in milliseconds) for the
         * {@link DefaultVOMSACService} that this builder is creating
         * 
         * @param timeout
         *          the timeout value in milliseconds
         * @return this {@link Builder} instance
         */
        public Builder readTimeout(int timeout) {

            this.readTimeout = timeout;
            return this;
        }

        /**
         * Sets a flag to skip VOMS hostname checking. Allows for creative VOMS
         * server side certificate configuration.
         * 
         * @param s
         *          <code>true</code> to skip the checks, <code>false</code>
         *          otherwise
         * 
         * @return this {@link Builder} instance
         */
        public Builder skipHostnameChecks(boolean s) {

            this.skipHostnameChecks = s;
            return this;
        }

        /**
         * Sets the vomses lookup strategy for the {@link DefaultVOMSACService} that
         * this builder is creating
         * 
         * @param strategy
         *          the {@link VOMSESLookupStrategy} object
         * @return this {@link Builder} instance
         */
        public Builder vomsesLookupStrategy(VOMSESLookupStrategy strategy) {

            this.vomsesLookupStrategy = strategy;
            return this;
        }

        /**
         * Sets a list of locations that will be used to build a
         * {@link VOMSESLookupStrategy} for the {@link DefaultVOMSACService} that
         * this builder is creating
         * 
         * @param vomsesLocations
         *          a list of paths where vomses information will be looked for
         * @return this {@link Builder} instance
         */
        public Builder vomsesLocations(List<String> vomsesLocations) {

            this.vomsesLocations = vomsesLocations;
            return this;
        }

        /**
         * Sets the http protocol implementation
         * 
         * @param httpProtocol
         *          the http protocol implementatino
         * @return this {@link Builder} instance
         */
        public Builder httpProtocol(VOMSProtocol httpProtocol) {

            this.httpProtocol = httpProtocol;
            return this;
        }

        /**
         * Sets the legacy protocol implementation
         * 
         * @param legacyProtocol
         *          the legacy protocol implementation
         *          
         *  @return 
         *        the {@link Builder}
         */
        public Builder legacyProtocol(VOMSProtocol legacyProtocol) {

            this.legacyProtocol = legacyProtocol;
            return this;
        }

        /**
         * Builds the server info store
         */
        protected void buildServerInfoStore() {

            if (serverInfoStore != null)
                return;

            serverInfoStore = new DefaultVOMSServerInfoStore.Builder().lookupStrategy(vomsesLookupStrategy)
                    .storeListener(storeListener).vomsesPaths(vomsesLocations).build();
        }

        /**
         * Builds default protocols if needed
         */
        protected void buildProtocols() {

            if (httpProtocol == null) {

                RESTProtocol p = new RESTProtocol(validator, protocolListener, connectTimeout, readTimeout);

                p.setSkipHostnameChecks(skipHostnameChecks);

                httpProtocol = p;

            }

            if (legacyProtocol == null) {

                LegacyProtocol p = new LegacyProtocol(validator, protocolListener, connectTimeout, readTimeout);

                p.setSkipHostnameChecks(skipHostnameChecks);

                legacyProtocol = p;

            }
        }

        /**
         * Builds the {@link DefaultVOMSACService}
         * 
         * @return a {@link DefaultVOMSACService} configured as required by this
         *         builder
         */
        public DefaultVOMSACService build() {

            buildServerInfoStore();
            buildProtocols();
            return new DefaultVOMSACService(this);
        }
    }
}