Skip to content

Commit

Permalink
#4344 - Allow format to inject default namespace if document does not…
Browse files Browse the repository at this point in the history
… specify one

- If the format defines a default namespace in its policy, then we inject it
- Fix bug that default namespace is not considerd for global attribute rules
  • Loading branch information
reckart committed Dec 4, 2023
1 parent 5cd5f9a commit 3c0c73f
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import java.util.stream.Collectors;

import javax.servlet.ServletContext;
import javax.xml.XMLConstants;

import org.apache.uima.cas.CAS;
import org.dkpro.core.api.xml.type.XmlDocument;
Expand Down Expand Up @@ -170,7 +171,19 @@ public ResponseEntity<String> getDocument(@PathVariable("projectId") long aProje
renderTextContent(cas, sanitizingHandler);
}
else {
var formatPolicy = formatRegistry.getFormatPolicy(doc);
var defaultNamespace = formatPolicy.flatMap(policy -> policy.getDefaultNamespace());

if (defaultNamespace.isPresent()) {
sanitizingHandler.startPrefixMapping(XMLConstants.DEFAULT_NS_PREFIX,
defaultNamespace.get());
}

renderXmlContent(doc, sanitizingHandler, aEditor, maybeXmlDocument.get());

if (defaultNamespace.isPresent()) {
sanitizingHandler.endPrefixMapping(XMLConstants.DEFAULT_NS_PREFIX);
}
}
rawHandler.endElement(null, null, BODY);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,17 @@ public class PolicyCollection
private final ElementAction defaultElementAction;
private final AttributeAction defaultAttributeAction;

private boolean debug = false;

private String name;
private String version;

public PolicyCollection(Map<QName, ElementPolicy> aElementPolicies,
private boolean debug = false;
private String defaultNamespace;

public PolicyCollection(String aDefaultNamespace, Map<QName, ElementPolicy> aElementPolicies,
Map<QName, AttributePolicy> aGlobalAttributePolicies,
ElementAction aDefaultElementAction, AttributeAction aDefaultAttributeAction)
{
defaultNamespace = aDefaultNamespace;
elementPolicies = aElementPolicies;
globalAttributePolicies = aGlobalAttributePolicies;
defaultElementAction = aDefaultElementAction;
Expand Down Expand Up @@ -106,6 +108,11 @@ public String getName()
return name;
}

public Optional<String> getDefaultNamespace()
{
return Optional.ofNullable(defaultNamespace);
}

public void setName(String aName)
{
name = aName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,14 +235,15 @@ public PolicyCollection build()
elementPolicies.put(entry.getKey(), entry.getValue().build());
}

return new PolicyCollection(elementPolicies, globalAttributePolicies, defaultElementAction,
defaultAttributeAction);
return new PolicyCollection(defaultNamespace, elementPolicies, globalAttributePolicies,
defaultElementAction, defaultAttributeAction);
}

