This project has retired. For details please refer to its Attic page.
ValidateReplication.java

ValidateReplication.java

/*
 * Copyright 2001-2008 The Apache Software Foundation.
 * 
 * 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.apache.juddi.validation;

import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.cert.CertificateException;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.persistence.EntityManager;
import javax.xml.ws.WebServiceContext;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import static org.apache.juddi.api.impl.AuthenticatedService.UTF8;
import org.apache.juddi.config.AppConfig;
import org.apache.juddi.config.Property;
import org.apache.juddi.model.Node;
import org.apache.juddi.model.UddiEntityPublisher;
import org.apache.juddi.v3.client.cryptor.CryptorFactory;
import org.apache.juddi.v3.client.cryptor.DigSigUtil;
import org.apache.juddi.v3.error.ErrorMessage;
import org.apache.juddi.v3.error.FatalErrorException;
import org.apache.juddi.v3.error.InvalidValueException;
import org.apache.juddi.v3.error.TransferNotAllowedException;
import org.apache.juddi.v3.error.ValueNotAllowedException;
import org.uddi.custody_v3.TransferEntities;
import org.uddi.repl_v3.CommunicationGraph.Edge;
import org.uddi.repl_v3.HighWaterMarkVectorType;
import org.uddi.repl_v3.NotifyChangeRecordsAvailable;
import org.uddi.repl_v3.Operator;
import org.uddi.repl_v3.ReplicationConfiguration;
import org.uddi.repl_v3.TransferCustody;
import org.uddi.v3_service.DispositionReportFaultMessage;

/**
 * @author <a href="mailto:alexoree@apache.org">Alex O'Ree</a>
 * Processing an inbound replication message may fail due to a server internal
 * error. The common behavior for all error cases is to return an E_fatalError
 * error code. Error reporting SHALL be that specified by Section 4.8 – Success
 * and Error Reporting of this specification.
 */
public class ValidateReplication extends ValidateUDDIApi {

        private final static Log log = LogFactory.getLog(ValidateReplication.class);
        public ValidateReplication(UddiEntityPublisher publisher) {
                super(publisher);
        }
        
       public ValidateReplication(UddiEntityPublisher publisher, String nodeid) {
		 super(publisher, nodeid);
	}

        public void validateNotifyChangeRecordsAvailable(NotifyChangeRecordsAvailable body, WebServiceContext ctx) throws DispositionReportFaultMessage {
                //TODO
        }

        public void validateGetChangeRecords(String requestingNode, HighWaterMarkVectorType changesAlreadySeen, BigInteger responseLimitCount, HighWaterMarkVectorType responseLimitVector, ReplicationConfiguration FetchEdges, WebServiceContext ctx) throws DispositionReportFaultMessage {
                //TODO

                if (requestingNode == null || requestingNode.trim().equalsIgnoreCase("")) {
                        //requestingNode: The requestingNode element provides the identity of the calling node.  
                        //This is the unique key for the calling node and SHOULD be specified within the Replication Configuration Structure.
                        throw new FatalErrorException(new ErrorMessage("errors.replication.nodeNotSpecified"));
                }
                //if (!ContainsNode(requestingNode, FetchEdges)) {
                //        throw new FatalErrorException(new ErrorMessage("errors.replication.unknownNode"));
                //}

                if (changesAlreadySeen != null) {
                        // changesAlreadySeen: The changesAlreadySeen element, if present, indicates changes from each
                        //node that the requestor has successfully processed, and thus which should not be resent, if possible.

                        //no validation needed?
                }

                if (responseLimitCount != null && responseLimitVector != null) {
                        throw new FatalErrorException(new ErrorMessage("errors.replication.bothLimitsSpecified"));
                }
                if (responseLimitCount != null) {
                        //can't be 0 since 0 is banned as being a change record id
                        if (responseLimitCount.longValue() <= 0) {
                                throw new FatalErrorException(new ErrorMessage("errors.replication.negativeLimit", responseLimitCount.toString()));
                        }
                }
                if (responseLimitVector != null) {
                        for (int i = 0; i < responseLimitVector.getHighWaterMark().size(); i++) {
                                if (responseLimitVector.getHighWaterMark().get(i).getOriginatingUSN() == null
                                        || responseLimitVector.getHighWaterMark().get(i).getOriginatingUSN() <= 0) {
                                        throw new FatalErrorException(new ErrorMessage("errors.replication.limitVectorNull"));
                                }
                                if (responseLimitVector.getHighWaterMark().get(i).getNodeID() == null
                                        || responseLimitVector.getHighWaterMark().get(i).getNodeID().trim().equalsIgnoreCase("")) {
                                        throw new FatalErrorException(new ErrorMessage("errors.replication.limitVectorNoNode"));
                                }
                        }
                }

                /**
                 * responseLimitCount or responseLimitVector: A caller MAY place
                 * an upper bound on the number of change records he wishes to
                 * receive in response to this message by either providing a
                 * integer responseLimitCount, or, using responseLimitVector,
                 * indicating for each node in the graph the first change
                 * originating there that he does not wish to be returned.
                 *
                 */
        }

