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

Commit e9b0747

Browse files
committed
SANTUARIO-532 User-defined and delayed evaluation of which XML elements need to be secured
1 parent 282cc09 commit e9b0747

53 files changed

Lines changed: 2033 additions & 385 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

pom.xml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,7 @@
523523
<targetJdk>1.8</targetJdk>
524524
<clirr.version>2.8</clirr.version>
525525
<maven-owasp-plugin-version>5.2.4</maven-owasp-plugin-version>
526+
<mockito.version>3.3.0</mockito.version>
526527

527528
<!-- Allow Clirr severity to be overriden by the command-line option -DminSeverity=level -->
528529
<minSeverity>info</minSeverity>
@@ -559,6 +560,12 @@
559560
<version>${junit.version}</version>
560561
<scope>test</scope>
561562
</dependency>
563+
<dependency>
564+
<groupId>org.junit.jupiter</groupId>
565+
<artifactId>junit-jupiter-params</artifactId>
566+
<version>${junit.version}</version>
567+
<scope>test</scope>
568+
</dependency>
562569
<dependency>
563570
<groupId>org.xmlunit</groupId>
564571
<artifactId>xmlunit-core</artifactId>
@@ -628,6 +635,12 @@
628635
<version>0.7.2</version>
629636
<scope>test</scope>
630637
</dependency>
638+
<dependency>
639+
<groupId>org.mockito</groupId>
640+
<artifactId>mockito-junit-jupiter</artifactId>
641+
<version>${mockito.version}</version>
642+
<scope>test</scope>
643+
</dependency>
631644
</dependencies>
632645

633646
<distributionManagement>

src/main/java/org/apache/xml/security/resource/xmlsecurity_de.properties

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -189,11 +189,13 @@ stax.unsupportedKeyTransp = Der public-key Algorithmus ist zu kurz um den symmet
189189
stax.recursiveKeyReference = Rekursive Schl\u00fcssel referenzierung detektiert.
190190
stax.ecParametersNotSupported = ECParameters werden nicht unterst\u00fctzt.
191191
stax.namedCurveMissing = NamedCurve fehlt.
192-
stax.encryption.securePartNotFound = Part zum Verschl\u00fcsseln nicht gefunden: {0}
193-
stax.signature.securePartNotFound = Part zum Signieren nicht gefunden: {0}
194-
stax.multipleSignaturesNotSupported = Mehrere Signaturen werden nicht unterstützt.
192+
stax.encryption.tooFewOccurrences = Zu wenige ({0}/{1}) Elemente gefunden zum Verschl\u00fcsslen: {2}
193+
stax.encryption.tooManyOccurrences = Zu viele ({0}/{1}) Elemente gefunden zum Verschl\u00fcsslen: {2}
194+
stax.signature.tooFewOccurrences = Zu wenige ({0}/{1}) Elemente gefunden zum Signieren: {2}
195+
stax.signature.tooManyOccurrences = Zu viele ({0}/{1}) Elemente gefunden zum Signieren: {2}
196+
stax.multipleSignaturesNotSupported = Mehrere Signaturen werden nicht unterst\u00fctzt.
195197
stax.signature.keyNameMissing = KeyName nicht konfiguriert.
196-
stax.keyNotFoundForName = Kein Schl\u00fcssel für Schl\u00fcsselname konfiguriert: {0}
198+
stax.keyNotFoundForName = Kein Schl\u00fcssel f\u00fcr Schl\u00fcsselname konfiguriert: {0}
197199
stax.keyTypeNotSupported = Key vom Typ {0} nicht f\u00fcr einen Key-Namenssuche unterst\u00fctzt
198200
stax.idsetbutnotgenerated = An Id attribute is specified, but Id generation is disabled
199201
stax.idgenerationdisablewithmultipleparts = Id generation must not be disabled when multiple parts need signing

src/main/java/org/apache/xml/security/resource/xmlsecurity_en.properties

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,8 +189,10 @@ stax.unsupportedKeyTransp = public key algorithm too weak to encrypt symmetric k
189189
stax.recursiveKeyReference = Recursive key reference detected.
190190
stax.ecParametersNotSupported = ECParameters not supported.
191191
stax.namedCurveMissing = NamedCurve is missing.
192-
stax.encryption.securePartNotFound = Part to encrypt not found: {0}
193-
stax.signature.securePartNotFound = Part to sign not found: {0}
192+
stax.encryption.tooFewOccurrences = Too few ({0}/{1}) elements found to encrypt: {2}
193+
stax.encryption.tooManyOccurrences = Too many ({0}/{1}) elements found to encrypt: {2}
194+
stax.signature.tooFewOccurrences = Too few ({0}/{1}) elements found to sign: {2}
195+
stax.signature.tooManyOccurrences = Too many ({0}/{1}) elements found to sign: {2}
194196
stax.multipleSignaturesNotSupported = Multiple signatures are not supported.
195197
stax.signature.keyNameMissing = KeyName not configured.
196198
stax.keyNotFoundForName = No key configured for KeyName: {0}

