blob: 61baa1595ad87c473e280a985f7c670c8bdd4d20 [file] [log] [blame]
/*
* reserved comment block
* DO NOT REMOVE OR ALTER!
*/
/*
* Copyright 1999-2004 The Apache Software Foundation.
*
* 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 com.sun.org.apache.xml.internal.security.utils;
import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.WeakHashMap;
import java.util.Map;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
/**
* Purpose of this class is to enable the XML Parser to keep track of ID
* attributes. This is done by 'registering' attributes of type ID at the
* IdResolver. This is necessary if we create a document from scratch and we
* sign some resources with a URI using a fragent identifier...
* <BR />
* The problem is that if you do not validate a document, you cannot use the
* <CODE>getElementByID</CODE> functionality. So this modules uses some implicit
* knowledge on selected Schemas and DTDs to pick the right Element for a given
* ID: We know that all <CODE>@Id</CODE> attributes in an Element from the XML
* Signature namespace are of type <CODE>ID</CODE>.
*
* @author $Author: mullan $
* @see <A HREF="http://www.xml.com/lpt/a/2001/11/07/id.html">"Identity Crisis" on xml.com</A>
*/
public class IdResolver {
/** {@link java.util.logging} logging facility */
private static java.util.logging.Logger log =
java.util.logging.Logger.getLogger(IdResolver.class.getName());
private static Map<Document, Map<String, WeakReference<Element>>> docMap =
new WeakHashMap<Document, Map<String, WeakReference<Element>>>();
/**
* Constructor IdResolver
*
*/
private IdResolver() {
// we don't allow instantiation
}
/**
* Method registerElementById
*
* @param element the element to register
* @param idValue the value of the ID attribute
*/
public static void registerElementById(Element element, String idValue) {
Document doc = element.getOwnerDocument();
Map<String, WeakReference<Element>> elementMap;
synchronized (docMap) {
elementMap = docMap.get(doc);
if (elementMap == null) {
elementMap = new WeakHashMap<String, WeakReference<Element>>();
docMap.put(doc, elementMap);
}
}
elementMap.put(idValue, new WeakReference<Element>(element));
}
/**
* Method registerElementById
*
* @param element the element to register
* @param id the ID attribute
*/
public static void registerElementById(Element element, Attr id) {
IdResolver.registerElementById(element, id.getNodeValue());
}
/**
* Method getElementById
*
* @param doc the document
* @param id the value of the ID
* @return the element obtained by the id, or null if it is not found.
*/
public static Element getElementById(Document doc, String id) {
Element result = IdResolver.getElementByIdType(doc, id);
if (result != null) {
log.log(java.util.logging.Level.FINE,
"I could find an Element using the simple getElementByIdType method: "
+ result.getTagName());
return result;
}
result = IdResolver.getElementByIdUsingDOM(doc, id);
if (result != null) {
log.log(java.util.logging.Level.FINE,
"I could find an Element using the simple getElementByIdUsingDOM method: "
+ result.getTagName());
return result;
}
// this must be done so that Xalan can catch ALL namespaces
//XMLUtils.circumventBug2650(doc);
result = IdResolver.getElementBySearching(doc, id);
if (result != null) {
IdResolver.registerElementById(result, id);
return result;
}
return null;
}
/**
* Method getElementByIdUsingDOM
*
* @param doc the document
* @param id the value of the ID
* @return the element obtained by the id, or null if it is not found.
*/
private static Element getElementByIdUsingDOM(Document doc, String id) {
if (log.isLoggable(java.util.logging.Level.FINE))
log.log(java.util.logging.Level.FINE, "getElementByIdUsingDOM() Search for ID " + id);
return doc.getElementById(id);
}
/**
* Method getElementByIdType
*
* @param doc the document
* @param id the value of the ID
* @return the element obtained by the id, or null if it is not found.
*/
private static Element getElementByIdType(Document doc, String id) {
if (log.isLoggable(java.util.logging.Level.FINE))
log.log(java.util.logging.Level.FINE, "getElementByIdType() Search for ID " + id);
Map<String, WeakReference<Element>> elementMap;
synchronized (docMap) {
elementMap = docMap.get(doc);
}
if (elementMap != null) {
WeakReference<Element> weakReference = elementMap.get(id);
if (weakReference != null) {
return weakReference.get();
}
}
return null;
}
private static java.util.List<String> names;
private static int namesLength;
static {
String namespaces[]={
Constants.SignatureSpecNS,
EncryptionConstants.EncryptionSpecNS,
"http://schemas.xmlsoap.org/soap/security/2000-12",
"http://www.w3.org/2002/03/xkms#",
"urn:oasis:names:tc:SAML:1.0:assertion",
"urn:oasis:names:tc:SAML:1.0:protocol"
};
names = Arrays.asList(namespaces);
namesLength = names.size();
}
private static Element getElementBySearching(Node root,String id) {
Element []els=new Element[namesLength + 1];
getEl(root,id,els);
for (int i=0;i<els.length;i++) {
if (els[i]!=null) {
return els[i];
}
}
return null;
}
private static int getEl(Node currentNode,String id,Element []els) {
Node sibling=null;
Node parentNode=null;
do {
switch (currentNode.getNodeType()) {
case Node.DOCUMENT_FRAGMENT_NODE :
case Node.DOCUMENT_NODE :
sibling= currentNode.getFirstChild();
break;
case Node.ELEMENT_NODE :
Element currentElement = (Element) currentNode;
if (isElement(currentElement, id, els)==1)
return 1;
sibling= currentNode.getFirstChild();
if (sibling==null) {
if (parentNode != null) {
sibling= currentNode.getNextSibling();
}
} else {
parentNode=currentElement;
}
break;
} while (sibling==null && parentNode!=null) {
sibling=parentNode.getNextSibling();
parentNode=parentNode.getParentNode();
if (!(parentNode instanceof Element)) {
parentNode=null;
}
}
if (sibling==null)
return 1;
currentNode=sibling;
sibling=currentNode.getNextSibling();
} while(true);
}
public static int isElement(Element el, String id,Element[] els) {
if (!el.hasAttributes()) {
return 0;
}
NamedNodeMap ns=el.getAttributes();
int elementIndex=names.indexOf(el.getNamespaceURI());
elementIndex=(elementIndex<0) ? namesLength : elementIndex;
for (int length=ns.getLength(), i=0; i<length; i++) {
Attr n=(Attr)ns.item(i);
String s=n.getNamespaceURI();
int index=s==null ? elementIndex : names.indexOf(n.getNamespaceURI());
index=(index<0) ? namesLength : index;
String name=n.getLocalName();
if (name.length()>2)
continue;
String value=n.getNodeValue();
if (name.charAt(0)=='I') {
char ch=name.charAt(1);
if (ch=='d' && value.equals(id)) {
els[index]=el;
if (index==0) {
return 1;
}
} else if (ch=='D' &&value.endsWith(id)) {
if (index!=3) {
index=namesLength;
}
els[index]=el;
}
} else if ( "id".equals(name) && value.equals(id) ) {
if (index!=2) {
index=namesLength;
}
els[index]=el;
}
}
//For an element namespace search for importants
if ((elementIndex==3)&&(
el.getAttribute("OriginalRequestID").equals(id) ||
el.getAttribute("RequestID").equals(id) ||
el.getAttribute("ResponseID").equals(id))) {
els[3]=el;
} else if ((elementIndex==4)&&(
el.getAttribute("AssertionID").equals(id))) {
els[4]=el;
} else if ((elementIndex==5)&&(
el.getAttribute("RequestID").equals(id) ||
el.getAttribute("ResponseID").equals(id))) {
els[5]=el;
}
return 0;
}
}