        private static boolean ContainsNode(String requestingNode, ReplicationConfiguration FetchEdges) {
                if (FetchEdges == null) {
                        return false;
                }
                if (FetchEdges.getCommunicationGraph() == null) {
                        return false;
                }
                for (int i = 0; i < FetchEdges.getCommunicationGraph().getNode().size(); i++) {
                        if (FetchEdges.getCommunicationGraph().getNode().get(i).equalsIgnoreCase(requestingNode)) {
                                return true;
                        }
                }
                return false;
        }

        public void validateSetReplicationNodes(ReplicationConfiguration replicationConfiguration, EntityManager em, String thisnode, Configuration config) throws DispositionReportFaultMessage, ConfigurationException {
                if (replicationConfiguration == null) {
                        throw new InvalidValueException(new ErrorMessage("errors.replication.configNull"));

                }
                if (replicationConfiguration.getCommunicationGraph() == null) {
                        throw new InvalidValueException(new ErrorMessage("errors.replication.configNull"));
                }
                if (replicationConfiguration.getRegistryContact() == null) {
                        throw new InvalidValueException(new ErrorMessage("errors.replication.contactNull"));
                }
                if (replicationConfiguration.getRegistryContact().getContact() == null) {
                        throw new InvalidValueException(new ErrorMessage("errors.replication.contactNull"));
                }
                if (replicationConfiguration.getRegistryContact().getContact().getPersonName().get(0) == null) {
                        throw new InvalidValueException(new ErrorMessage("errors.replication.contactNull"));
                }

                if (replicationConfiguration.getOperator() == null || replicationConfiguration.getOperator().isEmpty()) {
                        throw new InvalidValueException(new ErrorMessage("errors.replication.contactNull", "Operator is null or empty"));
                }
                for (int i = 0; i < replicationConfiguration.getOperator().size(); i++) {
                        if (replicationConfiguration.getOperator().get(i).getSoapReplicationURL() == null
                                || "".equals(replicationConfiguration.getOperator().get(i).getSoapReplicationURL())) {
                                throw new InvalidValueException(new ErrorMessage("errors.replication.contactNull", "Replication URL is null or empty"));
                        }
                        if (!replicationConfiguration.getOperator().get(i).getSoapReplicationURL().toLowerCase().startsWith("http")) {
                                throw new InvalidValueException(new ErrorMessage("errors.replication.contactNull", "Replication URL is invalid, only HTTP is supported"));
                        }
                        if (replicationConfiguration.getOperator().get(i).getOperatorNodeID() == null
                                || replicationConfiguration.getOperator().get(i).getOperatorNodeID().equalsIgnoreCase("")) {
                                throw new InvalidValueException(new ErrorMessage("errors.replication.contactNull", "Node ID is not defined"));
                        }
                }
                if (replicationConfiguration.getCommunicationGraph() != null) {
                        for (String s : replicationConfiguration.getCommunicationGraph().getNode()) {
                                if (!Contains(replicationConfiguration.getOperator(), s)) {
                                        throw new InvalidValueException(new ErrorMessage("errors.replication.configNodeNotFound"));
                                }
                        }
                        for (Edge s : replicationConfiguration.getCommunicationGraph().getEdge()) {
                                //TODO revisit this for correctness
                                //Node find = null;
                                //if (!thisnode.equalsIgnoreCase(s.getMessageReceiver())) {
                                if (!Contains(replicationConfiguration.getOperator(), s.getMessageReceiver())) {
                                        throw new InvalidValueException(new ErrorMessage("errors.replication.configNodeNotFound"));
                                        //}
                                }
                                //find = null;
                                //if (!thisnode.equalsIgnoreCase(s.getMessageSender())) {
                                if (!Contains(replicationConfiguration.getOperator(), s.getMessageSender())) {
                                        throw new InvalidValueException(new ErrorMessage("errors.replication.configNodeNotFound"));
                                        //}
                                }
                                if (s.getMessageReceiver().equalsIgnoreCase(s.getMessageSender())){
                                        throw new InvalidValueException(new ErrorMessage("errors.replication.configNodeLoop"));
                                }
                                for (String id : s.getMessageReceiverAlternate()) {
                                        if (!Contains(replicationConfiguration.getOperator(), id)) {
                                                throw new InvalidValueException(new ErrorMessage("errors.replication.configNodeNotFound"));
                                        }
                                }

                        }
                }
                boolean shouldcheck = config.getBoolean(Property.JUDDI_REJECT_ENTITIES_WITH_INVALID_SIG_ENABLE, false);
                initDigSig(config);
                if (shouldcheck && !replicationConfiguration.getSignature().isEmpty() && ds != null) {
                        AtomicReference<String> outmsg = new AtomicReference<String>();
                        boolean ok = ds.verifySignedUddiEntity(replicationConfiguration, outmsg);
                        if (!ok) {
                                throw new FatalErrorException(new ErrorMessage("errors.digitalsignature.validationfailure" + " " + outmsg.get()));
                        }

                }
        }
        
         private org.apache.juddi.v3.client.cryptor.DigSigUtil ds = null;

