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

SANTUARIO-532 User-defined and delayed evaluation of which XML elements need to be secured #21

Open
wants to merge 1 commit into
base: trunk
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,7 @@
<targetJdk>1.8</targetJdk>
<clirr.version>2.8</clirr.version>
<maven-owasp-plugin-version>5.2.4</maven-owasp-plugin-version>
<mockito.version>3.3.0</mockito.version>

<!-- Allow Clirr severity to be overriden by the command-line option -DminSeverity=level -->
<minSeverity>info</minSeverity>
Expand Down Expand Up @@ -577,6 +578,12 @@
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.xmlunit</groupId>
<artifactId>xmlunit-core</artifactId>
Expand Down Expand Up @@ -646,6 +653,12 @@
<version>0.7.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
</dependencies>

<distributionManagement>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,11 +189,13 @@ stax.unsupportedKeyTransp = Der public-key Algorithmus ist zu kurz um den symmet
stax.recursiveKeyReference = Rekursive Schl\u00fcssel referenzierung detektiert.
stax.ecParametersNotSupported = ECParameters werden nicht unterst\u00fctzt.
stax.namedCurveMissing = NamedCurve fehlt.
stax.encryption.securePartNotFound = Part zum Verschl\u00fcsseln nicht gefunden: {0}
stax.signature.securePartNotFound = Part zum Signieren nicht gefunden: {0}
stax.multipleSignaturesNotSupported = Mehrere Signaturen werden nicht unterstützt.
stax.encryption.tooFewOccurrences = Zu wenige ({0}/{1}) Elemente gefunden zum Verschl\u00fcsslen: {2}
stax.encryption.tooManyOccurrences = Zu viele ({0}/{1}) Elemente gefunden zum Verschl\u00fcsslen: {2}
stax.signature.tooFewOccurrences = Zu wenige ({0}/{1}) Elemente gefunden zum Signieren: {2}
stax.signature.tooManyOccurrences = Zu viele ({0}/{1}) Elemente gefunden zum Signieren: {2}
stax.multipleSignaturesNotSupported = Mehrere Signaturen werden nicht unterst\u00fctzt.
stax.signature.keyNameMissing = KeyName nicht konfiguriert.
stax.keyNotFoundForName = Kein Schl\u00fcssel für Schl\u00fcsselname konfiguriert: {0}
stax.keyNotFoundForName = Kein Schl\u00fcssel f\u00fcr Schl\u00fcsselname konfiguriert: {0}
stax.keyTypeNotSupported = Key vom Typ {0} nicht f\u00fcr einen Key-Namenssuche unterst\u00fctzt
stax.idsetbutnotgenerated = An Id attribute is specified, but Id generation is disabled
stax.idgenerationdisablewithmultipleparts = Id generation must not be disabled when multiple parts need signing
Original file line number Diff line number Diff line change
Expand Up @@ -189,8 +189,10 @@ stax.unsupportedKeyTransp = public key algorithm too weak to encrypt symmetric k
stax.recursiveKeyReference = Recursive key reference detected.
stax.ecParametersNotSupported = ECParameters not supported.
stax.namedCurveMissing = NamedCurve is missing.
stax.encryption.securePartNotFound = Part to encrypt not found: {0}
stax.signature.securePartNotFound = Part to sign not found: {0}
stax.encryption.tooFewOccurrences = Too few ({0}/{1}) elements found to encrypt: {2}
stax.encryption.tooManyOccurrences = Too many ({0}/{1}) elements found to encrypt: {2}
stax.signature.tooFewOccurrences = Too few ({0}/{1}) elements found to sign: {2}
stax.signature.tooManyOccurrences = Too many ({0}/{1}) elements found to sign: {2}
stax.multipleSignaturesNotSupported = Multiple signatures are not supported.
stax.signature.keyNameMissing = KeyName not configured.
stax.keyNotFoundForName = No key configured for KeyName: {0}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,10 @@
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.Attribute;

