Author: alessio.soldano(a)jboss.com
Date: 2010-10-15 12:08:25 -0400 (Fri, 15 Oct 2010)
New Revision: 479
Added:
trust/trunk/jbossws-native/src/main/java/org/picketlink/trust/jbossws/Constants.java
trust/trunk/jbossws-native/src/main/java/org/picketlink/trust/jbossws/Util.java
Modified:
trust/trunk/jbossws-native/src/main/java/org/picketlink/trust/jbossws/handler/SAML2Handler.java
Log:
Removing jbossws-native specific references from the SAML2Handler and prevent DOM
exception when the assertion element does not come from the same document as the SOAP
message
Added:
trust/trunk/jbossws-native/src/main/java/org/picketlink/trust/jbossws/Constants.java
===================================================================
--- trust/trunk/jbossws-native/src/main/java/org/picketlink/trust/jbossws/Constants.java
(rev 0)
+++
trust/trunk/jbossws-native/src/main/java/org/picketlink/trust/jbossws/Constants.java 2010-10-15
16:08:25 UTC (rev 479)
@@ -0,0 +1,76 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2010, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file 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.picketlink.trust.jbossws;
+
+import javax.xml.namespace.QName;
+
+import org.apache.xml.security.utils.EncryptionConstants;
+
+/**
+ * @author Jason T. Greene
+ */
+public class Constants
+{
+ public static final String WSS_SOAP_NS =
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0";
+
+ public static final String JBOSS_WSSE_NS =
"http://www.jboss.com/jbossws/ws-security";
+
+ public static final String JBOSS_WSSE_PREFIX = "jboss-wsse";
+
+ public static final String WSSE_PREFIX = "wsse";
+
+ public static final String WSSE_NS =
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
+
+ public static final String WSU_PREFIX = "wsu";
+
+ public static final String WSU_NS =
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd";
+
+ public static final String XML_SIGNATURE_NS =
org.apache.xml.security.utils.Constants.SignatureSpecNS;
+
+ public static final String XML_ENCRYPTION_NS = EncryptionConstants.EncryptionSpecNS;
+
+ public static final String XML_ENCRYPTION_PREFIX = "ds"; //xmlsec 1.4.2
requires this to be "ds" to correctly create KeyInfo elements
+
+ public static final String ID = "Id";
+
+ public static final String WSU_ID = WSU_PREFIX + ":" + ID;
+
+ public static final String BASE64_ENCODING_TYPE = WSS_SOAP_NS +
"#Base64Binary";
+
+ public static final String PASSWORD_TEXT_TYPE =
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText";
+
+ public static final String PASSWORD_DIGEST_TYPE =
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest";
+
+ public static final String WSSE_HEADER = WSSE_PREFIX + ":Security";
+
+ public static final String XMLNS_NS = "http://www.w3.org/2000/xmlns/";
+
+ public static final String XENC_DATAREFERENCE = "DataReference";
+
+ public static final String XENC_REFERENCELIST = "ReferenceList";
+
+ public static final String XENC_ELEMENT_TYPE = EncryptionConstants.TYPE_ELEMENT;
+
+ public static final String XENC_CONTENT_TYPE = EncryptionConstants.TYPE_CONTENT;
+
+ public static final QName WSSE_HEADER_QNAME = new QName(WSSE_NS,
"Security");
+}
Added: trust/trunk/jbossws-native/src/main/java/org/picketlink/trust/jbossws/Util.java
===================================================================
--- trust/trunk/jbossws-native/src/main/java/org/picketlink/trust/jbossws/Util.java
(rev 0)
+++
trust/trunk/jbossws-native/src/main/java/org/picketlink/trust/jbossws/Util.java 2010-10-15
16:08:25 UTC (rev 479)
@@ -0,0 +1,216 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2010, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file 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.picketlink.trust.jbossws;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.namespace.QName;
+
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+/**
+ * @author Jason T. Greene
+ */
+public class Util
+{
+ public static int count = 0;
+
+ public static String assignWsuId(Element element)
+ {
+ String id = element.getAttributeNS(Constants.WSU_NS, Constants.ID);
+
+ if (id == null || id.length() < 1)
+ {
+ id = generateId();
+ element.setAttributeNS(Constants.WSU_NS, Constants.WSU_ID, id);
+ addNamespace(element, Constants.WSU_PREFIX, Constants.WSU_NS);
+ }
+
+ return id;
+ }
+
+ public static Element getFirstChildElement(Node node)
+ {
+ Node child = node.getFirstChild();
+ while (child != null && child.getNodeType() != Node.ELEMENT_NODE)
+ child = child.getNextSibling();
+
+ return (Element)child;
+ }
+
+ public static Element getNextSiblingElement(Element element)
+ {
+ Node sibling = element.getNextSibling();
+ while (sibling != null && sibling.getNodeType() != Node.ELEMENT_NODE)
+ sibling = sibling.getNextSibling();
+
+ return (Element)sibling;
+ }
+
+ public static Element getPreviousSiblingElement(Element element)
+ {
+ Node sibling = element.getPreviousSibling();
+ while (sibling != null && sibling.getNodeType() != Node.ELEMENT_NODE)
+ sibling = sibling.getPreviousSibling();
+
+ return (Element)sibling;
+ }
+
+ public static Element findElement(Element root, String localName, String namespace)
+ {
+ return findElement(root, new QName(namespace, localName));
+ }
+
+ public static Element findElement(Element root, QName name)
+ {
+ // Here lies your standard recusive DFS.....
+ if (matchNode(root, name))
+ return root;
+
+ // Search children
+ for (Node child = root.getFirstChild(); child != null; child =
child.getNextSibling())
+ {
+ if (child.getNodeType() != Node.ELEMENT_NODE)
+ continue;
+
+ Node possibleMatch = findElement((Element)child, name);
+ if (possibleMatch != null)
+ return (Element)possibleMatch;
+ }
+
+ return null;
+ }
+
+ public static List<Node> findAllElements(Element root, QName name, boolean
local)
+ {
+ List<Node> list = new ArrayList<Node>();
+ if (matchNode(root, name, local))
+ list.add(root);
+
+ for (Node child = root.getFirstChild(); child != null; child =
child.getNextSibling())
+ {
+ if (child.getNodeType() != Node.ELEMENT_NODE)
+ continue;
+
+ list.addAll(findAllElements((Element) child, name, local));
+ }
+
+ return list;
+ }
+
+ public static Element findElementByWsuId(Element root, String id)
+ {
+ // Here lies another standard recusive DFS.....
+ if (id.equals(getWsuId(root)))
+ return root;
+
+ // Search children
+ for (Node child = root.getFirstChild(); child != null; child =
child.getNextSibling())
+ {
+ if (child.getNodeType() != Node.ELEMENT_NODE)
+ continue;
+
+ Node possibleMatch = findElementByWsuId((Element)child, id);
+ if (possibleMatch != null)
+ return (Element)possibleMatch;
+ }
+
+ return null;
+ }
+
+ public static Element findOrCreateSoapHeader(Element envelope)
+ {
+ String prefix = envelope.getPrefix();
+ String uri = envelope.getNamespaceURI();
+ QName name = new QName(uri, "Header");
+ Element header = findElement(envelope, name);
+ if (header == null)
+ {
+ header = envelope.getOwnerDocument().createElementNS(uri, prefix +
":Header");
+ envelope.insertBefore(header, envelope.getFirstChild());
+ }
+
+ return header;
+ }
+
+ public static String getWsuId(Element element)
+ {
+ if (element.hasAttributeNS(Constants.WSU_NS, Constants.ID))
+ return element.getAttributeNS(Constants.WSU_NS, Constants.ID);
+
+ if (element.hasAttribute(Constants.ID))
+ {
+ String ns = element.getNamespaceURI();
+ if (Constants.XML_SIGNATURE_NS.equals(ns) ||
Constants.XML_ENCRYPTION_NS.equals(ns))
+ return element.getAttribute(Constants.ID);
+ }
+
+ return null;
+ }
+
+ public static boolean equalStrings(String string1, String string2)
+ {
+ if (string1 == null && string2 == null)
+ return true;
+
+ return string1 != null && string1.equals(string2);
+ }
+
+ public static boolean matchNode(Node node, QName name)
+ {
+ return matchNode(node, name, false);
+ }
+
+ public static boolean matchNode(Node node, QName name, boolean local)
+ {
+ return equalStrings(node.getLocalName(), name.getLocalPart())
+ && (local || equalStrings(node.getNamespaceURI(),
name.getNamespaceURI()));
+ }
+
+ public static String generateId()
+ {
+ return generateId("element");
+ }
+
+ public static void addNamespace(Element element, String prefix, String uri)
+ {
+ element.setAttributeNS(Constants.XMLNS_NS, "xmlns:" + prefix, uri);
+ }
+
+ public static String generateId(String prefix)
+ {
+ StringBuilder id = new StringBuilder();
+ long time = System.currentTimeMillis();
+
+ // reasonably gaurantee uniqueness
+ synchronized (Util.class)
+ {
+ count++;
+ }
+
+
id.append(prefix).append("-").append(count).append("-").append(time).append("-").append(id.hashCode());
+
+ return id.toString();
+ }
+}
Modified:
trust/trunk/jbossws-native/src/main/java/org/picketlink/trust/jbossws/handler/SAML2Handler.java
===================================================================
---
trust/trunk/jbossws-native/src/main/java/org/picketlink/trust/jbossws/handler/SAML2Handler.java 2010-10-15
14:20:10 UTC (rev 478)
+++
trust/trunk/jbossws-native/src/main/java/org/picketlink/trust/jbossws/handler/SAML2Handler.java 2010-10-15
16:08:25 UTC (rev 479)
@@ -21,20 +21,24 @@
*/
package org.picketlink.trust.jbossws.handler;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
import javax.security.auth.Subject;
import javax.xml.namespace.QName;
+import javax.xml.soap.SOAPMessage;
import javax.xml.ws.handler.MessageContext;
+import javax.xml.ws.handler.soap.SOAPMessageContext;
import org.jboss.logging.Logger;
import org.jboss.security.SecurityContext;
-import org.jboss.ws.core.CommonMessageContext;
-import org.jboss.ws.core.soap.SOAPMessageImpl;
-import org.jboss.ws.extensions.security.Util;
-import org.jboss.ws.extensions.security.element.SecurityHeader;
-import org.jboss.ws.extensions.security.jaxws.WSSecurityHandlerServer;
+import org.jboss.wsf.common.handler.GenericSOAPHandler;
import org.picketlink.identity.federation.bindings.jboss.subject.PicketLinkPrincipal;
import org.picketlink.identity.federation.core.wstrust.SamlCredential;
+import org.picketlink.trust.jbossws.Constants;
import org.picketlink.trust.jbossws.SAML2Constants;
+import org.picketlink.trust.jbossws.Util;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
@@ -42,20 +46,36 @@
* A SAMLv2 WS handler.
*
* @author <a href="mmoyses(a)redhat.com">Marcus Moyses</a>
+ * @author <a href="alessio.soldano(a)jboss.com">Alessio Soldano</a>
* @version $Revision: 1 $
*/
-public class SAML2Handler extends WSSecurityHandlerServer
+public class SAML2Handler extends GenericSOAPHandler
{
protected Logger log = Logger.getLogger(this.getClass());
+ private static Set<QName> headers;
+
+ static
+ {
+ HashSet<QName> set = new HashSet<QName>();
+ set.add(Constants.WSSE_HEADER_QNAME);
+ headers = Collections.unmodifiableSet(set);
+ }
+
+ public Set<QName> getHeaders()
+ {
+ //return a collection with just the wsse:Security header to pass the MustUnderstand
check on it
+ return headers;
+ }
+
/**
* Retrieves the SAML assertion from the SOAP payload and lets invocation go to JAAS
for validation.
*/
protected boolean handleInbound(MessageContext msgContext)
{
- CommonMessageContext ctx = (CommonMessageContext) msgContext;
- SOAPMessageImpl soapMessage = (SOAPMessageImpl) ctx.getSOAPMessage();
+ SOAPMessageContext ctx = (SOAPMessageContext) msgContext;
+ SOAPMessage soapMessage = ctx.getMessage();
// retrieve the assertion
Document document = soapMessage.getSOAPPart();
@@ -82,8 +102,8 @@
*/
protected boolean handleOutbound(MessageContext msgContext)
{
- CommonMessageContext ctx = (CommonMessageContext) msgContext;
- SOAPMessageImpl soapMessage = (SOAPMessageImpl) ctx.getSOAPMessage();
+ SOAPMessageContext ctx = (SOAPMessageContext) msgContext;
+ SOAPMessage soapMessage = ctx.getMessage();
// retrieve assertion
Element assertion = (Element) ctx.get(SAML2Constants.SAML2_ASSERTION_PROPERTY);
@@ -91,25 +111,42 @@
// add wsse header
Document document = soapMessage.getSOAPPart();
Element soapHeader = Util.findOrCreateSoapHeader(document.getDocumentElement());
- SecurityHeader secHeader = new SecurityHeader(document);
try
{
- Element wsse = secHeader.getElement();
+ Element wsse = getSecurityHeaderElement(document);
wsse.setAttributeNS(soapHeader.getNamespaceURI(), soapHeader.getPrefix() +
":mustUnderstand", "1");
if (assertion != null)
{
// add the assertion as a child of the wsse header
- wsse.appendChild(assertion);
+ // check if the assertion element comes from the same document, otherwise
import the node
+ if (document != assertion.getOwnerDocument())
+ {
+ wsse.appendChild(document.importNode(assertion, true));
+ }
+ else
+ {
+ wsse.appendChild(assertion);
+ }
}
soapHeader.insertBefore(wsse, soapHeader.getFirstChild());
}
catch (Exception e)
{
+ e.printStackTrace();
log.error(e);
return false;
}
return true;
}
-
+
+ private Element getSecurityHeaderElement(Document document)
+ {
+ Element element = document.createElementNS(Constants.WSSE_NS,
Constants.WSSE_HEADER);
+ Util.addNamespace(element, Constants.WSSE_PREFIX, Constants.WSSE_NS);
+ Util.addNamespace(element, Constants.WSU_PREFIX, Constants.WSU_NS);
+ Util.addNamespace(element, Constants.XML_ENCRYPTION_PREFIX,
Constants.XML_SIGNATURE_NS);
+ return element;
+ }
+
}