/*
* JBoss, Home of Professional Open Source Copyright 2008, 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 static org.jboss.soa.esb.services.security.auth.ws.SoapExtractionUtil.*;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.Collections;
import java.util.Set;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import org.jboss.internal.soa.esb.assertion.AssertArgument;
import org.jboss.soa.esb.services.security.auth.AuthenticationRequest;
import org.jboss.soa.esb.services.security.auth.AuthenticationRequestImpl;
import org.jboss.soa.esb.services.security.auth.SecurityInfoExtractor;
import org.picketlink.identity.federation.core.wstrust.SamlCredential;
/**
* Extracts SAML Assertions from a String containing a SOAP Message.</p>
*
* @author <a href="mailto:dbevenius@redhat.com">Daniel Bevenius</a>
*/
public class SamlAssertionExtractor implements SecurityInfoExtractor<String>
{
private static final XMLInputFactory XML_INPUT_FACTORY = getXmlInputFactory();
private static final XMLOutputFactory XML_OUTPUT_FACTORY = getXmlOutputFactory();
private final QName assertionQName;
/**
* Constructs an instance setting its assertion namespace to
* "urn:oasis:names:tc:SAML:2.0:assertion".
*/
public SamlAssertionExtractor()
{
this("urn:oasis:names:tc:SAML:2.0:assertion");
}
/**
* Constructs an instance setting its assertion namespace to
* value of the passed in namespace argument.
*
* @param namespace The namespace for the assertion.
*/
public SamlAssertionExtractor(final String namespace)
{
AssertArgument.isNotNullAndNotEmpty(namespace, "namespace");
assertionQName = new QName(namespace, "Assertion");
}
/**
* Will extract a SAML security token from the passed in SOAP message.
*
* @param soap The SOAP message.
* @return {@link AuthenticationRequest} A AuthenticationRequest containing a SamlCredential, or null if no SAML token was present.
*/
public AuthenticationRequest extractSecurityInfo(final String soap)
{
if (soap == null || !soap.startsWith("<"))
return null;
String samlToken;
try
{
samlToken = extractSamlAssertion(soap);
}
catch (final XMLStreamException e)
{
throw new SecurityException("Could not extract saml token info from :" + soap, e);
}
if (samlToken != null)
{
Set<Object> credential = Collections.<Object>singleton(new SamlCredential(samlToken));
return new AuthenticationRequestImpl.Builder(null, credential).build();
}
return null;
}
/**
* Extracts a SAML security assertion element from a SOAP message.
*
* @param soap The SOAP message.
* @return The extracted security assertion element as a String or null if none existed.
* @throws XMLStreamException
*/
public String extractSamlAssertion(final String soap) throws XMLStreamException
{
if (soap == null || !soap.startsWith("<"))
return null;
final XMLEventReader xmlReader = XML_INPUT_FACTORY.createXMLEventReader(new StringReader(soap));
final StringWriter stringWriter = new StringWriter();
final XMLEventWriter xmlWriter = XML_OUTPUT_FACTORY.createXMLEventWriter(stringWriter);
while(xmlReader.hasNext())
{
XMLEvent event = xmlReader.nextEvent();
if (isStartOfHeader(event))
{
while (xmlReader.hasNext())
{
event = xmlReader.nextEvent();
if (isStartOfAssertion(event))
{
xmlWriter.add(event);
while (xmlReader.hasNext())
{
XMLEvent nextEvent = xmlReader.nextEvent();
xmlWriter.add(nextEvent);
if (isEndOfAssertion(nextEvent))
{
xmlWriter.flush();
return stringWriter.toString();
}
}
}
if (isEndOfHeader(event))
return null;
}
}
if (isStartOfBody(event))
return null;
}
return null;
}
private boolean isStartOfAssertion(final XMLEvent event)
{
return event.isStartElement() && ((StartElement)event).getName().equals(assertionQName);
}
private boolean isEndOfAssertion(final XMLEvent event)
{
return event.isEndElement() && ((EndElement)event).getName().equals(assertionQName);
}
private static XMLOutputFactory getXmlOutputFactory()
{
final XMLOutputFactory factory = XMLOutputFactory.newInstance();
// set any properies here if required before returning.
return factory;
}
private static XMLInputFactory getXmlInputFactory()
{
final XMLInputFactory factory = XMLInputFactory.newInstance();
// set any properies here if required before returning.
return factory;
}
}