src/main/java/org/apache/xml/security/stax/ext/AbstractBufferingOutputProcessor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ protected Deque<XMLSecEvent> getXmlSecEventBuffer() {
4141
}
4242

4343
@Override
44-
public void processNextEvent(XMLSecEvent xmlSecEvent, OutputProcessorChain outputProcessorChain)
44+
public void processEvent(XMLSecEvent xmlSecEvent, OutputProcessorChain outputProcessorChain)
4545
throws XMLStreamException, XMLSecurityException {
4646
xmlSecEventBuffer.offer(xmlSecEvent);
4747
}

src/main/java/org/apache/xml/security/stax/ext/AbstractOutputProcessor.java

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,10 @@
2222
import java.util.Collections;
2323
import java.util.HashSet;
2424
import java.util.List;
25-
import java.util.Map;
2625
import java.util.Set;
2726

2827
import javax.xml.namespace.QName;
2928
import javax.xml.stream.XMLStreamException;
30-
import javax.xml.stream.events.Attribute;
3129

3230
import org.apache.xml.security.exceptions.XMLSecurityException;
3331
import org.apache.xml.security.stax.ext.stax.XMLSecAttribute;
@@ -37,6 +35,7 @@
3735
import org.apache.xml.security.stax.ext.stax.XMLSecEventFactory;
3836
import org.apache.xml.security.stax.ext.stax.XMLSecNamespace;
3937
import org.apache.xml.security.stax.ext.stax.XMLSecStartElement;
38+
import org.apache.xml.security.utils.KeyValue;
4039
import org.w3c.dom.Attr;
4140
import org.w3c.dom.Element;
4241
import org.w3c.dom.NamedNodeMap;
@@ -230,24 +229,24 @@ protected void outputAsEvent(OutputProcessorChain outputProcessorChain, XMLSecEv
230229
outputProcessorChain.processEvent(xmlSecEvent);
231230
}
232231

