1 /* 2 * reserved comment block 3 * DO NOT REMOVE OR ALTER! 4 */ 5 /** 6 * Licensed to the Apache Software Foundation (ASF) under one 7 * or more contributor license agreements. See the NOTICE file 8 * distributed with this work for additional information 9 * regarding copyright ownership. The ASF licenses this file 10 * to you under the Apache License, Version 2.0 (the 11 * "License"); you may not use this file except in compliance 12 * with the License. You may obtain a copy of the License at 13 * 14 * http://www.apache.org/licenses/LICENSE-2.0 15 * 16 * Unless required by applicable law or agreed to in writing, 17 * software distributed under the License is distributed on an 18 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 19 * KIND, either express or implied. See the License for the 20 * specific language governing permissions and limitations 21 * under the License. 22 */ 23 package com.sun.org.apache.xml.internal.security.utils; 24 25 import java.math.BigInteger; 26 import java.util.concurrent.ConcurrentHashMap; 27 import java.util.Map; 28 29 import com.sun.org.apache.xml.internal.security.exceptions.Base64DecodingException; 30 import com.sun.org.apache.xml.internal.security.exceptions.XMLSecurityException; 31 import org.w3c.dom.Attr; 32 import org.w3c.dom.Document; 33 import org.w3c.dom.Element; 34 import org.w3c.dom.Node; 35 import org.w3c.dom.NodeList; 36 import org.w3c.dom.Text; 37 38 /** 39 * This is the base class to all Objects which have a direct 1:1 mapping to an 40 * Element in a particular namespace. 41 */ 42 public abstract class ElementProxy { 43 44 protected static final java.util.logging.Logger log = 45 java.util.logging.Logger.getLogger(ElementProxy.class.getName()); 46 47 /** Field constructionElement */ 48 protected Element _constructionElement = null; 49 50 /** Field baseURI */ 51 protected String _baseURI = null; 52 53 /** Field doc */ 54 protected Document _doc = null; 55 56 /** Field prefixMappings */ 57 private static Map<String, String> prefixMappings = new ConcurrentHashMap<String, String>(); 58 59 /** 60 * Constructor ElementProxy 61 * 62 */ 63 public ElementProxy() { 64 } 65 66 /** 67 * Constructor ElementProxy 68 * 69 * @param doc 70 */ 71 public ElementProxy(Document doc) { 72 if (doc == null) { 73 throw new RuntimeException("Document is null"); 74 } 75 76 this._doc = doc; 77 this._constructionElement = 78 createElementForFamilyLocal(this._doc, this.getBaseNamespace(), this.getBaseLocalName()); 79 } 80 81 /** 82 * Constructor ElementProxy 83 * 84 * @param element 85 * @param BaseURI 86 * @throws XMLSecurityException 87 */ 88 public ElementProxy(Element element, String BaseURI) throws XMLSecurityException { 89 if (element == null) { 90 throw new XMLSecurityException("ElementProxy.nullElement"); 91 } 92 93 if (log.isLoggable(java.util.logging.Level.FINE)) { 94 log.log(java.util.logging.Level.FINE, "setElement(\"" + element.getTagName() + "\", \"" + BaseURI + "\")"); 95 } 96 97 this._doc = element.getOwnerDocument(); 98 this._constructionElement = element; 99 this._baseURI = BaseURI; 100 101 this.guaranteeThatElementInCorrectSpace(); 102 } 103 104 /** 105 * Returns the namespace of the Elements of the sub-class. 106 * 107 * @return the namespace of the Elements of the sub-class. 108 */ 109 public abstract String getBaseNamespace(); 110 111 /** 112 * Returns the localname of the Elements of the sub-class. 113 * 114 * @return the localname of the Elements of the sub-class. 115 */ 116 public abstract String getBaseLocalName(); 117 118 119 protected Element createElementForFamilyLocal( 120 Document doc, String namespace, String localName 121 ) { 122 Element result = null; 123 if (namespace == null) { 124 result = doc.createElementNS(null, localName); 125 } else { 126 String baseName = this.getBaseNamespace(); 127 String prefix = ElementProxy.getDefaultPrefix(baseName); 128 if ((prefix == null) || (prefix.length() == 0)) { 129 result = doc.createElementNS(namespace, localName); 130 result.setAttributeNS(Constants.NamespaceSpecNS, "xmlns", namespace); 131 } else { 132 result = doc.createElementNS(namespace, prefix + ":" + localName); 133 result.setAttributeNS(Constants.NamespaceSpecNS, "xmlns:" + prefix, namespace); 134 } 135 } 136 return result; 137 } 138 139 140 /** 141 * This method creates an Element in a given namespace with a given localname. 142 * It uses the {@link ElementProxy#getDefaultPrefix} method to decide whether 143 * a particular prefix is bound to that namespace. 144 * <BR /> 145 * This method was refactored out of the constructor. 146 * 147 * @param doc 148 * @param namespace 149 * @param localName 150 * @return The element created. 151 */ 152 public static Element createElementForFamily(Document doc, String namespace, String localName) { 153 Element result = null; 154 String prefix = ElementProxy.getDefaultPrefix(namespace); 155 156 if (namespace == null) { 157 result = doc.createElementNS(null, localName); 158 } else { 159 if ((prefix == null) || (prefix.length() == 0)) { 160 result = doc.createElementNS(namespace, localName); 161 result.setAttributeNS(Constants.NamespaceSpecNS, "xmlns", namespace); 162 } else { 163 result = doc.createElementNS(namespace, prefix + ":" + localName); 164 result.setAttributeNS(Constants.NamespaceSpecNS, "xmlns:" + prefix, namespace); 165 } 166 } 167 168 return result; 169 } 170 171 /** 172 * Method setElement 173 * 174 * @param element 175 * @param BaseURI 176 * @throws XMLSecurityException 177 */ 178 public void setElement(Element element, String BaseURI) throws XMLSecurityException { 179 if (element == null) { 180 throw new XMLSecurityException("ElementProxy.nullElement"); 181 } 182 183 if (log.isLoggable(java.util.logging.Level.FINE)) { 184 log.log(java.util.logging.Level.FINE, "setElement(" + element.getTagName() + ", \"" + BaseURI + "\""); 185 } 186 187 this._doc = element.getOwnerDocument(); 188 this._constructionElement = element; 189 this._baseURI = BaseURI; 190 } 191 192 193 /** 194 * Returns the Element which was constructed by the Object. 195 * 196 * @return the Element which was constructed by the Object. 197 */ 198 public final Element getElement() { 199 return this._constructionElement; 200 } 201 202 /** 203 * Returns the Element plus a leading and a trailing CarriageReturn Text node. 204 * 205 * @return the Element which was constructed by the Object. 206 */ 207 public final NodeList getElementPlusReturns() { 208 209 HelperNodeList nl = new HelperNodeList(); 210 211 nl.appendChild(this._doc.createTextNode("\n")); 212 nl.appendChild(this.getElement()); 213 nl.appendChild(this._doc.createTextNode("\n")); 214 215 return nl; 216 } 217 218 /** 219 * Method getDocument 220 * 221 * @return the Document where this element is contained. 222 */ 223 public Document getDocument() { 224 return this._doc; 225 } 226 227 /** 228 * Method getBaseURI 229 * 230 * @return the base uri of the namespace of this element 231 */ 232 public String getBaseURI() { 233 return this._baseURI; 234 } 235 236 /** 237 * Method guaranteeThatElementInCorrectSpace 238 * 239 * @throws XMLSecurityException 240 */ 241 void guaranteeThatElementInCorrectSpace() throws XMLSecurityException { 242 243 String expectedLocalName = this.getBaseLocalName(); 244 String expectedNamespaceUri = this.getBaseNamespace(); 245 246 String actualLocalName = this._constructionElement.getLocalName(); 247 String actualNamespaceUri = this._constructionElement.getNamespaceURI(); 248 249 if(!expectedNamespaceUri.equals(actualNamespaceUri) 250 && !expectedLocalName.equals(actualLocalName)) { 251 Object exArgs[] = { actualNamespaceUri + ":" + actualLocalName, 252 expectedNamespaceUri + ":" + expectedLocalName}; 253 throw new XMLSecurityException("xml.WrongElement", exArgs); 254 } 255 } 256 257 /** 258 * Method addBigIntegerElement 259 * 260 * @param bi 261 * @param localname 262 */ 263 public void addBigIntegerElement(BigInteger bi, String localname) { 264 if (bi != null) { 265 Element e = XMLUtils.createElementInSignatureSpace(this._doc, localname); 266 267 Base64.fillElementWithBigInteger(e, bi); 268 this._constructionElement.appendChild(e); 269 XMLUtils.addReturnToElement(this._constructionElement); 270 } 271 } 272 273 /** 274 * Method addBase64Element 275 * 276 * @param bytes 277 * @param localname 278 */ 279 public void addBase64Element(byte[] bytes, String localname) { 280 if (bytes != null) { 281 Element e = Base64.encodeToElement(this._doc, localname, bytes); 282 283 this._constructionElement.appendChild(e); 284 if (!XMLUtils.ignoreLineBreaks()) { 285 this._constructionElement.appendChild(this._doc.createTextNode("\n")); 286 } 287 } 288 } 289 290 /** 291 * Method addTextElement 292 * 293 * @param text 294 * @param localname 295 */ 296 public void addTextElement(String text, String localname) { 297 Element e = XMLUtils.createElementInSignatureSpace(this._doc, localname); 298 Text t = this._doc.createTextNode(text); 299 300 e.appendChild(t); 301 this._constructionElement.appendChild(e); 302 XMLUtils.addReturnToElement(this._constructionElement); 303 } 304 305 /** 306 * Method addBase64Text 307 * 308 * @param bytes 309 */ 310 public void addBase64Text(byte[] bytes) { 311 if (bytes != null) { 312 Text t = XMLUtils.ignoreLineBreaks() 313 ? this._doc.createTextNode(Base64.encode(bytes)) 314 : this._doc.createTextNode("\n" + Base64.encode(bytes) + "\n"); 315 this._constructionElement.appendChild(t); 316 } 317 } 318 319 /** 320 * Method addText 321 * 322 * @param text 323 */ 324 public void addText(String text) { 325 if (text != null) { 326 Text t = this._doc.createTextNode(text); 327 328 this._constructionElement.appendChild(t); 329 } 330 } 331 332 /** 333 * Method getVal 334 * 335 * @param localname 336 * @param namespace 337 * @return The biginteger contained in the given element 338 * @throws Base64DecodingException 339 */ 340 public BigInteger getBigIntegerFromChildElement( 341 String localname, String namespace 342 ) throws Base64DecodingException { 343 return Base64.decodeBigIntegerFromText( 344 XMLUtils.selectNodeText( 345 this._constructionElement.getFirstChild(), namespace, localname, 0 346 ) 347 ); 348 } 349 350 /** 351 * Method getBytesFromChildElement 352 * @deprecated 353 * @param localname 354 * @param namespace 355 * @return the bytes 356 * @throws XMLSecurityException 357 */ 358 @Deprecated 359 public byte[] getBytesFromChildElement(String localname, String namespace) 360 throws XMLSecurityException { 361 Element e = 362 XMLUtils.selectNode( 363 this._constructionElement.getFirstChild(), namespace, localname, 0 364 ); 365 366 return Base64.decode(e); 367 } 368 369 /** 370 * Method getTextFromChildElement 371 * 372 * @param localname 373 * @param namespace 374 * @return the Text of the textNode 375 */ 376 public String getTextFromChildElement(String localname, String namespace) { 377 return XMLUtils.selectNode( 378 this._constructionElement.getFirstChild(), 379 namespace, 380 localname, 381 0).getTextContent(); 382 } 383 384 /** 385 * Method getBytesFromTextChild 386 * 387 * @return The base64 bytes from the text children of this element 388 * @throws XMLSecurityException 389 */ 390 public byte[] getBytesFromTextChild() throws XMLSecurityException { 391 return Base64.decode(XMLUtils.getFullTextChildrenFromElement(this._constructionElement)); 392 } 393 394 /** 395 * Method getTextFromTextChild 396 * 397 * @return the Text obtained by concatenating all the text nodes of this 398 * element 399 */ 400 public String getTextFromTextChild() { 401 return XMLUtils.getFullTextChildrenFromElement(this._constructionElement); 402 } 403 404 /** 405 * Method length 406 * 407 * @param namespace 408 * @param localname 409 * @return the number of elements {namespace}:localname under this element 410 */ 411 public int length(String namespace, String localname) { 412 int number = 0; 413 Node sibling = this._constructionElement.getFirstChild(); 414 while (sibling != null) { 415 if (localname.equals(sibling.getLocalName()) 416 && namespace.equals(sibling.getNamespaceURI())) { 417 number++; 418 } 419 sibling = sibling.getNextSibling(); 420 } 421 return number; 422 } 423 424 /** 425 * Adds an xmlns: definition to the Element. This can be called as follows: 426 * 427 * <PRE> 428 * // set namespace with ds prefix 429 * xpathContainer.setXPathNamespaceContext("ds", "http://www.w3.org/2000/09/xmldsig#"); 430 * xpathContainer.setXPathNamespaceContext("xmlns:ds", "http://www.w3.org/2000/09/xmldsig#"); 431 * </PRE> 432 * 433 * @param prefix 434 * @param uri 435 * @throws XMLSecurityException 436 */ 437 public void setXPathNamespaceContext(String prefix, String uri) 438 throws XMLSecurityException { 439 String ns; 440 441 if ((prefix == null) || (prefix.length() == 0)) { 442 throw new XMLSecurityException("defaultNamespaceCannotBeSetHere"); 443 } else if (prefix.equals("xmlns")) { 444 throw new XMLSecurityException("defaultNamespaceCannotBeSetHere"); 445 } else if (prefix.startsWith("xmlns:")) { 446 ns = prefix;//"xmlns:" + prefix.substring("xmlns:".length()); 447 } else { 448 ns = "xmlns:" + prefix; 449 } 450 451 Attr a = this._constructionElement.getAttributeNodeNS(Constants.NamespaceSpecNS, ns); 452 453 if (a != null) { 454 if (!a.getNodeValue().equals(uri)) { 455 Object exArgs[] = { ns, this._constructionElement.getAttributeNS(null, ns) }; 456 457 throw new XMLSecurityException("namespacePrefixAlreadyUsedByOtherURI", exArgs); 458 } 459 return; 460 } 461 462 this._constructionElement.setAttributeNS(Constants.NamespaceSpecNS, ns, uri); 463 } 464 465 /** 466 * Method setDefaultPrefix 467 * 468 * @param namespace 469 * @param prefix 470 * @throws XMLSecurityException 471 */ 472 public static void setDefaultPrefix(String namespace, String prefix) 473 throws XMLSecurityException { 474 if (prefixMappings.containsValue(prefix)) { 475 String storedPrefix = prefixMappings.get(namespace); 476 if (!storedPrefix.equals(prefix)) { 477 Object exArgs[] = { prefix, namespace, storedPrefix }; 478 479 throw new XMLSecurityException("prefix.AlreadyAssigned", exArgs); 480 } 481 } 482 483 if (Constants.SignatureSpecNS.equals(namespace)) { 484 XMLUtils.setDsPrefix(prefix); 485 } 486 if (EncryptionConstants.EncryptionSpecNS.equals(namespace)) { 487 XMLUtils.setXencPrefix(prefix); 488 } 489 prefixMappings.put(namespace, prefix); 490 } 491 492 /** 493 * This method registers the default prefixes. 494 */ 495 public static void registerDefaultPrefixes() throws XMLSecurityException { 496 setDefaultPrefix("http://www.w3.org/2000/09/xmldsig#", "ds"); 497 setDefaultPrefix("http://www.w3.org/2001/04/xmlenc#", "xenc"); 498 setDefaultPrefix("http://www.w3.org/2009/xmlenc11#", "xenc11"); 499 setDefaultPrefix("http://www.xmlsecurity.org/experimental#", "experimental"); 500 setDefaultPrefix("http://www.w3.org/2002/04/xmldsig-filter2", "dsig-xpath-old"); 501 setDefaultPrefix("http://www.w3.org/2002/06/xmldsig-filter2", "dsig-xpath"); 502 setDefaultPrefix("http://www.w3.org/2001/10/xml-exc-c14n#", "ec"); 503 setDefaultPrefix( 504 "http://www.nue.et-inf.uni-siegen.de/~geuer-pollmann/#xpathFilter", "xx" 505 ); 506 } 507 508 /** 509 * Method getDefaultPrefix 510 * 511 * @param namespace 512 * @return the default prefix bind to this element. 513 */ 514 public static String getDefaultPrefix(String namespace) { 515 return prefixMappings.get(namespace); 516 } 517 518 protected void setLocalIdAttribute(String attrName, String value) { 519 520 if (value != null) { 521 Attr attr = getDocument().createAttributeNS(null, attrName); 522 attr.setValue(value); 523 getElement().setAttributeNodeNS(attr); 524 getElement().setIdAttributeNode(attr, true); 525 } 526 else { 527 getElement().removeAttributeNS(null, attrName); 528 } 529 } 530 }