001: /*
002: * $Id: ResolverId.java,v 1.5 2007/01/08 16:06:09 shyam_rao Exp $
003: */
004:
005: /*
006: * The contents of this file are subject to the terms
007: * of the Common Development and Distribution License
008: * (the License). You may not use this file except in
009: * compliance with the License.
010: *
011: * You can obtain a copy of the license at
012: * https://glassfish.dev.java.net/public/CDDLv1.0.html.
013: * See the License for the specific language governing
014: * permissions and limitations under the License.
015: *
016: * When distributing Covered Code, include this CDDL
017: * Header Notice in each file and include the License file
018: * at https://glassfish.dev.java.net/public/CDDLv1.0.html.
019: * If applicable, add the following below the CDDL Header,
020: * with the fields enclosed by brackets [] replaced by
021: * you own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Copyright 2006 Sun Microsystems Inc. All Rights Reserved
025: */
026:
027: package com.sun.xml.wss.impl.resolver;
028:
029: import java.util.HashSet;
030: import java.util.Set;
031: import java.util.logging.Level;
032: import java.util.logging.Logger;
033:
034: import javax.xml.transform.TransformerException;
035:
036: import org.w3c.dom.Attr;
037: import org.w3c.dom.Document;
038: import org.w3c.dom.Element;
039: import org.w3c.dom.NamedNodeMap;
040: import org.w3c.dom.Node;
041: import org.w3c.dom.NodeList;
042:
043: import com.sun.org.apache.xml.internal.utils.URI;
044: import com.sun.org.apache.xml.internal.security.signature.XMLSignatureInput;
045: import com.sun.org.apache.xml.internal.security.utils.XMLUtils;
046: import com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolverException;
047: import com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolverSpi;
048: import com.sun.org.apache.xpath.internal.XPathAPI;
049: import com.sun.xml.wss.impl.MessageConstants;
050: import com.sun.xml.wss.logging.LogDomainConstants;
051:
052: /**
053: * This resolver is used for resolving same-document URIs for eg. URI="#wsuId".
054: *
055: * @author Anil Tappetla
056: */
057:
058: public class ResolverId extends ResourceResolverSpi {
059:
060: /* Implementation class name */
061: private static String implementationClassName = ResolverId.class
062: .getName();
063:
064: protected static final Logger log = Logger.getLogger(
065: LogDomainConstants.WSS_API_DOMAIN,
066: LogDomainConstants.WSS_API_DOMAIN_BUNDLE);
067:
068: /**
069: * Method getResolverName
070: *
071: * @return The resolver implementation class name
072: */
073: public static String getResolverName() {
074: return implementationClassName;
075: }
076:
077: /**
078: * Method engineResolve
079: *
080: * @param uri
081: * @param BaseURI
082: *
083: * @return XMLSignatureInput
084: *
085: * @throws ResourceResolverException
086: */
087: public XMLSignatureInput engineResolve(Attr uri, String BaseURI)
088: throws ResourceResolverException {
089: String uriNodeValue = uri.getNodeValue();
090: if (MessageConstants.debug) {
091: log.log(Level.FINEST, "uri = " + uriNodeValue);
092: }
093: Document doc = uri.getOwnerDocument();
094:
095: // this must be done so that Xalan can catch ALL namespaces
096: XMLUtils.circumventBug2650(doc);
097:
098: Element selectedElem = null;
099: if (uriNodeValue.equals("")) {
100: selectedElem = doc.getDocumentElement();
101: } else {
102: /*
103: * URI="#wsuId"
104: * Identifies a node-set containing the element with wsu:Id attribute
105: * value 'wsuId' of the XML resource containing the signature.
106: * XML Signature (and its applications) modify this node-set to
107: * include the element plus all descendents including namespaces and
108: * attributes -- but not comments.
109: */
110: String id = uriNodeValue.substring(1);
111: try {
112: selectedElem = getElementById(doc, id);
113: } catch (TransformerException e) {
114: log.log(Level.SEVERE,
115: "WSS0603.xpathapi.transformer.exception", e
116: .getMessage());
117: throw new ResourceResolverException("empty", e, uri,
118: BaseURI);
119: }
120: }
121:
122: if (selectedElem == null) {
123: log.log(Level.SEVERE, "WSS0604.cannot.find.element");
124: throw new ResourceResolverException("empty", uri, BaseURI);
125: }
126: Set resultSet = dereferenceSameDocumentURI(selectedElem);
127: XMLSignatureInput result = new XMLSignatureInput(resultSet);
128:
129: result.setMIMEType("text/xml");
130:
131: try {
132: URI uriNew = new URI(new URI(BaseURI), uri.getNodeValue());
133: result.setSourceURI(uriNew.toString());
134: } catch (URI.MalformedURIException ex) {
135: result.setSourceURI(BaseURI);
136: }
137:
138: return result;
139: }
140:
141: /**
142: * Method engineCanResolve
143: *
144: * @param uri
145: * @param BaseURI
146: *
147: * @return true if uri node can be resolved, false otherwise
148: */
149: public boolean engineCanResolve(Attr uri, String BaseURI) {
150: if (uri == null)
151: return false;
152:
153: String uriNodeValue = uri.getNodeValue();
154: if (uriNodeValue.startsWith("#"))
155: return true;
156:
157: return false;
158: }
159:
160: /**
161: * Looks up elements with wsu:Id or Id in xenc or dsig namespace
162: *
163: * @param doc
164: * @param id
165: *
166: * @return element
167: *
168: * @throws TransformerException
169: */
170: private Element getElementById(Document doc, String id)
171: throws TransformerException {
172:
173: Element selement = doc.getElementById(id);
174: if (selement != null) {
175: if (MessageConstants.debug) {
176: log.log(Level.FINEST,
177: "Document.getElementById() returned "
178: + selement);
179: }
180: return selement;
181: }
182:
183: if (MessageConstants.debug) {
184: log.log(Level.FINEST,
185: "Document.getElementById() FAILED......'" + id
186: + "'");
187: }
188:
189: Element nscontext = XMLUtils.createDSctx(doc, "wsu",
190: MessageConstants.WSU_NS);
191: Element element = (Element) XPathAPI.selectSingleNode(doc,
192: "//*[@wsu:Id='" + id + "']", nscontext);
193:
194: if (element == null) {
195: NodeList elems = XPathAPI.selectNodeList(doc, "//*[@Id='"
196: + id + "']", nscontext);
197: for (int i = 0; i < elems.getLength(); i++) {
198: Element elem = (Element) elems.item(i);
199: String namespace = elem.getNamespaceURI();
200: if (namespace.equals(MessageConstants.DSIG_NS)
201: || namespace.equals(MessageConstants.XENC_NS)) {
202: element = elem;
203: break;
204: }
205: }
206: }
207: // look for SAML AssertionID
208: if (element == null) {
209:
210: NodeList assertions = doc
211: .getElementsByTagName(MessageConstants.SAML_ASSERTION_LNAME);
212:
213: int len = assertions.getLength();
214: if (len > 0) {
215: for (int i = 0; i < len; i++) {
216: Element elem = (Element) assertions.item(i);
217: String assertionId = elem
218: .getAttribute(MessageConstants.SAML_ASSERTIONID_LNAME);
219: if (id.equals(assertionId)) {
220: element = elem;
221: break;
222: }
223: }
224: }
225: }
226:
227: return element;
228: }
229:
230: /**
231: * Dereferences a same-document URI fragment.
232: *
233: * @param node the node referenced by the -
234: * URI fragment. If null, returns -
235: * an empty set.
236: * @return
237: */
238: private Set dereferenceSameDocumentURI(Node node) {
239: Set nodeSet = new HashSet();
240: if (node != null) {
241: nodeSetMinusCommentNodes(node, nodeSet, null);
242: }
243: return nodeSet;
244: }
245:
246: /**
247: * Method nodeSetMinusCommentNodes
248: *
249: * @param node the node to traverse
250: * @param nodeSet the set of nodes traversed so far
251: * @param the previous sibling node
252: */
253: private void nodeSetMinusCommentNodes(Node node, Set nodeSet,
254: Node prevSibling) {
255: switch (node.getNodeType()) {
256: case Node.ELEMENT_NODE:
257: NamedNodeMap attrs = node.getAttributes();
258: if (attrs != null) {
259: for (int i = 0; i < attrs.getLength(); i++) {
260: nodeSet.add(attrs.item(i));
261: }
262: }
263: nodeSet.add(node);
264: Node pSibling = null;
265: for (Node child = node.getFirstChild(); child != null; child = child
266: .getNextSibling()) {
267: nodeSetMinusCommentNodes(child, nodeSet, pSibling);
268: pSibling = child;
269: }
270: break;
271: case Node.TEXT_NODE:
272: case Node.CDATA_SECTION_NODE:
273: // emulate XPath which only returns the first node in
274: // contiguous text/cdata nodes
275: if (prevSibling != null
276: && (prevSibling.getNodeType() == Node.TEXT_NODE || prevSibling
277: .getNodeType() == Node.CDATA_SECTION_NODE)) {
278: return;
279: }
280: case Node.PROCESSING_INSTRUCTION_NODE:
281: nodeSet.add(node);
282: }
283: }
284: }
|