Source code: com/sun/xacml/finder/impl/FilePolicyModule.java
1
2 /*
3 * @(#)FilePolicyModule.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.MatchResult;
42 import com.sun.xacml.Policy;
43 import com.sun.xacml.PolicySet;
44
45 import com.sun.xacml.ctx.Status;
46
47 import com.sun.xacml.finder.PolicyFinder;
48 import com.sun.xacml.finder.PolicyFinderModule;
49 import com.sun.xacml.finder.PolicyFinderResult;
50
51 import java.io.File;
52 import java.io.FileInputStream;
53
54 import java.util.ArrayList;
55 import java.util.HashSet;
56 import java.util.Iterator;
57 import java.util.List;
58 import java.util.Set;
59
60 import java.util.logging.Level;
61 import java.util.logging.Logger;
62
63 import javax.xml.parsers.DocumentBuilder;
64 import javax.xml.parsers.DocumentBuilderFactory;
65
66 import org.w3c.dom.Document;
67 import org.w3c.dom.Element;
68 import org.w3c.dom.NamedNodeMap;
69 import org.w3c.dom.Node;
70 import org.w3c.dom.NodeList;
71
72 import org.xml.sax.ErrorHandler;
73 import org.xml.sax.SAXException;
74 import org.xml.sax.SAXParseException;
75
76
77 /**
78 * This module represents a collection of files containing polices,
79 * each of which will be searched through when trying to find a
80 * policy that is applicable to a specific request.
81 * <p>
82 * Note: this module is provided only as an example and for testing
83 * purposes. It is not part of the standard, and it should not be
84 * relied upon for production systems. In the future, this will likely
85 * be moved into a package with other similar example and testing
86 * code.
87 *
88 * @since 1.0
89 * @author Seth Proctor
90 */
91 public class FilePolicyModule extends PolicyFinderModule
92 implements ErrorHandler
93 {
94
95 /**
96 * The property which is used to specify the schema
97 * file to validate against (if any)
98 */
99 public static final String POLICY_SCHEMA_PROPERTY =
100 "com.sun.xacml.PolicySchema";
101
102
103 public static final String JAXP_SCHEMA_LANGUAGE =
104 "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
105
106 public static final String W3C_XML_SCHEMA =
107 "http://www.w3.org/2001/XMLSchema";
108
109 public static final String JAXP_SCHEMA_SOURCE =
110 "http://java.sun.com/xml/jaxp/properties/schemaSource";
111
112
113 // the finder that is using this module
114 private PolicyFinder finder;
115
116 //
117 private File schemaFile;
118
119 //
120 private Set fileNames;
121
122 //
123 private Set policies;
124
125 // the logger we'll use for all messages
126 private static final Logger logger =
127 Logger.getLogger(FilePolicyModule.class.getName());
128
129 /**
130 * Constructor which retrieves the schema file to validate policies against
131 * from the POLICY_SCHEMA_PROPERTY. If the retrieved property
132 * is null, then no schema validation will occur.
133 */
134 public FilePolicyModule() {
135 fileNames = new HashSet();
136 policies = new HashSet();
137
138 String schemaName = System.getProperty(POLICY_SCHEMA_PROPERTY);
139
140 if (schemaName == null)
141 schemaFile = null;
142 else
143 schemaFile = new File(schemaName);
144 }
145
146 /**
147 * Constructor that uses the specified input as the schema file to
148 * validate policies against. If schema validation is not desired,
149 * a null value should be used.
150 *
151 * @param schemaFile the schema file to validate policies against,
152 * or null if schema validation is not desired.
153 */
154 public FilePolicyModule(File schemaFile) {
155 fileNames = new HashSet();
156 policies = new HashSet();
157
158 this.schemaFile = schemaFile;
159 }
160
161 /**
162 * Constructor that specifies a set of initial policy files to use.
163 * No schema validation is performed.
164 *
165 * @param fileNames a <code>List</code> of <code>String</code>s that
166 * identify policy files
167 */
168 public FilePolicyModule(List fileNames) {
169 this();
170
171 if (fileNames != null)
172 this.fileNames.addAll(fileNames);
173 }
174
175 /**
176 * Indicates whether this module supports finding policies based on
177 * a request (target matching). Since this module does support
178 * finding policies based on requests, it returns true.
179 *
180 * @return true, since finding policies based on requests is supported
181 */
182 public boolean isRequestSupported() {
183 return true;
184 }
185
186 /**
187 * Initializes the <code>FilePolicyModule</code> by loading
188 * the policies contained in the collection of files associated
189 * with this module. This method also uses the specified
190 * <code>PolicyFinder</code> to help in instantiating PolicySets.
191 *
192 * @param finder a PolicyFinder used to help in instantiating PolicySets
193 */
194 public void init(PolicyFinder finder) {
195 this.finder = finder;
196
197 Iterator it = fileNames.iterator();
198 while (it.hasNext()) {
199 String fname = (String)(it.next());
200 AbstractPolicy policy = loadPolicy(fname, finder,
201 schemaFile, this);
202 if (policy != null)
203 policies.add(policy);
204 }
205 }
206
207 /**
208 * Adds a file (containing a policy) to the collection of filenames
209 * associated with this module.
210 *
211 * @param filename the file to add to this module's collection of files
212 */
213 public boolean addPolicy(String filename) {
214 return fileNames.add(filename);
215 }
216
217 /**
218 * Loads a policy from the specified filename and uses the specified
219 * <code>PolicyFinder</code> to help with instantiating PolicySets.
220 *
221 * @param filename the file to load the policy from
222 * @param finder a PolicyFinder used to help in instantiating PolicySets
223 *
224 * @return a (potentially schema-validated) policy associated with the
225 * specified filename, or null if there was an error
226 */
227 public static AbstractPolicy loadPolicy(String filename,
228 PolicyFinder finder) {
229 return loadPolicy(filename, finder, null, null);
230 }
231
232 /**
233 * Loads a policy from the specified filename, using the specified
234 * <code>PolicyFinder</code> to help with instantiating PolicySets,
235 * and using the specified input as the schema file to validate
236 * policies against. If schema validation is not desired, a null
237 * value should be used for schemaFile
238 *
239 * @param filename the file to load the policy from
240 * @param finder a PolicyFinder used to help in instantiating PolicySets
241 * @param schemaFile the schema file to validate policies against, or
242 * null if schema validation is not desired
243 * @param handler an error handler used to print warnings and errors
244 * during parsing
245 *
246 * @return a (potentially schema-validated) policy associated with the
247 * specified filename, or null if there was an error
248 */
249 public static AbstractPolicy loadPolicy(String filename,
250 PolicyFinder finder,
251 File schemaFile,
252 ErrorHandler handler) {
253 try {
254 // create the factory
255 DocumentBuilderFactory factory =
256 DocumentBuilderFactory.newInstance();
257 factory.setIgnoringComments(true);
258
259 DocumentBuilder db = null;
260
261 // as of 1.2, we always are namespace aware
262 factory.setNamespaceAware(true);
263
264 // set the factory to work the way the system requires
265 if (schemaFile == null) {
266 // we're not doing any validation
267 factory.setValidating(false);
268
269 db = factory.newDocumentBuilder();
270 } else {
271 // we're using a validating parser
272 factory.setValidating(true);
273
274 factory.setAttribute(JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA);
275 factory.setAttribute(JAXP_SCHEMA_SOURCE, schemaFile);
276
277 db = factory.newDocumentBuilder();
278 db.setErrorHandler(handler);
279 }
280
281 // try to load the policy file
282 Document doc = db.parse(new FileInputStream(filename));
283
284 // handle the policy, if it's a known type
285 Element root = doc.getDocumentElement();
286 String name = root.getTagName();
287
288 if (name.equals("Policy")) {
289 return Policy.getInstance(root);
290 } else if (name.equals("PolicySet")) {
291 return PolicySet.getInstance(root, finder);
292 } else {
293 // this isn't a root type that we know how to handle
294 throw new Exception("Unknown root document type: " + name);
295 }
296
297 } catch (Exception e) {
298 if (logger.isLoggable(Level.WARNING))
299 logger.log(Level.WARNING, "Error reading policy from file " +
300 filename, e);
301 }
302
303 // a default fall-through in the case of an error
304 return null;
305 }
306
307 /**
308 * Finds a policy based on a request's context. This may involve using
309 * the request data as indexing data to lookup a policy. This will always
310 * do a Target match to make sure that the given policy applies. If more
311 * than one applicable policy is found, this will return an error.
312 * NOTE: this is basically just a subset of the OnlyOneApplicable Policy
313 * Combining Alg that skips the evaluation step. See comments in there
314 * for details on this algorithm.
315 *
316 * @param context the representation of the request data
317 *
318 * @return the result of trying to find an applicable policy
319 */
320 public PolicyFinderResult findPolicy(EvaluationCtx context) {
321 AbstractPolicy selectedPolicy = null;
322 Iterator it = policies.iterator();
323
324 while (it.hasNext()) {
325 AbstractPolicy policy = (AbstractPolicy)(it.next());
326
327 // see if we match
328 MatchResult match = policy.match(context);
329 int result = match.getResult();
330
331 // if there was an error, we stop right away
332 if (result == MatchResult.INDETERMINATE)
333 return new PolicyFinderResult(match.getStatus());
334
335 if (result == MatchResult.MATCH) {
336 // if we matched before, this is an error...
337 if (selectedPolicy != null) {
338 ArrayList code = new ArrayList();
339 code.add(Status.STATUS_PROCESSING_ERROR);
340 Status status = new Status(code, "too many applicable top-"
341 + "level policies");
342 return new PolicyFinderResult(status);
343 }
344
345 // ...otherwise remember this policy
346 selectedPolicy = policy;
347 }
348 }
349
350 // if we found a policy, return it, otherwise we're N/A
351 if (selectedPolicy != null)
352 return new PolicyFinderResult(selectedPolicy);
353 else
354 return new PolicyFinderResult();
355 }
356
357 /**
358 * Standard handler routine for the XML parsing.
359 *
360 * @param exception information on what caused the problem
361 */
362 public void warning(SAXParseException exception) throws SAXException {
363 if (logger.isLoggable(Level.WARNING))
364 logger.warning("Warning on line " + exception.getLineNumber() +
365 ": " + exception.getMessage());
366 }
367
368 /**
369 * Standard handler routine for the XML parsing.
370 *
371 * @param exception information on what caused the problem
372 *
373 * @throws SAXException always to halt parsing on errors
374 */
375 public void error(SAXParseException exception) throws SAXException {
376 if (logger.isLoggable(Level.WARNING))
377 logger.warning("Error on line " + exception.getLineNumber() +
378 ": " + exception.getMessage() + " ... " +
379 "Policy will not be available");
380
381 throw new SAXException("error parsing policy");
382 }
383
384 /**
385 * Standard handler routine for the XML parsing.
386 *
387 * @param exception information on what caused the problem
388 *
389 * @throws SAXException always to halt parsing on errors
390 */
391 public void fatalError(SAXParseException exception) throws SAXException {
392 if (logger.isLoggable(Level.WARNING))
393 logger.warning("Fatal error on line " + exception.getLineNumber() +
394 ": " + exception.getMessage() + " ... " +
395 "Policy will not be available");
396
397 throw new SAXException("fatal error parsing policy");
398 }
399
400 }