NameID attributes were improperly set in DOM.
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / aa / attrresolv / provider / SAML2PersistentID.java
index 0f65d20..7574614 100644 (file)
@@ -1,26 +1,17 @@
 /*
- * The Shibboleth License, Version 1. Copyright (c) 2002 University Corporation for Advanced Internet Development, Inc.
- * All rights reserved Redistribution and use in source and binary forms, with or without modification, are permitted
- * provided that the following conditions are met: Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials
- * provided with the distribution, if any, must include the following acknowledgment: "This product includes software
- * developed by the University Corporation for Advanced Internet Development <http://www.ucaid.edu>Internet2 Project.
- * Alternately, this acknowledegement may appear in the software itself, if and wherever such third-party
- * acknowledgments normally appear. Neither the name of Shibboleth nor the names of its contributors, nor Internet2, nor
- * the University Corporation for Advanced Internet Development, Inc., nor UCAID may be used to endorse or promote
- * products derived from this software without specific prior written permission. For written permission, please contact
- * shibboleth@shibboleth.org Products derived from this software may not be called Shibboleth, Internet2, UCAID, or the
- * University Corporation for Advanced Internet Development, nor may Shibboleth appear in their name, without prior
- * written permission of the University Corporation for Advanced Internet Development. THIS SOFTWARE IS PROVIDED BY THE
- * COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE
- * DISCLAIMED AND THE ENTIRE RISK OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. IN NO
- * EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC.
- * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
- * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Copyright [2005] [University Corporation for Advanced Internet Development, Inc.]
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
 package edu.internet2.middleware.shibboleth.aa.attrresolv.provider;
@@ -34,15 +25,19 @@ import java.security.Principal;
 import java.security.UnrecoverableKeyException;
 import java.security.cert.CertificateException;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Iterator;
 
 import javax.crypto.SecretKey;
 import javax.naming.NamingException;
 import javax.naming.directory.Attribute;
 import javax.naming.directory.Attributes;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
 
 import org.apache.log4j.Logger;
 import org.bouncycastle.util.encoders.Base64;
+import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
@@ -53,6 +48,8 @@ import edu.internet2.middleware.shibboleth.aa.attrresolv.Dependencies;
 import edu.internet2.middleware.shibboleth.aa.attrresolv.ResolutionPlugInException;
 import edu.internet2.middleware.shibboleth.aa.attrresolv.ResolverAttribute;
 import edu.internet2.middleware.shibboleth.common.ShibResource;
+import edu.internet2.middleware.shibboleth.common.XML;
+import edu.internet2.middleware.shibboleth.xml.Parser;
 
 /**
  * <code>AttributeDefinition</code> implementation that provides a persistent, but pseudonymous, identifier for
@@ -235,9 +232,7 @@ public class SAML2PersistentID extends BaseAttributeDefinition implements Attrib
                        return;
                }
 
-               if (lifeTime != -1) {
-                       attribute.setLifetime(lifeTime);
-               }
+               standardProcessing(attribute);
 
                // Hash the data together to produce the persistent ID.
                try {
@@ -248,18 +243,26 @@ public class SAML2PersistentID extends BaseAttributeDefinition implements Attrib
                        md.update((byte) '!');
                        String result = new String(Base64.encode(md.digest(salt)));
 
-                       // :-/ String-ified SAML2 persistent NameId format
-                       StringBuffer id = new StringBuffer();
-                       id.append("<saml2:NameID Format=\"urn:oasis:names:tc:SAML:2.0:nameid-format:persistent\" NameQualifier=\"");
-                       id.append(responder);
-                       id.append("\" SPNameQualifier=\"");
-                       id.append(requester);
-                       id.append("\" >");
-                       id.append(result.replaceAll(System.getProperty("line.separator"), ""));
-                       id.append("</saml2:NameID>");
-
-                       attribute.addValue(id.toString());
-                       attribute.setResolved();
+                       // SAML2 persistent NameId format
+                       try {
+                               DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
+                               docFactory.setNamespaceAware(true);
+                               Document placeHolder = docFactory.newDocumentBuilder().newDocument();
+                               Element nameIDNode = placeHolder.createElementNS(XML.SAML2ASSERT_NS, "NameID");
+                               nameIDNode.setAttributeNS(org.opensaml.XML.XMLNS_NS, "xmlns", XML.SAML2ASSERT_NS);
+                               nameIDNode.setAttributeNS(null, "Format", "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent");
+                               nameIDNode.setAttributeNS(null, "NameQualifier", responder);
+                               nameIDNode.setAttributeNS(null, "SPNameQualifier", requester);
+                               nameIDNode.appendChild(placeHolder.createTextNode(result.replaceAll(System
+                                               .getProperty("line.separator"), "")));
+
+                               attribute.addValue(nameIDNode);
+                               attribute.registerValueHandler(new DOMValueHandler());
+                               attribute.setResolved();
+                       } catch (ParserConfigurationException pce) {
+                               log.error("Unable to create DOM for persistent ID: " + pce);
+                               throw new ResolutionPlugInException("Error generating persistent ID.");
+                       }
 
                } catch (NoSuchAlgorithmException e) {
                        log.error("Unable to load SHA-1 hash algorithm.");
@@ -275,4 +278,49 @@ public class SAML2PersistentID extends BaseAttributeDefinition implements Attrib
                                (byte) 0x61, (byte) 0xEF};
                return Arrays.equals(defaultKey, salt);
        }
-}
+
+       class DOMValueHandler implements ValueHandler {
+
+               public void toDOM(Element valueElement, Object value, Document document) {
+
+                       valueElement.appendChild(document.importNode((Node) value, true));
+               }
+
+               public Iterator getValues(Collection internalValues) {
+
+                       return new SerializationIterator(internalValues.iterator());
+               }
+
+               public boolean equals(Object object) {
+
+                       if (object instanceof DOMValueHandler) { return true; }
+                       return false;
+               }
+
+               private class SerializationIterator implements Iterator {
+
+                       Iterator wrapped;
+
+                       SerializationIterator(Iterator i) {
+
+                               wrapped = i;
+                       }
+
+                       public boolean hasNext() {
+
+                               return wrapped.hasNext();
+                       }
+
+                       public Object next() {
+
+                               return Parser.serialize(((Element) wrapped.next()));
+                       }
+
+                       public void remove() {
+
+                               wrapped.remove();
+                       }
+
+               }
+       }
+}
\ No newline at end of file