/*
* JBoss, Home of Professional Open Source Copyright 2009, Red Hat Middleware
* LLC, and individual contributors by the @authors tag. See the copyright.txt
* in the distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* 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, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF
* site: http://www.fsf.org.
*/
package org.jboss.soa.esb.services.security.auth.ws;
import java.io.IOException;
import java.io.Writer;
import javax.xml.namespace.QName;
import org.milyn.SmooksException;
import org.milyn.cdr.annotation.ConfigParam;
import org.milyn.container.ExecutionContext;
import org.milyn.delivery.annotation.Initialize;
import org.milyn.delivery.sax.SAXElement;
import org.milyn.delivery.sax.SAXElementVisitor;
import org.milyn.delivery.sax.SAXText;
import org.milyn.delivery.sax.SAXVisitor;
import org.milyn.delivery.sax.WriterUtil;
import org.milyn.javabean.decoders.BooleanDecoder;
import org.xml.sax.helpers.AttributesImpl;
/**
* SOAPSecurityHeaderVisitor is a Smooks Visitor that will add a
* SOAP Security Header to a SOAP Envelope.
*
* The Security Header can be any xml header returned by the abstract
* method {@link #getHeaderToInsert()}.
* </p>
*
* @author <a href="mailto:dbevenius@jboss.com">Daniel Bevenius</a>
*
*/
public abstract class SOAPSecurityHeaderVisitor implements SAXElementVisitor
{
private static final String HEADER_ADDED = "addedToken";
private String headerElementName;
private String headerNS;
private String securityElementName;
private String securityNS;
private String securityPrefix;
private boolean addSecurityXmlNsAttribute;
private QName securityQName;
private QName headerQName;
private SAXVisitor writerOwner = this;
private boolean rewriteEntities = true;
/**
* Should be implemented by subclasses and return the security
* header to be inserted into the SOAP Security Header.
*
* @return String The header to be inserted into the SOAP Security Header.
*/
protected abstract String getHeaderToInsert();
@Initialize
public void initialize()
{
securityQName = new QName(securityNS, securityElementName, securityPrefix);
headerQName = new QName(headerNS, headerElementName);
}
/**
* Will simply write the start of the element.
*
* @param element The current {@link SAXElement}.
* @param executionContext Smooks {@link ExecutionContext}.
*/
public void visitBefore(final SAXElement element, final ExecutionContext executionContext) throws SmooksException, IOException
{
WriterUtil.writeStartElement(element, element.getWriter(writerOwner), rewriteEntities);
}
/**
* Will write the SAXTex.
*
* @param element The current {@link SAXElement}.
* @param text The {@link SAXText} to write.
* @param executionContext Smooks {@link ExecutionContext}.
*/
public void onChildText(final SAXElement element, final SAXText text, final ExecutionContext executionContext) throws SmooksException, IOException
{
if (element.isWriterOwner(writerOwner))
{
text.toWriter(element.getWriter(writerOwner), rewriteEntities);
}
}
/**
* Will create a new Security header element if one does not exist and add the header, the String
* returned from {@link #getHeaderToInsert()}, into the newly created Security header.
*
* If a Security header does exist the header, the String returned from {@link #getHeaderToInsert()},
* will be inserted into the existing header.
*
* @param element The current {@link SAXElement}.
* @param text The {@link SAXText} to write.
* @param executionContext Smooks {@link ExecutionContext}.
*/
public void visitAfter(final SAXElement element, final ExecutionContext executionContext) throws SmooksException, IOException
{
if (element.isWriterOwner(writerOwner))
{
final Boolean headerAdded = (Boolean) executionContext.getAttribute(HEADER_ADDED);
if (headerAdded == null)
{
final QName elementQName = element.getName();
if (elementQName.equals(securityQName))
{
// A Security Header exists so we simply add the header to it.
addHeader(element);
}
else if (elementQName.equals(headerQName))
{
// No Security Header exists in the SOAP Header so we must create one.
final AttributesImpl attributesImpl = new AttributesImpl();
if (addSecurityXmlNsAttribute)
{
// Add a xmlns to the Security Header that is to be created.
attributesImpl.addAttribute("", securityQName.getLocalPart(), "xmlns:" + securityQName.getPrefix(), "CDDATA",securityQName.getNamespaceURI());
}
// Create the Security Header
final SAXElement securityElement = new SAXElement(securityQName, attributesImpl, element);
final Writer writer = element.getWriter(writerOwner);
// Write the start of the Security Header element
WriterUtil.writeStartElement(securityElement, writer, rewriteEntities);
// Add the specific Security Header to the newly created Security Header
addHeader(element);
// Write the end of the Security Header element
WriterUtil.writeEndElement(securityElement, writer);
}
executionContext.setAttribute(HEADER_ADDED, Boolean.TRUE);
}
WriterUtil.writeEndElement(element, element.getWriter(writerOwner));
}
}
/**
* Will write the String returned from {@link #getHeaderToInsert()} to the
* passed in {@link SAXElement}..
*
* @param element The current {@link SAXElement}.
*/
private void addHeader(final SAXElement element) throws IOException
{
Writer writer = element.getWriter(writerOwner);
final String header = getHeaderToInsert();
if (header != null)
{
writer.append(header);
}
}
/**
* No Operation implemented.
*/
public void onChildElement(final SAXElement element, final SAXElement childElement, final ExecutionContext executionContext) throws SmooksException, IOException
{
// NoOP
}
/**
* This is the name of the Security element header. This will be used for matching
* a pre-existing Security header element in a SOAP Envelope and also used when
* creating a new Security header (that is if one does not exist).
* For example:
* Envelope/Header/Security
*
* @param securityElementName The name of the Security header element
* @return {@link SOAPSecurityHeaderVisitor} To support method chaining.
*/
@ConfigParam (defaultVal = "Security")
public SOAPSecurityHeaderVisitor setSecurityElementName(String securityElementName)
{
this.securityElementName = securityElementName;
return this;
}
/**
* The name of the SOAP Header element.
*
* @param name The name of the SOAP Header element.
* @return {@link SOAPSecurityHeaderVisitor} To support method chaining.
*/
@ConfigParam (defaultVal = "Header")
public SOAPSecurityHeaderVisitor setHeaderElementName(String name)
{
this.headerElementName = name;
return this;
}
/**
* The namespace(ns) of the SOAP Envelope. Used to support different version.
*
* @param ns The SOAP Envelope namespace.
* @return {@link SOAPSecurityHeaderVisitor} To support method chaining.
*/
@ConfigParam (defaultVal = "http://schemas.xmlsoap.org/soap/envelope/")
public SOAPSecurityHeaderVisitor setHeaderNS(String ns)
{
this.headerNS = ns;
return this;
}
/**
* The namespace(ns) of the SOAP Secuirty Headerl. Used to support different version.
*
* @param ns The SOAP SecurityHeader namespace.
* @return {@link SOAPSecurityHeaderVisitor} To support method chaining.
*/
@ConfigParam (defaultVal = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd")
public SOAPSecurityHeaderVisitor setSecurityNS(String securityNS)
{
this.securityNS = securityNS;
return this;
}
/**
* This is the Security Header prefix which will be used when creating a new Security header.
* For example, setting this to sec would generate a Security Header looking like:
* <pre>
* <sec:Security xmlns:sec=...
* </pre>
*
* @param prefix The prefix for the Security Header element.
* @return {@link SOAPSecurityHeaderVisitor} To support method chaining.
*/
@ConfigParam (defaultVal = "wsse")
public SOAPSecurityHeaderVisitor setSecurityPrefix(String prefix)
{
this.securityPrefix = prefix;
return this;
}
/**
* Determines if an xmlns attribute should be added to a created Security Header
* element. This might not be needed or desired if you know that this declaration
* exist in the higher up in the SOAP Envelope.
*
* @param add True will add the xmlns attribute to a created Security Header element. False will not.
* @return {@link SOAPSecurityHeaderVisitor} To support method chaining.
*/
@ConfigParam (defaultVal = "false", decoder = BooleanDecoder.class)
public SOAPSecurityHeaderVisitor setAddSecurityXmlNSAttribute(final boolean add)
{
this.addSecurityXmlNsAttribute = add;
return this;
}
}