SAML-Toolkits / java-saml

Java SAML toolkit
MIT License
634 stars 398 forks source link

XML validation fails to load schemas in OSGi context #382

Open sandro-h opened 2 years ago

sandro-h commented 2 years ago

Hi, this is a rather specific problem, I suppose: we're using java-saml in a plugin for a third-party service. The plugin is loaded by an OSGi framework in this service. We don't have any control over the OSGi bundle manifests.

In this context, XML validation during Auth.processResponse fails to load the schema resource files with MalformedURLException: unknown protocol: bundle:

jvm 1    | 2022-04-19 10:12:27,990  WARN [qtp1903462050-26] p.c.a.g.s.p.c.a.g.s.s.JavaSamlLogPropagator:97 [plugin-ch.adnovum.gocd.saml2.plugin] - org.xml.sax.SAXParseException: schema_reference.4: Failed to read schema document 'bundle://00a4a7ff-512c-49a7-b4f0-ebbd54ac8a10_7.0:5/schemas/saml-schema-protocol-2.0.xsd', because 1) could not find the document; 2) the document could not be read; 3) the root element of the document is not <xsd:schema>.
jvm 1    |  at java.xml/com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException(Unknown Source)
jvm 1    |  at java.xml/com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.error(Unknown Source)
jvm 1    |  at java.xml/com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(Unknown Source)
jvm 1    |  at java.xml/com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(Unknown Source)
jvm 1    |  at java.xml/com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.reportSchemaErr(Unknown Source)
jvm 1    |  at java.xml/com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.reportSchemaError(Unknown Source)
jvm 1    |  at java.xml/com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.getSchemaDocument1(Unknown Source)
jvm 1    |  at java.xml/com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.getSchemaDocument(Unknown Source)
jvm 1    |  at java.xml/com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.parseSchema(Unknown Source)
jvm 1    |  at java.xml/com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaLoader.loadSchema(Unknown Source)
jvm 1    |  at java.xml/com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaLoader.loadGrammar(Unknown Source)
jvm 1    |  at java.xml/com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaLoader.loadGrammar(Unknown Source)
jvm 1    |  at java.xml/com.sun.org.apache.xerces.internal.jaxp.validation.XMLSchemaFactory.newSchema(Unknown Source)
jvm 1    |  at java.xml/javax.xml.validation.SchemaFactory.newSchema(Unknown Source)
jvm 1    |  at java.xml/javax.xml.validation.SchemaFactory.newSchema(Unknown Source)
jvm 1    |  at com.onelogin.saml2.util.SchemaFactory.loadFromUrl(SchemaFactory.java:106)
jvm 1    |  at com.onelogin.saml2.util.Util.validateXML(Util.java:305)
jvm 1    |  at com.onelogin.saml2.authn.SamlResponse.isValid(SamlResponse.java:220)
[...]
jvm 1    | Caused by: java.net.MalformedURLException: unknown protocol: bundle
jvm 1    |  at java.base/java.net.URL.<init>(Unknown Source)
jvm 1    |  at java.base/java.net.URL.<init>(Unknown Source)
jvm 1    |  at java.base/java.net.URL.<init>(Unknown Source)
jvm 1    |  at java.xml/com.sun.org.apache.xerces.internal.impl.XMLEntityManager.setupCurrentEntity(Unknown Source)
jvm 1    |  at java.xml/com.sun.org.apache.xerces.internal.impl.XMLVersionDetector.determineDocVersion(Unknown Source)
jvm 1    |  at java.xml/com.sun.org.apache.xerces.internal.impl.xs.opti.SchemaParsingConfig.parse(Unknown Source)
jvm 1    |  at java.xml/com.sun.org.apache.xerces.internal.impl.xs.opti.SchemaParsingConfig.parse(Unknown Source)
jvm 1    |  at java.xml/com.sun.org.apache.xerces.internal.impl.xs.opti.SchemaDOMParser.parse(Unknown Source)
jvm 1    |  ... 148 common frames omitted

In general, resource loading works fine in the plugin. I'm not sure why it's not working specifically for the javax.xml.validation.SchemaFactory:newSchema call. I also must admit I don't know enough about OSGi to pinpoint what could be happening here.

At the moment, I'm working around this by using a custom SamlMessageFactory that creates SamlResponses with custom xml validation, using an adapted SchemaFactory which loads the schemas via getResourceAsStream instead of URLs:

public abstract class SchemaFactory {

    // Resource path string instead of URL:
    public static final String SAML_SCHEMA_METADATA_2_0 = "/schemas/saml-schema-metadata-2.0.xsd";
    public static final String SAML_SCHEMA_PROTOCOL_2_0 = "/schemas/saml-schema-protocol-2.0.xsd";

    // Pass resource string instead of URL:
    public static Schema loadFromUrl(String schemaResourcePath) throws SAXException {
        [...]

        // Loads this via getResourceAsStream instead of directly from URL:
        InputStream inputStream = SchemaFactory.class.getResourceAsStream(schemaResourcePath);
        return factory.newSchema(new StreamSource(inputStream));
    }
}

I figured I'd report it, in case you've seen this before and know a better solution, or perhaps you're willing to accept that workaround, though it unfortunately breaks the API of SchemaFactory.