[Sunxacml-commit] sunxacml/com/sun/xacml ConfigurationStore.java,NONE,1.1
Brought to you by:
farrukh_najmi,
sethp
From: Seth P. <se...@us...> - 2004-03-15 22:02:50
|
Update of /cvsroot/sunxacml/sunxacml/com/sun/xacml In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv15474/com/sun/xacml Added Files: ConfigurationStore.java Log Message: first version of a new run-time configuration system --- NEW FILE: ConfigurationStore.java --- /* * @(#)ConfigurationStore.java * * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistribution of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Sun Microsystems, Inc. or the names of contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. * * You acknowledge that this software is not designed or intended for use in * the design, construction, operation or maintenance of any nuclear facility. */ package com.sun.xacml; import com.sun.xacml.attr.AttributeFactory; import com.sun.xacml.attr.AttributeProxy; import com.sun.xacml.attr.BaseAttributeFactory; import com.sun.xacml.attr.StandardAttributeFactory; import com.sun.xacml.combine.BaseCombiningAlgFactory; import com.sun.xacml.combine.CombiningAlgFactory; import com.sun.xacml.combine.CombiningAlgorithm; import com.sun.xacml.combine.StandardCombiningAlgFactory; import com.sun.xacml.cond.BaseFunctionFactory; import com.sun.xacml.cond.BasicFunctionFactoryProxy; import com.sun.xacml.cond.Function; import com.sun.xacml.cond.FunctionProxy; import com.sun.xacml.cond.FunctionFactory; import com.sun.xacml.cond.FunctionFactoryProxy; import com.sun.xacml.cond.StandardFunctionFactory; import com.sun.xacml.cond.cluster.FunctionCluster; import com.sun.xacml.finder.AttributeFinder; import com.sun.xacml.finder.PolicyFinder; import com.sun.xacml.finder.ResourceFinder; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.xml.sax.SAXException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * This class supports run-time loading of configuration data. It loads the * configurations from an XML file that conforms to the configuration schema. * By design this class does not get used automatically, nor does it change * the state of the system directly. A programmer must choose to support this * mechanism in their program, and then must explicitly use loaded elements. * This way, the programmer still has full control over their security model, * but also has the convenience of re-using a common configuration * mechanism. * <p> * Note that becuase this doesn't tie directly into the rest of the code, you * are still free to design your own run-time configuration mechanisms. This * is simply provided as a convenience, and so that all programmers can start * from a common point. * <p> * NOTE: The name of this class, its interfaces, and they way it interacts * with the rest of the code is currently unstable, so expect some changes * between now and the next release. * * @author Seth Proctor */ public class ConfigurationStore { /** * Property used to specify the configuration file. */ public static final String PDP_CONFIG_PROPERTY = "com.sun.xacml.PDPConfigFile"; /** * Identifier for the default named elements. */ public static final String DEFAULT_IDENTIFIER = "default"; // pdp elements private PDPConfig defaultPDPConfig; private HashMap pdpConfigMap; // attribute factory elements private AttributeFactory defaultAttributeFactory; private HashMap attributeMap; // combining algorithm factory elements private CombiningAlgFactory defaultCombiningFactory; private HashMap combiningMap; // function factory elements private FunctionFactoryProxy defaultFunctionFactoryProxy; private HashMap functionMap; /** * Default constructor. This constructor uses the * <code>PDP_CONFIG_PROPERTY</code> property to load the configuration. * If the property isn't set, if it names a file that can't be accessed, * or if the file is invalid, then an exception is thrown. * * @throws ParsingException if anything goes wrong during the parsing * of the configuration file, the class loading, * or the factory and pdp setup */ public ConfigurationStore() throws ParsingException { String configFile = System.getProperty(PDP_CONFIG_PROPERTY); // make sure that the right property was set if (configFile == null) { throw new ParsingException("Config property " + PDP_CONFIG_PROPERTY + " needs to be set"); } setupConfig(new File(configFile)); } /** * Constructor that explicitly specifies the configuration file to load. * This is useful if your security model doesn't allow the use of * properties, if you don't want to use a property to specify a * configuration file, or if you want to use more then one configuration * file. If the file can't be accessed, or if the file is invalid, then * an exception is thrown. * * @throws ParsingException if anything goes wrong during the parsing * of the configuration file, the class loading, * or the factory and pdp setup */ public ConfigurationStore(File configFile) throws ParsingException { setupConfig(configFile); } /** * Private helper function used by both constructors to actually load the * configuration data. This is the root of several private methods used * to setup all the pdps and factories. */ private void setupConfig(File configFile) throws ParsingException { Node root = null; System.out.println("Loading PDP configuration..."); // get the root node from the configuration file root = getRootNode(configFile); // initialize all the maps pdpConfigMap = new HashMap(); attributeMap = new HashMap(); combiningMap = new HashMap(); functionMap = new HashMap(); // loop through all the root-level elements, for each one getting its // name and then loading the right kind of element NodeList children = root.getChildNodes(); for (int i = 0; i < children.getLength(); i++) { Node child = children.item(i); String childName = child.getNodeName(); String elementName = null; // get the element's name, or default to DEFAULT_IDENTIFIER NamedNodeMap attrMap = child.getAttributes(); if (attrMap == null) { elementName = DEFAULT_IDENTIFIER; } else { Node attr = attrMap.getNamedItem("name"); if (attr == null) elementName = DEFAULT_IDENTIFIER; else elementName = attr.getNodeValue(); } // see if this is a pdp or a factory, and load accordingly, // putting the new element into the respective map...make sure // that we're never loading something with the same name twice if (childName.equals("pdp")) { System.out.println(" Loading PDP \"" + elementName + "\"..."); if (pdpConfigMap.containsKey(elementName)) throw new ParsingException("more that one pdp with " + "name \"" + elementName +"\""); pdpConfigMap.put(elementName, parsePDPConfig(child)); System.out.println(" done"); } else if (childName.equals("attributeFactory")) { System.out.println(" Loading AttributeFactory \"" + elementName + "\"..."); if (attributeMap.containsKey(elementName)) throw new ParsingException("more that one " + "attributeFactory with name " + elementName +"\""); attributeMap.put(elementName, parseAttributeFactory(child)); System.out.println(" done"); } else if (childName.equals("combiningAlgFactory")) { System.out.println(" Loading CombiningAlgFactory \"" + elementName + "\"..."); if (combiningMap.containsKey(elementName)) throw new ParsingException("more that one " + "combiningAlgFactory with " + "name \"" + elementName +"\""); combiningMap.put(elementName, parseCombiningAlgFactory(child)); System.out.println(" done"); } else if (childName.equals("functionFactory")) { System.out.println(" Loading FunctionFactory \"" + elementName + "\"..."); if (functionMap.containsKey(elementName)) throw new ParsingException("more that one functionFactory" + " with name \"" + elementName +"\""); functionMap.put(elementName, parseFunctionFactory(child)); System.out.println(" done"); } } // finally, extract the default elements, if they were specified... // this is done as an optimizaion on the assumption that people will // call the getDefault* methods more than once, but may be removed // if that assumption proves to be incorrect in practice defaultPDPConfig = (PDPConfig)(pdpConfigMap.get(DEFAULT_IDENTIFIER)); defaultAttributeFactory = (AttributeFactory) (attributeMap.get(DEFAULT_IDENTIFIER)); defaultCombiningFactory = (CombiningAlgFactory) (combiningMap.get(DEFAULT_IDENTIFIER)); defaultFunctionFactoryProxy = (FunctionFactoryProxy) (functionMap.get(DEFAULT_IDENTIFIER)); } /** * Private helper that parses the file and sets up the DOM tree. * FIXME: add schema validation support before 1.2 is final */ private Node getRootNode(File configFile) throws ParsingException { DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); dbFactory.setIgnoringComments(true); dbFactory.setNamespaceAware(false); dbFactory.setValidating(false); DocumentBuilder db = null; try { db = dbFactory.newDocumentBuilder(); } catch (ParserConfigurationException pce) { throw new ParsingException("couldn't get a document builder", pce); } Document doc = null; try { doc = db.parse(new FileInputStream(configFile)); } catch (IOException ioe) { throw new ParsingException("failed to load the file ", ioe); } catch (SAXException saxe) { throw new ParsingException("error parsing the XML tree", saxe); } catch (IllegalArgumentException iae) { throw new ParsingException("no data to parse", iae); } Element root = doc.getDocumentElement(); if (! root.getTagName().equals("config")) throw new ParsingException("unknown document type: " + root.getTagName()); return root; } /** * Private helper that handles the pdp elements. */ private PDPConfig parsePDPConfig(Node root) throws ParsingException { ArrayList attrModules = new ArrayList(); HashSet policyModules = new HashSet(); ArrayList rsrcModules = new ArrayList(); // go through all elements of the pdp, loading the specified modules NodeList children = root.getChildNodes(); for (int i = 0; i < children.getLength(); i++) { Node child = children.item(i); String name = child.getNodeName(); if (name.equals("policyFinderModule")) { policyModules.add(loadClass("module", child)); } else if (name.equals("attributeFinderModule")) { attrModules.add(loadClass("module", child)); } else if (name.equals("resourceFinderModule")) { rsrcModules.add(loadClass("module", child)); } } // after loading the modules, use the collections to setup a // PDPConfig based on this pdp element AttributeFinder attrFinder = new AttributeFinder(); attrFinder.setModules(attrModules); PolicyFinder policyFinder = new PolicyFinder(); policyFinder.setModules(policyModules); ResourceFinder rsrcFinder = new ResourceFinder(); rsrcFinder.setModules(rsrcModules); return new PDPConfig(attrFinder, policyFinder, rsrcFinder); } /** * Private helper that handles the attributeFactory elements. */ private AttributeFactory parseAttributeFactory(Node root) throws ParsingException { AttributeFactory factory = null; // check if we're starting with the standard factory setup if (useStandard(root, "useStandardDatatypes")) factory = StandardAttributeFactory.getNewFactory(); else factory = new BaseAttributeFactory(); // now look for all datatypes specified for this factory, adding // them as we go NodeList children = root.getChildNodes(); for (int i = 0; i < children.getLength(); i++) { Node child = children.item(i); if (child.getNodeName().equals("datatype")) { // a datatype is a class with an identifier String identifier = child.getAttributes(). getNamedItem("identifier").getNodeValue(); AttributeProxy proxy = (AttributeProxy)(loadClass("datatype", child)); try { factory.addDatatype(identifier, proxy); } catch (IllegalArgumentException iae) { throw new ParsingException("duplicate datatype: " + identifier, iae); } } } return factory; } /** * Private helper that handles the combiningAlgFactory elements. */ private CombiningAlgFactory parseCombiningAlgFactory(Node root) throws ParsingException { CombiningAlgFactory factory = null; // check if we're starting with the standard factory setup if (useStandard(root, "useStandardAlgorithms")) factory = StandardCombiningAlgFactory.getNewFactory(); else factory = new BaseCombiningAlgFactory(); // now look for all algorithms specified for this factory, adding // them as we go NodeList children = root.getChildNodes(); for (int i = 0; i < children.getLength(); i++) { Node child = children.item(i); if (child.getNodeName().equals("algorithm")) { // an algorithm is a simple class element CombiningAlgorithm alg = (CombiningAlgorithm)(loadClass("algorithm", child)); try { factory.addAlgorithm(alg); } catch (IllegalArgumentException iae) { throw new ParsingException("duplicate combining " + "algorithm: " + alg.getIdentifier().toString(), iae); } } } return factory; } /** * Private helper that handles the functionFactory elements. This one * is a little more complex than the other two factory helper methods, * since it consists of three factories (target, condition, and general). */ private FunctionFactoryProxy parseFunctionFactory(Node root) throws ParsingException { FunctionFactoryProxy proxy = null; FunctionFactory generalFactory = null; FunctionFactory conditionFactory = null; FunctionFactory targetFactory = null; // check if we're starting with the standard factory setup, and // make sure that the proxy is pre-configured if (useStandard(root, "useStandardFunctions")) { proxy = StandardFunctionFactory.getNewFactory(); targetFactory = proxy.getTargetFactory(); conditionFactory = proxy.getConditionFactory(); generalFactory = proxy.getGeneralFactory(); } else { generalFactory = new BaseFunctionFactory(); conditionFactory = new BaseFunctionFactory(generalFactory); targetFactory = new BaseFunctionFactory(conditionFactory); proxy = new BasicFunctionFactoryProxy(targetFactory, conditionFactory, generalFactory); } // go through and load the three sections, putting the loaded // functions into the appropriate factory NodeList children = root.getChildNodes(); for (int i = 0; i < children.getLength(); i++) { Node child = children.item(i); String name = child.getNodeName(); if (name.equals("target")) { System.out.println(" [TARGET Functions]"); functionParserHelper(child, targetFactory); } else if (name.equals("condition")) { System.out.println(" [CONDITION Functions]"); functionParserHelper(child, conditionFactory); } else if (name.equals("general")) { System.out.println(" [GENERAL Functions]"); functionParserHelper(child, generalFactory); } } return proxy; } /** * Private helper used by the function factory code to load a specific * target, condition, or general section. */ private void functionParserHelper(Node root, FunctionFactory factory) throws ParsingException { // go through all elements in the section NodeList children = root.getChildNodes(); for (int i = 0; i < children.getLength(); i++) { Node child = children.item(i); String name = child.getNodeName(); if (name.equals("function")) { // a function section is a simple class element Function function = (Function)(loadClass("function", child)); try { factory.addFunction(function); } catch (IllegalArgumentException iae) { throw new ParsingException("duplicate function", iae); } } else if (name.equals("abstractFunction")) { // an abstract function is a class with an identifier URI identifier = null; try { identifier = new URI(child.getAttributes(). getNamedItem("identifier"). getNodeValue()); } catch (URISyntaxException urise) { throw new ParsingException("invalid function identifier", urise); } FunctionProxy proxy = (FunctionProxy)(loadClass("abstract function", child)); try { factory.addAbstractFunction(proxy, identifier); } catch (IllegalArgumentException iae) { throw new ParsingException("duplicate abstract function", iae); } } else if (name.equals("functionCluster")) { // a cluster is a class that will give us a collection of // functions that need to be added one by one into the factory FunctionCluster cluster = (FunctionCluster)(loadClass("function cluster", child)); Iterator it = cluster.getSupportedFunctions().iterator(); while (it.hasNext()) { try { factory.addFunction((Function)(it.next())); } catch (IllegalArgumentException iae) { throw new ParsingException("duplicate function", iae); } } } } } /** * Private helper that is used by all the code to load an instance of * the given class...this assumes that the class is in the classpath, * both for simplicity and for stronger security */ private Object loadClass(String prefix, Node root) throws ParsingException { // get the name of the class String className = root.getAttributes().getNamedItem("class").getNodeValue(); System.out.print(" [ " + prefix + ": " + className + " "); // use the system classloader to load the given class ClassLoader cl = ClassLoader.getSystemClassLoader(); Class c = null; try { c = cl.loadClass(className); } catch (ClassNotFoundException cnfe) { throw new ParsingException("couldn't load class " + className, cnfe); } Object instance = null; // figure out if there are any parameters to the constructor if (! root.hasChildNodes()) { // we're using a null constructor, so this is easy try { instance = c.newInstance(); } catch (InstantiationException ie) { throw new ParsingException("couldn't instantiate " + className + " with empty constructor", ie); } catch (IllegalAccessException iae) { throw new ParsingException("couldn't get access to instance " + "of " + className, iae); } } else { // parse the arguments to the constructor List args = null; try { args = getArgs(root); } catch (IllegalArgumentException iae) { throw new ParsingException("illegal class arguments", iae); } int argLength = args.size(); // next we need to see if there's a constructor that matches the // arguments provided...this has to be done by hand since // Class.getConstructor(Class []) doesn't handle sub-classes and // generic types (for instance, a constructor taking List won't // match a parameter list containing ArrayList) // get the list of all available constructors Constructor [] cons = c.getConstructors(); Constructor constructor = null; for (int i = 0; i < cons.length; i++) { // get the parameters for this constructor Class [] params = cons[i].getParameterTypes(); if (params.length == argLength) { Iterator it = args.iterator(); int j = 0; // loop through the parameters and see if each one is // assignable from the coresponding input argument while (it.hasNext()) { if (! params[j].isAssignableFrom(it.next().getClass())) break; j++; } // if we looked at all the parameters, then this // constructor matches the input if (j == argLength) constructor = cons[i]; } // if we've found a matching constructor then stop looping if (constructor != null) break; } // make sure we found a matching constructor if (constructor == null) throw new ParsingException("couldn't find a matching " + "constructor"); // finally, instantiate the class try { instance = constructor.newInstance(args.toArray()); } catch (InstantiationException ie) { throw new ParsingException("couldn't instantiate " + className, ie); } catch (IllegalAccessException iae) { throw new ParsingException("couldn't get access to instance " + "of " + className, iae); } catch (InvocationTargetException ite) { throw new ParsingException("couldn't create " + className, ite); } } System.out.println("]"); return instance; } /** * Private helper that gets the constructor arguments for a given class. * Right now this just supports String and List, but it's trivial to * add support for other types...the question is what types are needed. * FIXME: decide on what types will be supported */ private List getArgs(Node root) { List args = new ArrayList(); NodeList children = root.getChildNodes(); for (int i = 0; i < children.getLength(); i++) { Node child = children.item(i); String name = child.getNodeName(); if (child.getNodeType() == Node.ELEMENT_NODE) { if (name.equals("string")) { args.add(child.getFirstChild().getNodeValue()); } else if (name.equals("list")) { args.add(getArgs(child)); } else { throw new IllegalArgumentException("unkown arg type: " + name); } } } return args; } /** * Private helper used by the three factory routines to see if the * given factory should be based on the standard setup */ private boolean useStandard(Node node, String attributeName) { NamedNodeMap map = node.getAttributes(); if (map == null) return true; Node attrNode = map.getNamedItem(attributeName); if (attrNode == null) return true; return attrNode.getNodeValue().equals("true"); } /** * Returns the default PDP configuration. If no default was specified * then this throws an exception. * * @return the default PDP configuration * * @throws UnknownIdentifierException if there is no default config */ public PDPConfig getDefaultPDPConfig() throws UnknownIdentifierException { if (defaultPDPConfig == null) throw new UnknownIdentifierException("no default available"); return defaultPDPConfig; } /** * Returns the PDP configuration with the given name. If no such * configuration exists then an exception is thrown. * * @return the matching PDP configuation * * @throws UnknownIdentifierException if the name is unknown */ public PDPConfig getPDPConfig(String name) throws UnknownIdentifierException { Object object = pdpConfigMap.get(name); if (object == null) throw new UnknownIdentifierException("unknown pdp: " + name); return (PDPConfig)object; } /** * Returns a set of identifiers representing each PDP configuration * available. * * @return a <code>Set</code> of <code>String</code>s */ public Set getSupportedPDPConfigurations() { return Collections.unmodifiableSet(pdpConfigMap.keySet()); } /** * Returns the default attribute factory. If no default was specified * then this throws an exception. * * @return the default attribute factory * * @throws UnknownIdentifierException if there is no default factory */ public AttributeFactory getDefaultAttributeFactory() throws UnknownIdentifierException { if (defaultAttributeFactory == null) throw new UnknownIdentifierException("no default available"); return defaultAttributeFactory; } /** * Returns the attribute factory with the given name. If no such * factory exists then an exception is thrown. * * @return the matching attribute factory * * @throws UnknownIdentifierException if the name is unknown */ public AttributeFactory getAttributeFactory(String name) throws UnknownIdentifierException { Object object = attributeMap.get(name); if (object == null) throw new UnknownIdentifierException("unknown factory: " + name); return (AttributeFactory)object; } /** * Returns a set of identifiers representing each attribute factory * available. * * @return a <code>Set</code> of <code>String</code>s */ public Set getSupportedAttributeFactories() { return Collections.unmodifiableSet(attributeMap.keySet()); } /** * Returns the default combiningAlg factory. If no default was specified * then this throws an exception. * * @return the default combiningAlg factory * * @throws UnknownIdentifierException if there is no default factory */ public CombiningAlgFactory getDefaultCombiningAlgFactory() throws UnknownIdentifierException { if (defaultCombiningFactory == null) throw new UnknownIdentifierException("no default available"); return defaultCombiningFactory; } /** * Returns the combiningAlg factory with the given name. If no such * factory exists then an exception is thrown. * * @return the matching combiningAlg factory * * @throws UnknownIdentifierException if the name is unknown */ public CombiningAlgFactory getCombiningAlgFactory(String name) throws UnknownIdentifierException { Object object = combiningMap.get(name); if (object == null) throw new UnknownIdentifierException("unknown factory: " + name); return (CombiningAlgFactory)object; } /** * Returns a set of identifiers representing each combiningAlg factory * available. * * @return a <code>Set</code> of <code>String</code>s */ public Set getSupportedCombiningAlgFactories() { return Collections.unmodifiableSet(combiningMap.keySet()); } /** * Returns the default function factory proxy. If no default was specified * then this throws an exception. * * @return the default function factory proxy * * @throws UnknownIdentifierException if there is no default factory */ public FunctionFactoryProxy getDefaultFunctionFactoryProxy() throws UnknownIdentifierException { if (defaultFunctionFactoryProxy == null) throw new UnknownIdentifierException("no default available"); return defaultFunctionFactoryProxy; } /** * Returns the function factory proxy with the given name. If no such * proxy exists then an exception is thrown. * * @return the matching function factory proxy * * @throws UnknownIdentifierException if the name is unknown */ public FunctionFactoryProxy getFunctionFactoryProxy(String name) throws UnknownIdentifierException { Object object = functionMap.get(name); if (object == null) throw new UnknownIdentifierException("unknown factory: " + name); return (FunctionFactoryProxy)object; } /** * Returns a set of identifiers representing each function factory proxy * available. * * @return a <code>Set</code> of <code>String</code>s */ public Set getSupportedFunctionFactories() { return Collections.unmodifiableSet(functionMap.keySet()); } } |