233-
protected SecurePart securePartMatches(XMLSecStartElement xmlSecStartElement,
234-
OutputProcessorChain outputProcessorChain, String dynamicParts) {
235-
Map<Object, SecurePart> dynamicSecureParts = outputProcessorChain.getSecurityContext().getAsMap(dynamicParts);
236-
return securePartMatches(xmlSecStartElement, dynamicSecureParts);
232+
protected KeyValue<SecurePartSelector, SecurePart> securePartMatches(XMLSecStartElement xmlSecStartElement,
233+
OutputProcessorChain outputProcessorChain, String dynamicPartSelectors) {
234+
OutboundSecurityContext securityContext = outputProcessorChain.getSecurityContext();
235+
List<SecurePartSelector> dynamicSecurePartSelectors = securityContext.get(dynamicPartSelectors);
236+
Element currentElement = securityContext.get(XMLSecurityConstants.CURRENT_ELEMENT);
237+
return securePartMatches(xmlSecStartElement, dynamicSecurePartSelectors, currentElement);
237238
}
238239

239-
protected SecurePart securePartMatches(XMLSecStartElement xmlSecStartElement, Map<Object, SecurePart> secureParts) {
240-
SecurePart securePart = null;
241-
if (secureParts != null) {
242-
securePart = secureParts.get(xmlSecStartElement.getName());
243-
if (securePart == null) {
244-
Attribute attribute = xmlSecStartElement.getAttributeByName(securityProperties.getIdAttributeNS());
245-
if (attribute != null) {
246-
securePart = secureParts.get(attribute.getValue());
240+
protected KeyValue<SecurePartSelector, SecurePart> securePartMatches(XMLSecStartElement xmlSecStartElement, List<SecurePartSelector> securePartSelectors, Element currentElement) {
241+
if (securePartSelectors != null) {
242+
for (SecurePartSelector securePartSelector : securePartSelectors) {
243+
SecurePart securePart = securePartSelector.select(xmlSecStartElement.getName(), currentElement);
244+
if (securePart != null) {
245+
return new KeyValue<>(securePartSelector, securePart);
247246
}
248247
}
249248
}
250-
return securePart;
249+
return null;
251250
}
252251

253252
protected void outputDOMElement(Element element, OutputProcessorChain outputProcessorChain)
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.xml.security.stax.ext;
20+
21+
import org.w3c.dom.Element;
22+
23+
import javax.xml.namespace.QName;
24+
25+
/**
26+
* This interface allows implementors to select <i>which</i> elements to secure, based on an element's qualified name
27+
* and skeleton DOM element.
28+
*/
29+
public interface ElementSelector {
30+
31+
/**
32+
* Selects a given element for securing.
33+
* The given element is a combination of qualified name and skeleton DOM element.
34+
* The skeleton DOM element has no content nor comments, it only has:
35+
* <ol>
36+
* <li>local name, namespace URI and prefix;</li>
37+
* <li>attributes;</li>
38+
* <li>namespace declarations (which are just a special type of attributes).</li>
39+
* </ol>
40+
* The hierarchical structure of the skeleton DOM element depends on the
41+
* {@link org.apache.xml.security.stax.ext.XMLSecurityProperties.ElementModifier} set on
42+
* {@link XMLSecurityProperties}.
43+
* The qualified name and/or skeleton DOM element may be {@code null}
44+
* The skeleton DOM element may be {@code null}, if an only if the qualified name is {@code null} or the element
45+
* modifier is set to {@code null} using
46+
* {@link XMLSecurityProperties#setElementModifier(XMLSecurityProperties.ElementModifier)}.
47+
* <table>
48+
* <tr>
49+
* <th>Name</th>
50+
* <th>Element</th>
51+
* <th>Meaning</th>
52+
* </tr>
53+
* <tr>
54+
* <td>Not {@code null}</td>
55+
* <td>Not {@code null}</td>
56+
* <td>
57+
* A regular element, when the element modifier has a regular non-null value.
58+
* The framework calls this method for every element in the document.
59+
* </td>
60+
* </tr>
61+
* <tr>
62+
* <td>Not {@code null}</td>
63+
* <td>{@code null}</td>
64+
* <td>
65+
* A regular element, when the element modifier has explicitly been set to {@code null} using
66+
* {@link XMLSecurityProperties#setElementModifier(XMLSecurityProperties.ElementModifier)}.
67+
* The framework calls this method for every element in the document.
68+
* </td>
69+
* </tr>
70+
* <tr>
71+
* <td>{@code null}</td>
72+
* <td>{@code null}</td>
73+
* <td>
74+
* The document element.
75+
* The framework may call this method once for the whole document, even if it has no elements.
76+
* When the qualified name is {@code null}, the skeleton DOM element is also {@code null}.
77+
* In practice, it is only used by the signature action when finalizing processing, to resolve any
78+
* external references which are global to the document but not bound to any specific element.
79+
* </td>
80+
* </tr>
81+
* </table>
82+
*
83+
* @param name The qualified name, possibly {@code null}.
84+
* @param element The skeleton DOM element, possibly {@code null}.
85+
* @return {@code true} to select the given skeleton element for securing, {@code false} otherwise.
86+
*/
87+
boolean select(QName name, Element element);
88+
}

src/main/java/org/apache/xml/security/stax/ext/InboundXMLSec.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ public XMLStreamReader processInMessage(
115115
requestSecurityEvents = Collections.emptyList();
116116
}
117117

118-
final InboundSecurityContextImpl inboundSecurityContext = new InboundSecurityContextImpl();
118+
final InboundSecurityContextImpl inboundSecurityContext = new InboundSecurityContextImpl(securityProperties);
119119
inboundSecurityContext.putList(SecurityEvent.class, requestSecurityEvents);
120120
inboundSecurityContext.addSecurityEventListener(securityEventListener);
121121

src/main/java/org/apache/xml/security/stax/ext/OutboundXMLSec.java

Lines changed: 12 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ public XMLStreamWriter processOutMessage(XMLStreamWriter xmlStreamWriter, String
9494

9595
private XMLStreamWriter processOutMessage(
9696
Object output, String encoding, SecurityEventListener eventListener) throws XMLSecurityException {
97-
final OutboundSecurityContextImpl outboundSecurityContext = new OutboundSecurityContextImpl();
97+
final OutboundSecurityContextImpl outboundSecurityContext = new OutboundSecurityContextImpl(securityProperties);
9898

9999
if (eventListener != null) {
100100
outboundSecurityContext.addSecurityEventListener(eventListener);
@@ -105,64 +105,27 @@ private XMLStreamWriter processOutMessage(
105105

106106
OutputProcessorChainImpl outputProcessorChain = new OutputProcessorChainImpl(outboundSecurityContext, documentContext);
107107

108-
SecurePart signEntireRequestPart = null;
109-
SecurePart encryptEntireRequestPart = null;
110-
111108
for (XMLSecurityConstants.Action action : securityProperties.getActions()) {
112109
if (XMLSecurityConstants.SIGNATURE.equals(action)) {
113110
XMLSignatureOutputProcessor signatureOutputProcessor = new XMLSignatureOutputProcessor();
114111
initializeOutputProcessor(outputProcessorChain, signatureOutputProcessor, action);
115112

116113
configureSignatureKeys(outboundSecurityContext);
117-
List<SecurePart> signatureParts = securityProperties.getSignatureSecureParts();
118-
for (SecurePart securePart : signatureParts) {
119-
if (securePart.getIdToSecure() == null && securePart.getName() != null) {
120-
outputProcessorChain.getSecurityContext().putAsMap(
121-
XMLSecurityConstants.SIGNATURE_PARTS,
122-
securePart.getName(),
123-
securePart
124-
);
125-
} else if (securePart.getIdToSecure() != null) {
126-
outputProcessorChain.getSecurityContext().putAsMap(
127-
XMLSecurityConstants.SIGNATURE_PARTS,
128-
securePart.getIdToSecure(),
129-
securePart
130-
);
131-
} else if (securePart.getExternalReference() != null) {
132-
outputProcessorChain.getSecurityContext().putAsMap(
133-
XMLSecurityConstants.SIGNATURE_PARTS,
134-
securePart.getExternalReference(),
135-
securePart
136-
);
137-
} else if (securePart.isSecureEntireRequest()) {
138-
// Special functionality to sign the first element in the request
139-
signEntireRequestPart = securePart;
140-
}
141-
}
114+
List<SecurePartSelector> signaturePartSelectors = securityProperties.getSignaturePartSelectors();
115+
outputProcessorChain.getSecurityContext().put(
116+
XMLSecurityConstants.SIGNATURE_PART_SELECTORS,
117+
signaturePartSelectors
118+
);
142119
} else if (XMLSecurityConstants.ENCRYPTION.equals(action)) {
143120
XMLEncryptOutputProcessor encryptOutputProcessor = new XMLEncryptOutputProcessor();
144121
initializeOutputProcessor(outputProcessorChain, encryptOutputProcessor, action);
145122

146123
configureEncryptionKeys(outboundSecurityContext);
147-
List<SecurePart> encryptionParts = securityProperties.getEncryptionSecureParts();
148-
for (SecurePart securePart : encryptionParts) {
149-
if (securePart.getIdToSecure() == null && securePart.getName() != null) {
150-
outputProcessorChain.getSecurityContext().putAsMap(
151-
XMLSecurityConstants.ENCRYPTION_PARTS,
152-
securePart.getName(),
153-
securePart
154-
);
155-
} else if (securePart.getIdToSecure() != null) {
156-
outputProcessorChain.getSecurityContext().putAsMap(
157-
XMLSecurityConstants.ENCRYPTION_PARTS,
158-
securePart.getIdToSecure(),
159-
securePart
160-
);
161-
} else if (securePart.isSecureEntireRequest()) {
162-
// Special functionality to encrypt the first element in the request
163-
encryptEntireRequestPart = securePart;
164-
}
165-
}
124+
List<SecurePartSelector> encryptionPartSelectors = securityProperties.getEncryptionPartSelectors();
125+
outputProcessorChain.getSecurityContext().put(
126+
XMLSecurityConstants.ENCRYPTION_PART_SELECTORS,
127+
encryptionPartSelectors
128+
);
166129
}
167130
}
168131
if (output instanceof OutputStream) {
@@ -177,11 +140,7 @@ private XMLStreamWriter processOutMessage(
177140
throw new IllegalArgumentException(output + " is not supported as output");
178141
}
179142

180-
XMLSecurityStreamWriter streamWriter = new XMLSecurityStreamWriter(outputProcessorChain);
181-
streamWriter.setSignEntireRequestPart(signEntireRequestPart);
182-
streamWriter.setEncryptEntireRequestPart(encryptEntireRequestPart);
183-
184-
return streamWriter;
143+
return new XMLSecurityStreamWriter(outputProcessorChain);
185144
}
186145

187146
private void initializeOutputProcessor(OutputProcessorChainImpl outputProcessorChain, OutputProcessor outputProcessor, XMLSecurityConstants.Action action) throws XMLSecurityException {

src/main/java/org/apache/xml/security/stax/ext/OutputProcessor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ public interface OutputProcessor {
9292
* @throws XMLStreamException thrown when a streaming error occurs
9393
* @throws XMLSecurityException thrown when a Security failure occurs
9494
*/
95-
void processNextEvent(XMLSecEvent xmlSecEvent, OutputProcessorChain outputProcessorChain) throws XMLStreamException, XMLSecurityException;
95+
void processEvent(XMLSecEvent xmlSecEvent, OutputProcessorChain outputProcessorChain) throws XMLStreamException, XMLSecurityException;
9696

9797
/**
9898
* Will be called when the whole document is processed.

0 commit comments

Comments
 (0)