Sensible XPath processing optimizations.

This commit is contained in:
Marvin S. Addison 2015-02-20 10:26:01 -05:00
parent cc46504126
commit 618b8a5dab
3 changed files with 104 additions and 77 deletions

View File

@ -0,0 +1,87 @@
package org.jasig.cas.client.util;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;
import javax.xml.xpath.*;
/**
* Thread local XPath expression.
*
* @author Marvin S. Addison
* @since 3.3
*/
public class ThreadLocalXPathExpression extends ThreadLocal<XPathExpression> implements XPathExpression {
/** XPath expression */
private String expression;
/** Namespace context. */
private NamespaceContext context;
/**
* Creates a new instance from an XPath expression and namespace context.
*
* @param xPath XPath expression.
* @param context Namespace context for handling namespace prefix to URI mappings.
*/
public ThreadLocalXPathExpression(final String xPath, final NamespaceContext context) {
this.expression = xPath;
this.context = context;
}
public Object evaluate(final Object o, final QName qName) throws XPathExpressionException {
return get().evaluate(o, qName);
}
public String evaluate(final Object o) throws XPathExpressionException {
return get().evaluate(o);
}
public Object evaluate(final InputSource inputSource, final QName qName) throws XPathExpressionException {
return get().evaluate(inputSource, qName);
}
public String evaluate(final InputSource inputSource) throws XPathExpressionException {
return get().evaluate(inputSource);
}
/**
* Evaluates the XPath expression and returns the result coerced to a string.
*
* @param o Object on which to evaluate the expression; typically a DOM node.
*
* @return Evaluation result as a string.
*
* @throws XPathExpressionException On XPath evaluation errors.
*/
public String evaluateAsString(final Object o) throws XPathExpressionException {
return (String) evaluate(o, XPathConstants.STRING);
}
/**
* Evaluates the XPath expression and returns the result coerced to a node list.
*
* @param o Object on which to evaluate the expression; typically a DOM node.
*
* @return Evaluation result as a node list.
*
* @throws XPathExpressionException On XPath evaluation errors.
*/
public NodeList evaluateAsNodeList(final Object o) throws XPathExpressionException {
return (NodeList) evaluate(o, XPathConstants.NODESET);
}
@Override
protected XPathExpression initialValue() {
try {
final XPath xPath = XPathFactory.newInstance().newXPath();
xPath.setNamespaceContext(context);
return xPath.compile(expression);
} catch (XPathExpressionException e) {
throw new IllegalArgumentException("Invalid XPath expression");
}
}
}

View File

