diff options
Diffstat (limited to 'eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils')
14 files changed, 3468 insertions, 0 deletions
diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/DOMUtils.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/DOMUtils.java new file mode 100644 index 00000000..f28700a1 --- /dev/null +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/DOMUtils.java @@ -0,0 +1,1243 @@ +/******************************************************************************* + *******************************************************************************/ + + +package at.gv.egiz.eaaf.core.impl.utils; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.Vector; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Result; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.xerces.parsers.DOMParser; +import org.apache.xerces.parsers.SAXParser; +import org.apache.xerces.parsers.XMLGrammarPreparser; +import org.apache.xerces.util.SymbolTable; +import org.apache.xerces.util.XMLGrammarPoolImpl; +import org.apache.xerces.xni.grammars.XMLGrammarDescription; +import org.apache.xerces.xni.grammars.XMLGrammarPool; +import org.apache.xerces.xni.parser.XMLInputSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Attr; +import org.w3c.dom.Document; +import org.w3c.dom.DocumentFragment; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.EntityResolver; +import org.xml.sax.ErrorHandler; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +import at.gv.egiz.eaaf.core.api.data.XMLNamespaceConstants; + +/** + * Various utility functions for handling XML DOM trees. + * + * The parsing methods in this class make use of some features internal to the + * Xerces DOM parser, mainly for performance reasons. As soon as JAXP + * (currently at version 1.2) is better at schema handling, it should be used as + * the parser interface. + * + */ +public class DOMUtils { + private static final Logger log = LoggerFactory.getLogger(DOMUtils.class); + + /** Feature URI for namespace aware parsing. */ + private static final String NAMESPACES_FEATURE = + "http://xml.org/sax/features/namespaces"; + /** Feature URI for validating parsing. */ + private static final String VALIDATION_FEATURE = + "http://xml.org/sax/features/validation"; + /** Feature URI for schema validating parsing. */ + private static final String SCHEMA_VALIDATION_FEATURE = + "http://apache.org/xml/features/validation/schema"; + /** Feature URI for normalization of element/attribute values. */ + private static final String NORMALIZED_VALUE_FEATURE = + "http://apache.org/xml/features/validation/schema/normalized-value"; + /** Feature URI for parsing ignorable whitespace. */ + private static final String INCLUDE_IGNORABLE_WHITESPACE_FEATURE = + "http://apache.org/xml/features/dom/include-ignorable-whitespace"; + /** Feature URI for creating EntityReference nodes in the DOM tree. */ + private static final String CREATE_ENTITY_REF_NODES_FEATURE = + "http://apache.org/xml/features/dom/create-entity-ref-nodes"; + /** Property URI for providing external schema locations. */ + private static final String EXTERNAL_SCHEMA_LOCATION_PROPERTY = + "http://apache.org/xml/properties/schema/external-schemaLocation"; + /** Property URI for providing the external schema location for elements + * without a namespace. */ + private static final String EXTERNAL_NO_NAMESPACE_SCHEMA_LOCATION_PROPERTY = + "http://apache.org/xml/properties/schema/external-noNamespaceSchemaLocation"; + + private static final String EXTERNAL_GENERAL_ENTITIES_FEATURE = + "http://xml.org/sax/features/external-general-entities"; + + private static final String EXTERNAL_PARAMETER_ENTITIES_FEATURE = + "http://xml.org/sax/features/external-parameter-entities"; + + public static final String DISALLOW_DOCTYPE_FEATURE = + "http://apache.org/xml/features/disallow-doctype-decl"; + + + + /** Property URI for the Xerces grammar pool. */ + private static final String GRAMMAR_POOL = + org.apache.xerces.impl.Constants.XERCES_PROPERTY_PREFIX + + org.apache.xerces.impl.Constants.XMLGRAMMAR_POOL_PROPERTY; + /** A prime number for initializing the symbol table. */ + private static final int BIG_PRIME = 2039; + /** Symbol table for the grammar pool. */ + private static SymbolTable symbolTable = new SymbolTable(BIG_PRIME); + /** Xerces schema grammar pool. */ + private static XMLGrammarPool grammarPool = new XMLGrammarPoolImpl(); + /** Set holding the NamespaceURIs of the grammarPool, to prevent multiple + * entries of same grammars to the pool */ + private static Set grammarNamespaces; + + static { + grammarPool.lockPool(); + grammarNamespaces = new HashSet(); + } + + /** + * Preparse a schema and add it to the schema pool. + * The method only adds the schema to the pool if a schema having the same + * <code>systemId</code> (namespace URI) is not already present in the pool. + * + * @param inputStream An <code>InputStream</code> providing the contents of + * the schema. + * @param systemId The systemId (namespace URI) to use for the schema. + * @throws IOException An error occurred reading the schema. + */ + public static void addSchemaToPool(InputStream inputStream, String systemId) + throws IOException { + XMLGrammarPreparser preparser; + + if (!grammarNamespaces.contains(systemId)) { + + grammarNamespaces.add(systemId); + + // unlock the pool so that we can add another grammar + grammarPool.unlockPool(); + + // prepare the preparser + preparser = new XMLGrammarPreparser(symbolTable); + preparser.registerPreparser(XMLGrammarDescription.XML_SCHEMA, null); + preparser.setProperty(GRAMMAR_POOL, grammarPool); + preparser.setFeature(NAMESPACES_FEATURE, true); + preparser.setFeature(VALIDATION_FEATURE, true); + + // add the grammar to the pool + preparser.preparseGrammar( + XMLGrammarDescription.XML_SCHEMA, + new XMLInputSource(null, systemId, null, inputStream, null)); + + // lock the pool again so that schemas are not added automatically + grammarPool.lockPool(); + } + } + + /** + * Parse an XML document from an <code>InputStream</code>. + * + * @param inputStream The <code>InputStream</code> containing the XML + * document. + * @param validating If <code>true</code>, parse validating. + * @param externalSchemaLocations A <code>String</code> containing namespace + * URI to schema location pairs, the same way it is accepted by the <code>xsi: + * schemaLocation</code> attribute. + * @param externalNoNamespaceSchemaLocation The schema location of the + * schema for elements without a namespace, the same way it is accepted by the + * <code>xsi:noNamespaceSchemaLocation</code> attribute. + * @param entityResolver An <code>EntityResolver</code> to resolve external + * entities (schemas and DTDs). If <code>null</code>, it will not be set. + * @param errorHandler An <code>ErrorHandler</code> to decide what to do + * with parsing errors. If <code>null</code>, it will not be set. + * @return The parsed XML document as a DOM tree. + * @throws SAXException An error occurred parsing the document. + * @throws IOException An error occurred reading the document. + * @throws ParserConfigurationException An error occurred configuring the XML + * parser. + */ + public static Document parseDocument( + InputStream inputStream, + boolean validating, + String externalSchemaLocations, + String externalNoNamespaceSchemaLocation, + EntityResolver entityResolver, + ErrorHandler errorHandler, + Map<String, Object> parserFeatures) + throws SAXException, IOException, ParserConfigurationException { + + DOMParser parser; + +// class MyEntityResolver implements EntityResolver { +// +// public InputSource resolveEntity(String publicId, String systemId) +// throws SAXException, IOException { +// return new InputSource(new ByteArrayInputStream(new byte[0])); +// } +// } + + + //if Debug is enabled make a copy of inputStream to enable debug output in case of SAXException + byte buffer [] = null; + ByteArrayInputStream baStream = null; + if(true == log.isDebugEnabled()) { + buffer = IOUtils.toByteArray(inputStream); + baStream = new ByteArrayInputStream(buffer); + + } + + + + // create the DOM parser + if (symbolTable != null) { + parser = new DOMParser(symbolTable, grammarPool); + } else { + parser = new DOMParser(); + } + + // set parser features and properties + try { + parser.setFeature(NAMESPACES_FEATURE, true); + parser.setFeature(VALIDATION_FEATURE, validating); + parser.setFeature(SCHEMA_VALIDATION_FEATURE, validating); + parser.setFeature(NORMALIZED_VALUE_FEATURE, false); + parser.setFeature(INCLUDE_IGNORABLE_WHITESPACE_FEATURE, true); + parser.setFeature(CREATE_ENTITY_REF_NODES_FEATURE, false); + parser.setFeature(EXTERNAL_GENERAL_ENTITIES_FEATURE, false); + parser.setFeature(EXTERNAL_PARAMETER_ENTITIES_FEATURE, false); + + //set external added parser features + if (parserFeatures != null) { + for (Entry<String, Object> el : parserFeatures.entrySet()) { + String key = el.getKey(); + if (StringUtils.isNotEmpty(key)) { + Object value = el.getValue(); + if (value != null && value instanceof Boolean) + parser.setFeature(key, (boolean)value); + + else + log.warn("This XML parser only allows features with 'boolean' values"); + + } else + log.warn("Can not set 'null' feature to XML parser"); + } + } + + //fix XXE problem + //parser.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + + + if (validating) { + if (externalSchemaLocations != null) { + parser.setProperty( + EXTERNAL_SCHEMA_LOCATION_PROPERTY, + externalSchemaLocations); + } + if (externalNoNamespaceSchemaLocation != null) { + parser.setProperty( + EXTERNAL_NO_NAMESPACE_SCHEMA_LOCATION_PROPERTY, + externalNoNamespaceSchemaLocation); + } + } + + // set entity resolver and error handler + if (entityResolver != null) { + parser.setEntityResolver(entityResolver); + } + if (errorHandler != null) { + parser.setErrorHandler(errorHandler); + } + + // parse the document and return it + // if debug is enabled: use copy of strem (baStream) else use orig stream + if(null != baStream) + parser.parse(new InputSource(baStream)); + else + parser.parse(new InputSource(inputStream)); + } catch(SAXException e) { + if(true == log.isDebugEnabled() && null != buffer) { + String xmlContent = new String(buffer); + log.debug("SAXException in:\n" + xmlContent); + } + throw(e); + } + + return parser.getDocument(); + } + + /** + * Parse an XML document from an <code>InputStream</code>. + * + * @param inputStream The <code>InputStream</code> containing the XML + * document. + * @param validating If <code>true</code>, parse validating. + * @param externalSchemaLocations A <code>String</code> containing namespace + * URI to schema location pairs, the same way it is accepted by the <code>xsi: + * schemaLocation</code> attribute. + * @param externalNoNamespaceSchemaLocation The schema location of the + * schema for elements without a namespace, the same way it is accepted by the + * <code>xsi:noNamespaceSchemaLocation</code> attribute. + * @param entityResolver An <code>EntityResolver</code> to resolve external + * entities (schemas and DTDs). If <code>null</code>, it will not be set. + * @param errorHandler An <code>ErrorHandler</code> to decide what to do + * with parsing errors. If <code>null</code>, it will not be set. + * @return The parsed XML document as a DOM tree. + * @throws SAXException An error occurred parsing the document. + * @throws IOException An error occurred reading the document. + * @throws ParserConfigurationException An error occurred configuring the XML + * parser. + */ + public static Document parseDocumentSimple(InputStream inputStream) + throws SAXException, IOException, ParserConfigurationException { + + DOMParser parser; + + parser = new DOMParser(); + // set parser features and properties + parser.setFeature(NAMESPACES_FEATURE, true); + parser.setFeature(VALIDATION_FEATURE, false); + parser.setFeature(SCHEMA_VALIDATION_FEATURE, false); + parser.setFeature(NORMALIZED_VALUE_FEATURE, false); + parser.setFeature(INCLUDE_IGNORABLE_WHITESPACE_FEATURE, true); + parser.setFeature(CREATE_ENTITY_REF_NODES_FEATURE, false); + + parser.parse(new InputSource(inputStream)); + + return parser.getDocument(); + } + + + /** + * Parse an XML document from an <code>InputStream</code>. + * + * It uses a <code>MOAEntityResolver</code> as the <code>EntityResolver</code> + * and a <code>MOAErrorHandler</code> as the <code>ErrorHandler</code>. + * + * @param inputStream The <code>InputStream</code> containing the XML + * document. + * @param validating If <code>true</code>, parse validating. + * @param externalSchemaLocations A <code>String</code> containing namespace + * URI to schema location pairs, the same way it is accepted by the <code>xsi: + * schemaLocation</code> attribute. + * @param externalNoNamespaceSchemaLocation The schema location of the + * schema for elements without a namespace, the same way it is accepted by the + * <code>xsi:noNamespaceSchemaLocation</code> attribute. + * @param parserFeatures + * @return The parsed XML document as a DOM tree. + * @throws SAXException An error occurred parsing the document. + * @throws IOException An error occurred reading the document. + * @throws ParserConfigurationException An error occurred configuring the XML + * parser. + */ + public static Document parseDocument( + InputStream inputStream, + boolean validating, + String externalSchemaLocations, + String externalNoNamespaceSchemaLocation, Map<String, Object> parserFeatures) + throws SAXException, IOException, ParserConfigurationException { + + + + return parseDocument( + inputStream, + validating, + externalSchemaLocations, + externalNoNamespaceSchemaLocation, + new EAAFDomEntityResolver(), + null, + parserFeatures); + } + + /** + * Parse an XML document from a <code>String</code>. + * + * It uses a <code>MOAEntityResolver</code> as the <code>EntityResolver</code> + * and a <code>MOAErrorHandler</code> as the <code>ErrorHandler</code>. + * + * @param xmlString The <code>String</code> containing the XML document. + * @param encoding The encoding of the XML document. + * @param validating If <code>true</code>, parse validating. + * @param externalSchemaLocations A <code>String</code> containing namespace + * URI to schema location pairs, the same way it is accepted by the <code>xsi: + * schemaLocation</code> attribute. + * @param externalNoNamespaceSchemaLocation The schema location of the + * schema for elements without a namespace, the same way it is accepted by the + * <code>xsi:noNamespaceSchemaLocation</code> attribute. + * @return The parsed XML document as a DOM tree. + * @throws SAXException An error occurred parsing the document. + * @throws IOException An error occurred reading the document. + * @throws ParserConfigurationException An error occurred configuring the XML + * parser. + */ + public static Document parseDocument( + String xmlString, + String encoding, + boolean validating, + String externalSchemaLocations, + String externalNoNamespaceSchemaLocation, + Map<String, Object> parserFeatures) + throws SAXException, IOException, ParserConfigurationException { + + InputStream in = new ByteArrayInputStream(xmlString.getBytes(encoding)); + return parseDocument( + in, + validating, + externalSchemaLocations, + externalNoNamespaceSchemaLocation, + parserFeatures); + } + + + /** + * Parse an XML document from a <code>String</code>. + * + * It uses a <code>MOAEntityResolver</code> as the <code>EntityResolver</code> + * and a <code>MOAErrorHandler</code> as the <code>ErrorHandler</code>. + * + * @param xmlString The <code>String</code> containing the XML document. + * @param encoding The encoding of the XML document. + * @param validating If <code>true</code>, parse validating. + * @param externalSchemaLocations A <code>String</code> containing namespace + * URI to schema location pairs, the same way it is accepted by the <code>xsi: + * schemaLocation</code> attribute. + * @param externalNoNamespaceSchemaLocation The schema location of the + * schema for elements without a namespace, the same way it is accepted by the + * <code>xsi:noNamespaceSchemaLocation</code> attribute. + * @return The parsed XML document as a DOM tree. + * @throws SAXException An error occurred parsing the document. + * @throws IOException An error occurred reading the document. + * @throws ParserConfigurationException An error occurred configuring the XML + * parser. + */ + public static Document parseDocument( + String xmlString, + String encoding, + boolean validating, + String externalSchemaLocations, + String externalNoNamespaceSchemaLocation) + throws SAXException, IOException, ParserConfigurationException { + + InputStream in = new ByteArrayInputStream(xmlString.getBytes(encoding)); + return parseDocument( + in, + validating, + externalSchemaLocations, + externalNoNamespaceSchemaLocation, + null); + } + + /** + * Parse an UTF-8 encoded XML document from a <code>String</code>. + * + * @param xmlString The <code>String</code> containing the XML document. + * @param validating If <code>true</code>, parse validating. + * @param externalSchemaLocations A <code>String</code> containing namespace + * URI to schema location pairs, the same way it is accepted by the <code>xsi: + * schemaLocation</code> attribute. + * @param externalNoNamespaceSchemaLocation The schema location of the + * schema for elements without a namespace, the same way it is accepted by the + * <code>xsi:noNamespaceSchemaLocation</code> attribute. + * @return The parsed XML document as a DOM tree. + * @throws SAXException An error occurred parsing the document. + * @throws IOException An error occurred reading the document. + * @throws ParserConfigurationException An error occurred configuring the XML + * parser. + */ + public static Document parseDocument( + String xmlString, + boolean validating, + String externalSchemaLocations, + String externalNoNamespaceSchemaLocation) + throws SAXException, IOException, ParserConfigurationException { + + return parseDocument( + xmlString, + "UTF-8", + validating, + externalSchemaLocations, + externalNoNamespaceSchemaLocation); + } + + /** + * A convenience method to parse an XML document validating. + * + * @param inputStream The <code>InputStream</code> containing the XML + * document. + * @return The root element of the parsed XML document. + * @throws SAXException An error occurred parsing the document. + * @throws IOException An error occurred reading the document. + * @throws ParserConfigurationException An error occurred configuring the XML + * parser. + */ + public static Element parseXmlValidating(InputStream inputStream) + throws ParserConfigurationException, SAXException, IOException { + return DOMUtils + .parseDocument(inputStream, true, XMLNamespaceConstants.ALL_SCHEMA_LOCATIONS, null, null) + .getDocumentElement(); + } + + /** + * A convenience method to parse an XML document validating. + * + * @param inputStream The <code>InputStream</code> containing the XML + * document. + * @param parserFeatures Set additional features to XML parser + * @return The root element of the parsed XML document. + * @throws SAXException An error occurred parsing the document. + * @throws IOException An error occurred reading the document. + * @throws ParserConfigurationException An error occurred configuring the XML + * parser. + */ + public static Element parseXmlValidating(InputStream inputStream, Map<String, Object> parserFeatures) + throws ParserConfigurationException, SAXException, IOException { + return DOMUtils + .parseDocument(inputStream, true, XMLNamespaceConstants.ALL_SCHEMA_LOCATIONS, null, parserFeatures) + .getDocumentElement(); + } + + /** + * A convenience method to parse an XML document non validating. + * This method disallow DocType declarations + * + * @param inputStream The <code>InputStream</code> containing the XML + * document. + * @return The root element of the parsed XML document. + * @throws SAXException An error occurred parsing the document. + * @throws IOException An error occurred reading the document. + * @throws ParserConfigurationException An error occurred configuring the XML + * parser. + */ + public static Element parseXmlNonValidating(InputStream inputStream) + throws ParserConfigurationException, SAXException, IOException { + return DOMUtils + .parseDocument(inputStream, false, XMLNamespaceConstants.ALL_SCHEMA_LOCATIONS, null, + Collections.unmodifiableMap(new HashMap<String, Object>() { + private static final long serialVersionUID = 1L; + { + put(DOMUtils.DISALLOW_DOCTYPE_FEATURE, true); + + } + })).getDocumentElement(); + } + + /** + * Schema validate a given DOM element. + * + * @param element The element to validate. + * @param externalSchemaLocations A <code>String</code> containing namespace + * URI to schema location pairs, the same way it is accepted by the <code>xsi: + * schemaLocation</code> attribute. + * @param externalNoNamespaceSchemaLocation The schema location of the + * schema for elements without a namespace, the same way it is accepted by the + * <code>xsi:noNamespaceSchemaLocation</code> attribute. + * @return <code>true</code>, if the <code>element</code> validates against + * the schemas declared in it. + * @throws SAXException An error occurred parsing the document. + * @throws IOException An error occurred reading the document from its + * serialized representation. + * @throws ParserConfigurationException An error occurred configuring the XML + * @throws TransformerException An error occurred serializing the element. + */ + public static boolean validateElement( + Element element, + String externalSchemaLocations, + String externalNoNamespaceSchemaLocation) + throws + ParserConfigurationException, + IOException, + SAXException, + TransformerException { + + byte[] docBytes; + SAXParser parser; + + // create the SAX parser + if (symbolTable != null) { + parser = new SAXParser(symbolTable, grammarPool); + } else { + parser = new SAXParser(); + } + + // serialize the document + docBytes = serializeNode(element, "UTF-8"); + + // set up parser features and attributes + parser.setFeature(NAMESPACES_FEATURE, true); + parser.setFeature(VALIDATION_FEATURE, true); + parser.setFeature(SCHEMA_VALIDATION_FEATURE, true); + parser.setFeature(EXTERNAL_GENERAL_ENTITIES_FEATURE, false); + parser.setFeature(DISALLOW_DOCTYPE_FEATURE, true); + + + if (externalSchemaLocations != null) { + parser.setProperty( + EXTERNAL_SCHEMA_LOCATION_PROPERTY, + externalSchemaLocations); + } + if (externalNoNamespaceSchemaLocation != null) { + parser.setProperty( + EXTERNAL_NO_NAMESPACE_SCHEMA_LOCATION_PROPERTY, + "externalNoNamespaceSchemaLocation"); + } + + // set up entity resolver and error handler + parser.setEntityResolver(new EAAFDomEntityResolver()); + + // parse validating + parser.parse(new InputSource(new ByteArrayInputStream(docBytes))); + return true; + } + + + /** + * Schema validate a given DOM element. + * + * @param element The element to validate. + * @param externalSchemaLocations A <code>String</code> containing namespace + * URI to schema location pairs, the same way it is accepted by the <code>xsi: + * schemaLocation</code> attribute. + * @param externalNoNamespaceSchemaLocation The schema location of the + * schema for elements without a namespace, the same way it is accepted by the + * <code>xsi:noNamespaceSchemaLocation</code> attribute. + * @return <code>true</code>, if the <code>element</code> validates against + * the schemas declared in it. + * @throws SAXException An error occurred parsing the document. + * @throws IOException An error occurred reading the document from its + * serialized representation. + * @throws ParserConfigurationException An error occurred configuring the XML + * @throws TransformerException An error occurred serializing the element. + */ + public static boolean validateElement( + Element element, + String externalSchemaLocations, + String externalNoNamespaceSchemaLocation, + EntityResolver entityResolver) + throws + ParserConfigurationException, + IOException, + SAXException, + TransformerException { + + byte[] docBytes; + SAXParser parser; + + // create the SAX parser + if (symbolTable != null) { + parser = new SAXParser(symbolTable, grammarPool); + } else { + parser = new SAXParser(); + } + + // serialize the document + docBytes = serializeNode(element, "UTF-8"); + + // set up parser features and attributes + parser.setFeature(NAMESPACES_FEATURE, true); + parser.setFeature(VALIDATION_FEATURE, true); + parser.setFeature(SCHEMA_VALIDATION_FEATURE, true); + + if (externalSchemaLocations != null) { + parser.setProperty( + EXTERNAL_SCHEMA_LOCATION_PROPERTY, + externalSchemaLocations); + } + if (externalNoNamespaceSchemaLocation != null) { + parser.setProperty( + EXTERNAL_NO_NAMESPACE_SCHEMA_LOCATION_PROPERTY, + "externalNoNamespaceSchemaLocation"); + } + + // set up entity resolver and error handler + parser.setEntityResolver(entityResolver); + + // parse validating + parser.parse(new InputSource(new ByteArrayInputStream(docBytes))); + return true; + } + + /** + * Serialize the given DOM node. + * + * The node will be serialized using the UTF-8 encoding. + * + * @param node The node to serialize. + * @return String The <code>String</code> representation of the given DOM + * node. + * @throws TransformerException An error occurred transforming the + * node to a <code>String</code>. + * @throws IOException An IO error occurred writing the node to a byte array. + */ + public static String serializeNode(Node node) + throws TransformerException, IOException { + return new String(serializeNode(node, "UTF-8", false), "UTF-8"); + } + + + /** + * Serialize the given DOM node. + * + * The node will be serialized using the UTF-8 encoding. + * + * @param node The node to serialize. + * @param omitXmlDeclaration The boolean value for omitting the XML Declaration. + * @return String The <code>String</code> representation of the given DOM + * node. + * @throws TransformerException An error occurred transforming the + * node to a <code>String</code>. + * @throws IOException An IO error occurred writing the node to a byte array. + */ + public static String serializeNode(Node node, boolean omitXmlDeclaration) + throws TransformerException, IOException { + return new String(serializeNode(node, "UTF-8", omitXmlDeclaration), "UTF-8"); + } + + /** + * Serialize the given DOM node. + * + * The node will be serialized using the UTF-8 encoding. + * + * @param node The node to serialize. + * @param omitXmlDeclaration The boolean value for omitting the XML Declaration. + * @param lineSeperator Sets the line seperator String of the parser + * @return String The <code>String</code> representation of the given DOM + * node. + * @throws TransformerException An error occurred transforming the + * node to a <code>String</code>. + * @throws IOException An IO error occurred writing the node to a byte array. + */ + public static String serializeNode(Node node, boolean omitXmlDeclaration, String lineSeperator) + throws TransformerException, IOException { + return new String(serializeNode(node, "UTF-8", omitXmlDeclaration, lineSeperator), "UTF-8"); + } + + /** + * Serialize the given DOM node to a byte array. + * + * @param node The node to serialize. + * @param xmlEncoding The XML encoding to use. + * @return The serialized node, as a byte array. Using a compatible encoding + * this can easily be converted into a <code>String</code>. + * @throws TransformerException An error occurred transforming the node to a + * byte array. + * @throws IOException An IO error occurred writing the node to a byte array. + */ + public static byte[] serializeNode(Node node, String xmlEncoding) + throws TransformerException, IOException { + return serializeNode(node, xmlEncoding, false); + } + + /** + * Serialize the given DOM node to a byte array. + * + * @param node The node to serialize. + * @param xmlEncoding The XML encoding to use. + * @param omitDeclaration The boolean value for omitting the XML Declaration. + * @return The serialized node, as a byte array. Using a compatible encoding + * this can easily be converted into a <code>String</code>. + * @throws TransformerException An error occurred transforming the node to a + * byte array. + * @throws IOException An IO error occurred writing the node to a byte array. + */ + public static byte[] serializeNode(Node node, String xmlEncoding, boolean omitDeclaration) + throws TransformerException, IOException { + return serializeNode(node, xmlEncoding, omitDeclaration, null); + } + + + /** + * Serialize the given DOM node to a byte array. + * + * @param node The node to serialize. + * @param xmlEncoding The XML encoding to use. + * @param omitDeclaration The boolean value for omitting the XML Declaration. + * @param lineSeperator Sets the line seperator String of the parser + * @return The serialized node, as a byte array. Using a compatible encoding + * this can easily be converted into a <code>String</code>. + * @throws TransformerException An error occurred transforming the node to a + * byte array. + * @throws IOException An IO error occurred writing the node to a byte array. + */ + public static byte[] serializeNode(Node node, String xmlEncoding, boolean omitDeclaration, String lineSeperator) + throws TransformerException, IOException { + + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + Transformer transformer = transformerFactory.newTransformer(); + ByteArrayOutputStream bos = new ByteArrayOutputStream(16384); + + transformer.setOutputProperty(OutputKeys.METHOD, "xml"); + transformer.setOutputProperty(OutputKeys.ENCODING, xmlEncoding); + String omit = omitDeclaration ? "yes" : "no"; + transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, omit); + if (null!=lineSeperator) { + transformer.setOutputProperty("{http://xml.apache.org/xalan}line-separator", lineSeperator);//does not work for xalan <= 2.5.1 + } + transformer.transform(new DOMSource(node), new StreamResult(bos)); + + bos.flush(); + bos.close(); + + return bos.toByteArray(); + } + + /** + * Return the text that a node contains. + * + * This routine: + * <ul> + * <li>Ignores comments and processing instructions.</li> + * <li>Concatenates TEXT nodes, CDATA nodes, and the results recursively + * processing EntityRef nodes.</li> + * <li>Ignores any element nodes in the sublist. (Other possible options are + * to recurse into element sublists or throw an exception.)</li> + * </ul> + * + * @param node A DOM node from which to extract text. + * @return A String representing its contents. + */ + public static String getText(Node node) { + if (!node.hasChildNodes()) { + return ""; + } + + StringBuffer result = new StringBuffer(); + NodeList list = node.getChildNodes(); + + for (int i = 0; i < list.getLength(); i++) { + Node subnode = list.item(i); + if (subnode.getNodeType() == Node.TEXT_NODE) { + result.append(subnode.getNodeValue()); + } else if (subnode.getNodeType() == Node.CDATA_SECTION_NODE) { + result.append(subnode.getNodeValue()); + } else if (subnode.getNodeType() == Node.ENTITY_REFERENCE_NODE) { + // Recurse into the subtree for text + // (and ignore comments) + result.append(getText(subnode)); + } + } + return result.toString(); + } + + /** + * Build the namespace prefix to namespace URL mapping in effect for a given + * node. + * + * @param node The context node for which build the map. + * @return The namespace prefix to namespace URL mapping ( + * a <code>String</code> value to <code>String</code> value mapping). + */ + public static Map getNamespaceDeclarations(Node node) { + Map nsDecls = new HashMap(); + int i; + + do { + if (node.hasAttributes()) { + NamedNodeMap attrs = node.getAttributes(); + + for (i = 0; i < attrs.getLength(); i++) { + Attr attr = (Attr) attrs.item(i); + + // add prefix mapping if none exists + if ("xmlns".equals(attr.getPrefix()) + || "xmlns".equals(attr.getName())) { + + String nsPrefix = + attr.getPrefix() != null ? attr.getLocalName() : ""; + + if (nsDecls.get(nsPrefix) == null) { + nsDecls.put(nsPrefix, attr.getValue()); + } + } + } + } + } while ((node = node.getParentNode()) != null); + + return nsDecls; + } + + /** + * Add all namespace declarations declared in the parent(s) of a given + * element and used in the subtree of the given element to the given element. + * + * @param context The element to which to add the namespaces. + */ + public static void localizeNamespaceDeclarations(Element context) { + Node parent = context.getParentNode(); + + if (parent != null) { + Map namespaces = getNamespaceDeclarations(context.getParentNode()); + Set nsUris = collectNamespaceURIs(context); + Iterator iter; + + for (iter = namespaces.entrySet().iterator(); iter.hasNext();) { + Map.Entry e = (Map.Entry) iter.next(); + + if (nsUris.contains(e.getValue())) { + String prefix = (String) e.getKey(); + String nsUri = (String) e.getValue(); + String nsAttrName = "".equals(prefix) ? "xmlns" : "xmlns:" + prefix; + + context.setAttributeNS(XMLNamespaceConstants.XMLNS_NS_URI, nsAttrName, nsUri); + } + } + } + } + + /** + * Collect all the namespace URIs used in the subtree of a given element. + * + * @param context The element that should be searched for namespace URIs. + * @return All namespace URIs used in the subtree of <code>context</code>, + * including the ones used in <code>context</code> itself. + */ + public static Set collectNamespaceURIs(Element context) { + Set result = new HashSet(); + + collectNamespaceURIsImpl(context, result); + return result; + } + + /** + * A recursive method to do the work of <code>collectNamespaceURIs</code>. + * + * @param context The context element to evaluate. + * @param result The result, passed as a parameter to avoid unnecessary + * instantiations of <code>Set</code>. + */ + private static void collectNamespaceURIsImpl(Element context, Set result) { + NamedNodeMap attrs = context.getAttributes(); + NodeList childNodes = context.getChildNodes(); + String nsUri; + int i; + + // add the namespace of the context element + nsUri = context.getNamespaceURI(); + if (nsUri != null && nsUri != XMLNamespaceConstants.XMLNS_NS_URI) { + result.add(nsUri); + } + + // add all namespace URIs from attributes + for (i = 0; i < attrs.getLength(); i++) { + nsUri = attrs.item(i).getNamespaceURI(); + if (nsUri != null && nsUri != XMLNamespaceConstants.XMLNS_NS_URI) { + result.add(nsUri); + } + } + + // add all namespaces from subelements + for (i = 0; i < childNodes.getLength(); i++) { + Node node = childNodes.item(i); + + if (node.getNodeType() == Node.ELEMENT_NODE) { + collectNamespaceURIsImpl((Element) node, result); + } + } + } + + /** + * Check, that each attribute node in the given <code>NodeList</code> has its + * parent in the <code>NodeList</code> as well. + * + * @param nodes The <code>NodeList</code> to check. + * @return <code>true</code>, if each attribute node in <code>nodes</code> + * has its parent in <code>nodes</code> as well. + */ + public static boolean checkAttributeParentsInNodeList(NodeList nodes) { + Set nodeSet = new HashSet(); + int i; + + // put the nodes into the nodeSet + for (i = 0; i < nodes.getLength(); i++) { + nodeSet.add(nodes.item(i)); + } + + // check that each attribute node's parent is in the node list + for (i = 0; i < nodes.getLength(); i++) { + Node n = nodes.item(i); + + if (n.getNodeType() == Node.ATTRIBUTE_NODE) { + Attr attr = (Attr) n; + Element owner = attr.getOwnerElement(); + + if (owner == null) { + if (!isNamespaceDeclaration(attr)) { + return false; + } + } + + if (!nodeSet.contains(owner) && !isNamespaceDeclaration(attr)) { + return false; + } + } + } + + return true; + } + + /** + * Convert an unstructured <code>NodeList</code> into a + * <code>DocumentFragment</code>. + * + * @param nodeList Contains the node list to be converted into a DOM + * DocumentFragment. + * @return the resulting DocumentFragment. The DocumentFragment will be + * backed by a new DOM Document, i.e. all noded of the node list will be + * cloned. + * @throws ParserConfigurationException An error occurred creating the + * DocumentFragment. + * @precondition The nodes in the node list appear in document order + * @precondition for each Attr node in the node list, the owning Element is + * in the node list as well. + * @precondition each Element or Attr node in the node list is namespace + * aware. + */ + public static DocumentFragment nodeList2DocumentFragment(NodeList nodeList) + throws ParserConfigurationException { + + DocumentBuilder builder = + DocumentBuilderFactory.newInstance().newDocumentBuilder(); + Document doc = builder.newDocument(); + DocumentFragment result = doc.createDocumentFragment(); + + if (null == nodeList || nodeList.getLength() == 0) { + return result; + } + + int currPos = 0; + currPos = + nodeList2DocumentFragment(nodeList, currPos, result, null, null) + 1; + + while (currPos < nodeList.getLength()) { + currPos = + nodeList2DocumentFragment(nodeList, currPos, result, null, null) + 1; + } + return result; + } + + /** + * Helper method for the <code>nodeList2DocumentFragment</code>. + * + * @param nodeList The <code>NodeList</code> to convert. + * @param currPos The current position in the <code>nodeList</code>. + * @param result The resulting <code>DocumentFragment</code>. + * @param currOrgElem The current original element. + * @param currClonedElem The current cloned element. + * @return The current position. + */ + private static int nodeList2DocumentFragment( + NodeList nodeList, + int currPos, + DocumentFragment result, + Element currOrgElem, + Element currClonedElem) { + + while (currPos < nodeList.getLength()) { + Node currentNode = nodeList.item(currPos); + switch (currentNode.getNodeType()) { + case Node.COMMENT_NODE : + case Node.PROCESSING_INSTRUCTION_NODE : + case Node.TEXT_NODE : + { + // Append current node either to resulting DocumentFragment or to + // current cloned Element + if (null == currClonedElem) { + result.appendChild( + result.getOwnerDocument().importNode(currentNode, false)); + } else { + // Stop processing if current Node is not a descendant of + // current Element + if (!isAncestor(currOrgElem, currentNode)) { + return --currPos; + } + + currClonedElem.appendChild( + result.getOwnerDocument().importNode(currentNode, false)); + } + break; + } + + case Node.ELEMENT_NODE : + { + Element nextCurrOrgElem = (Element) currentNode; + Element nextCurrClonedElem = + result.getOwnerDocument().createElementNS( + nextCurrOrgElem.getNamespaceURI(), + nextCurrOrgElem.getNodeName()); + + // Append current Node either to resulting DocumentFragment or to + // current cloned Element + if (null == currClonedElem) { + result.appendChild(nextCurrClonedElem); + currOrgElem = nextCurrOrgElem; + currClonedElem = nextCurrClonedElem; + } else { + // Stop processing if current Node is not a descendant of + // current Element + if (!isAncestor(currOrgElem, currentNode)) { + return --currPos; + } + + currClonedElem.appendChild(nextCurrClonedElem); + } + + // Process current Node (of type Element) recursively + currPos = + nodeList2DocumentFragment( + nodeList, + ++currPos, + result, + nextCurrOrgElem, + nextCurrClonedElem); + + break; + } + + case Node.ATTRIBUTE_NODE : + { + Attr currAttr = (Attr) currentNode; + + // GK 20030411: Hack to overcome problems with IAIK IXSIL + if (currAttr.getOwnerElement() == null) + break; + if (currClonedElem == null) + break; + + // currClonedElem must be the owner Element of currAttr if + // preconditions are met + currClonedElem.setAttributeNS( + currAttr.getNamespaceURI(), + currAttr.getNodeName(), + currAttr.getValue()); + break; + } + + default : + { + // All other nodes will be ignored + } + } + + currPos++; + } + + return currPos; + } + + /** + * Check, if the given attribute is a namespace declaration. + * + * @param attr The attribute to check. + * @return <code>true</code>, if the attribute is a namespace declaration, + * <code>false</code> otherwise. + */ + private static boolean isNamespaceDeclaration(Attr attr) { + return XMLNamespaceConstants.XMLNS_NS_URI.equals(attr.getNamespaceURI()); + } + + /** + * Check, if a given DOM element is an ancestor of a given node. + * + * @param candAnc The DOM element to check for being the ancestor. + * @param cand The node to check for being the child. + * @return <code>true</code>, if <code>candAnc</code> is an (indirect) + * ancestor of <code>cand</code>; <code>false</code> otherwise. + */ + public static boolean isAncestor(Element candAnc, Node cand) { + Node currPar = cand.getParentNode(); + + while (currPar != null) { + if (candAnc == currPar) + return true; + currPar = currPar.getParentNode(); + } + return false; + } + + /** + * Selects the (first) element from a node list and returns it. + * + * @param nl The NodeList to get the element from. + * @return The (first) element included in the node list or <code>null</code> + * if the node list is <code>null</code> or empty or no element is + * included in the list. + */ + public static Element getElementFromNodeList (NodeList nl) { + if ((nl == null) || (nl.getLength() == 0)) { + return null; + } + for (int i=0; i<nl.getLength(); i++) { + Node node = nl.item(i); + if (node.getNodeType() == Node.ELEMENT_NODE) { + return (Element)node; + } + } + return null; + } + + /** + * Returns all child elements of the given element. + * + * @param parent The element to get the child elements from. + * + * @return A list including all child elements of the given element. + * Maybe empty if the parent element has no child elements. + */ + public static List getChildElements (Element parent) { + Vector v = new Vector(); + NodeList nl = parent.getChildNodes(); + int length = nl.getLength(); + for (int i=0; i < length; i++) { + Node node = nl.item(i); + if (node.getNodeType() == Node.ELEMENT_NODE) { + v.add((Element)node); + } + } + return v; + } + + /** + * Returns a byte array from given node. + * @param node + * @return + * @throws TransformerException + */ + public static byte[] nodeToByteArray(Node node) throws TransformerException { + Source source = new DOMSource(node); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + //StringWriter stringWriter = new StringWriter(); + Result result = new StreamResult(out); + TransformerFactory factory = TransformerFactory.newInstance(); + Transformer transformer = factory.newTransformer(); + transformer.transform(source, result); + return out.toByteArray(); + } + + +} diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/DataURLBuilder.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/DataURLBuilder.java new file mode 100644 index 00000000..de8fda45 --- /dev/null +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/DataURLBuilder.java @@ -0,0 +1,113 @@ +/******************************************************************************* + * Copyright 2014 Federal Chancellery Austria + * MOA-ID has been developed in a cooperation between BRZ, the Federal + * Chancellery Austria - ICT staff unit, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + ******************************************************************************/ +/* + * Copyright 2003 Federal Chancellery Austria + * MOA-ID has been developed in a cooperation between BRZ, the Federal + * Chancellery Austria - ICT staff unit, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + */ + + +package at.gv.egiz.eaaf.core.impl.utils; + +import org.apache.commons.lang3.StringUtils; + +import at.gv.egiz.eaaf.core.api.data.EAAFConstants; + +/** + * Builds a DataURL parameter meant for the security layer implementation + * to respond to. + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class DataURLBuilder { + + /** + * Constructor for DataURLBuilder. + */ + public DataURLBuilder() { + super(); + } + + /** + * Constructs a data URL for <code>VerifyIdentityLink</code> or <code>VerifyAuthenticationBlock</code>, + * including the <code>MOASessionID</code> as a parameter. + * + * @param authBaseURL base URL (context path) of the MOA ID Authentication component, + * including a trailing <code>'/'</code> + * @param authServletName request part of the data URL + * @param pendingReqId sessionID to be included in the dataURL + * @return String + */ + public String buildDataURL(String authBaseURL, String authServletName, String pendingReqId) { + String dataURL; + if (!authBaseURL.endsWith("/")) + authBaseURL += "/"; + + if (authServletName.startsWith("/")) + authServletName = authServletName.substring(1); + + dataURL = authBaseURL + authServletName; + + if (StringUtils.isNotEmpty(pendingReqId)) + dataURL = addParameter(dataURL, EAAFConstants.PARAM_HTTP_TARGET_PENDINGREQUESTID, pendingReqId); + + return dataURL; + } + + /** + * Method addParameter. + * @param urlString represents the url + * @param paramname is the parameter to be added + * @param value is the value of that parameter + * @return String + */ + private String addParameter(String urlString, String paramname, String value) { + String url = urlString; + if (paramname != null) { + if (url.indexOf("?") < 0) + url += "?"; + else + url += "&"; + url += paramname + "=" + value; + } + return url; + } +} diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/EAAFDomEntityResolver.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/EAAFDomEntityResolver.java new file mode 100644 index 00000000..5b39386c --- /dev/null +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/EAAFDomEntityResolver.java @@ -0,0 +1,104 @@ +/******************************************************************************* + *******************************************************************************/ + + +package at.gv.egiz.eaaf.core.impl.utils; + +import java.io.InputStream; + +import org.apache.xerces.util.URI; +import org.apache.xerces.util.URI.MalformedURIException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.xml.sax.EntityResolver; +import org.xml.sax.InputSource; + +import at.gv.egiz.eaaf.core.api.data.XMLNamespaceConstants; + +/** + * An <code>EntityResolver</code> that looks up entities stored as + * local resources. + * + * <p>The following DTDs are mapped to local resources: + * <ul> + * <li>The XMLSchema.dtd</li> + * <li>The datatypes.dtd</li> + * </ul> + * </p> + * <p>For all other resources, an attempt is made to resolve them as resources, + * either absolute or relative to <code>Constants.SCHEMA_ROOT</code>. + * + */ +public class EAAFDomEntityResolver implements EntityResolver { + private static final Logger log = LoggerFactory.getLogger(EAAFDomEntityResolver.class); + + /** + * Resolve an entity. + * + * The <code>systemId</code> parameter is used to perform the lookup of the + * entity as a resource, either by interpreting the <code>systemId</code> as + * an absolute resource path, or by appending the last path component of + * <code>systemId</code> to <code>Constants.SCHEMA_ROOT</code>. + * + * @param publicId The public ID of the resource. + * @param systemId The system ID of the resource. + * @return An <code>InputSource</code> from which the entity can be read, or + * <code>null</code>, if the entity could not be found. + * @see org.xml.sax.EntityResolver#resolveEntity(java.lang.String, java.lang.String) + */ + public InputSource resolveEntity(String publicId, String systemId) { + InputStream stream; + int slashPos; + + if (publicId != null) { + // check if we can resolve some standard dtd's + if (publicId.equalsIgnoreCase("-//W3C//DTD XMLSchema 200102//EN")) { + return new InputSource( + getClass().getResourceAsStream( + XMLNamespaceConstants.SCHEMA_ROOT + "XMLSchema.dtd")); + } else if (publicId.equalsIgnoreCase("datatypes")) { + return new InputSource( + getClass().getResourceAsStream( + XMLNamespaceConstants.SCHEMA_ROOT + "datatypes.dtd")); + } + } else if (systemId != null) { + // get the URI path + try { + URI uri = new URI(systemId); + systemId = uri.getPath(); + + if (!"file".equals(uri.getScheme()) || "".equals(systemId.trim())) { + return null; + } + + } catch (MalformedURIException e) { + return null; + } + + // try to get the resource from the full path + stream = getClass().getResourceAsStream(systemId); + if (stream != null) { + InputSource source = new InputSource(stream); + + source.setSystemId(systemId); + return source; + } + + // try to get the resource from the last path component + slashPos = systemId.lastIndexOf('/'); + if (slashPos >= 0 && systemId.length() > slashPos) { + systemId = systemId.substring(slashPos + 1, systemId.length()); + stream = + getClass().getResourceAsStream(XMLNamespaceConstants.SCHEMA_ROOT + systemId); + if (stream != null) { + InputSource source = new InputSource(stream); + + source.setSystemId(systemId); + return source; + } + } + } + + return null; // nothing found - let the parser handle the entity + } +}
\ No newline at end of file diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/FileUtils.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/FileUtils.java new file mode 100644 index 00000000..01fb7375 --- /dev/null +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/FileUtils.java @@ -0,0 +1,142 @@ +/******************************************************************************* + *******************************************************************************/ + + +package at.gv.egiz.eaaf.core.impl.utils; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; + +/** + * Utility for accessing files on the file system, and for reading from input streams. + * @author Paul Ivancsics + * @version $Id$ + */ +public class FileUtils { + + /** + * Reads a file, given by URL, into a byte array. + * @param urlString file URL + * @return file content + * @throws IOException on any exception thrown + */ + public static byte[] readURL(String urlString) throws IOException { + URL url = new URL(urlString); + InputStream in = new BufferedInputStream(url.openStream()); + byte[] content = StreamUtils.readStream(in); + in.close(); + return content; + } + + /** + * Reads a file from a resource. + * @param name resource name + * @return file content as a byte array + * @throws IOException on any exception thrown + */ + public static byte[] readResource(String name) throws IOException { + ClassLoader cl = FileUtils.class.getClassLoader(); + BufferedInputStream in = new BufferedInputStream(cl.getResourceAsStream(name)); + byte[] content = StreamUtils.readStream(in); + in.close(); + return content; + } + /** + * Reads a file from a resource. + * @param name filename + * @param encoding character encoding + * @return file content + * @throws IOException on any exception thrown + */ + public static String readResource(String name, String encoding) throws IOException { + byte[] content = readResource(name); + return new String(content, encoding); + } + + + /** + * Returns the absolute URL of a given url which is relative to the parameter root + * @param url + * @param root + * @return String + * @throws MalformedURLException + */ + public static String makeAbsoluteURL(String url, URI root) throws MalformedURLException { + return makeAbsoluteURL(url, root.toURL().toString()); + + } + + /** + * Returns the absolute URL of a given url which is relative to the parameter root + * @param url + * @param root + * @return String + */ + public static String makeAbsoluteURL(String url, String root) { + //if url is relative to rootConfigFileDirName make it absolute + + File keyFile; + String newURL = url; + + if(null == url) return null; + + if (url.startsWith("http:/") || url.startsWith("https:/") || url.startsWith("file:/") || url.startsWith("ftp:/")) { + return url; + + } else { + // check if absolute - if not make it absolute + keyFile = new File(url); + if (!keyFile.isAbsolute()) { + keyFile = new File(root, url); + + if (keyFile.toString().startsWith("file:")) + newURL = keyFile.toString(); + + else + newURL = keyFile.toURI().toString(); + + } + return newURL; + } + } + + + private static void copy( InputStream fis, OutputStream fos ) + { + try + { + byte[] buffer = new byte[ 0xFFFF ]; + for ( int len; (len = fis.read(buffer)) != -1; ) + fos.write( buffer, 0, len ); + } + catch( IOException e ) { + System.err.println( e ); + } + finally { + if ( fis != null ) + try { fis.close(); } catch ( IOException e ) { e.printStackTrace(); } + if ( fos != null ) + try { fos.close(); } catch ( IOException e ) { e.printStackTrace(); } + } + } + + public static void copyFile(File src, File dest) + { + try + { + copy( new FileInputStream( src ), new FileOutputStream( dest ) ); + } + catch( IOException e ) { + e.printStackTrace(); + } + } + +} diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/HTTPUtils.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/HTTPUtils.java new file mode 100644 index 00000000..cf1abaa7 --- /dev/null +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/HTTPUtils.java @@ -0,0 +1,178 @@ +/******************************************************************************* + * Copyright 2014 Federal Chancellery Austria + * MOA-ID has been developed in a cooperation between BRZ, the Federal + * Chancellery Austria - ICT staff unit, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + ******************************************************************************/ +/* + * Copyright 2003 Federal Chancellery Austria + * MOA-ID has been developed in a cooperation between BRZ, the Federal + * Chancellery Austria - ICT staff unit, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + */ + + +package at.gv.egiz.eaaf.core.impl.utils; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.commons.lang3.StringUtils; + + +/** + * + * @author Rudolf Schamberger + * + */ +public class HTTPUtils { + +// /** +// * Utility used to obtainin correct encoded HTTP content. +// * Reads a given Content adressed by HTTP-URL into String. +// * Content encoding is considered by using the Content-Type HTTP header charset value. +// * @param URL HTTP URL to read from. +// * @return String representation of content +// * @throws IOException on data-reading problems +// */ +// public static String readHttpURL(String URL) +// throws IOException { +// +// URL url = new URL(URL); +// HttpURLConnection conn = (HttpURLConnection)url.openConnection(); +// conn.setRequestMethod("GET"); +// String contentType = conn.getContentType(); +// RE regExp = null; +// try { +// regExp = new RE("(;.*charset=)(\"*)(.*[^\"])"); +// } catch (RESyntaxException e) { +// //RESyntaxException is not possible = expr. is costant +// } +// boolean charsetSupplied = regExp.match(contentType); +// String encoding = "ISO-8859-1"; //default HTTP encoding +// if (charsetSupplied) { +// encoding = regExp.getParen(3); +// } +// InputStream instream = new BufferedInputStream(conn.getInputStream()); +// InputStreamReader isr = new InputStreamReader(instream, encoding); +// Reader in = new BufferedReader(isr); +// int ch; +// StringBuffer buffer = new StringBuffer(); +// while ((ch = in.read()) > -1) { +// buffer.append((char)ch); +// } +// in.close(); +// conn.disconnect(); +// return buffer.toString(); +// } + + /** + * Helper method to retrieve server URL including context path + * @param request HttpServletRequest + * @return Server URL including context path (e.g. http://localhost:8443/moa-id-auth + */ + public static String getBaseURL(HttpServletRequest request) { + StringBuffer buffer = new StringBuffer(getServerURL(request)); + + // add context path if available + String contextPath = request.getContextPath(); + if (!StringUtils.isEmpty(contextPath)) { + buffer.append(contextPath); + } + + return buffer.toString(); + } + + /** + * Helper method to retrieve server URL + * @param request HttpServletRequest + * @return Server URL (e.g. http://localhost:8443) + */ + public static String getServerURL(HttpServletRequest request) { + StringBuffer buffer = new StringBuffer(); + + // get protocol + String protocol = request.getScheme(); + buffer.append(protocol).append("://"); + + // server name + buffer.append(request.getServerName()); + + // add port if necessary + int port = request.getServerPort(); + if ((protocol.equals("http") && port != 80) || (protocol.equals("https") && port != 443)) { + buffer.append(':'); + buffer.append(port); + } + + return buffer.toString(); + } + + /** + * Extract the IDP PublicURLPrefix from authrequest + * + * @param req HttpServletRequest + * @return PublicURLPrefix <String> which ends always without / + */ + public static String extractAuthURLFromRequest(HttpServletRequest req) { + String authURL = req.getScheme() + "://" + req.getServerName(); + if ((req.getScheme().equalsIgnoreCase("https") && req.getServerPort()!=443) || (req.getScheme().equalsIgnoreCase("http") && req.getServerPort()!=80)) { + authURL = authURL.concat(":" + req.getServerPort()); + } + authURL = authURL.concat(req.getContextPath()); + return authURL; + + } + + /** + * Extract the IDP requested URL from authrequest + * + * @param req HttpServletRequest + * @return RequestURL <String> which ends always without / + */ + public static String extractAuthServletPathFromRequest(HttpServletRequest req) { + return extractAuthURLFromRequest(req).concat(req.getServletPath()); + + } + + public static String addURLParameter(String url, String paramname, + String paramvalue) { + String param = paramname + "=" + paramvalue; + if (url.indexOf("?") < 0) + return url + "?" + param; + else + return url + "&" + param; + } + +} diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/KeyStoreUtils.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/KeyStoreUtils.java new file mode 100644 index 00000000..3583a294 --- /dev/null +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/KeyStoreUtils.java @@ -0,0 +1,175 @@ +/******************************************************************************* + *******************************************************************************/ + + +package at.gv.egiz.eaaf.core.impl.utils; + +import java.io.BufferedInputStream; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; + +/** + * Utility for creating and loading key stores. + * + * @author Paul Ivancsics + * @version $Id$ + */ +public class KeyStoreUtils { + + /** + * JAVA KeyStore + */ + private static final String KEYSTORE_TYPE_JKS = "JKS"; + + /** + * PKCS12 KeyStore + */ + private static final String KEYSTORE_TYPE_PKCS12 = "PKCS12"; + + + + /** + * Loads a key store from file. + * + * @param keystoreType key store type + * @param urlString URL of key store + * @param password password protecting the key store + * @return key store loaded + * @throws IOException thrown while reading the key store from file + * @throws GeneralSecurityException thrown while creating the key store + */ + public static KeyStore loadKeyStore( + String keystoreType, + String urlString, + String password) + throws IOException, GeneralSecurityException { + + URL keystoreURL = new URL(urlString); + InputStream in = keystoreURL.openStream(); + return loadKeyStore(keystoreType, in, password); + } + /** + * Loads a key store from an <code>InputStream</code>, and + * closes the <code>InputStream</code>. + * + * @param keystoreType key store type + * @param in input stream + * @param password password protecting the key store + * @return key store loaded + * @throws IOException thrown while reading the key store from the stream + * @throws GeneralSecurityException thrown while creating the key store + */ + public static KeyStore loadKeyStore( + String keystoreType, + InputStream in, + String password) + throws IOException, GeneralSecurityException { + + char[] chPassword = null; + if (password != null) + chPassword = password.toCharArray(); + KeyStore ks = KeyStore.getInstance(keystoreType); + ks.load(in, chPassword); + in.close(); + return ks; + } + /** + * Creates a key store from X509 certificate files, aliasing them with + * the index in the <code>String[]</code>, starting with <code>"0"</code>. + * + * @param keyStoreType key store type + * @param certFilenames certificate filenames + * @return key store created + * @throws IOException thrown while reading the certificates from file + * @throws GeneralSecurityException thrown while creating the key store + */ + public static KeyStore createKeyStore( + String keyStoreType, + String[] certFilenames) + throws IOException, GeneralSecurityException { + + KeyStore ks = KeyStore.getInstance(keyStoreType); + ks.load(null, null); + for (int i = 0; i < certFilenames.length; i++) { + Certificate cert = loadCertificate(certFilenames[i]); + ks.setCertificateEntry("" + i, cert); + } + return ks; + } + + /** + * Loads an X509 certificate from file. + * @param certFilename filename + * @return the certificate loaded + * @throws IOException thrown while reading the certificate from file + * @throws GeneralSecurityException thrown while creating the certificate + */ + private static Certificate loadCertificate(String certFilename) + throws IOException, GeneralSecurityException { + + FileInputStream in = new FileInputStream(certFilename); + CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); + Certificate cert = certFactory.generateCertificate(in); + in.close(); + return cert; + } + + + /** + * Loads a keyStore without knowing the keyStore type + * @param keyStorePath URL to the keyStore + * @param password Password protecting the keyStore + * @return keyStore loaded + * @throws KeyStoreException thrown if keyStore cannot be loaded + * @throws FileNotFoundException + * @throws IOException + */ + public static KeyStore loadKeyStore(String keyStorePath, String password) throws KeyStoreException, IOException{ + + //InputStream is = new FileInputStream(keyStorePath); + URL keystoreURL = new URL(keyStorePath); + InputStream in = keystoreURL.openStream(); + InputStream isBuffered = new BufferedInputStream(in); + return loadKeyStore(isBuffered, password); + + } + + /** + * Loads a keyStore without knowing the keyStore type + * @param in input stream + * @param password Password protecting the keyStore + * @return keyStore loaded + * @throws KeyStoreException thrown if keyStore cannot be loaded + * @throws FileNotFoundException + * @throws IOException + */ +public static KeyStore loadKeyStore(InputStream is, String password) throws KeyStoreException, IOException{ + is.mark(1024*1024); + KeyStore ks = null; + try { + try { + ks = loadKeyStore(KEYSTORE_TYPE_PKCS12, is, password); + } catch (IOException e2) { + is.reset(); + ks = loadKeyStore(KEYSTORE_TYPE_JKS, is, password); + } + } catch(Exception e) { + e.printStackTrace(); + + } + return ks; + + } + + + + +} diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/KeyValueUtils.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/KeyValueUtils.java new file mode 100644 index 00000000..49b957cc --- /dev/null +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/KeyValueUtils.java @@ -0,0 +1,319 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eaaf.core.impl.utils; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Properties; +import java.util.Set; + +import org.apache.commons.lang3.StringUtils; + +/** + * @author tlenz + * + */ +public class KeyValueUtils { + + public static final String KEY_DELIMITER = "."; + public static final String CSV_DELIMITER = ","; + + /** + * Convert Java properties into a Map<String, String> + * <br><br> + * <b>Important:</b> The key/values from properties must be of type String! + * + * @param properties + * @return + */ + public static Map<String, String> convertPropertiesToMap(Properties properties) { + return new HashMap<String, String>((Map) properties); + + //INFO Java8 solution ;) +// return properties.entrySet().stream().collect( +// Collectors.toMap( +// e -> e.getKey().toString(), +// e -> e.getValue().toString() +// ) +// ); + + } + + /** + * Extract the first child of an input key after a the prefix + * + * @param key Full input key + * @param prefix Prefix + * @return Child key {String} if it exists or null + */ + public static String getFirstChildAfterPrefix(String key, String prefix) { + String idAfterPrefix = removePrefixFromKey(key, prefix); + if (idAfterPrefix != null) { + int index = idAfterPrefix.indexOf(KEY_DELIMITER); + if (index > 0) { + String adding = idAfterPrefix.substring(0, index); + if (!(adding.isEmpty())) { + return adding; + + } + } else if (!(idAfterPrefix.isEmpty())) { + return idAfterPrefix; + + } + + } + return null; + } + + /** + * Extract the prefix from an input key + * + * @param key Full input key + * @param suffix Suffix of this key + * @return Prefix {String} of the key or null if input key does not ends with postfix string + */ + public static String getPrefixFromKey(String key, String suffix) { + if (key != null && key.endsWith(suffix)) { + String idPreforeSuffix = key.substring(0, key.length()-suffix.length()); + if (idPreforeSuffix.endsWith(KEY_DELIMITER)) + return idPreforeSuffix.substring(0, idPreforeSuffix.length()-1); + else + return idPreforeSuffix; + } + return null; + + } + + /** + * Remove a prefix string from a key + * + * @param key Full input key + * @param prefix Prefix, which should be removed + * @return The suffix of the input key or null if the input does not starts with the prefix + */ + public static String removePrefixFromKey(String key, String prefix) { + if (prefix == null) + prefix = new String(); + + if (key!=null && key.startsWith(prefix)) { + String afterPrefix = key.substring(prefix.length()); + int index = afterPrefix.indexOf(KEY_DELIMITER); + + if (index == 0) { + afterPrefix = afterPrefix.substring(1); + + } + return afterPrefix; + + } + return null; + } + + /** + * Remove a prefix string from all keys in {Map<String, String>} of key/value pairs + * + * @param keys Input data of key/value pairs + * @param prefix Prefix which should be removed + * @return {Map<String, String>} of key/value pairs without prefix in key, but never null + */ + public static Map<String, String> removePrefixFromKeys(Map<String, String> keys, String prefix) { + Map<String, String> result = new HashMap<String, String>(); + Iterator<Entry<String, String>> interator = keys.entrySet().iterator(); + while(interator.hasNext()) { + Entry<String, String> el = interator.next(); + String newKey = removePrefixFromKey(el.getKey(), prefix); + if (StringUtils.isNotEmpty(newKey)) { + result.put(newKey, el.getValue()); + } + } + + return result; + } + + /** + * Get a subset of key/value pairs which starts with a prefix string + * The Prefix is removed from the key + * + * @param keys Input data of key/value pairs + * @param prefix Prefix string + * @return {Map<String, String>} of key/value pairs without prefix in key, but never null + */ + public static Map<String, String> getSubSetWithPrefix(Map<String, String> keys, String prefix) { + return removePrefixFromKeys(keys, prefix); + } + + + /** + * Add a prefix to key/value pairs to make the key absolute according to key namespace convention + * + * @param input Input key/value pairs which should be updated + * @param prefix Key prefix, which should be added if the key is not absolute + * @param absolutIdentifier Key identifier, which indicates an absolute key + * @return {Map<String, String>} of key/value pairs in which all keys are absolute but never null + */ + public static Map<String, String> makeKeysAbsolut(Map<String, String> input, String prefix, String absolutIdentifier) { + Map<String, String> result = new HashMap<String, String>(); + Iterator<Entry<String, String>> interator = input.entrySet().iterator(); + while(interator.hasNext()) { + Entry<String, String> el = interator.next(); + if (!el.getKey().startsWith(absolutIdentifier)) { + //key is not absolute -> add prefix + result.put(prefix + + KEY_DELIMITER + + el.getKey(), + el.getValue()); + + } else { + //key is absolute + result.put(el.getKey(), el.getValue()); + } + } + return result; + } + + /** + * Get the parent key string from an input key + * + * @param key input key + * @return parent key or the empty String if no parent exists + */ + public static String getParentKey(String key) { + if (StringUtils.isNotEmpty(key)) { + int index = key.lastIndexOf(KEY_DELIMITER); + if (index > 0) { + return key.substring(0, index); + + } + } + + return new String(); + } + + /** + * Find the highest free list counter + * + * @param input Array of list keys + * @param listPrefix {String} prefix of the list + * @return {int} highest free list counter + */ + public static int findNextFreeListCounter(String[] input, + String listPrefix) { + List<Integer> counters = new ArrayList<Integer>(); + if (input == null || input.length == 0) + return 0; + + else { + for (String key : input) { + String listIndex = getFirstChildAfterPrefix(key, listPrefix); + counters.add(Integer.parseInt(listIndex)); + + } + Collections.sort(counters); + return counters.get(counters.size()-1) + 1; + } + } + + /** + * Find the highest free list counter + * + * @param keySet {Set<String>} of list keys + * @param listPrefix {String} prefix of the list + * @return {int} highest free list counter + */ + public static int findNextFreeListCounter(Set<String> keySet, + String listPrefix) { + if (keySet.isEmpty()) + return 0; + + String[] array = new String[keySet.size()]; + keySet.toArray(array); + return findNextFreeListCounter(array, listPrefix); + } + + + /** + * Normalize a CSV encoded list of value of an key/value pair + * + * This method removes all whitespace at the begin or the + * end of CSV values and remove newLine signs at the end of value. + * The ',' is used as list delimiter + * + * @param value CSV encoded input data + * @return normalized CSV encoded data or null if {value} is null or empty + */ + public static String normalizeCSVValueString(String value) { + String normalizedCodes = null; + if (StringUtils.isNotEmpty(value)) { + String[] codes = value.split(CSV_DELIMITER); + for (String el: codes) { + if (normalizedCodes == null) + normalizedCodes = StringUtils.chomp(el.trim()); + else + normalizedCodes += "," + StringUtils.chomp(el.trim()); + + } + } + return normalizedCodes; + } + + + /** + * Check a String if it is a comma separated list of values + * + * This method uses the ',' as list delimiter. + * + * @param value CSV encoded input data + * @return true if the input data contains a ',' and has more then 1 list element, otherwise false + */ + public static boolean isCSVValueString(String value) { + if (StringUtils.isNotEmpty(value)) { + String[] codes = value.split(CSV_DELIMITER); + if (codes.length >= 2) { + if (StringUtils.isNotEmpty(codes[1].trim())) + return true; + + } + } + + return false; + } + + /** + * Convert a CSV list to a List of CSV values + * <br><br> + * This method removes all whitespace at the begin or the + * end of CSV values and remove newLine signs at the end of value. + * The ',' is used as list delimiter + * + * @param csv CSV encoded input data + * @return List of CSV normalized values, but never null + */ + public static List<String> getListOfCSVValues(String csv) { + List<String> list = new ArrayList<String>(); + if (StringUtils.isNotEmpty(csv)) { + String[] values = csv.split(CSV_DELIMITER); + for (String el: values) + list.add(el.trim()); + + } + + return list; + } + + /** + * This method remove all newline delimiter (\n or \r\n) from input data + * + * @param value Input String + * @return Input String without newline characters + */ + public static String removeAllNewlineFromString(String value) { + return value.replaceAll("(\\t|\\r?\\n)+", ""); + + } + +} diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/NodeIteratorAdapter.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/NodeIteratorAdapter.java new file mode 100644 index 00000000..c752c91f --- /dev/null +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/NodeIteratorAdapter.java @@ -0,0 +1,89 @@ +/******************************************************************************* + *******************************************************************************/ + + +package at.gv.egiz.eaaf.core.impl.utils; + +import java.util.ListIterator; + +import org.w3c.dom.DOMException; +import org.w3c.dom.Node; +import org.w3c.dom.traversal.NodeFilter; +import org.w3c.dom.traversal.NodeIterator; + +/** + * A <code>NodeIterator</code> implementation based on a + * <code>ListIterator</code>. + * + * @see java.util.ListIterator + * @see org.w3c.dom.traversal.NodeIterator + * + */ +public class NodeIteratorAdapter implements NodeIterator { + + /** The <code>ListIterator</code> to wrap. */ + private ListIterator nodeIterator; + + /** + * Create a new <code>NodeIteratorAdapter</code>. + * @param nodeIterator The <code>ListIterator</code> to iterate over. + */ + public NodeIteratorAdapter(ListIterator nodeIterator) { + this.nodeIterator = nodeIterator; + } + + /** + * @see org.w3c.dom.traversal.NodeIterator#getRoot() + */ + public Node getRoot() { + return null; + } + + /** + * @see org.w3c.dom.traversal.NodeIterator#getWhatToShow() + */ + public int getWhatToShow() { + return NodeFilter.SHOW_ALL; + } + + /** + * @see org.w3c.dom.traversal.NodeIterator#getFilter() + */ + public NodeFilter getFilter() { + return null; + } + + /** + * @see org.w3c.dom.traversal.NodeIterator#getExpandEntityReferences() + */ + public boolean getExpandEntityReferences() { + return false; + } + + /** + * @see org.w3c.dom.traversal.NodeIterator#nextNode() + */ + public Node nextNode() throws DOMException { + if (nodeIterator.hasNext()) { + return (Node) nodeIterator.next(); + } + return null; + } + + /** + * @see org.w3c.dom.traversal.NodeIterator#previousNode() + */ + public Node previousNode() throws DOMException { + if (nodeIterator.hasPrevious()) { + return (Node) nodeIterator.previous(); + } + return null; + } + + /** + * @see org.w3c.dom.traversal.NodeIterator#detach() + */ + public void detach() { + } + +} diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/NodeListAdapter.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/NodeListAdapter.java new file mode 100644 index 00000000..61933907 --- /dev/null +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/NodeListAdapter.java @@ -0,0 +1,46 @@ +/******************************************************************************* + *******************************************************************************/ + + +package at.gv.egiz.eaaf.core.impl.utils; + +import java.util.List; + +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +/** + * A <code>NodeList</code> implementation based on a <code>List</code>. + * + * @see java.util.List + * @see org.w3c.dom.NodeList + */ + +public class NodeListAdapter implements NodeList { + /** The <code>List</code> to wrap. */ + private List nodeList; + + /** + * Create a new <code>NodeListAdapter</code>. + * + * @param nodeList The <code>List</code> containing the nodes. + */ + public NodeListAdapter(List nodeList) { + this.nodeList = nodeList; + } + + /** + * @see org.w3c.dom.NodeList#item(int) + */ + public Node item(int index) { + return (Node) nodeList.get(index); + } + + /** + * @see org.w3c.dom.NodeList#getLength() + */ + public int getLength() { + return nodeList.size(); + } + +} diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/Random.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/Random.java new file mode 100644 index 00000000..aad90649 --- /dev/null +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/Random.java @@ -0,0 +1,205 @@ +/******************************************************************************* + * Copyright 2014 Federal Chancellery Austria + * MOA-ID has been developed in a cooperation between BRZ, the Federal + * Chancellery Austria - ICT staff unit, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + ******************************************************************************/ +/* + * Copyright 2003 Federal Chancellery Austria + * MOA-ID has been developed in a cooperation between BRZ, the Federal + * Chancellery Austria - ICT staff unit, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + */ + + +package at.gv.egiz.eaaf.core.impl.utils; + + +import java.nio.ByteBuffer; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; + +import org.apache.commons.codec.binary.Hex; +import org.apache.commons.lang3.ArrayUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +/** + * Random number generator used to generate ID's + * @author Paul Ivancsics + * @version $Id$ + */ +public class Random { + private static final Logger log = LoggerFactory.getLogger(Random.class); + + private final static char[] allowedPreFix = + {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z', + 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'}; + private static final DateFormat dateFormater = new SimpleDateFormat("yyyyddMM"); + + /** random number generator used */ + private static SecureRandom random; + //private static SeedGenerator seedgenerator; + + static { + try { + random = SecureRandom.getInstance("SHA256PRNG-FIPS186"); + + } catch (NoSuchAlgorithmException e) { + log.warn("Can NOT initialize SecureRandom with: 'SHA256PRNG-FIPS186'. Use 'StrongSecureRandom' as backup"); + + try { + random = SecureRandom.getInstanceStrong(); + + } catch (NoSuchAlgorithmException e1) { + log.error("Can NOT initialize SecureRandom. StartUp process FAILED!"); + throw new RuntimeException("Can NOT initialize SecureRandom. StartUp process FAILED!", e); + + } + + } + + + //random = iaik.security.random.SHA256FIPS186Random.getDefault(); + } + + /** + * Generate a unique process reference-value [160bit], which always starts with a letter + * <br> + * This unique ID consists of single letter, a 64bit date String[yyyyddMM], + * and a 88bit random value. + * + * @return 160bit ID, which is hex encoded + */ + public static String nextProcessReferenceValue() { + //pre-process all three parts of a unique reference value + String now = dateFormater.format(new Date()); //8 bytes = 64bit + byte[] randValue = nextByteRandom(11); + char preFix = allowedPreFix[Math.abs(random.nextInt() % allowedPreFix.length)]; + + //generate ID + String returnValue = preFix + new String(Hex.encodeHex(ArrayUtils.addAll(now.getBytes(), randValue))); // 20 bytes = 160 bits + if (returnValue.length() > 40) + return returnValue.substring(0, 40); + else + return returnValue; + + } + + + + /** + * Creates a new random number [256bit], and encode it as hex value. + * + * @return random hex encoded value [256bit] + */ + public static String nextHexRandom32() { + return new String(Hex.encodeHex(nextByteRandom(32))); // 32 bytes = 256 bits + + } + + /** + * Creates a new random number [128bit], and encode it as hex value. + * + * @return random hex encoded value [128bit] + */ + public static String nextHexRandom16() { + return new String(Hex.encodeHex(nextByteRandom(16))); // 16 bytes = 128 bits + + } + + /** + * Creates a new random number [64bit], to be used as an ID. + * + * @return random long as a String [64bit] + */ + public static String nextLongRandom() { + return "".concat(String.valueOf(Math.abs(generateLongRandom(32)))); // 32 bytes = 256 bits + + } + + /** + * Creates a new random number, to be used as an ID. + * + * @return random long as a String [64bit] + */ + @Deprecated + public static String nextRandom() { + long l = ByteBuffer.wrap(nextByteRandom(32)).getLong(); // 32 bytes = 256 bits + return "" + Math.abs(l); + + } + +/** + * Creates a new random byte[] + * + * @param size Size of random number in byte + * @return + */ +public static byte[] nextBytes(int size) { + return nextByteRandom(size); + +} + + public static void seedRandom() { + //TODO: implement reflection on IAIK Seed generator +// seedgenerator = iaik.security.random.AutoSeedGenerator.getDefault(); +// if (seedgenerator.seedAvailable()) +// random.setSeed(seedgenerator.getSeed()); + + random.setSeed(System.nanoTime()); + } + + private static long generateLongRandom(int size) { + return ByteBuffer.wrap(nextByteRandom(size)).getLong(); + } + + /** + * Generate a new random number + * + * @param size Size of random number in byte + * @return + */ + private static synchronized byte[] nextByteRandom(int size) { + byte[] b = new byte[size]; + random.nextBytes(b); + return b; + + } +} diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/ServletUtils.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/ServletUtils.java new file mode 100644 index 00000000..c19e5ab1 --- /dev/null +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/ServletUtils.java @@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright 2014 Federal Chancellery Austria + * MOA-ID has been developed in a cooperation between BRZ, the Federal + * Chancellery Austria - ICT staff unit, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + ******************************************************************************/ +/* + * Copyright 2003 Federal Chancellery Austria + * MOA-ID has been developed in a cooperation between BRZ, the Federal + * Chancellery Austria - ICT staff unit, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + */ + + +/** + * + */ +package at.gv.egiz.eaaf.core.impl.utils; + +import javax.servlet.http.HttpServletRequest; + +public class ServletUtils { + + + public static String getBaseUrl( HttpServletRequest request ) { + if ( ( request.getServerPort() == 80 ) || + ( request.getServerPort() == 443 ) ) + return request.getScheme() + "://" + + request.getServerName() + + request.getContextPath(); + else + return request.getScheme() + "://" + + request.getServerName() + ":" + request.getServerPort() + + request.getContextPath(); + } + +} diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/StreamUtils.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/StreamUtils.java new file mode 100644 index 00000000..3e4143d4 --- /dev/null +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/StreamUtils.java @@ -0,0 +1,177 @@ +/******************************************************************************* + *******************************************************************************/ + + +package at.gv.egiz.eaaf.core.impl.utils; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintStream; + +/** + * Utility methods for streams. + * + * @author Patrick Peck + * @version $Id$ + */ +public class StreamUtils { + + /** + * Compare the contents of two <code>InputStream</code>s. + * + * @param is1 The 1st <code>InputStream</code> to compare. + * @param is2 The 2nd <code>InputStream</code> to compare. + * @return boolean <code>true</code>, if both streams contain the exactly the + * same content, <code>false</code> otherwise. + * @throws IOException An error occurred reading one of the streams. + */ + public static boolean compareStreams(InputStream is1, InputStream is2) + throws IOException { + + byte[] buf1 = new byte[256]; + byte[] buf2 = new byte[256]; + int length1; + int length2; + + try { + while (true) { + length1 = is1.read(buf1); + length2 = is2.read(buf2); + + if (length1 != length2) { + return false; + } + if (length1 <= 0) { + return true; + } + if (!compareBytes(buf1, buf2, length1)) { + return false; + } + } + } catch (IOException e) { + throw e; + } finally { + // close both streams + try { + is1.close(); + is2.close(); + } catch (IOException e) { + // ignore this + } + } + } + + /** + * Compare two byte arrays, up to a given maximum length. + * + * @param b1 1st byte array to compare. + * @param b2 2nd byte array to compare. + * @param length The maximum number of bytes to compare. + * @return <code>true</code>, if the byte arrays are equal, <code>false</code> + * otherwise. + */ + private static boolean compareBytes(byte[] b1, byte[] b2, int length) { + if (b1.length != b2.length) { + return false; + } + + for (int i = 0; i < b1.length && i < length; i++) { + if (b1[i] != b2[i]) { + return false; + } + } + + return true; + } + + /** + * Reads a byte array from a stream. + * @param in The <code>InputStream</code> to read. + * @return The bytes contained in the given <code>InputStream</code>. + * @throws IOException on any exception thrown + */ + public static byte[] readStream(InputStream in) throws IOException { + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + copyStream(in, out, null); + + /* + ByteArrayOutputStream out = new ByteArrayOutputStream(); + int b; + while ((b = in.read()) >= 0) + out.write(b); + + */ + in.close(); + return out.toByteArray(); + } + + /** + * Reads a <code>String</code> from a stream, using given encoding. + * @param in The <code>InputStream</code> to read. + * @param encoding The character encoding to use for converting the bytes + * of the <code>InputStream</code> into a <code>String</code>. + * @return The content of the given <code>InputStream</code> converted into + * a <code>String</code>. + * @throws IOException on any exception thrown + */ + public static String readStream(InputStream in, String encoding) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + copyStream(in, out, null); + + /* + ByteArrayOutputStream out = new ByteArrayOutputStream(); + int b; + while ((b = in.read()) >= 0) + out.write(b); + */ + in.close(); + return out.toString(encoding); + } + + /** + * Reads all data (until EOF is reached) from the given source to the + * destination stream. If the destination stream is null, all data is dropped. + * It uses the given buffer to read data and forward it. If the buffer is + * null, this method allocates a buffer. + * + * @param source The stream providing the data. + * @param destination The stream that takes the data. If this is null, all + * data from source will be read and discarded. + * @param buffer The buffer to use for forwarding. If it is null, the method + * allocates a buffer. + * @exception IOException If reading from the source or writing to the + * destination fails. + */ + private static void copyStream(InputStream source, OutputStream destination, byte[] buffer) throws IOException { + if (source == null) { + throw new NullPointerException("Argument \"source\" must not be null."); + } + if (buffer == null) { + buffer = new byte[8192]; + } + + if (destination != null) { + int bytesRead; + while ((bytesRead = source.read(buffer)) >= 0) { + destination.write(buffer, 0, bytesRead); + } + } else { + while (source.read(buffer) >= 0); + } + } + + /** + * Gets the stack trace of the <code>Throwable</code> passed in as a string. + * @param t The <code>Throwable</code>. + * @return a String representing the stack trace of the <code>Throwable</code>. + */ + public static String getStackTraceAsString(Throwable t) + { + ByteArrayOutputStream stackTraceBIS = new ByteArrayOutputStream(); + t.printStackTrace(new PrintStream(stackTraceBIS)); + return new String(stackTraceBIS.toByteArray()); + } +} diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/TransactionIDUtils.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/TransactionIDUtils.java new file mode 100644 index 00000000..7df211ef --- /dev/null +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/TransactionIDUtils.java @@ -0,0 +1,85 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eaaf.core.impl.utils; + + +import at.gv.egiz.eaaf.core.api.IRequest; + +/** + * @author tlenz + * + */ +public class TransactionIDUtils { + + //MDC variables for logging + public static final String MDC_TRANSACTION_ID = "transactionId"; + public static final String MDC_SESSION_ID = "sessionId"; + public static final String MDC_SERVICEPROVIDER_ID = "oaId"; + + /** + * Set all MDC variables from pending request to this threat context<br> + * These includes SessionID, TransactionID, and unique service-provider identifier + * + * @param pendingRequest + */ + public static void setAllLoggingVariables(IRequest pendingRequest) { + setTransactionId(pendingRequest.getUniqueTransactionIdentifier()); + setSessionId(pendingRequest.getUniqueSessionIdentifier()); + setServiceProviderId(pendingRequest.getServiceProviderConfiguration().getUniqueIdentifier()); + + } + + /** + * Remove all MDC variables from this threat context + * + */ + public static void removeAllLoggingVariables() { + removeSessionId(); + removeTransactionId(); + removeServiceProviderId(); + + } + + + public static void setServiceProviderId(String oaUniqueId) { + org.apache.log4j.MDC.put(MDC_SERVICEPROVIDER_ID, oaUniqueId); + org.slf4j.MDC.put(MDC_SERVICEPROVIDER_ID, oaUniqueId); + + } + + public static void removeServiceProviderId() { + org.apache.log4j.MDC.remove(MDC_SERVICEPROVIDER_ID); + org.slf4j.MDC.remove(MDC_SERVICEPROVIDER_ID); + + } + + public static void setTransactionId(String pendingRequestID) { + org.apache.log4j.MDC.put(MDC_TRANSACTION_ID, + "TID-" + pendingRequestID); + org.slf4j.MDC.put(MDC_TRANSACTION_ID, + "TID-" + pendingRequestID); + + } + + public static void removeTransactionId() { + org.apache.log4j.MDC.remove(MDC_TRANSACTION_ID); + org.slf4j.MDC.remove(MDC_TRANSACTION_ID); + + } + + public static void setSessionId(String uniqueSessionId) { + org.apache.log4j.MDC.put(MDC_SESSION_ID, + "SID-" + uniqueSessionId); + org.slf4j.MDC.put(MDC_SESSION_ID, + "SID-" + uniqueSessionId); + + } + + public static void removeSessionId() { + org.apache.log4j.MDC.remove(MDC_SESSION_ID); + org.slf4j.MDC.remove(MDC_SESSION_ID); + + } + + +} diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/XPathUtils.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/XPathUtils.java new file mode 100644 index 00000000..7df242bd --- /dev/null +++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/utils/XPathUtils.java @@ -0,0 +1,523 @@ +/******************************************************************************* + *******************************************************************************/ + + +package at.gv.egiz.eaaf.core.impl.utils; + +import java.util.List; +import java.util.Map; + +import org.jaxen.JaxenException; +import org.jaxen.NamespaceContext; +import org.jaxen.Navigator; +import org.jaxen.SimpleNamespaceContext; +import org.jaxen.dom.DOMXPath; +import org.jaxen.dom.DocumentNavigator; +import org.w3c.dom.Attr; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.traversal.NodeIterator; + +import at.gv.egiz.eaaf.core.api.data.XMLNamespaceConstants; +import at.gv.egiz.eaaf.core.exceptions.XPathException; + +/** + * Utility methods to evaluate XPath expressions on DOM nodes. + * + * @author Patrick Peck + * @version $Id$ + */ +public class XPathUtils { + + /** + * The XPath expression selecting all nodes under a given root (including the + * root node itself). + */ + public static final String ALL_NODES_XPATH = + "(.//. | .//@* | .//namespace::*)"; + + /** The <code>DocumentNavigator</code> to use for navigating the document. */ + private static Navigator documentNavigator = + DocumentNavigator.getInstance(); + /** The default namespace prefix to namespace URI mappings. */ + private static NamespaceContext NS_CONTEXT; + + static { + SimpleNamespaceContext ctx = new SimpleNamespaceContext(); + ctx.addNamespace(XMLNamespaceConstants.MOA_PREFIX, XMLNamespaceConstants.MOA_NS_URI); + ctx.addNamespace(XMLNamespaceConstants.MOA_CONFIG_PREFIX, XMLNamespaceConstants.MOA_CONFIG_NS_URI); + ctx.addNamespace(XMLNamespaceConstants.MOA_ID_CONFIG_PREFIX, XMLNamespaceConstants.MOA_ID_CONFIG_NS_URI); + ctx.addNamespace(XMLNamespaceConstants.SL10_PREFIX, XMLNamespaceConstants.SL10_NS_URI); + ctx.addNamespace(XMLNamespaceConstants.SL11_PREFIX, XMLNamespaceConstants.SL11_NS_URI); + ctx.addNamespace(XMLNamespaceConstants.SL12_PREFIX, XMLNamespaceConstants.SL12_NS_URI); + ctx.addNamespace(XMLNamespaceConstants.ECDSA_PREFIX, XMLNamespaceConstants.ECDSA_NS_URI); + ctx.addNamespace(XMLNamespaceConstants.PD_PREFIX, XMLNamespaceConstants.PD_NS_URI); + ctx.addNamespace(XMLNamespaceConstants.SAML_PREFIX, XMLNamespaceConstants.SAML_NS_URI); + ctx.addNamespace(XMLNamespaceConstants.SAMLP_PREFIX, XMLNamespaceConstants.SAMLP_NS_URI); + ctx.addNamespace(XMLNamespaceConstants.DSIG_PREFIX, XMLNamespaceConstants.DSIG_NS_URI); + ctx.addNamespace(XMLNamespaceConstants.XSLT_PREFIX, XMLNamespaceConstants.XSLT_NS_URI); + ctx.addNamespace(XMLNamespaceConstants.XSI_PREFIX, XMLNamespaceConstants.XSI_NS_URI); + ctx.addNamespace(XMLNamespaceConstants.DSIG_FILTER2_PREFIX, XMLNamespaceConstants.DSIG_FILTER2_NS_URI); + ctx.addNamespace(XMLNamespaceConstants.DSIG_EC_PREFIX, XMLNamespaceConstants.DSIG_EC_NS_URI); + ctx.addNamespace(XMLNamespaceConstants.MD_PREFIX, XMLNamespaceConstants.MD_NS_URI); + ctx.addNamespace(XMLNamespaceConstants.MDP_PREFIX, XMLNamespaceConstants.MDP_NS_URI); + ctx.addNamespace(XMLNamespaceConstants.MVV_PREFIX, XMLNamespaceConstants.MVV_NS_URI); + ctx.addNamespace(XMLNamespaceConstants.STB_PREFIX, XMLNamespaceConstants.STB_NS_URI); + ctx.addNamespace(XMLNamespaceConstants.WRR_PREFIX, XMLNamespaceConstants.WRR_NS_URI); + ctx.addNamespace(XMLNamespaceConstants.STORK_PREFIX, XMLNamespaceConstants.STORK_NS_URI); + ctx.addNamespace(XMLNamespaceConstants.STORKP_PREFIX, XMLNamespaceConstants.STORKP_NS_URI); + ctx.addNamespace(XMLNamespaceConstants.SAML2_PREFIX, XMLNamespaceConstants.SAML2_NS_URI); + ctx.addNamespace(XMLNamespaceConstants.SAML2P_PREFIX, XMLNamespaceConstants.SAML2P_NS_URI); + ctx.addNamespace(XMLNamespaceConstants.XENC_PREFIX, XMLNamespaceConstants.XENC_NS_URI); + ctx.addNamespace(XMLNamespaceConstants.XADES_1_1_1_NS_PREFIX, XMLNamespaceConstants.XADES_1_1_1_NS_URI); + NS_CONTEXT = ctx; + } + + /** + * Return a <code>NodeIterator</code> over the nodes matching the XPath + * expression. + * + * All namespace URIs and prefixes declared in the <code>Constants</code> + * interface are used for resolving namespaces. + * + * @param contextNode The root node from which to evaluate the XPath + * expression. + * @param exp The XPath expression to evaluate. + * @return An iterator over the resulting nodes. + * @throws XPathException An error occurred evaluating the XPath expression. + */ + public static NodeIterator selectNodeIterator(Node contextNode, String exp) + throws XPathException { + + return selectNodeIterator(contextNode, NS_CONTEXT, exp); + } + + /** + * Return a <code>NodeIterator</code> over the nodes matching the XPath + * expression. + * + * @param contextNode The root node from which to evaluate the XPath + * expression. + * @param namespaceElement An element from which to build the + * namespace mapping for evaluating the XPath expression + * @param exp The XPath expression to evaluate. + * @return An iterator over the resulting nodes. + * @throws XPathException An error occurred evaluating the XPath expression. + */ + public static NodeIterator selectNodeIterator( + Node contextNode, + Element namespaceElement, + String exp) + throws XPathException { + + try { + SimpleNamespaceContext ctx = new SimpleNamespaceContext(); + ctx.addElementNamespaces(documentNavigator, namespaceElement); + return selectNodeIterator(contextNode, ctx, exp); + + } catch (JaxenException e) { + throw new XPathException("XPath operation FAILED. Reason: " + e.getMessage(), e); + + } + } + + /** + * Return a <code>NodeIterator</code> over the nodes matching the XPath + * expression. + * + * @param contextNode The root node from which to evaluate the XPath + * expression. + * @param namespaceMapping A namespace prefix to namespace URI mapping + * (<code>String</code> to <code>String</code>) for evaluating the XPath + * expression. + * @param exp The XPath expression to evaluate. + * @return An iterator over the resulting nodes. + * @throws XPathException An error occurred evaluating the XPath expression. + */ + public static NodeIterator selectNodeIterator( + Node contextNode, + Map namespaceMapping, + String exp) + throws XPathException { + + SimpleNamespaceContext ctx = new SimpleNamespaceContext(namespaceMapping); + + return selectNodeIterator(contextNode, ctx, exp); + } + + /** + * Return a <code>NodeIterator</code> over the nodes matching the XPath + * expression. + * + * @param contextNode The root node from which to evaluate the XPath + * expression. + * @param nsContext The <code>NamespaceContext</code> for resolving namespace + * prefixes to namespace URIs for evaluating the XPath expression. + * @param exp The XPath expression to evaluate. + * @return An iterator over the resulting nodes. + * @throws XPathException An error occurred evaluating the XPath expression. + */ + private static NodeIterator selectNodeIterator( + Node contextNode, + NamespaceContext nsContext, + String exp) + throws XPathException { + + try { + DOMXPath xpath = new DOMXPath(exp); + List nodes; + + xpath.setNamespaceContext(nsContext); + nodes = xpath.selectNodes(contextNode); + return new NodeIteratorAdapter(nodes.listIterator()); + + } catch (JaxenException e) { + throw new XPathException("XPath operation FAILED. Reason: " + e.getMessage(), e); + + } + } + + /** + * Return a <code>NodeList</code> of all the nodes matching the XPath + * expression. + * + * All namespace URIs and prefixes declared in the <code>Constants</code> + * interface are used for resolving namespaces. + * + * @param contextNode The root node from which to evaluate the XPath + * expression. + * @param exp The XPath expression to evaluate. + * @return A <code>NodeList</code> containing the matching nodes. + * @throws XPathException An error occurred evaluating the XPath expression. + */ + public static NodeList selectNodeList(Node contextNode, String exp) + throws XPathException { + + return selectNodeList(contextNode, NS_CONTEXT, exp); + } + + /** + * Return a <code>NodeList</code> of all the nodes matching the XPath + * expression. + * + * @param contextNode The root node from which to evaluate the XPath + * expression. + * @param namespaceElement An element from which to build the + * namespace mapping for evaluating the XPath expression + * @param exp The XPath expression to evaluate. + * @return A <code>NodeList</code> containing the matching nodes. + * @throws XPathException An error occurred evaluating the XPath expression. + */ + public static NodeList selectNodeList( + Node contextNode, + Element namespaceElement, + String exp) + throws XPathException { + + try { + SimpleNamespaceContext ctx = new SimpleNamespaceContext(); + + ctx.addElementNamespaces(documentNavigator, namespaceElement); + return selectNodeList(contextNode, ctx, exp); + + } catch (JaxenException e) { + throw new XPathException("XPath operation FAILED. Reason: " + e.getMessage(), e); + + } + } + + /** + * Return a <code>NodeList</code> of all the nodes matching the XPath + * expression. + * + * @param contextNode The root node from which to evaluate the XPath + * expression. + * @param namespaceMapping A namespace prefix to namespace URI mapping + * (<code>String</code> to <code>String</code>) for evaluating the XPath + * expression. + * @param exp The XPath expression to evaluate. + * @return A <code>NodeList</code> containing the matching nodes. + * @throws XPathException An error occurred evaluating the XPath expression. + */ + public static NodeList selectNodeList( + Node contextNode, + Map namespaceMapping, + String exp) + throws XPathException { + + SimpleNamespaceContext ctx = new SimpleNamespaceContext(namespaceMapping); + + return selectNodeList(contextNode, ctx, exp); + } + + /** + * Return a <code>NodeList</code> of all the nodes matching the XPath + * expression. + * + * @param contextNode The root node from which to evaluate the XPath + * expression. + * @param nsContext The <code>NamespaceContext</code> for resolving namespace + * prefixes to namespace URIs for evaluating the XPath expression. + * @param exp The XPath expression to evaluate. + * @return A <code>NodeList</code> containing the matching nodes. + * @throws XPathException An error occurred evaluating the XPath expression. + */ + private static NodeList selectNodeList(Node contextNode, NamespaceContext nsContext, String exp) throws XPathException { + try { + DOMXPath xpath = new DOMXPath(exp); + List nodes; + xpath.setNamespaceContext(nsContext); + nodes = xpath.selectNodes(contextNode); + return new NodeListAdapter(nodes); + + } catch (JaxenException e) { + throw new XPathException("XPath operation FAILED. Reason: " + e.getMessage(), e); + + } + } + + /** + * Select the first node matching an XPath expression. + * + * All namespace URIs and prefixes declared in the <code>Constants</code> + * interface are used for resolving namespaces. + * + * @param contextNode The root node from which to evaluate the XPath + * expression. + * @param exp The XPath expression to evaluate. + * @return Node The first node matching the XPath expression, or + * <code>null</code>, if no node matched. + * @throws XPathException An error occurred evaluating the XPath expression. + */ + public static Node selectSingleNode(Node contextNode, String exp) + throws XPathException { + + return selectSingleNode(contextNode, NS_CONTEXT, exp); + } + + /** + * Select the first node matching an XPath expression. + * + * @param contextNode The root node from which to evaluate the XPath + * expression. + * @param namespaceElement An element from which to build the + * namespace mapping for evaluating the XPath expression + * @param exp The XPath expression to evaluate. + * @return Node The first node matching the XPath expression, or + * <code>null</code>, if no node matched. + * @throws XPathException An error occurred evaluating the XPath expression. + */ + public static Node selectSingleNode( + Node contextNode, + Element namespaceElement, + String exp) + throws XPathException { + + try { + SimpleNamespaceContext ctx = new SimpleNamespaceContext(); + ctx.addElementNamespaces(documentNavigator, namespaceElement); + return selectSingleNode(contextNode, ctx, exp); + + } catch (JaxenException e) { + throw new XPathException("XPath operation FAILED. Reason: " + e.getMessage(), e); + + } + } + + /** + * Select the first node matching an XPath expression. + * + * @param contextNode The root node from which to evaluate the XPath + * expression. + * @param namespaceMapping A namespace prefix to namespace URI mapping + * (<code>String</code> to <code>String</code>) for evaluating the XPath + * expression. + * @param exp The XPath expression to evaluate. + * @return Node The first node matching the XPath expression, or + * <code>null</code>, if no node matched. + * @throws XPathException An error occurred evaluating the XPath expression. + */ + public static Node selectSingleNode( + Node contextNode, + Map namespaceMapping, + String exp) + throws XPathException { + + SimpleNamespaceContext ctx = new SimpleNamespaceContext(namespaceMapping); + + return selectSingleNode(contextNode, ctx, exp); + } + + /** + * Select the first node matching an XPath expression. + * + * @param contextNode The root node from which to evaluate the XPath + * expression. + * @param nsContext The <code>NamespaceContext</code> for resolving namespace + * prefixes to namespace URIs for evaluating the XPath expression. + * @param exp The XPath expression to evaluate. + * @return Node The first node matching the XPath expression, or + * <code>null</code>, if no node matched. + * @throws XPathException An error occurred evaluating the XPath expression. + */ + public static Node selectSingleNode( + Node contextNode, + NamespaceContext nsContext, + String exp) + throws XPathException { + + try { + DOMXPath xpath = new DOMXPath(exp); + xpath.setNamespaceContext(nsContext); + return (Node) xpath.selectSingleNode(contextNode); + + } catch (JaxenException e) { + throw new XPathException("XPath operation FAILED. Reason: " + e.getMessage(), e); + + } + } + + /** + * Return the value of a DOM element whose location is given by an XPath + * expression. + * + * @param root The root element from which to evaluate the XPath. + * @param xpath The XPath expression pointing to the element whose value + * to return. + * @param def The default value to return, if no element can be found using + * the given <code>xpath</code>. + * @return The element value, if it can be located using the + * <code>xpath</code>. Otherwise, <code>def</code> is returned. + */ + public static String getElementValue( + Element root, + String xpath, + String def) { + + Element elem = (Element) XPathUtils.selectSingleNode(root, xpath); + return elem != null ? DOMUtils.getText(elem) : def; + } + + /** + * Return the value of a DOM attribute whose location is given by an XPath + * expression. + * + * @param root The root element from which to evaluate the XPath. + * @param xpath The XPath expression pointing to the attribute whose value to + * return. + * @param def The default value to return, if no attribute can be found using + * the given <code>xpath</code>. + * @return The element value, if it can be located using the + * <code>xpath</code>. Otherwise, <code>def</code> is returned. + */ + public static String getAttributeValue( + Element root, + String xpath, + String def) { + + Attr attr = (Attr) XPathUtils.selectSingleNode(root, xpath); + return attr != null ? attr.getValue() : def; + } + + /** + * Returns the namespace prefix used within <code>XPathUtils</code> for referring to + * the namespace of the specified (Security Layer command) element. + * + * This namespace prefix can be used in various XPath expression evaluation methods + * within <code> XPathUtils</code> without explicitely binding it to the particular + * namespace. + * + * @param contextElement The (Security Layer command) element. + * + * @return the namespace prefix used within <code>XPathUtils</code> for referring to + * the namespace of the specified (Security Layer command) element. + * + * throws XpathException If the specified element has a namespace other than the ones + * known by this implementation as valid Security Layer namespaces (cf. + * @link Constants#SL10_NS_URI, @link Constants#SL11_NS_URI, @link Constants#SL12_NS_URI). + */ + public static String getSlPrefix (Element contextElement) throws XPathException + { + String sLNamespace = contextElement.getNamespaceURI(); + String sLPrefix = null; + + if (sLNamespace.equals(XMLNamespaceConstants.SL10_NS_URI)) + sLPrefix = XMLNamespaceConstants.SL10_PREFIX; + + else if (sLNamespace.equals(XMLNamespaceConstants.SL12_NS_URI)) + sLPrefix = XMLNamespaceConstants.SL12_PREFIX; + + else if (sLNamespace.equals(XMLNamespaceConstants.SL11_NS_URI)) + sLPrefix = XMLNamespaceConstants.SL11_PREFIX; + + else + throw new XPathException("XPath operation FAILED. Reason: "); + + return sLPrefix; + } + + + /** + * Return the SecurityLayer namespace prefix of the context element. + * If the context element is not the element that lies within the + * SecurityLayer namespace. The Securitylayer namespace is derived from + * the <code>xmlns:sl10</code>, <code>sl11</code> or <code>sl</code> + * attribute of the context element. + * + * The returned prefix is needed for evaluating XPATH expressions. + * + * @param contextElement The element to get a prefix for the Securitylayer namespace, + * that is used within the corresponding document. + * + * @return The string <code>sl10</code>, <code>sl11</code> or <code>sl</code>, + * depending on the SecurityLayer namespace of the contextElement. + * + * throws XPathException If no (vlalid) SecurityLayer namespace prefix or namespace + * is defined. + */ + public static String getSlPrefixFromNoRoot (Element contextElement) throws XPathException { + + String slPrefix = checkSLnsDeclaration(contextElement, XMLNamespaceConstants.SL10_PREFIX, XMLNamespaceConstants.SL10_NS_URI); + if (slPrefix == null) + slPrefix = checkSLnsDeclaration(contextElement, XMLNamespaceConstants.SL11_PREFIX, XMLNamespaceConstants.SL11_NS_URI); + + if (slPrefix == null) + slPrefix = checkSLnsDeclaration(contextElement, XMLNamespaceConstants.SL12_PREFIX, XMLNamespaceConstants.SL12_NS_URI); + + return slPrefix; + + } + + /** + * Checks if the context element has an attribute <code>xmlns:slPrefix</code> and + * if the prefix of that attribute corresponds with a valid SecurityLayer namespace. + * + * @param contextElement The element to be checked. + * @param slPrefix The prefix which should be checked. Must be a valid SecurityLayer + * namespace prefix. + * @param slNameSpace The SecurityLayer namespace that corresponds to the specified prefix. + * + * @return The valid SecurityLayer prefix or <code>null</code> if this prefix is + * not used. + * @throws XPathException + */ + private static String checkSLnsDeclaration(Element contextElement, String slPrefix, String slNameSpace) + throws XPathException + { + String nsAtt = "xmlns:" + slPrefix; + String nameSpace = contextElement.getAttribute(nsAtt); + if (nameSpace == "") { + return null; + + } else { + // check if namespace is correct + if (nameSpace.equals(slNameSpace)) + return slPrefix; + else + throw new XPathException("Unknown Namespace declaration"); + + } + } + +} |