void attributePolicy(QName aElementName, QName aAttributeName, AttributePolicy aPolicy)
{
if (defaultNamespace == null) {
_attributePolicy(aElementName, aAttributeName, aPolicy);
return;
}

if (isEmpty(aElementName.getNamespaceURI()) && isEmpty(aAttributeName.getNamespaceURI())) {
Expand All @@ -261,21 +262,21 @@ void attributePolicy(QName aElementName, QName aAttributeName, AttributePolicy a
private void _attributePolicy(QName aElementName, QName aAttributeName, AttributePolicy aPolicy)
{
@SuppressWarnings("unchecked")
Map<QName, AttributePolicy> attributePolicies = elementAttributePolicies
.computeIfAbsent(aElementName, k -> mapSupplier.get());
var attributePolicies = elementAttributePolicies.computeIfAbsent(aElementName,
k -> mapSupplier.get());
var attributePolicy = attributePolicies.computeIfAbsent(aAttributeName,
k -> AttributePolicy.UNDEFINED);

if (aPolicy instanceof DelegatingAttributePolicy) {
((DelegatingAttributePolicy) aPolicy).setDelegate(attributePolicy);
attributePolicies.put(aAttributeName, aPolicy);
return;
}
else {
var oldPolicy = attributePolicies.put(aAttributeName, aPolicy);
if (!AttributePolicy.isUndefined(oldPolicy)) {
log.warn("On element [{}] overriding policy for attribute [{}]: [{}] -> [{}]",
aElementName, aAttributeName, oldPolicy, aPolicy);
}

var oldPolicy = attributePolicies.put(aAttributeName, aPolicy);
if (!AttributePolicy.isUndefined(oldPolicy)) {
log.warn("On element [{}] overriding policy for attribute [{}]: [{}] -> [{}]",
aElementName, aAttributeName, oldPolicy, aPolicy);
}
}

Expand All @@ -290,10 +291,30 @@ public void disallowAttribute(QName aAttribute, Pattern aPattern)
}

public void globalAttributePolicy(AttributePolicy aPolicy)
{
if (defaultNamespace == null) {
_globalAttributePolicy(aPolicy);
return;
}

if (isEmpty(aPolicy.getQName().getNamespaceURI())) {
var attributeName = useDefaultNamespaceForAttributes
? new QName(defaultNamespace, aPolicy.getQName().getLocalPart())
: aPolicy.getQName();
_globalAttributePolicy(new AttributePolicy(attributeName, aPolicy.getAction()));

if (matchWithoutNamespace) {
_globalAttributePolicy(aPolicy);
}
}
}

private void _globalAttributePolicy(AttributePolicy aPolicy)
{
var newPolicy = aPolicy;
var oldPolicy = globalAttributePolicies.put(aPolicy.getQName(), newPolicy);
if (!AttributePolicy.isUndefined(oldPolicy)) {
if (!AttributePolicy.isUndefined(oldPolicy)
&& oldPolicy.getAction() != newPolicy.getAction()) {
log.warn("Globally overriding policy for attribute [{}]: [{}] -> [{}]",
aPolicy.getQName(), oldPolicy, newPolicy);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ public static PolicyCollection loadPolicies(InputStream aIs) throws IOException
}
}

policyCollectionBuilder.allowAttributes("data-capture-root").globally();

var policies = policyCollectionBuilder.build();
policies.setDebug(externalCollection.isDebug());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@
import static java.util.stream.Collectors.toList;
import static org.apache.commons.lang3.StringUtils.startsWith;

import java.lang.invoke.MethodHandles;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.Stack;
Expand All @@ -49,7 +49,7 @@
public class SanitizingContentHandler
extends ContentHandlerAdapter
{
private final Logger log = LoggerFactory.getLogger(getClass());
private final static Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

private static final String MASKED = "MASKED-";
private static final String PRUNED = "PRUNED-";
Expand All @@ -63,10 +63,10 @@ public class SanitizingContentHandler

private char filteredCharacter = ' ';

private final LinkedHashMap<String, String> namespaceMappings = new LinkedHashMap<>();
private final Map<String, String> namespaceMappings = new LinkedHashMap<>();

private Set<QName> maskedElements = new HashSet<>();
private Map<QName, Set<QName>> maskedAttributes = new HashMap<>();
private final Set<QName> maskedElements = new HashSet<>();
private final Map<QName, Set<QName>> maskedAttributes = new HashMap<>();

public SanitizingContentHandler(ContentHandler aDelegate, PolicyCollection aPolicies)
{
Expand All @@ -85,15 +85,31 @@ public void startDocument() throws SAXException
super.startDocument();
}

@Override
public void startPrefixMapping(String aPrefix, String aUri) throws SAXException
{
namespaceMappings.put(aPrefix, aUri);

super.startPrefixMapping(aPrefix, aUri);
}

@Override
public void endPrefixMapping(String aPrefix) throws SAXException
{
namespaceMappings.remove(aPrefix);

super.endPrefixMapping(aPrefix);
}

@Override
public void startElement(String aUri, String aLocalName, String aQName, Attributes aAtts)
throws SAXException
{
var localNamespace = new LinkedHashMap<String, String>();
for (Entry<String, String> xmlns : prefixMappings(aAtts).entrySet()) {
var oldValue = namespaceMappings.put(xmlns.getKey(), xmlns.getValue());
var localNamespaces = new LinkedHashMap<String, String>();
for (var nsDecl : prefixMappings(aAtts).entrySet()) {
var oldValue = namespaceMappings.put(nsDecl.getKey(), nsDecl.getValue());
if (oldValue == null) {
localNamespace.put(xmlns.getKey(), xmlns.getValue());
localNamespaces.put(nsDecl.getKey(), nsDecl.getValue());
}
}

Expand All @@ -106,12 +122,12 @@ public void startElement(String aUri, String aLocalName, String aQName, Attribut
switch (action) {
case PASS:
var attributes = sanitizeAttributes(element, aAtts);
startElement(element, attributes, policy, action, localNamespace);
startElement(element, attributes, policy, action, localNamespaces);
break;
case PRUNE: // fall-through
case SKIP: // fall-through
case DROP:
startElement(element, null, policy, action, localNamespace);
startElement(element, null, policy, action, localNamespaces);
break;
default:
throw new SAXException("Unsupported element action: [" + action + "]");
Expand Down Expand Up @@ -187,14 +203,14 @@ public void endElement(String aUri, String aLocalName, String aQName) throws SAX
frame.namespaces.keySet().forEach(namespaceMappings::remove);

if (stack.isEmpty()) {
if (policies.isDebug() && log.isDebugEnabled()) {
log.debug("[{}] Masked elements: {}", policies.getName(), maskedElements.stream() //
if (policies.isDebug() && LOG.isDebugEnabled()) {
LOG.debug("[{}] Masked elements: {}", policies.getName(), maskedElements.stream() //
.map(QName::toString) //
.sorted() //
.collect(toList()));
for (var element : maskedAttributes.keySet().stream()
.sorted(comparing(QName::getLocalPart)).collect(toList())) {
log.debug("[{}] Masked attributes on {}: {}", policies.getName(), element,
LOG.debug("[{}] Masked attributes on {}: {}", policies.getName(), element,
maskedAttributes.get(element).stream().map(QName::toString) //
.sorted() //
.collect(toList()));
Expand Down

0 comments on commit 3c0c73f

Please sign in to comment.