blob: 02cb0284c8d6590eb6f56ddf6b1d41cdc5380fe8 [file] [log] [blame]
/*
* 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.unomi.scripting.internal;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.unomi.scripting.ExpressionFilter;
import org.apache.unomi.scripting.ExpressionFilterFactory;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.URL;
import java.util.*;
import java.util.regex.Pattern;
public class ExpressionFilterFactoryImpl implements ExpressionFilterFactory,BundleListener {
private static final Logger logger = LoggerFactory.getLogger(ExpressionFilterFactoryImpl.class.getName());
private final Map<Bundle,Map<String,Set<Pattern>>> allowedExpressionPatternsByBundle = new HashMap<>();
private final Map<String,Set<Pattern>> allowedExpressionPatternsByCollection = new HashMap<>();
private final Map<String,Set<Pattern>> forbiddenExpressionPatternsByCollection = new HashMap<>();
private BundleContext bundleContext = null;
private final ObjectMapper objectMapper = new ObjectMapper();
private boolean expressionFiltersActivated = Boolean.parseBoolean(System.getProperty("org.apache.unomi.scripting.filter.activated", "true"));
public void setBundleContext(BundleContext bundleContext) {
this.bundleContext = bundleContext;
}
public ExpressionFilterFactoryImpl() {
}
public void init() {
String initialFilterCollections = System.getProperty("org.apache.unomi.scripting.filter.collections", "mvel,ognl");
String[] initialFilterCollectionParts = initialFilterCollections.split(",");
if (initialFilterCollectionParts != null) {
for (String initialFilterCollection : initialFilterCollectionParts) {
String systemAllowedPatterns = System.getProperty("org.apache.unomi.scripting.filter."+initialFilterCollection+".allow", null);
if (systemAllowedPatterns != null) {
Set<Pattern> collectionAllowedExpressionPatterns = new HashSet<>();
if (!"all".equals(systemAllowedPatterns.trim())) {
collectionAllowedExpressionPatterns = null;
} else {
if (systemAllowedPatterns.trim().length() > 0) {
String[] systemAllowedPatternParts = systemAllowedPatterns.split(",");
collectionAllowedExpressionPatterns = new HashSet<>();
for (String systemAllowedPatternPart : systemAllowedPatternParts) {
collectionAllowedExpressionPatterns.add(Pattern.compile(systemAllowedPatternPart));
}
}
}
allowedExpressionPatternsByCollection.put(initialFilterCollection, collectionAllowedExpressionPatterns);
}
String systemForbiddenPatterns = System.getProperty("org.apache.unomi.scripting.filter."+initialFilterCollection+".forbid", ".*Runtime.*,.*ProcessBuilder.*,.*exec.*,.*invoke.*,.*getClass.*,.*Class.*,.*ClassLoader.*,.*System.*,.*Method.*,.*method.*,.*Compiler.*,.*Thread.*,.*FileWriter.*,.*forName.*,.*Socket.*,.*DriverManager.*,eval");
if (systemForbiddenPatterns != null) {
Set<Pattern> collectionForbiddenExpressionPatterns = new HashSet<>();
if (systemForbiddenPatterns.trim().length() > 0) {
String[] systemForbiddenPatternParts = systemForbiddenPatterns.split(",");
collectionForbiddenExpressionPatterns = new HashSet<>();
for (String systemForbiddenPatternPart : systemForbiddenPatternParts) {
collectionForbiddenExpressionPatterns.add(Pattern.compile(systemForbiddenPatternPart));
}
} else {
collectionForbiddenExpressionPatterns = null;
}
forbiddenExpressionPatternsByCollection.put(initialFilterCollection, collectionForbiddenExpressionPatterns);
}
}
}
if (bundleContext != null) {
loadPredefinedAllowedPatterns(bundleContext);
for (Bundle bundle : bundleContext.getBundles()) {
if (bundle.getBundleContext() != null && bundle.getBundleId() != bundleContext.getBundle().getBundleId()) {
loadPredefinedAllowedPatterns(bundle.getBundleContext());
}
}
bundleContext.addBundleListener(this);
}
}
public void destroy() {
if (bundleContext != null) {
bundleContext.removeBundleListener(this);
}
}
public void bundleChanged(BundleEvent event) {
switch (event.getType()) {
case BundleEvent.STARTED:
processBundleStartup(event.getBundle().getBundleContext());
break;
case BundleEvent.STOPPING:
processBundleStop(event.getBundle().getBundleContext());
break;
}
}
private void processBundleStartup(BundleContext bundleContext) {
if (bundleContext == null) {
return;
}
loadPredefinedAllowedPatterns(bundleContext);
}
private void processBundleStop(BundleContext bundleContext) {
if (bundleContext == null) {
return;
}
removePredefinedAllowedPatterns(bundleContext);
}
private void loadPredefinedAllowedPatterns(BundleContext bundleContext) {
Enumeration<URL> predefinedAllowedExpressions = bundleContext.getBundle().findEntries("META-INF/cxs/expressions", "*.json", true);
if (predefinedAllowedExpressions == null) {
return;
}
Map<String,Set<Pattern>> predefinedAllowedExpressionsForBundle = new HashMap<>();
while (predefinedAllowedExpressions.hasMoreElements()) {
URL predefinedAllowedExpressionsURL = predefinedAllowedExpressions.nextElement();
logger.debug("Found predefined allowed expressions at " + predefinedAllowedExpressionsURL + ", loading... ");
try {
JsonNode predefinedAllowedExpressionsNode = objectMapper.readTree(predefinedAllowedExpressionsURL);
Set<Pattern> bundleAllowedExpressions = new HashSet<>();
for (JsonNode predefinedAllowedExpressionNode : predefinedAllowedExpressionsNode) {
bundleAllowedExpressions.add(Pattern.compile(predefinedAllowedExpressionNode.asText()));
}
String collection = predefinedAllowedExpressionsURL.getFile().substring("/META-INF/cxs/expressions/".length(), predefinedAllowedExpressionsURL.getFile().length() - ".json".length());
predefinedAllowedExpressionsForBundle.put(collection, bundleAllowedExpressions);
Set<Pattern> existingAllowedExpressions = allowedExpressionPatternsByCollection.get(collection);
if (existingAllowedExpressions == null) {
existingAllowedExpressions = new HashSet<>();
}
existingAllowedExpressions.addAll(bundleAllowedExpressions);
allowedExpressionPatternsByCollection.put(collection, existingAllowedExpressions);
} catch (IOException e) {
logger.error("Error while loading expressions definition " + predefinedAllowedExpressionsURL, e);
}
}
allowedExpressionPatternsByBundle.put(bundleContext.getBundle(), predefinedAllowedExpressionsForBundle);
}
private void removePredefinedAllowedPatterns(BundleContext bundleContext) {
Map<String,Set<Pattern>> allowedExpressionPatternsForBundle = allowedExpressionPatternsByBundle.get(bundleContext.getBundle());
for (Map.Entry<String,Set<Pattern>> allowedExpressionPatternsEntry : allowedExpressionPatternsForBundle.entrySet()) {
Set<Pattern> allowedExpressionPatterns = allowedExpressionPatternsByCollection.get(allowedExpressionPatternsEntry.getKey());
allowedExpressionPatterns.removeAll(allowedExpressionPatternsEntry.getValue());
allowedExpressionPatternsByCollection.put(allowedExpressionPatternsEntry.getKey(), allowedExpressionPatterns);
}
}
@Override
public ExpressionFilter getExpressionFilter(String filterCollection) {
if (expressionFiltersActivated) {
return new ExpressionFilter(allowedExpressionPatternsByCollection.get(filterCollection), forbiddenExpressionPatternsByCollection.get(filterCollection));
} else {
// if expression filtering is turned off we build an expression filter with no filters and that will accept everything.
return new ExpressionFilter(null, null);
}
}
}