        private synchronized void initDigSig(Configuration config) {
                if (ds == null) {
                        
                        Properties p = new Properties();
                        /**
                         * <trustStorePath>truststore.jks</trustStorePath>
                         * <trustStoreType>JKS</trustStoreType>
                         * <trustStorePassword
                         * isPasswordEncrypted="false"
                         * cryptoProvider="org.apache.juddi.v3.client.crypto.AES128Cryptor">password</trustStorePassword>
                         *
                         * <checkTimestamps>true</checkTimestamps>
                         * <checkTrust>true</checkTrust>
                         * <checkRevocationCRL>true</checkRevocationCRL>
                         */
                        p.put(DigSigUtil.TRUSTSTORE_FILE, config.getString(Property.JUDDI_REJECT_ENTITIES_WITH_INVALID_SIG_PREFIX + "trustStorePath", ""));
                        p.put(DigSigUtil.TRUSTSTORE_FILETYPE, config.getString(Property.JUDDI_REJECT_ENTITIES_WITH_INVALID_SIG_PREFIX + "trustStoreType", ""));

                        String enc = config.getString(Property.JUDDI_REJECT_ENTITIES_WITH_INVALID_SIG_PREFIX + "trustStorePassword", "");
                        if (config.getBoolean(Property.JUDDI_REJECT_ENTITIES_WITH_INVALID_SIG_PREFIX + "trustStorePassword[@isPasswordEncrypted]", false)) {
                                log.debug("trust password is encrypted, decrypting...");
                                
                                String prov = config.getString(Property.JUDDI_REJECT_ENTITIES_WITH_INVALID_SIG_PREFIX + "trustStorePassword[@cryptoProvider]", "");
                                try {
                                        p.setProperty(DigSigUtil.TRUSTSTORE_FILE_PASSWORD, CryptorFactory.getCryptor(prov).decrypt(enc));
                                } catch (Exception ex) {
                                        log.warn("unable to decrypt trust store password " + ex.getMessage());
                                        log.debug("unable to decrypt trust store password " + ex.getMessage(), ex);
                                }

                        } else if (!"".equals(enc)){
                                log.warn("Hey, you should consider encrypting your trust store password!");
                                p.setProperty(DigSigUtil.TRUSTSTORE_FILE_PASSWORD, enc);
                        }

                        p.put(DigSigUtil.CHECK_REVOCATION_STATUS_CRL, config.getString(Property.JUDDI_REJECT_ENTITIES_WITH_INVALID_SIG_PREFIX + "checkRevocationCRL", "true"));
                        p.put(DigSigUtil.CHECK_TRUST_CHAIN, config.getString(Property.JUDDI_REJECT_ENTITIES_WITH_INVALID_SIG_PREFIX + "checkTrust", "true"));
                        p.put(DigSigUtil.CHECK_TIMESTAMPS, config.getString(Property.JUDDI_REJECT_ENTITIES_WITH_INVALID_SIG_PREFIX + "checkTimestamps", "true"));

                        try {
                                ds = new DigSigUtil(p);
                        } catch (CertificateException ex) {
                                log.error("", ex);
                        }
                        //System.out.println("loaded from " + AppConfig.getConfigFileURL());
                        //p.list(System.out);
                }
        }

      

        private boolean Contains(List<Operator> operator, String s) {
                if (operator == null) {
                        return false;
                }
                for (Operator o : operator) {
                        if (o.getOperatorNodeID().equalsIgnoreCase(s)) {
                                return true;
                        }
                }
                return false;
        }

        public void validateTransfer(EntityManager em, TransferCustody body) throws DispositionReportFaultMessage {

                if (body == null) {
                        throw new TransferNotAllowedException(new ErrorMessage("errors.NullInput"));
                }
                if (body.getTransferToken() == null) {
                        throw new TransferNotAllowedException(new ErrorMessage("errors.NullInput"));
                }
                if (body.getKeyBag() == null) {
                        throw new TransferNotAllowedException(new ErrorMessage("errors.NullInput"));
                }
                if (body.getTransferOperationalInfo() == null) {
                        throw new TransferNotAllowedException(new ErrorMessage("errors.NullInput"));
                }

                if (body.getTransferOperationalInfo().getNodeID() == null) {
                        throw new TransferNotAllowedException(new ErrorMessage("errors.NullInput"));
                }
                if (body.getTransferOperationalInfo().getAuthorizedName() == null) {
                        throw new TransferNotAllowedException(new ErrorMessage("errors.NullInput"));
                }

                //confirm i own the records in question
                //confirm i issued the transfer token
                TransferEntities x = new TransferEntities();
                x.setKeyBag(body.getKeyBag());
                x.setTransferToken(body.getTransferToken());
                String transferTokenId;
                try {
                    transferTokenId = new String(body.getTransferToken().getOpaqueToken(), UTF8);
                } catch (UnsupportedEncodingException ex) {
                    throw new InvalidValueException(new ErrorMessage("errors.stringEncoding"));
                }
                new ValidateCustodyTransfer(null).validateTransferLocalEntities(em, transferTokenId, body.getKeyBag().getKey());

        }

}