be.agiv.security.handler.WSAddressingHandler.java Source code

Java tutorial

Introduction

Here is the source code for be.agiv.security.handler.WSAddressingHandler.java

Source

/*
 * AGIV Java Security Project.
 * Copyright (C) 2011-2012 AGIV.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version
 * 3.0 as published by the Free Software Foundation.
 *
 * This software 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, see 
 * http://www.gnu.org/licenses/.
 */

package be.agiv.security.handler;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.UUID;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPFactory;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPHeaderElement;
import javax.xml.ws.ProtocolException;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPMessageContext;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import be.agiv.security.client.WSConstants;
import be.agiv.security.jaxb.wsaddr.ObjectFactory;
import be.agiv.security.jaxb.wsaddr.RelatesToType;

/**
 * A JAX-WS SOAP handler that implements the WS-Addressing.
 * 
 * @author Frank Cornelis
 * 
 */
public class WSAddressingHandler implements AGIVSOAPHandler {

    private static final Log LOG = LogFactory.getLog(WSAddressingHandler.class);

    private static final String TO_ID_CONTEXT_ATTRIBUTE = WSAddressingHandler.class.getName() + ".toId";

    private static final String MESSAGE_ID_CONTEXT_ATTRIBUTE = WSAddressingHandler.class.getName() + ".messageId";

    private final JAXBContext jaxbContext;

    private String action;

    private String to;

    /**
     * Default constructor.
     */
    public WSAddressingHandler() {
        try {
            this.jaxbContext = JAXBContext.newInstance(ObjectFactory.class);
        } catch (JAXBException e) {
            throw new ProtocolException(e);
        }
    }

    public boolean handleMessage(SOAPMessageContext context) {
        Boolean outboundProperty = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
        if (true == outboundProperty.booleanValue()) {
            try {
                handleOutboundMessage(context);
            } catch (SOAPException e) {
                throw new ProtocolException(e);
            }
        } else {
            handleInboundMessage(context);
        }
        return true;
    }

    private void handleInboundMessage(SOAPMessageContext context) {
        String messageId = (String) context.get(MESSAGE_ID_CONTEXT_ATTRIBUTE);
        LOG.debug("checking RelatesTo message id: " + messageId);
        Object[] headers = context.getHeaders(new QName(WSConstants.WS_ADDR_NAMESPACE, "RelatesTo"),
                this.jaxbContext, false);
        for (Object headerObject : headers) {
            JAXBElement<RelatesToType> element = (JAXBElement<RelatesToType>) headerObject;
            RelatesToType relatesTo = element.getValue();
            if (false == messageId.equals(relatesTo.getValue())) {
                throw new ProtocolException("incorrect a:RelatesTo value");
            }
        }
    }