@ -24,7 +24,6 @@ import java.util.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
@ -32,11 +31,9 @@ import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.XMLConstants;
import javax.xml.namespace.NamespaceContext;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.xpath.*;
/**
* Common utilities for easily parsing XML without duplicating logic.
@ -80,65 +77,6 @@ public final class XmlUtils {
}
}
/**
* Compiles the given XPath expression.
*
* @param expression XPath expression.
* @param nsContext XML namespace context for resolving namespace prefixes in XPath expressions.
*
* @return Compiled XPath expression.
*/
public static XPathExpression compileXPath(final String expression, final NamespaceContext nsContext) {
try {
final XPath xPath = XPathFactory.newInstance().newXPath();
xPath.setNamespaceContext(nsContext);
return xPath.compile(expression);
} catch (XPathExpressionException e) {
throw new IllegalArgumentException("Invalid XPath expression");
}
}
/**
* Evaluates the given XPath expression as a string result.
*
* @param expression XPath expression.
* @param nsContext XML namespace context for resolving namespace prefixes in XPath expressions.
* @param document DOM document on which to evaluate expression.
*
* @return Evaluated XPath expression as a string.
*/
public static String evaluateXPathString(
final String expression, final NamespaceContext nsContext, final Document document) {
try {
return (String) compileXPath(expression, nsContext).evaluate(document, XPathConstants.STRING);
} catch (XPathExpressionException e) {
throw new RuntimeException("XPath evaluation error", e);
}
}
/**
* Evaluates the given XPath expression as a node list result.
*
* @param expression XPath expression.
* @param nsContext XML namespace context for resolving namespace prefixes in XPath expressions.
* @param document DOM document on which to evaluate expression.
*
* @return Evaluated XPath expression as a node list.
*/
public static NodeList evaluateXPathNodeList(
final String expression, final NamespaceContext nsContext, final Document document) {
try {
return (NodeList) compileXPath(expression, nsContext).evaluate(document, XPathConstants.NODESET);
} catch (XPathExpressionException e) {
throw new RuntimeException("XPath evaluation error", e);
}
}
/**
* Get an instance of an XML reader from the XMLReaderFactory.
*

View File

@ -26,10 +26,7 @@ import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.*;
import org.jasig.cas.client.authentication.AttributePrincipalImpl;
import org.jasig.cas.client.util.CommonUtils;
import org.jasig.cas.client.util.IOUtils;
import org.jasig.cas.client.util.MapNamespaceContext;
import org.jasig.cas.client.util.XmlUtils;
import org.jasig.cas.client.util.*;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.Interval;
@ -54,25 +51,30 @@ public final class Saml11TicketValidator extends AbstractUrlBasedTicketValidator
private static final String SAML_REQUEST_TEMPLATE;
/** SAML 1.1. namespace context. */
private static final NamespaceContext SAML_NS_CONTEXT = new MapNamespaceContext(
private static final NamespaceContext NS_CONTEXT = new MapNamespaceContext(
"soap->http://schemas.xmlsoap.org/soap/envelope/",
"sa->urn:oasis:names:tc:SAML:1.0:assertion",
"sp->urn:oasis:names:tc:SAML:1.0:protocol");
/** XPath expression to extract Assertion validity start date. */
private static final String XPATH_ASSERTION_DATE_START = "//sa:Assertion/sa:Conditions/@NotBefore";
private static final ThreadLocalXPathExpression XPATH_ASSERTION_DATE_START =
new ThreadLocalXPathExpression("//sa:Assertion/sa:Conditions/@NotBefore", NS_CONTEXT);
/** XPath expression to extract Assertion validity end date. */
private static final String XPATH_ASSERTION_DATE_END = "//sa:Assertion/sa:Conditions/@NotOnOrAfter";
private static final ThreadLocalXPathExpression XPATH_ASSERTION_DATE_END =
new ThreadLocalXPathExpression("//sa:Assertion/sa:Conditions/@NotOnOrAfter", NS_CONTEXT);
/** XPath expression to extract NameIdentifier. */
private static final String XPATH_NAME_ID = "//sa:AuthenticationStatement/sa:Subject/sa:NameIdentifier";
private static final ThreadLocalXPathExpression XPATH_NAME_ID =
new ThreadLocalXPathExpression("//sa:AuthenticationStatement/sa:Subject/sa:NameIdentifier", NS_CONTEXT);
/** XPath expression to extract authentication method. */
private static final String XPATH_AUTH_METHOD = "//sa:AuthenticationStatement/@AuthenticationMethod";
private static final ThreadLocalXPathExpression XPATH_AUTH_METHOD =
new ThreadLocalXPathExpression("//sa:AuthenticationStatement/@AuthenticationMethod", NS_CONTEXT);
/** XPath expression to extract attributes. */
private static final String XPATH_ATTRIBUTES = "//sa:AttributeStatement/sa:Attribute";
private static final ThreadLocalXPathExpression XPATH_ATTRIBUTES =
new ThreadLocalXPathExpression("//sa:AttributeStatement/sa:Attribute", NS_CONTEXT);
private static final String HEX_CHARS = "0123456789abcdef";
@ -118,18 +120,18 @@ public final class Saml11TicketValidator extends AbstractUrlBasedTicketValidator
try {
final Document document = XmlUtils.newDocument(response);
final Date assertionValidityStart = CommonUtils.parseUtcDate(
XmlUtils.evaluateXPathString(XPATH_ASSERTION_DATE_START, SAML_NS_CONTEXT, document));
XPATH_ASSERTION_DATE_START.evaluateAsString(document));
final Date assertionValidityEnd = CommonUtils.parseUtcDate(
XmlUtils.evaluateXPathString(XPATH_ASSERTION_DATE_END, SAML_NS_CONTEXT, document));
XPATH_ASSERTION_DATE_END.evaluateAsString(document));
if (!isValidAssertion(assertionValidityStart, assertionValidityEnd)) {
throw new TicketValidationException("Invalid SAML assertion");
}
final String nameId = XmlUtils.evaluateXPathString(XPATH_NAME_ID, SAML_NS_CONTEXT, document);
final String nameId = XPATH_NAME_ID.evaluateAsString(document);
if (nameId == null) {
throw new TicketValidationException("SAML assertion does not contain NameIdentifier element");
}
final String authMethod = XmlUtils.evaluateXPathString(XPATH_AUTH_METHOD, SAML_NS_CONTEXT, document);
final NodeList attributes = XmlUtils.evaluateXPathNodeList(XPATH_ATTRIBUTES, SAML_NS_CONTEXT, document);
final String authMethod = XPATH_AUTH_METHOD.evaluateAsString(document);
final NodeList attributes = XPATH_ATTRIBUTES.evaluateAsNodeList(document);
final Map<String, Object> principalAttributes = new HashMap<String, Object>(attributes.getLength());
Element attribute;
NodeList values;