1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.ws.security.message;
19
20 import org.apache.commons.logging.Log;
21 import org.apache.commons.logging.LogFactory;
22 import org.apache.ws.security.SOAPConstants;
23 import org.apache.ws.security.WSSConfig;
24 import org.apache.ws.security.util.WSSecurityUtil;
25 import org.apache.xml.security.signature.XMLSignatureInput;
26 import org.apache.xml.security.utils.XMLUtils;
27 import org.apache.xml.security.utils.resolver.ResourceResolverException;
28 import org.apache.xml.security.utils.resolver.ResourceResolverSpi;
29 import org.apache.xml.utils.URI;
30 import org.w3c.dom.Attr;
31 import org.w3c.dom.Document;
32 import org.w3c.dom.Element;
33 import org.w3c.dom.NamedNodeMap;
34 import org.w3c.dom.Node;
35
36 import java.util.HashSet;
37 import java.util.Set;
38
39 /***
40 * XML-Security resolver that is used for resolving same-document URI like URI="#id".
41 * It is desgined to only work with SOAPEnvelopes.
42 * <p/>
43 *
44 * @author Davanum Srinivas (dims@yahoo.com).
45 */
46 public class EnvelopeIdResolver extends ResourceResolverSpi {
47 private static Log log =
48 LogFactory.getLog(EnvelopeIdResolver.class.getName());
49 private static Log tlog = LogFactory.getLog("org.apache.ws.security.TIME");
50
51 private static EnvelopeIdResolver resolver = null;
52 private WSSConfig wssConfig;
53
54 private boolean doDebug = false;
55
56 private Document document;
57
58 /***
59 * Singleton instance of the resolver.
60 * <p/>
61 *
62 * @return
63 */
64 public synchronized static ResourceResolverSpi getInstance(WSSConfig wssConfig) {
65
66
67 if (resolver == null || resolver.wssConfig != wssConfig) {
68 resolver = new EnvelopeIdResolver(wssConfig);
69 }
70 return resolver;
71 }
72
73 private EnvelopeIdResolver(WSSConfig wssConfig) {
74 this.wssConfig = wssConfig;
75 }
76
77 public EnvelopeIdResolver(WSSConfig wssConfig, Document document) {
78 this.wssConfig = wssConfig;
79 this.document = document;
80 }
81
82 /***
83 * This is the workhorse method used to resolve resources.
84 * <p/>
85 *
86 * @param uri
87 * @param BaseURI
88 * @return
89 * @throws ResourceResolverException
90 */
91 public XMLSignatureInput engineResolve(Attr uri, String BaseURI)
92 throws ResourceResolverException {
93
94 doDebug = log.isDebugEnabled();
95
96 long t0 = 0, t1 = 0;
97 if (tlog.isDebugEnabled()) {
98 t0 = System.currentTimeMillis();
99 }
100
101 String uriNodeValue = uri.getNodeValue();
102
103 if (doDebug) {
104 log.debug("enter engineResolve, look for: " + uriNodeValue);
105 }
106
107 Document doc = (this.document == null)? uri.getOwnerDocument() : this.document;
108 if (doc == null) {
109 throw new RuntimeException("No DOM Document is associated with the signature.");
110 }
111
112
113 XMLUtils.circumventBug2650(doc);
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128 String id = uriNodeValue.substring(1);
129 SOAPConstants sc = WSSecurityUtil.getSOAPConstants(doc.getDocumentElement());
130 Element selectedElem = WSSecurityUtil.findBodyElement(doc, sc);
131 if (selectedElem == null) {
132 throw new ResourceResolverException("generic.EmptyMessage",
133 new Object[]{"Body element not found"},
134 uri,
135 BaseURI);
136 }
137 String cId = selectedElem.getAttributeNS(wssConfig.getWsuNS(), "Id");
138
139
140
141
142
143
144 if (!id.equals(cId)) {
145 cId = null;
146 if ((selectedElem = WSSecurityUtil.getElementByWsuId(wssConfig, doc, uriNodeValue)) != null) {
147 cId = selectedElem.getAttribute("Id");
148 } else if ((selectedElem = WSSecurityUtil.getElementByGenId(doc, uriNodeValue)) != null) {
149 cId = selectedElem.getAttribute("Id");
150 }
151 if (cId == null) {
152 throw new ResourceResolverException("generic.EmptyMessage",
153 new Object[]{"Id not found"},
154 uri,
155 BaseURI);
156 }
157 }
158
159 Set resultSet = dereferenceSameDocumentURI(selectedElem);
160 XMLSignatureInput result = new XMLSignatureInput(resultSet);
161 result.setMIMEType("text/xml");
162 try {
163 URI uriNew = new URI(new URI(BaseURI), uri.getNodeValue());
164 result.setSourceURI(uriNew.toString());
165 } catch (URI.MalformedURIException ex) {
166 result.setSourceURI(BaseURI);
167 }
168 if (tlog.isDebugEnabled()) {
169 t1 = System.currentTimeMillis();
170 tlog.debug("engineResolve= " + (t1 - t0));
171 }
172 if (doDebug) {
173 log.debug("exit engineResolve, result: " + result);
174 }
175 return result;
176 }
177
178 /***
179 * This method helps the ResourceResolver to decide whether a
180 * ResourceResolverSpi is able to perform the requested action.
181 * <p/>
182 *
183 * @param uri
184 * @param BaseURI
185 * @return
186 */
187 public boolean engineCanResolve(Attr uri, String BaseURI) {
188 if (uri == null) {
189 return false;
190 }
191 String uriNodeValue = uri.getNodeValue();
192 return uriNodeValue.startsWith("#");
193 }
194
195 /***
196 * Dereferences a same-document URI fragment.
197 *
198 * @param node the node (document or element) referenced by the
199 * URI fragment. If null, returns an empty set.
200 * @return a set of nodes (minus any comment nodes)
201 */
202 private Set dereferenceSameDocumentURI(Node node) {
203 Set nodeSet = new HashSet();
204 if (node != null) {
205 nodeSetMinusCommentNodes(node, nodeSet, null);
206 }
207 return nodeSet;
208 }
209
210 /***
211 * Recursively traverses the subtree, and returns an XPath-equivalent
212 * node-set of all nodes traversed, excluding any comment nodes.
213 *
214 * @param node the node to traverse
215 * @param nodeSet the set of nodes traversed so far
216 * @param the previous sibling node
217 */
218 private void nodeSetMinusCommentNodes(Node node,
219 Set nodeSet,
220 Node prevSibling) {
221 if (doDebug) {
222 log.debug("Tag: "
223 + node.getNodeName()
224 + ", '"
225 + node.getNodeValue()
226 + "'");
227 }
228 switch (node.getNodeType()) {
229 case Node.ELEMENT_NODE:
230 NamedNodeMap attrs = node.getAttributes();
231 if (attrs != null) {
232 for (int i = 0; i < attrs.getLength(); i++) {
233 if (doDebug) {
234 log.debug("Attr: "
235 + attrs.item(i).getNodeName()
236 + ", '"
237 + attrs.item(i).getNodeValue()
238 + "'");
239 }
240 nodeSet.add(attrs.item(i));
241 }
242 }
243 nodeSet.add(node);
244 Node pSibling = null;
245 for (Node child = node.getFirstChild();
246 child != null;
247 child = child.getNextSibling()) {
248 nodeSetMinusCommentNodes(child, nodeSet, pSibling);
249 pSibling = child;
250 }
251 break;
252 case Node.TEXT_NODE:
253 case Node.CDATA_SECTION_NODE:
254
255
256 if (prevSibling != null
257 && (prevSibling.getNodeType() == Node.TEXT_NODE
258 || prevSibling.getNodeType()
259 == Node.CDATA_SECTION_NODE)) {
260 return;
261 }
262 case Node.PROCESSING_INSTRUCTION_NODE:
263 nodeSet.add(node);
264 }
265 }
266 }