Reputation: 1268
I am using Spring SAML, from IDP response I am receiving issuer as an attribute ID, So I wanted to change the response after receiving in spring saml, so I have overridden method unmarshall which will parse the message and changed the xml elements for this purpose. While doing this if I directly use the Element after parsing to unmarshall it is working but if I do dom manipulation it is giving the below exception. I am not getting why it is throwing exception in first code but not in second code attached below. How to make it work without converting again to string and get dom object.
Exception in thread "main" java.lang.IllegalArgumentException: local part cannot be "null" when creating a QName
at java.xml/javax.xml.namespace.QName.<init>(QName.java:185)
at java.xml/javax.xml.namespace.QName.<init>(QName.java:129)
at org.opensaml.xml.util.XMLHelper.constructQName(XMLHelper.java:433)
at org.opensaml.xml.util.XMLHelper.getNodeQName(XMLHelper.java:171)
at org.opensaml.xml.io.UnmarshallerFactory.getUnmarshaller(UnmarshallerFactory.java:81)
at org.opensaml.xml.io.AbstractXMLObjectUnmarshaller.unmarshallChildElement(AbstractXMLObjectUnmarshaller.java:334)
at org.opensaml.xml.io.AbstractXMLObjectUnmarshaller.unmarshall(AbstractXMLObjectUnmarshaller.java:127)
at org.opensaml.xml.io.AbstractXMLObjectUnmarshaller.unmarshallChildElement(AbstractXMLObjectUnmarshaller.java:355)
at org.opensaml.xml.io.AbstractXMLObjectUnmarshaller.unmarshall(AbstractXMLObjectUnmarshaller.java:127)
at org.opensaml.ws.message.decoder.BaseMessageDecoder.unmarshallMessage(unmarshallMessage.java:208)
For the below code it is throwing exception as explained above.
protected XMLObject unmarshallMessage(InputStream messageStream) throws MessageDecodingException {
log.debug("Parsing message stream into DOM document");
try {
Document messageDoc = parserPool.parse(messageStream);
Element messageElem = messageDoc.getDocumentElement();
Element elementsByTagName = (Element) messageElem.getElementsByTagName("saml2:Assertion").item(0);
Element issuer = messageDoc.createElement("saml2:Issuer");
issuer.setTextContent("emIDAM");
issuer.setAttribute("Format", "urn:oasis:names:tc:SAML:2.0:nameid-format:entity");
log.debug("Unmarshalling message DOM");
Unmarshaller unmarshaller = Configuration.getUnmarshallerFactory().getUnmarshaller(messageElem);
if (unmarshaller == null) {
log.debug("Unable to unmarshall message, no unmarshaller registered for message element "
+ XMLHelper.getNodeQName(messageElem));
throw new MessageDecodingException(
"Unable to unmarshall message, no unmarshaller registered for message element "
+ XMLHelper.getNodeQName(messageElem));
}
XMLObject message = unmarshaller.unmarshall(messageElem);
return message;
} catch (XMLParserException e) {
log.error("Encountered error parsing message into its DOM representation", e);
throw new MessageDecodingException("Encountered error parsing message into its DOM representation", e);
} catch (UnmarshallingException e) {
log.error("Encountered error unmarshalling message from its DOM representation", e);
throw new MessageDecodingException("Encountered error unmarshalling message from its DOM representation", e);
}
catch (Exception e) {
log.warn("Encountered error unmarshalling message from its DOM representation --->", e);
throw new MessageDecodingException("Encountered error unmarshalling message from its DOM representation", e);
}
}
But for the below code it is working fine, because here I have again converted to string and created the input stream and then get the dom which is passed for unmarshalling.
protected XMLObject unmarshallMessage(InputStream messageStream) throws MessageDecodingException {
log.debug("Parsing message stream into DOM document");
try {
Document messageDoc = parserPool.parse(messageStream);
Element messageElem = messageDoc.getDocumentElement();
Element elementsByTagName = (Element) messageElem.getElementsByTagName("saml2:Assertion").item(0);
Element issuer = messageDoc.createElement("saml2:Issuer");
issuer.setTextContent("emIDAM");
issuer.setAttribute("Format", "urn:oasis:names:tc:SAML:2.0:nameid-format:entity");
//converting again to string and getting dom
String xml = XMLHelper.nodeToString(messageElem);
messageStream = new ByteArrayInputStream(xml.getBytes());
messageDoc = parserPool.parse(messageStream);
messageElem = messageDoc.getDocumentElement();
log.debug("Unmarshalling message DOM");
Unmarshaller unmarshaller = Configuration.getUnmarshallerFactory().getUnmarshaller(messageElem);
if (unmarshaller == null) {
log.debug("Unable to unmarshall message, no unmarshaller registered for message element "
+ XMLHelper.getNodeQName(messageElem));
throw new MessageDecodingException(
"Unable to unmarshall message, no unmarshaller registered for message element "
+ XMLHelper.getNodeQName(messageElem));
}
XMLObject message = unmarshaller.unmarshall(messageElem);
return message;
} catch (XMLParserException e) {
log.error("Encountered error parsing message into its DOM representation", e);
throw new MessageDecodingException("Encountered error parsing message into its DOM representation", e);
} catch (UnmarshallingException e) {
log.error("Encountered error unmarshalling message from its DOM representation", e);
throw new MessageDecodingException("Encountered error unmarshalling message from its DOM representation", e);
}
catch (Exception e) {
log.warn("Encountered error unmarshalling message from its DOM representation --->", e);
throw new MessageDecodingException("Encountered error unmarshalling message from its DOM representation", e);
}
}
Upvotes: 4
Views: 1676
Reputation: 53451
Your problem may be related to your parser configuration, probably not configured as namespace aware.
Please, use something similar to the following code when initializing your parserPool
:
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
documentBuilderFactory.setNamespaceAware(true);
DocumentBuilder parserPool = documentBuilderFactory.newDocumentBuilder();
My guess is that the second test runs successfully because under the hood XMLHelper
nodeToString
is using writeNode
which in turn uses LSSerializer
for performing the actual XML serialization. Since the parser is declared to be non-namespace aware, the resulting XML output and corresponding parsed Document
will not contain namespace information or at least, it contains the information in such a way that the parsing process subsequently carried out by Unmarshaller
deems it appropriate.
In addition, please, be aware that the problem may be related with the way you are creating your new Element
as well because you are not providing namespace information as well: when you use createElement
, the resulting Element
will not contain localName
, prefix
, or namespaceURI
, all are set to null
. Please, try the following code instead:
Document messageDoc = parserPool.parse(messageStream);
Element messageElem = messageDoc.getDocumentElement();
Element assertionElem = (Element) messageElem.getElementsByTagName("saml2:Assertion").item(0);
Element issuerElement = messageDoc.createElementNS("urn:oasis:names:tc:SAML:2.0:assertion", "saml2:Issuer");
Attr formatAttr = messageDoc.createAttributeNS("urn:oasis:names:tc:SAML:2.0:assertion", "saml2:Format");
formatAttr.setNodeValue("urn:oasis:names:tc:SAML:2.0:nameid-format:entity");
issuerElement.setAttributeNode(formatAttr);
Text text = messageDoc.createTextNode("emIDAM");
issuerElement.appendChild(text);
assertionElem.appendChild(issuerElement);
Upvotes: 1