Source code: com/sun/xacml/attr/AttributeDesignator.java
1
2 /*
3 * @(#)AttributeDesignator.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.attr;
38
39 import com.sun.xacml.EvaluationCtx;
40 import com.sun.xacml.Indenter;
41 import com.sun.xacml.MatchResult;
42 import com.sun.xacml.ParsingException;
43 import com.sun.xacml.TargetMatch;
44
45 import com.sun.xacml.cond.Evaluatable;
46 import com.sun.xacml.cond.EvaluationResult;
47
48 import com.sun.xacml.ctx.Attribute;
49 import com.sun.xacml.ctx.Status;
50 import com.sun.xacml.ctx.StatusDetail;
51
52 import java.io.OutputStream;
53 import java.io.PrintStream;
54
55 import java.net.URI;
56
57 import java.util.ArrayList;
58 import java.util.Collections;
59 import java.util.Iterator;
60 import java.util.List;
61
62 import java.util.logging.Level;
63 import java.util.logging.Logger;
64
65 import org.w3c.dom.NamedNodeMap;
66 import org.w3c.dom.Node;
67 import org.w3c.dom.NodeList;
68
69
70 /**
71 * Represents all four kinds of Designators in XACML.
72 *
73 * @since 1.0
74 * @author Seth Proctor
75 */
76 public class AttributeDesignator implements Evaluatable
77 {
78
79 /**
80 * Tells designator to search in the subject section of the request
81 */
82 public static final int SUBJECT_TARGET = 0;
83
84 /**
85 * Tells designator to search in the resource section of the request
86 */
87 public static final int RESOURCE_TARGET = 1;
88
89 /**
90 * Tells designator to search in the action section of the request
91 */
92 public static final int ACTION_TARGET = 2;
93
94 /**
95 * Tells designator to search in the environment section of the request
96 */
97 public static final int ENVIRONMENT_TARGET = 3;
98
99 /**
100 * The standard URI for the default subject category value
101 */
102 public static final String SUBJECT_CATEGORY_DEFAULT =
103 "urn:oasis:names:tc:xacml:1.0:subject-category:access-subject";
104
105 // helper array of strings
106 static final private String [] targetTypes = { "Subject", "Resource",
107 "Action", "Environment" };
108
109 // the type of designator we are
110 private int target;
111
112 // required attributes
113 private URI type;
114 private URI id;
115
116 // optional attribute
117 private URI issuer;
118
119 // must resolution find something
120 private boolean mustBePresent;
121
122 // if we're a subject this is the category
123 private URI subjectCategory;
124
125 // the logger we'll use for all messages
126 private static final Logger logger =
127 Logger.getLogger(AttributeDesignator.class.getName());
128
129 /**
130 * Creates a new <code>AttributeDesignator</code> without the optional
131 * issuer.
132 *
133 * @param target the type of designator as specified by the 4 member
134 * *_TARGET fields
135 * @param type the data type resolved by this designator
136 * @param id the attribute id looked for by this designator
137 * @param mustBePresent whether resolution must find a value
138 */
139 public AttributeDesignator(int target, URI type, URI id,
140 boolean mustBePresent) {
141 this(target, type, id, mustBePresent, null);
142 }
143
144 /**
145 * Creates a new <code>AttributeDesignator</code> with the optional
146 * issuer.
147 *
148 * @param target the type of designator as specified by the 4 member
149 * *_TARGET fields
150 * @param type the data type resolved by this designator
151 * @param id the attribute id looked for by this designator
152 * @param mustBePresent whether resolution must find a value
153 * @param issuer the issuer of the values to search for or null if no
154 * issuer is specified
155 *
156 * @throws IllegalArgumentException if the input target isn't a valid value
157 */
158 public AttributeDesignator(int target, URI type, URI id,
159 boolean mustBePresent, URI issuer)
160 throws IllegalArgumentException{
161
162 // check if input target is a valid value
163 if ((target != SUBJECT_TARGET) &&
164 (target != RESOURCE_TARGET) &&
165 (target != ACTION_TARGET) &&
166 (target != ENVIRONMENT_TARGET))
167 throw new IllegalArgumentException("Input target is not a valid" +
168 "value");
169 this.target = target;
170 this.type = type;
171 this.id = id;
172 this.mustBePresent = mustBePresent;
173 this.issuer = issuer;
174
175 subjectCategory = null;
176 }
177
178 /**
179 * Sets the category if this is a SubjectAttributeDesignatorType
180 *
181 * @param category the subject category
182 */
183 public void setSubjectCategory(URI category) {
184 if (target == SUBJECT_TARGET)
185 subjectCategory = category;
186 }
187
188 /**
189 * Creates a new <code>AttributeDesignator</code> based on the DOM
190 * root of the XML data.
191 *
192 * @param root the DOM root of the AttributeDesignatorType XML type
193 * @param target the type of designator to create as specified in the
194 * four member *_TARGET fields
195 *
196 * @return the designator
197 *
198 * @throws ParsingException if the AttributeDesignatorType was invalid
199 */
200 public static AttributeDesignator getInstance(Node root, int target)
201 throws ParsingException
202 {
203 URI type = null;
204 URI id = null;
205 URI issuer = null;
206 boolean mustBePresent = false;
207 URI subjectCategory = null;
208
209 NamedNodeMap attrs = root.getAttributes();
210
211 try {
212 // there's always an Id
213 id = new URI(attrs.getNamedItem("AttributeId").getNodeValue());
214 } catch (Exception e) {
215 throw new ParsingException("Required AttributeId missing in " +
216 "AttributeDesignator", e);
217 }
218
219 try {
220 // there's always a data type
221 type = new URI(attrs.getNamedItem("DataType").getNodeValue());
222 } catch (Exception e) {
223 throw new ParsingException("Required DataType missing in " +
224 "AttributeDesignator", e);
225 }
226
227 try {
228 // there might be an issuer
229 Node node = attrs.getNamedItem("Issuer");
230 if (node != null)
231 issuer = new URI(node.getNodeValue());
232
233 // if it's for the Subject section, there's another attr
234 if (target == SUBJECT_TARGET) {
235 Node scnode = attrs.getNamedItem("SubjectCategory");
236 if (scnode != null)
237 subjectCategory = new URI(scnode.getNodeValue());
238 else
239 subjectCategory = new URI(SUBJECT_CATEGORY_DEFAULT);
240 }
241
242 // there might be a mustBePresent flag
243 node = attrs.getNamedItem("MustBePresent");
244 if (node != null)
245 if (node.getNodeValue().equals("true"))
246 mustBePresent = true;
247 } catch (Exception e) {
248 // this shouldn't ever happen, but in theory something could go
249 // wrong in the code in this try block
250 throw new ParsingException("Error parsing AttributeDesignator " +
251 "optional attributes", e);
252 }
253
254 AttributeDesignator ad =
255 new AttributeDesignator(target, type, id, mustBePresent, issuer);
256 ad.setSubjectCategory(subjectCategory);
257
258 return ad;
259 }
260
261 /**
262 * Returns the type of this designator as specified by the *_TARGET
263 * fields.
264 *
265 * @return the designator type
266 */
267 public int getDesignatorType() {
268 return target;
269 }
270
271 /**
272 * Returns the type of attribute that is resolved by this designator.
273 * While an AD will always return a bag, this method will always return
274 * the type that is stored in the bag.
275 *
276 * @return the attribute type
277 */
278 public URI getType() {
279 return type;
280 }
281
282 /**
283 * Returns the AttributeId of the values resolved by this designator.
284 *
285 * @return identifier for the values to resolve
286 */
287 public URI getId() {
288 return id;
289 }
290
291 /**
292 * Returns the subject category for this designator. If this is not a
293 * SubjectAttributeDesignator then this will always return null.
294 *
295 * @return the subject category or null if this isn't a
296 * SubjectAttributeDesignator
297 */
298 public URI getCategory() {
299 return subjectCategory;
300 }
301
302 /**
303 * Returns the issuer of the values resolved by this designator if
304 * specified.
305 *
306 * @return the attribute issuer or null if unspecified
307 */
308 public URI getIssuer() {
309 return issuer;
310 }
311
312 /**
313 * Returns whether or not a value is required to be resolved by this
314 * designator.
315 *
316 * @return true if a value is required, false otherwise
317 */
318 public boolean mustBePresent() {
319 return mustBePresent;
320 }
321
322 /**
323 * Always returns true, since a designator always returns a bag of
324 * attribute values.
325 *
326 * @return true
327 */
328 public boolean evaluatesToBag() {
329 return true;
330 }
331
332 /**
333 * Always returns an empty list since designators never have children.
334 *
335 * @return an empty <code>List</code>
336 */
337 public List getChildren() {
338 return Collections.EMPTY_LIST;
339 }
340
341 /**
342 * Evaluates the pre-assigned meta-data against the given context,
343 * trying to find some matching values.
344 *
345 * @param context the representation of the request
346 *
347 * @return a result containing a bag either empty because no values were
348 * found or containing at least one value, or status associated with an
349 * Indeterminate result
350 */
351 public EvaluationResult evaluate(EvaluationCtx context) {
352 EvaluationResult result = null;
353
354 // look in the right section for some attribute values
355 switch(target) {
356 case SUBJECT_TARGET:
357 result = context.getSubjectAttribute(type, id,
358 issuer, subjectCategory);
359 break;
360 case RESOURCE_TARGET:
361 result = context.getResourceAttribute(type, id, issuer);
362 break;
363 case ACTION_TARGET:
364 result = context.getActionAttribute(type, id, issuer);
365 break;
366 case ENVIRONMENT_TARGET:
367 result = context.getEnvironmentAttribute(type, id, issuer);
368 break;
369 }
370
371 // if the lookup was indeterminate, then we return immediately
372 if (result.indeterminate())
373 return result;
374
375 BagAttribute bag = (BagAttribute)(result.getAttributeValue());
376
377 if (bag.isEmpty()) {
378 // if it's empty, this may be an error
379 if (mustBePresent) {
380 if (logger.isLoggable(Level.INFO))
381 logger.info("AttributeDesignator failed to resolve a " +
382 "value for a required attribute: " +
383 id.toString());
384
385 ArrayList code = new ArrayList();
386 code.add(Status.STATUS_MISSING_ATTRIBUTE);
387
388 String message = "Couldn't find " + targetTypes[target] +
389 "AttributeDesignator attribute";
390
391 // Note that there is a bug in the XACML spec. You can't
392 // specify an identifier without specifying acceptable
393 // values. Until this is fixed, this code will only
394 // return the status code, and not any hints about what
395 // was missing
396
397 /*List attrs = new ArrayList();
398 attrs.add(new Attribute(id,
399 ((issuer == null) ? null :
400 issuer.toString()),
401 null, null));
402 StatusDetail detail = new StatusDetail(attrs);*/
403
404 return new EvaluationResult(new Status(code, message));
405 }
406 }
407
408 // if we got here the bag wasn't empty, or mustBePresent was false,
409 // so we just return the result
410 return result;
411 }
412
413 /**
414 * Encodes this designator into its XML representation and
415 * writes this encoding to the given <code>OutputStream</code> with no
416 * indentation.
417 *
418 * @param output a stream into which the XML-encoded data is written
419 */
420 public void encode(OutputStream output) {
421 encode(output, new Indenter(0));
422 }
423
424 /**
425 * Encodes this designator into its XML representation and
426 * writes this encoding to the given <code>OutputStream</code> with
427 * indentation.
428 *
429 * @param output a stream into which the XML-encoded data is written
430 * @param indenter an object that creates indentation strings
431 */
432 public void encode(OutputStream output, Indenter indenter) {
433 PrintStream out = new PrintStream(output);
434 String indent = indenter.makeString();
435
436 String tag = "<" + targetTypes[target] + "AttributeDesignator";
437
438 if ((target == SUBJECT_TARGET) && (subjectCategory != null))
439 tag += " SubjectCategory=\"" + subjectCategory.toString() + "\"";
440
441 tag += " AttributeId=\"" + id.toString() + "\"";
442 tag += " DataType=\"" + type.toString() + "\"";
443
444 if (issuer != null)
445 tag += " Issuer=\"" + issuer.toString() + "\"";
446
447 if (mustBePresent)
448 tag += " MustBePresent=\"true\"";
449
450 tag += "/>";
451
452 out.println(indent + tag);
453 }
454
455 }