Source code: com/sun/xacml/Rule.java
1
2 /*
3 * @(#)Rule.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;
38
39 import com.sun.xacml.attr.BooleanAttribute;
40
41 import com.sun.xacml.cond.Apply;
42 import com.sun.xacml.cond.EvaluationResult;
43
44 import com.sun.xacml.ctx.Result;
45 import com.sun.xacml.ctx.Status;
46
47 import java.io.OutputStream;
48 import java.io.PrintStream;
49
50 import java.net.URI;
51 import java.net.URISyntaxException;
52
53 import java.util.ArrayList;
54 import java.util.Collections;
55 import java.util.List;
56
57 import org.w3c.dom.NamedNodeMap;
58 import org.w3c.dom.Node;
59 import org.w3c.dom.NodeList;
60
61
62 /**
63 * Represents the RuleType XACML type. This has a target for matching, and
64 * encapsulates the condition and all sub-operations that make up the heart
65 * of most policies.
66 *
67 * @since 1.0
68 * @author Seth Proctor
69 */
70 public class Rule implements PolicyTreeElement
71 {
72
73 // the attributes associated with this Rule
74 private URI idAttr;
75 private int effectAttr;
76
77 // the elements in the rule, each of which is optional
78 private String description = null;
79 private Target target = null;
80 private Apply condition = null;
81
82 /**
83 * Creates a new <code>Rule</code> object.
84 *
85 * @param id the rule's identifier
86 * @param effect the effect to return if the rule applies (either
87 * Pemit or Deny) as specified in <code>Result</code>
88 * @param description a textual description, or null
89 * @param target the rule's target, or null if the target is to be
90 * inherited from the encompassing policy
91 * @param condition the rule's condition, or null if there is none
92 */
93 public Rule(URI id, int effect, String description, Target target,
94 Apply condition) {
95 idAttr = id;
96 effectAttr = effect;
97 this.description = description;
98 this.target = target;
99 this.condition = condition;
100 }
101
102 /**
103 * Returns a new instance of the <code>Rule</code> class based on a
104 * DOM node. The node must be the root of an XML RuleType.
105 *
106 * @param root the DOM root of a RuleType XML type
107 * @param xpathVersion the XPath version to use in any selectors or XPath
108 * functions, or null if this is unspecified (ie, not
109 * supplied in the defaults section of the policy)
110 *
111 * @throws ParsingException if the RuleType is invalid
112 */
113 public static Rule getInstance(Node root, String xpathVersion)
114 throws ParsingException
115 {
116 URI id = null;
117 String name = null;
118 int effect = 0;
119 String description = null;
120 Target target = null;
121 Apply condition = null;
122
123 // first, get the attributes
124 NamedNodeMap attrs = root.getAttributes();
125
126 try {
127 // get the two required attrs...
128 id = new URI(attrs.getNamedItem("RuleId").getNodeValue());
129 } catch (URISyntaxException use) {
130 throw new ParsingException("Error parsing required attribute " +
131 "RuleId", use);
132 }
133
134 String str = attrs.getNamedItem("Effect").getNodeValue();
135 if (str.equals("Permit")) {
136 effect = Result.DECISION_PERMIT;
137 } else if (str.equals("Deny")) {
138 effect = Result.DECISION_DENY;
139 } else {
140 throw new ParsingException("Invalid Effect: " + effect);
141 }
142
143 // next, get the elements
144 NodeList children = root.getChildNodes();
145 for (int i = 0; i < children.getLength(); i++) {
146 Node child = children.item(i);
147 String cname = child.getNodeName();
148
149 if (cname.equals("Description")) {
150 description = child.getFirstChild().getNodeValue();
151 } else if (cname.equals("Target")) {
152 target = Target.getInstance(child, xpathVersion);
153 } else if (cname.equals("Condition")) {
154 condition = Apply.getConditionInstance(child, xpathVersion);
155 }
156 }
157
158 return new Rule(id, effect, description, target, condition);
159 }
160
161 /**
162 * Returns the effect that this <code>Rule</code> will return from
163 * the evaluate method (Permit or Deny) if the request applies.
164 *
165 * @return a decision effect, as defined in <code>Result</code>
166 */
167 public int getEffect() {
168 return effectAttr;
169 }
170
171 /**
172 * Returns the id of this <code>Rule</code>
173 *
174 * @return the rule id
175 */
176 public URI getId() {
177 return idAttr;
178 }
179
180 /**
181 * Returns the given description of this <code>Rule</code> or null if
182 * there is no description
183 *
184 * @return the description or null
185 */
186 public String getDescription() {
187 return description;
188 }
189
190 /**
191 * Returns the target for this <code>Rule</code> or null if there
192 * is no target
193 *
194 * @return the rule's target
195 */
196 public Target getTarget() {
197 return target;
198 }
199
200 /**
201 * Since a rule is always a leaf in a policy tree because it can have
202 * no children, this always returns an empty <code>List</code>.
203 *
204 * @return a <code>List</code> with no elements
205 */
206 public List getChildren() {
207 return Collections.EMPTY_LIST;
208 }
209
210 /**
211 * Returns the condition for this <code>Rule</code> or null if there
212 * is no condition
213 *
214 * @return the rule's condition
215 */
216 public Apply getCondition() {
217 return condition;
218 }
219
220 /**
221 * Given the input context sees whether or not the request matches this
222 * <code>Rule</code>'s <code>Target</code>. Note that unlike the matching
223 * done by the <code>evaluate</code> method, if the <code>Target</code>
224 * is missing than this will return Indeterminate. This lets you write
225 * your own custom matching routines for rules but lets evaluation
226 * proceed normally.
227 *
228 * @param context the representation of the request
229 *
230 * @return the result of trying to match this rule and the request
231 */
232 public MatchResult match(EvaluationCtx context) {
233 if (target == null) {
234 ArrayList code = new ArrayList();
235 code.add(Status.STATUS_PROCESSING_ERROR);
236 Status status = new Status(code, "no target available for " +
237 "matching a rule");
238
239 return new MatchResult(MatchResult.INDETERMINATE, status);
240 }
241
242 return target.match(context);
243 }
244
245 /**
246 * Evaluates the rule against the supplied context. This will check that
247 * the target matches, and then try to evaluate the condition. If the
248 * target and condition apply, then the rule's effect is returned in
249 * the result.
250 * <p>
251 * Note that rules are not required to have targets. If no target is
252 * specified, then the rule inherits its parent's target. In the event
253 * that this <code>Rule</code> has no <code>Target</code> then the
254 * match is assumed to be true, since evaluating a policy tree to this
255 * level required the parent's target to match.
256 *
257 * @param context the representation of the request we're evaluating
258 *
259 * @return the result of the evaluation
260 */
261 public Result evaluate(EvaluationCtx context) {
262 // If the Target is null then it's supposed to inherit from the
263 // parent policy, so we skip the matching step assuming we wouldn't
264 // be here unless the parent matched
265 if (target != null) {
266 MatchResult match = target.match(context);
267 int result = match.getResult();
268
269 // if the target didn't match, then this Rule doesn't apply
270 if (result == MatchResult.NO_MATCH)
271 return new Result(Result.DECISION_NOT_APPLICABLE,
272 context.getResourceId().encode());
273
274 // if the target was indeterminate, we can't go on
275 if (result == MatchResult.INDETERMINATE)
276 return new Result(Result.DECISION_INDETERMINATE,
277 match.getStatus(),
278 context.getResourceId().encode());
279 }
280
281 // if there's no condition, then we just return the effect...
282 if (condition == null)
283 return new Result(effectAttr, context.getResourceId().encode());
284
285 // ...otherwise we evaluate the condition
286 EvaluationResult result = condition.evaluate(context);
287
288 if (result.indeterminate()) {
289 // if it was INDETERMINATE, then that's what we return
290 return new Result(Result.DECISION_INDETERMINATE,
291 result.getStatus(),
292 context.getResourceId().encode());
293 } else {
294 // otherwise we return the effect on tue, and NA on false
295 BooleanAttribute bool =
296 (BooleanAttribute)(result.getAttributeValue());
297
298 if (bool.getValue())
299 return new Result(effectAttr,
300 context.getResourceId().encode());
301 else
302 return new Result(Result.DECISION_NOT_APPLICABLE,
303 context.getResourceId().encode());
304 }
305 }
306
307 /**
308 * Encodes this <code>Rule</code> into its XML representation and writes
309 * this encoding to the given <code>OutputStream</code> with no
310 * indentation.
311 *
312 * @param output a stream into which the XML-encoded data is written
313 */
314 public void encode(OutputStream output) {
315 encode(output, new Indenter(0));
316 }
317
318 /**
319 * Encodes this <code>Rule</code> into its XML representation and writes
320 * this encoding to the given <code>OutputStream</code> with
321 * indentation.
322 *
323 * @param output a stream into which the XML-encoded data is written
324 * @param indenter an object that creates indentation strings
325 */
326 public void encode(OutputStream output, Indenter indenter) {
327 PrintStream out = new PrintStream(output);
328 String indent = indenter.makeString();
329
330 out.print(indent + "<Rule RuleId=\"" + idAttr.toString() +
331 "\" Effect=\"" + Result.DECISIONS[effectAttr] + "\"");
332
333 if ((description != null) || (target != null) || (condition != null)) {
334 // there is some content in the Rule
335 out.println(">");
336
337 indenter.in();
338 String nextIndent = indenter.makeString();
339
340 if (description != null)
341 out.println(nextIndent + "<Description>" + description +
342 "</Description>");
343
344 if (target != null)
345 target.encode(output, indenter);
346
347 if (condition != null)
348 condition.encode(output, indenter);
349
350 indenter.out();
351 out.println(indent + "</Rule>");
352 } else {
353 // the Rule is empty, so close the tag and we're done
354 out.println("/>");
355 }
356 }
357
358 }