Source code: com/sun/xacml/finder/impl/SelectorModule.java
1
2 /*
3 * @(#)SelectorModule.java
4 *
5 * Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * 1. Redistribution of source code must retain the above copyright notice,
11 * this list of conditions and the following disclaimer.
12 *
13 * 2. Redistribution in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * Neither the name of Sun Microsystems, Inc. or the names of contributors may
18 * be used to endorse or promote products derived from this software without
19 * specific prior written permission.
20 *
21 * This software is provided "AS IS," without a warranty of any kind. ALL
22 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
23 * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
24 * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN")
25 * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
26 * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
27 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
28 * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
29 * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
30 * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
31 * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
32 *
33 * You acknowledge that this software is not designed or intended for use in
34 * the design, construction, operation or maintenance of any nuclear facility.
35 */
36
37 package com.sun.xacml.finder.impl;
38
39 import com.sun.xacml.AbstractPolicy;
40 import com.sun.xacml.EvaluationCtx;
41 import com.sun.xacml.ParsingException;
42 import com.sun.xacml.UnknownIdentifierException;
43
44 import com.sun.xacml.attr.AttributeFactory;
45 import com.sun.xacml.attr.BagAttribute;
46
47 import com.sun.xacml.cond.EvaluationResult;
48
49 import com.sun.xacml.ctx.Status;
50
51 import com.sun.xacml.finder.AttributeFinderModule;
52
53 import java.net.URI;
54
55 import java.util.ArrayList;
56
57 import org.apache.xpath.XPathAPI;
58
59 import org.w3c.dom.NamedNodeMap;
60 import org.w3c.dom.Node;
61 import org.w3c.dom.NodeList;
62
63
64 /**
65 * This module implements the basic behavior of the AttributeSelectorType,
66 * looking for attribute values in the physical request document using the
67 * given XPath expression. This is implemented as a separate module (instead
68 * of being implemented directly in <code>AttributeSelector</code> so that
69 * programmers can remove this functionality if they want (it's optional in
70 * the spec), so they can replace this code with more efficient, specific
71 * code as needed, and so they can easily swap in different XPath libraries.
72 * <p>
73 * Note that if no matches are found, this module will return an empty bag
74 * (unless some error occurred). The <code>AttributeSelector</code> is still
75 * deciding what to return to the policy based on the MustBePresent
76 * attribute.
77 * <p>
78 * This module uses the Xalan XPath implementation, and supports only version
79 * 1.0 of XPath. It is a fully functional, correct implementation of XACML's
80 * AttributeSelector functionality, but is not designed for environments
81 * that make significant use of XPath queries. Developers for any such
82 * environment should consider implementing their own module.
83 *
84 * @since 1.0
85 * @author Seth Proctor
86 */
87 public class SelectorModule extends AttributeFinderModule
88 {
89
90 /**
91 * Returns true since this module supports retrieving attributes based on
92 * the data provided in an AttributeSelectorType.
93 *
94 * @return true
95 */
96 public boolean isSelectorSupported() {
97 return true;
98 }
99
100 /**
101 * Private helper to create a new processing error status result
102 */
103 private EvaluationResult createProcessingError(String msg) {
104 ArrayList code = new ArrayList();
105 code.add(Status.STATUS_PROCESSING_ERROR);
106 return new EvaluationResult(new Status(code, msg));
107 }
108
109 /**
110 * Tries to find attribute values based on the given selector data.
111 * The result, if successful, always contains a <code>BagAttribute</code>,
112 * even if only one value was found. If no values were found, but no other
113 * error occurred, an empty bag is returned.
114 *
115 * @param path the XPath expression to search against
116 * @param namespaceNode the DOM node defining namespace mappings to use,
117 * or null if mappings come from the context root
118 * @param type the datatype of the attributes to find
119 * @param context the representation of the request data
120 * @param xpathVersion the XPath version to use
121 *
122 * @return the result of attribute retrieval, which will be a bag of
123 * attributes or an error
124 */
125 public EvaluationResult findAttribute(String path, Node namespaceNode,
126 URI type, EvaluationCtx context,
127 String xpathVersion) {
128 // we only support 1.0
129 if (! xpathVersion.equals(AbstractPolicy.XPATH_1_0_VERSION))
130 return new EvaluationResult(BagAttribute.createEmptyBag(type));
131
132 // get the DOM root of the request document
133 Node root = context.getRequestRoot();
134
135 // if we were provided with a non-null namespace node, then use it
136 // to resolve namespaces, otherwise use the context root node
137 Node nsNode = (namespaceNode != null) ? namespaceNode : root;
138
139 // setup the root path (pre-pended to the context path), which...
140 String rootPath = "";
141
142 // ...only has content if the context path is relative
143 if (path.charAt(0) != '/') {
144 String rootName = root.getLocalName();
145
146 // see if the request root is in a namespace
147 String namespace = root.getNamespaceURI();
148
149 if (namespace == null) {
150 // no namespacing, so we're done
151 rootPath = "/" + rootName + "/";
152 } else {
153 // namespaces are used, so we need to lookup the correct
154 // prefix to use in the search string
155 NamedNodeMap nmap = namespaceNode.getAttributes();
156 rootPath = null;
157
158 for (int i = 0; i < nmap.getLength(); i++) {
159 Node n = nmap.item(i);
160 if (n.getNodeValue().equals(namespace)) {
161 // we found the matching namespace, so get the prefix
162 // and then break out
163 String name = n.getNodeName();
164 int pos = name.indexOf(':');
165
166 if (pos == -1) {
167 // the namespace was the default namespace
168 rootPath = "/";
169 } else {
170 // we found a prefixed namespace
171 rootPath = "/" + name.substring(pos + 1);
172 }
173
174 // finish off the string
175 rootPath += ":" + rootName + "/";
176
177 break;
178 }
179 }
180
181 // if the rootPath is still null, then we don't have any
182 // definitions for the namespace
183 if (rootPath == null)
184 return createProcessingError("Failed to map a namespace" +
185 " in an XPath expression");
186 }
187 }
188
189 // now do the query, pre-pending the root path to the context path
190 NodeList matches = null;
191 try {
192 // NOTE: see comments in XALAN docs about why this is slow
193 matches = XPathAPI.selectNodeList(root, rootPath + path, nsNode);
194 } catch (Exception e) {
195 // in the case of any exception, we need to return an error
196 return createProcessingError("error in XPath: " + e.getMessage());
197 }
198
199 if (matches.getLength() == 0) {
200 // we didn't find anything, so we return an empty bag
201 return new EvaluationResult(BagAttribute.createEmptyBag(type));
202 }
203
204 // there was at least one match, so try to generate the values
205 try {
206 ArrayList list = new ArrayList();
207 AttributeFactory attrFactory = AttributeFactory.getInstance();
208
209 for (int i = 0; i < matches.getLength(); i++) {
210 String text = null;
211 Node node = matches.item(i);
212 short nodeType = node.getNodeType();
213
214 // see if this is straight text, or a node with data under
215 // it and then get the values accordingly
216 if ((nodeType == Node.CDATA_SECTION_NODE) ||
217 (nodeType == Node.COMMENT_NODE) ||
218 (nodeType == Node.TEXT_NODE) ||
219 (nodeType == Node.ATTRIBUTE_NODE)) {
220 // there is no child to this node
221 text = node.getNodeValue();
222 } else {
223 // the data is in a child node
224 text = node.getFirstChild().getNodeValue();
225 }
226
227 list.add(attrFactory.createValue(type, text));
228 }
229
230 return new EvaluationResult(new BagAttribute(type, list));
231 } catch (ParsingException pe) {
232 return createProcessingError(pe.getMessage());
233 } catch (UnknownIdentifierException uie) {
234 return createProcessingError("unknown attribute type: " + type);
235 }
236 }
237
238 }