    private void handleOutboundMessage(SOAPMessageContext context) throws SOAPException {
        LOG.debug("adding WS-Addressing headers");
        SOAPEnvelope envelope = context.getMessage().getSOAPPart().getEnvelope();
        SOAPHeader header = envelope.getHeader();
        if (null == header) {
            header = envelope.addHeader();
        }

        String wsuPrefix = null;
        String wsAddrPrefix = null;
        Iterator namespacePrefixesIter = envelope.getNamespacePrefixes();
        while (namespacePrefixesIter.hasNext()) {
            String namespacePrefix = (String) namespacePrefixesIter.next();
            String namespace = envelope.getNamespaceURI(namespacePrefix);
            if (WSConstants.WS_ADDR_NAMESPACE.equals(namespace)) {
                wsAddrPrefix = namespacePrefix;
            } else if (WSConstants.WS_SECURITY_UTILITY_NAMESPACE.equals(namespace)) {
                wsuPrefix = namespacePrefix;
            }
        }
        if (null == wsAddrPrefix) {
            wsAddrPrefix = getUniquePrefix("a", envelope);
            envelope.addNamespaceDeclaration(wsAddrPrefix, WSConstants.WS_ADDR_NAMESPACE);
        }
        if (null == wsuPrefix) {
            /*
             * Using "wsu" is very important for the IP-STS X509 credential.
             * Apparently the STS refuses when the namespace prefix of the
             * wsu:Id on the WS-Addressing To element is different from the
             * wsu:Id prefix on the WS-Security timestamp.
             */
            wsuPrefix = "wsu";
            envelope.addNamespaceDeclaration(wsuPrefix, WSConstants.WS_SECURITY_UTILITY_NAMESPACE);
        }

        SOAPFactory factory = SOAPFactory.newInstance();

        SOAPHeaderElement actionHeaderElement = header
                .addHeaderElement(new QName(WSConstants.WS_ADDR_NAMESPACE, "Action", wsAddrPrefix));
        actionHeaderElement.setMustUnderstand(true);
        actionHeaderElement.addTextNode(this.action);

        SOAPHeaderElement messageIdElement = header
                .addHeaderElement(new QName(WSConstants.WS_ADDR_NAMESPACE, "MessageID", wsAddrPrefix));
        String messageId = "urn:uuid:" + UUID.randomUUID().toString();
        context.put(MESSAGE_ID_CONTEXT_ATTRIBUTE, messageId);
        messageIdElement.addTextNode(messageId);

        SOAPHeaderElement replyToElement = header
                .addHeaderElement(new QName(WSConstants.WS_ADDR_NAMESPACE, "ReplyTo", wsAddrPrefix));
        SOAPElement addressElement = factory.createElement("Address", wsAddrPrefix, WSConstants.WS_ADDR_NAMESPACE);
        addressElement.addTextNode("http://www.w3.org/2005/08/addressing/anonymous");
        replyToElement.addChildElement(addressElement);

        SOAPHeaderElement toElement = header
                .addHeaderElement(new QName(WSConstants.WS_ADDR_NAMESPACE, "To", wsAddrPrefix));
        toElement.setMustUnderstand(true);

        toElement.addTextNode(this.to);

        String toIdentifier = "to-id-" + UUID.randomUUID().toString();
        toElement.addAttribute(new QName(WSConstants.WS_SECURITY_UTILITY_NAMESPACE, "Id", wsuPrefix), toIdentifier);
        try {
            toElement.setIdAttributeNS(WSConstants.WS_SECURITY_UTILITY_NAMESPACE, "Id", true);
        } catch (UnsupportedOperationException e) {
            // Axis2 has missing implementation of setIdAttributeNS
            LOG.error("error setting Id attribute: " + e.getMessage());
        }
        context.put(TO_ID_CONTEXT_ATTRIBUTE, toIdentifier);
    }

    private String getUniquePrefix(String preferredPrefix, SOAPEnvelope envelope) {
        int suffixNr = 0;
        boolean conflict;
        String prefix = preferredPrefix;
        do {
            conflict = false;
            Iterator namespacePrefixesIter = envelope.getNamespacePrefixes();
            while (namespacePrefixesIter.hasNext()) {
                String existingPrefix = (String) namespacePrefixesIter.next();
                if (prefix.equals(existingPrefix)) {
                    conflict = true;
                    break;
                }
            }
            if (conflict) {
                suffixNr++;
                prefix = preferredPrefix + suffixNr;
            }
        } while (conflict);
        return prefix;
    }

    public boolean handleFault(SOAPMessageContext context) {
        return true;
    }

    public void close(MessageContext context) {

    }

    public Set<QName> getHeaders() {
        Set<QName> headers = new HashSet<QName>();
        headers.add(new QName(WSConstants.WS_ADDR_NAMESPACE, "Action"));
        headers.add(new QName(WSConstants.WS_ADDR_NAMESPACE, "To"));
        return headers;
    }

    /**
     * Sets the WS-Addressing parameters.
     * 
     * @param action
     *            the WS-Addressing Action element value.
     * @param to
     *            the WS-Addressing To element value
     */
    public void setAddressing(String action, String to) {
        this.action = action;
        this.to = to;
    }

    /**
     * Gives back the u:Id attribute value of the WS-Addressing To element.
     * 
     * @param context
     *            the JAX-WS SOAP message context.
     * @return the Id attribute value of the To element.
     */
    public static String getToIdentifier(SOAPMessageContext context) {
        return (String) context.get(TO_ID_CONTEXT_ATTRIBUTE);
    }
}