import org.apache.xml.security.exceptions.XMLSecurityException;
import org.apache.xml.security.stax.ext.stax.XMLSecAttribute;
Expand All @@ -37,6 +35,7 @@
import org.apache.xml.security.stax.ext.stax.XMLSecEventFactory;
import org.apache.xml.security.stax.ext.stax.XMLSecNamespace;
import org.apache.xml.security.stax.ext.stax.XMLSecStartElement;
import org.apache.xml.security.utils.KeyValue;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
Expand All @@ -56,7 +55,7 @@ public abstract class AbstractOutputProcessor implements OutputProcessor {
private Set<Object> beforeProcessors;
private Set<Object> afterProcessors;

protected AbstractOutputProcessor() throws XMLSecurityException {
protected AbstractOutputProcessor() {
super();
}

Expand Down Expand Up @@ -230,24 +229,26 @@ protected void outputAsEvent(OutputProcessorChain outputProcessorChain, XMLSecEv
outputProcessorChain.processEvent(xmlSecEvent);
}

protected SecurePart securePartMatches(XMLSecStartElement xmlSecStartElement,
OutputProcessorChain outputProcessorChain, String dynamicParts) {
Map<Object, SecurePart> dynamicSecureParts = outputProcessorChain.getSecurityContext().getAsMap(dynamicParts);
return securePartMatches(xmlSecStartElement, dynamicSecureParts);
protected KeyValue<SecurePartSelector, SecurePart> securePartMatches(XMLSecStartElement xmlSecStartElement,
OutputProcessorChain outputProcessorChain,
String dynamicPartSelectors) {
OutboundSecurityContext securityContext = outputProcessorChain.getSecurityContext();
List<SecurePartSelector> dynamicSecurePartSelectors = securityContext.get(dynamicPartSelectors);
return securePartMatches(xmlSecStartElement, outputProcessorChain, dynamicSecurePartSelectors);
}

protected SecurePart securePartMatches(XMLSecStartElement xmlSecStartElement, Map<Object, SecurePart> secureParts) {
SecurePart securePart = null;
if (secureParts != null) {
securePart = secureParts.get(xmlSecStartElement.getName());
if (securePart == null) {
Attribute attribute = xmlSecStartElement.getAttributeByName(securityProperties.getIdAttributeNS());
if (attribute != null) {
securePart = secureParts.get(attribute.getValue());
protected KeyValue<SecurePartSelector, SecurePart> securePartMatches(XMLSecStartElement xmlSecStartElement,
OutputProcessorChain outputProcessorChain,
List<SecurePartSelector> securePartSelectors) {
if (securePartSelectors != null) {
for (SecurePartSelector securePartSelector : securePartSelectors) {
SecurePart securePart = securePartSelector.select(xmlSecStartElement, outputProcessorChain);
if (securePart != null) {
return new KeyValue<>(securePartSelector, securePart);
}
}
}
return securePart;
return null;
}

protected void outputDOMElement(Element element, OutputProcessorChain outputProcessorChain)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package org.apache.xml.security.stax.ext;

import java.util.function.Supplier;

import javax.xml.namespace.QName;
import javax.xml.stream.events.Attribute;

import org.apache.xml.security.stax.ext.stax.XMLSecStartElement;

import static java.util.Objects.requireNonNull;

/**
* Selects elements to secure based on a given attribute name and value.
* This is equivalent to {@link SecurePart#setIdToSecure(String)} +
* {@link XMLSecurityProperties#setIdAttributeNS(QName)}.
*/
public class ByAttributeElementSelector implements ElementSelector {

private final Supplier<QName> nameSupplier;
private final String value;

ByAttributeElementSelector(Supplier<QName> nameSupplier, String value) {
requireNonNull(value, "value is null");
this.nameSupplier = nameSupplier;
this.value = value;
}

public ByAttributeElementSelector(QName name, String value) {
this(() -> name, value);
}

@Override
public boolean select(XMLSecStartElement element, OutputProcessorChain outputProcessorChain) {
if (element != null) {
QName name = nameSupplier.get();
if (name != null) {
Attribute attribute = element.getAttributeByName(name);
if (attribute != null && value.equals(attribute.getValue())) {
return true;
}
}
}
return false;
}

@Override
public String toString() {
return "//*[@" + nameSupplier.get() + "='" + value + "']";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.apache.xml.security.stax.ext;

import javax.xml.namespace.QName;

import org.apache.xml.security.stax.ext.stax.XMLSecStartElement;

import static java.util.Objects.requireNonNull;

/**
* Selects elements to secure by element name.
* This is equivalent to {@link SecurePart#setName(QName)}.
*/
public class ByNameElementSelector implements ElementSelector {

private final QName name;

public ByNameElementSelector(QName name) {
requireNonNull(name, "name is null");
this.name = name;
}

@Override
public boolean select(XMLSecStartElement element, OutputProcessorChain outputProcessorChain) {
return element != null && element.getName().equals(name);
}

@Override
public String toString() {
return "//" + name;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package org.apache.xml.security.stax.ext;

import org.apache.xml.security.stax.ext.stax.XMLSecStartElement;

/**
* Selects the document element (the {@code null} element).
* Use this selector to secure parts that are not specific to a certain element, but rather apply to the document as a
* whole, such as a secure part that has an external reference.
* This is equivalent to {@link SecurePart#setExternalReference(String)}.
*/
public class DocumentElementSelector implements ElementSelector {

private static class LazilyInitialized {

@SuppressWarnings("PMD.AccessorClassGeneration")
private static final DocumentElementSelector INSTANCE = new DocumentElementSelector();
}

private DocumentElementSelector() {
}

@Override
public boolean select(XMLSecStartElement element, OutputProcessorChain outputProcessorChain) {
return element == null;
}

@Override
public String toString() {
return "/";
}

public static DocumentElementSelector getInstance() {
return LazilyInitialized.INSTANCE;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.xml.security.stax.ext;

import org.apache.xml.security.stax.ext.stax.XMLSecStartElement;

/**
* An element selector defines <i>which</i> elements to secure, based on a given element and in the context provided by
* the output processor chain.
* An implementation may cooperate with a specific output processor implementation, which can be installed on the output
* processor chain using {@link #init(OutputProcessorChain)}.
* Implementations must be stateless, operating solely based on constructor parameters and parameters in the context
* provided by the output processor chain.
* If at all, parameters are typically passed from the cooperating output processor to the element selector (and further
* on the secure part factory) in the security context on the output processor chain, which can be accessed with
* {@link OutputProcessorChain#getSecurityContext()}.
*/
public interface ElementSelector {

/**
* Initializes an output processor chain with an output processor, allowing implementations to install a cooperating
* output processor.
* Such an output processor may populate the context with additional parameters to be used upon
* Implementations that don't need extra parameters beyond what's provided by {@link XMLSecStartElement} don't need
* a cooperating output processor, and can leave this method unimplemented.
* This method will be called upon initialization of document processing.
*
* @param outputProcessorChain The output processor chain to initialize, never {@code null}.
*/
default void init(OutputProcessorChain outputProcessorChain) {}

/**
* Selects a given element for securing, or {@code null} to indicate the document element (the document as a whole).
* In practice, the element {@code null} is used to select secure parts that define external references to be
* digested.
*
* @param element The element to select, possibly {@code null}.
* @param outputProcessorChain The output processor chain providing security context and document context,
* never {@code null}.
* @return {@code true} to select the given element for securing, {@code false} otherwise.
*/
boolean select(XMLSecStartElement element, OutputProcessorChain outputProcessorChain);
}
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ public XMLStreamReader processInMessage(
requestSecurityEvents = Collections.emptyList();
}

final InboundSecurityContextImpl inboundSecurityContext = new InboundSecurityContextImpl();
final InboundSecurityContextImpl inboundSecurityContext = new InboundSecurityContextImpl(securityProperties);
inboundSecurityContext.putList(SecurityEvent.class, requestSecurityEvents);
inboundSecurityContext.addSecurityEventListener(securityEventListener);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.apache.xml.security.stax.ext;

import org.apache.xml.security.stax.ext.stax.XMLSecStartElement;

/**
* An element selector that selects no elements.
*/
public class NoElementSelector implements ElementSelector {

private static class LazilyInitialized {

@SuppressWarnings("PMD.AccessorClassGeneration")
private static final NoElementSelector INSTANCE = new NoElementSelector();
}

private NoElementSelector() {
}

@Override
public boolean select(XMLSecStartElement element, OutputProcessorChain outputProcessorChain) {
return false;
}

@Override
public String toString() {
return "";
}

public static NoElementSelector getInstance() {
return LazilyInitialized.INSTANCE;
}
}
Loading