Skip to content
This repository was archived by the owner on May 26, 2020. It is now read-only.

Commit 92f67cd

Browse files
committed
SANTUARIO--523 XMLSecurityStreamReader now correctly reads info from XML declaration. Thanks to Peter De Maeyer for the patch. This closes #18.
git-svn-id: https://svn.apache.org/repos/asf/santuario/xml-security-java/trunk@1873911 13f79535-47bb-0310-9956-ffa450edef68
1 parent cb6ce07 commit 92f67cd

3 files changed

Lines changed: 115 additions & 16 deletions

File tree

src/main/java/org/apache/xml/security/stax/impl/XMLSecurityStreamReader.java

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import javax.xml.stream.events.EntityReference;
3434
import javax.xml.stream.events.Namespace;
3535
import javax.xml.stream.events.ProcessingInstruction;
36+
import javax.xml.stream.events.StartDocument;
3637

3738
import org.apache.xml.security.exceptions.XMLSecurityException;
3839
import org.apache.xml.security.stax.ext.InputProcessorChain;
@@ -49,7 +50,11 @@ public class XMLSecurityStreamReader implements XMLStreamReader {
4950

5051
private final InputProcessorChain inputProcessorChain;
5152
private XMLSecEvent currentXMLSecEvent;
52-
private boolean skipDocumentEvents = false;
53+
private final boolean skipDocumentEvents;
54+
private String version;
55+
private boolean standalone;
56+
private boolean standaloneSet;
57+
private String characterEncodingScheme;
5358

5459
private static final String ERR_STATE_NOT_ELEM = "Current state not START_ELEMENT or END_ELEMENT";
5560
private static final String ERR_STATE_NOT_STELEM = "Current state not START_ELEMENT";
@@ -75,9 +80,18 @@ public int next() throws XMLStreamException {
7580
inputProcessorChain.reset();
7681
currentXMLSecEvent = inputProcessorChain.processEvent();
7782
eventType = currentXMLSecEvent.getEventType();
78-
if (eventType == START_DOCUMENT && this.skipDocumentEvents) {
79-
currentXMLSecEvent = inputProcessorChain.processEvent();
80-
eventType = currentXMLSecEvent.getEventType();
83+
if (eventType == START_DOCUMENT) {
84+
// Even when skipDocumentEvents is true, we still want to get the information out of the event.
85+
// We only skip the event itself.
86+
StartDocument startDocument = (StartDocument) currentXMLSecEvent;
87+
version = startDocument.getVersion();
88+
characterEncodingScheme = startDocument.getCharacterEncodingScheme();
89+
standalone = startDocument.isStandalone();
90+
standaloneSet = startDocument.standaloneSet();
91+
if (skipDocumentEvents) {
92+
currentXMLSecEvent = inputProcessorChain.processEvent();
93+
eventType = currentXMLSecEvent.getEventType();
94+
}
8195
}
8296
} catch (XMLSecurityException e) {
8397
throw new XMLStreamException(e);
@@ -592,22 +606,22 @@ public String getPrefix() {
592606

593607
@Override
594608
public String getVersion() {
595-
return null;
609+
return version;
596610
}
597611

598612
@Override
599613
public boolean isStandalone() {
600-
return false;
614+
return standalone;
601615
}
602616

603617
@Override
604618
public boolean standaloneSet() {
605-
return false;
619+
return standaloneSet;
606620
}
607621

608622
@Override
609623
public String getCharacterEncodingScheme() {
610-
return null;
624+
return characterEncodingScheme;
611625
}
612626

613627
@Override

src/test/java/org/apache/xml/security/test/stax/XMLSecurityStreamReaderTest.java

Lines changed: 86 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
package org.apache.xml.security.test.stax;
2020

2121
import java.io.BufferedReader;
22+
import java.io.ByteArrayInputStream;
2223
import java.io.ByteArrayOutputStream;
2324
import java.io.InputStream;
2425
import java.io.InputStreamReader;
@@ -51,6 +52,9 @@
5152
import org.junit.jupiter.api.Test;
5253
import org.xmlunit.matchers.CompareMatcher;
5354

55+
import static org.hamcrest.MatcherAssert.assertThat;
56+
import static org.hamcrest.Matchers.equalTo;
57+
import static org.hamcrest.Matchers.is;
5458
import static org.junit.jupiter.api.Assertions.assertEquals;
5559
import static org.junit.jupiter.api.Assertions.assertFalse;
5660
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -105,6 +109,70 @@ public void testIdentityTransformSource() throws Exception {
105109
MatcherAssert.assertThat(readTestFile(), CompareMatcher.isSimilarTo(baos.toString(StandardCharsets.UTF_8.name())));
106110
}
107111

112+
@Test
113+
public void testDocumentDeclaration() throws Exception {
114+
String xml = "<?xml version='1.1' encoding='ISO-8859-1' standalone='yes'?>\n"
115+
+ "<Document/>";
116+
ByteArrayInputStream xmlInput = new ByteArrayInputStream(xml.getBytes(StandardCharsets.ISO_8859_1));
117+
XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
118+
XMLStreamReader stdXmlStreamReader = xmlInputFactory.createXMLStreamReader(xmlInput);
119+
InboundSecurityContextImpl securityContext = new InboundSecurityContextImpl();
120+
InputProcessorChainImpl inputProcessorChain = new InputProcessorChainImpl(securityContext);
121+
inputProcessorChain.addProcessor(new EventReaderProcessor(stdXmlStreamReader));
122+
XMLSecurityProperties securityProperties = new XMLSecurityProperties();
123+
securityProperties.setSkipDocumentEvents(false);
124+
XMLSecurityStreamReader xmlSecurityStreamReader = new XMLSecurityStreamReader(inputProcessorChain, securityProperties);
125+
advanceToFirstEvent(xmlSecurityStreamReader);
126+
assertThat(xmlSecurityStreamReader.getEventType(), is(XMLStreamConstants.START_DOCUMENT));
127+
assertThat(xmlSecurityStreamReader.getVersion(), is(equalTo("1.1")));
128+
assertThat(xmlSecurityStreamReader.getCharacterEncodingScheme(), is(equalTo("ISO-8859-1")));
129+
assertThat(xmlSecurityStreamReader.isStandalone(), is(true));
130+
assertThat(xmlSecurityStreamReader.standaloneSet(), is(true));
131+
132+
assertThat(xmlSecurityStreamReader.hasNext(), is(true));
133+
assertThat(xmlSecurityStreamReader.next(), is(XMLStreamConstants.START_ELEMENT));
134+
// Strictly speaking, we should assert that getVersion() etc. throw when not on a START_DOCUMENT event.
135+
// However, we have to be lenient to compensate for XML reader implementations such as Xalan,
136+
// which access getVersion() etc. when they're positioned on START_ELEMENT, _beyond_ START_DOCUMENT.
137+
assertThat(xmlSecurityStreamReader.getVersion(), is(equalTo("1.1")));
138+
assertThat(xmlSecurityStreamReader.getCharacterEncodingScheme(), is(equalTo("ISO-8859-1")));
139+
assertThat(xmlSecurityStreamReader.isStandalone(), is(true));
140+
assertThat(xmlSecurityStreamReader.standaloneSet(), is(true));
141+
}
142+
143+
@Test
144+
public void testDocumentDeclarationWhenSkipDocumentEvents() throws Exception {
145+
String xml = "<?xml version='1.1' encoding='ISO-8859-1' standalone='yes'?>\n"
146+
+ "<Document/>";
147+
ByteArrayInputStream xmlInput = new ByteArrayInputStream(xml.getBytes(StandardCharsets.ISO_8859_1));
148+
XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
149+
XMLStreamReader stdXmlStreamReader = xmlInputFactory.createXMLStreamReader(xmlInput);
150+
InboundSecurityContextImpl securityContext = new InboundSecurityContextImpl();
151+
InputProcessorChainImpl inputProcessorChain = new InputProcessorChainImpl(securityContext);
152+
inputProcessorChain.addProcessor(new EventReaderProcessor(stdXmlStreamReader));
153+
XMLSecurityProperties securityProperties = new XMLSecurityProperties();
154+
securityProperties.setSkipDocumentEvents(true);
155+
XMLSecurityStreamReader xmlSecurityStreamReader = new XMLSecurityStreamReader(inputProcessorChain, securityProperties);
156+
advanceToFirstEvent(xmlSecurityStreamReader);
157+
assertThat(xmlSecurityStreamReader.getEventType(), is(XMLStreamConstants.START_ELEMENT));
158+
assertThat(xmlSecurityStreamReader.getVersion(), is(equalTo("1.1")));
159+
assertThat(xmlSecurityStreamReader.getCharacterEncodingScheme(), is(equalTo("ISO-8859-1")));
160+
assertThat(xmlSecurityStreamReader.isStandalone(), is(true));
161+
assertThat(xmlSecurityStreamReader.standaloneSet(), is(true));
162+
}
163+
164+
/**
165+
* This method advances the reader until it's <i>on</i> the first event, if that's not already the case.
166+
* Depending on the implementation, the {@code xmlStreamReader} may be positioned <i>before</i> or <i>on</i>
167+
* the first event upon creation.
168+
*/
169+
private static void advanceToFirstEvent(XMLStreamReader xmlStreamReader) throws XMLStreamException {
170+
if (xmlStreamReader.getEventType() <= 0) {
171+
assertThat(xmlStreamReader.hasNext(), is(true));
172+
xmlStreamReader.next();
173+
}
174+
}
175+
108176
@Test
109177
public void testCorrectness() throws Exception {
110178
XMLSecurityProperties securityProperties = new XMLSecurityProperties();
@@ -123,6 +191,9 @@ public void testCorrectness() throws Exception {
123191
"org/apache/xml/security/c14n/inExcl/plain-soap-1.1.xml"));
124192

125193
//hmm why does a streamreader return a DOCUMENT_EVENT before we did call next() ??
194+
// A: Because some implementations of XMLStreamReader are positioned _on_ the first event rather than before it.
195+
// Woodstox is a typical example of such an implementation.
196+
// We have to compensate for both types of implementation, see the method advanceToFirstEvent(XMLStreamReader).
126197
int stdXMLEventType = stdXmlStreamReader.getEventType();
127198
int secXMLEventType = xmlSecurityStreamReader.getEventType();
128199
do {
@@ -213,9 +284,9 @@ public void testCorrectness() throws Exception {
213284
assertEquals(stdXmlStreamReader.getTextLength(), xmlSecurityStreamReader.getTextLength());
214285
break;
215286
case XMLStreamConstants.START_DOCUMENT:
216-
assertEquals(stdXmlStreamReader.getCharacterEncodingScheme(), xmlSecurityStreamReader.getCharacterEncodingScheme());
287+
assertEquals(StandardCharsets.UTF_8.name(), xmlSecurityStreamReader.getCharacterEncodingScheme());
217288
assertEquals(stdXmlStreamReader.getEncoding(), xmlSecurityStreamReader.getEncoding());
218-
//assertEquals(stdXmlStreamReader.getVersion(), xmlSecurityStreamReader.getVersion());
289+
assertEquals(stdXmlStreamReader.getVersion(), xmlSecurityStreamReader.getVersion());
219290
break;
220291
case XMLStreamConstants.END_DOCUMENT:
221292
break;
@@ -269,17 +340,24 @@ private String readTestFile() throws Exception {
269340
return stringBuilder.toString();
270341
}
271342

343+
private static XMLStreamReader createXmlStreamReader() throws XMLStreamException {
344+
XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
345+
xmlInputFactory.setProperty(XMLInputFactory.IS_COALESCING, true);
346+
xmlInputFactory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, true);
347+
return xmlInputFactory.createXMLStreamReader(InputProcessor.class.getClassLoader().getResourceAsStream(
348+
"org/apache/xml/security/c14n/inExcl/plain-soap-1.1.xml"));
349+
}
350+
272351
class EventReaderProcessor implements InputProcessor {
273352

274353
private XMLStreamReader xmlStreamReader;
275354

355+
EventReaderProcessor(XMLStreamReader xmlStreamReader) {
356+
this.xmlStreamReader = xmlStreamReader;
357+
}
358+
276359
EventReaderProcessor() throws Exception {
277-
XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
278-
xmlInputFactory.setProperty(XMLInputFactory.IS_COALESCING, true);
279-
xmlInputFactory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, true);
280-
xmlStreamReader =
281-
xmlInputFactory.createXMLStreamReader(this.getClass().getClassLoader().getResourceAsStream(
282-
"org/apache/xml/security/c14n/inExcl/plain-soap-1.1.xml"));
360+
this(createXmlStreamReader());
283361
}
284362

285363
@Override

src/test/java/org/apache/xml/security/test/stax/utils/XmlReaderToWriter.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,13 @@ private XmlReaderToWriter() {
3131

3232
public static void writeAll(XMLStreamReader xmlr, XMLStreamWriter writer)
3333
throws XMLStreamException {
34+
// Some implementations, Woodstox for example, already position their reader ON the first event, which is.
35+
// typically a START_DOCUMENT event.
36+
// If already positioned on an event, that is indicated by the event type.
37+
// Make sure we don't miss the initial event.
38+
if (xmlr.getEventType() > 0) {
39+
write(xmlr, writer);
40+
}
3441
while (xmlr.hasNext()) {
3542
xmlr.next();
3643
write(xmlr, writer);

0 commit comments

Comments
 (0)