1 /* 2 * reserved comment block 3 * DO NOT REMOVE OR ALTER! 4 */ 5 /* 6 * Copyright 1999-2004 The Apache Software Foundation. 7 * 8 * Licensed under the Apache License, Version 2.0 (the "License"); 9 * you may not use this file except in compliance with the License. 10 * You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 * 20 */ 21 package com.sun.org.apache.xml.internal.security.utils; 22 23 import java.lang.ref.WeakReference; 24 import java.util.Arrays; 25 import java.util.WeakHashMap; 26 import java.util.Map; 27 28 import org.w3c.dom.Attr; 29 import org.w3c.dom.Document; 30 import org.w3c.dom.Element; 31 import org.w3c.dom.NamedNodeMap; 32 import org.w3c.dom.Node; 33 34 35 /** 36 * Purpose of this class is to enable the XML Parser to keep track of ID 37 * attributes. This is done by 'registering' attributes of type ID at the 38 * IdResolver. This is necessary if we create a document from scratch and we 39 * sign some resources with a URI using a fragent identifier... 40 * <BR /> 41 * The problem is that if you do not validate a document, you cannot use the 42 * <CODE>getElementByID</CODE> functionality. So this modules uses some implicit 43 * knowledge on selected Schemas and DTDs to pick the right Element for a given 44 * ID: We know that all <CODE>@Id</CODE> attributes in an Element from the XML 45 * Signature namespace are of type <CODE>ID</CODE>. 46 * 47 * @author $Author: mullan $ 48 * @see <A HREF="http://www.xml.com/lpt/a/2001/11/07/id.html">"Identity Crisis" on xml.com</A> 49 */ 50 public class IdResolver { 51 52 /** {@link java.util.logging} logging facility */ 53 private static java.util.logging.Logger log = 54 java.util.logging.Logger.getLogger(IdResolver.class.getName()); 55 56 private static Map<Document, Map<String, WeakReference<Element>>> docMap = 57 new WeakHashMap<Document, Map<String, WeakReference<Element>>>(); 58 59 /** 60 * Constructor IdResolver 61 * 62 */ 63 private IdResolver() { 64 // we don't allow instantiation 65 } 66 67 /** 68 * Method registerElementById 69 * 70 * @param element the element to register 71 * @param idValue the value of the ID attribute 72 */ 73 public static void registerElementById(Element element, String idValue) { 74 Document doc = element.getOwnerDocument(); 75 Map<String, WeakReference<Element>> elementMap; 76 synchronized (docMap) { 77 elementMap = docMap.get(doc); 78 if (elementMap == null) { 79 elementMap = new WeakHashMap<String, WeakReference<Element>>(); 80 docMap.put(doc, elementMap); 81 } 82 } 83 elementMap.put(idValue, new WeakReference<Element>(element)); 84 } 85 86 /** 87 * Method registerElementById 88 * 89 * @param element the element to register 90 * @param id the ID attribute 91 */ 92 public static void registerElementById(Element element, Attr id) { 93 IdResolver.registerElementById(element, id.getNodeValue()); 94 } 95 96 /** 97 * Method getElementById 98 * 99 * @param doc the document 100 * @param id the value of the ID 101 * @return the element obtained by the id, or null if it is not found. 102 */ 103 public static Element getElementById(Document doc, String id) { 104 105 Element result = IdResolver.getElementByIdType(doc, id); 106 107 if (result != null) { 108 log.log(java.util.logging.Level.FINE, 109 "I could find an Element using the simple getElementByIdType method: " 110 + result.getTagName()); 111 112 return result; 113 } 114 115 result = IdResolver.getElementByIdUsingDOM(doc, id); 116 117 if (result != null) { 118 log.log(java.util.logging.Level.FINE, 119 "I could find an Element using the simple getElementByIdUsingDOM method: " 120 + result.getTagName()); 121 122 return result; 123 } 124 // this must be done so that Xalan can catch ALL namespaces 125 //XMLUtils.circumventBug2650(doc); 126 result = IdResolver.getElementBySearching(doc, id); 127 128 if (result != null) { 129 IdResolver.registerElementById(result, id); 130 131 return result; 132 } 133 134 return null; 135 } 136 137 138 /** 139 * Method getElementByIdUsingDOM 140 * 141 * @param doc the document 142 * @param id the value of the ID 143 * @return the element obtained by the id, or null if it is not found. 144 */ 145 private static Element getElementByIdUsingDOM(Document doc, String id) { 146 if (log.isLoggable(java.util.logging.Level.FINE)) 147 log.log(java.util.logging.Level.FINE, "getElementByIdUsingDOM() Search for ID " + id); 148 return doc.getElementById(id); 149 } 150 151 /** 152 * Method getElementByIdType 153 * 154 * @param doc the document 155 * @param id the value of the ID 156 * @return the element obtained by the id, or null if it is not found. 157 */ 158 private static Element getElementByIdType(Document doc, String id) { 159 if (log.isLoggable(java.util.logging.Level.FINE)) 160 log.log(java.util.logging.Level.FINE, "getElementByIdType() Search for ID " + id); 161 Map<String, WeakReference<Element>> elementMap; 162 synchronized (docMap) { 163 elementMap = docMap.get(doc); 164 } 165 if (elementMap != null) { 166 WeakReference<Element> weakReference = elementMap.get(id); 167 if (weakReference != null) { 168 return weakReference.get(); 169 } 170 } 171 return null; 172 } 173 174 private static java.util.List<String> names; 175 private static int namesLength; 176 static { 177 String namespaces[]={ 178 Constants.SignatureSpecNS, 179 EncryptionConstants.EncryptionSpecNS, 180 "http://schemas.xmlsoap.org/soap/security/2000-12", 181 "http://www.w3.org/2002/03/xkms#", 182 "urn:oasis:names:tc:SAML:1.0:assertion", 183 "urn:oasis:names:tc:SAML:1.0:protocol" 184 }; 185 names = Arrays.asList(namespaces); 186 namesLength = names.size(); 187 } 188 189 190 private static Element getElementBySearching(Node root,String id) { 191 Element []els=new Element[namesLength + 1]; 192 getEl(root,id,els); 193 for (int i=0;i<els.length;i++) { 194 if (els[i]!=null) { 195 return els[i]; 196 } 197 } 198 return null; 199 } 200 201 private static int getEl(Node currentNode,String id,Element []els) { 202 Node sibling=null; 203 Node parentNode=null; 204 do { 205 switch (currentNode.getNodeType()) { 206 case Node.DOCUMENT_FRAGMENT_NODE : 207 case Node.DOCUMENT_NODE : 208 sibling= currentNode.getFirstChild(); 209 break; 210 211 212 case Node.ELEMENT_NODE : 213 Element currentElement = (Element) currentNode; 214 if (isElement(currentElement, id, els)==1) 215 return 1; 216 sibling= currentNode.getFirstChild(); 217 if (sibling==null) { 218 if (parentNode != null) { 219 sibling= currentNode.getNextSibling(); 220 } 221 } else { 222 parentNode=currentElement; 223 } 224 break; 225 } while (sibling==null && parentNode!=null) { 226 sibling=parentNode.getNextSibling(); 227 parentNode=parentNode.getParentNode(); 228 if (parentNode != null && parentNode.getNodeType() != Node.ELEMENT_NODE) { 229 parentNode=null; 230 } 231 } 232 if (sibling==null) 233 return 1; 234 currentNode=sibling; 235 sibling=currentNode.getNextSibling(); 236 } while(true); 237 238 } 239 public static int isElement(Element el, String id,Element[] els) { 240 if (!el.hasAttributes()) { 241 return 0; 242 } 243 NamedNodeMap ns=el.getAttributes(); 244 int elementIndex=names.indexOf(el.getNamespaceURI()); 245 elementIndex=(elementIndex<0) ? namesLength : elementIndex; 246 for (int length=ns.getLength(), i=0; i<length; i++) { 247 Attr n=(Attr)ns.item(i); 248 String s=n.getNamespaceURI(); 249 250 int index=s==null ? elementIndex : names.indexOf(n.getNamespaceURI()); 251 index=(index<0) ? namesLength : index; 252 String name=n.getLocalName(); 253 if (name == null) 254 name = n.getName(); 255 if (name.length()>2) 256 continue; 257 String value=n.getNodeValue(); 258 if (name.charAt(0)=='I') { 259 char ch=name.charAt(1); 260 if (ch=='d' && value.equals(id)) { 261 els[index]=el; 262 if (index==0) { 263 return 1; 264 } 265 } else if (ch=='D' &&value.endsWith(id)) { 266 if (index!=3) { 267 index=namesLength; 268 } 269 els[index]=el; 270 } 271 } else if ( "id".equals(name) && value.equals(id) ) { 272 if (index!=2) { 273 index=namesLength; 274 } 275 els[index]=el; 276 } 277 } 278 //For an element namespace search for importants 279 if ((elementIndex==3)&&( 280 el.getAttribute("OriginalRequestID").equals(id) || 281 el.getAttribute("RequestID").equals(id) || 282 el.getAttribute("ResponseID").equals(id))) { 283 els[3]=el; 284 } else if ((elementIndex==4)&&( 285 el.getAttribute("AssertionID").equals(id))) { 286 els[4]=el; 287 } else if ((elementIndex==5)&&( 288 el.getAttribute("RequestID").equals(id) || 289 el.getAttribute("ResponseID").equals(id))) { 290 els[5]=el; 291 } 292 return 0; 293 } 294 }