blob: ed2ac80c98406ff0f1c049bfc3d2dc786c9e2ad0 [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 flex.messaging.validators;
import flex.messaging.config.ConfigMap;
import java.util.*;
import java.util.regex.Pattern;
/**
* The <code>ClassDeserializationValidator</code> is provided as a default
* implementation of <code>DeserializationValidator</code> and it simply
* validates the creation of allowed and disallowed classes as specified in
* the configuration.
*/
public class ClassDeserializationValidator implements DeserializationValidator {
//--------------------------------------------------------------------------
//
// Public Static Constants
//
//--------------------------------------------------------------------------
public static final String PROPERTY_ALLOW_CLASSES = "allow-classes";
public static final String PROPERTY_DISALLOW_CLASSES = "disallow-classes";
public static final String PROPERTY_CLASS_ATTR = "class";
public static final String PROPERTY_NAME_ATTR = "name";
private static final String[] DEFAULT_ALLOW_CLASSES = {
"flex.messaging.io.amf.SerializedObject",
"flex.messaging.io.ArrayCollection",
"flex.messaging.io.ObjectProxy",
"flex.messaging.io.SerializationProxy",
"flex.messaging.messages.AcknowledgeMessageExt",
"flex.messaging.messages.AsyncMessageExt",
"flex.messaging.messages.CommandMessageExt",
"flex.data.messages.DataMessageExt",
"flex.data.messages.ManagedRemotingMessageExt",
"flex.data.messages.PagedMessageExt",
"flex.data.messages.SequencedMessageExt",
"flex.data.messages.UpdateCollectionMessageExt",
"flex.data.ChangedItems",
"java.lang.Boolean",
"java.lang.Byte",
"java.lang.Character",
"java.lang.Double",
"java.lang.Float",
"java.lang.Integer",
"java.lang.Long",
"java.lang.Object",
"java.lang.Short",
"java.lang.String",
"java.io.Externalizable",
"java.util.Date",
"org.w3c.dom.Document",
"\\[B",
"\\[Ljava.lang.Object;"
};
//--------------------------------------------------------------------------
//
// Variables
//
//--------------------------------------------------------------------------
/**
* Instance level lock for thread-safe state changes.
*/
protected final Object lock = new Object();
/**
* Used to keep track of encountered allow and disallow classes for faster lookup.
*/
private Set<String> allowClasses;
private Set<String> disallowClasses;
/**
* Used to keep track of allow and disallow class patterns.
*/
private Map<String, Pattern> allowClassPatterns;
private Map<String, Pattern> disallowClassPatterns;
public ClassDeserializationValidator() {
}
//--------------------------------------------------------------------------
//
// Public Methods
//
//--------------------------------------------------------------------------
/**
* Adds a class pattern that should be allowed to be created.
*
* @param classNamePattern The name of the class which can be a regular expression.
*/
public void addAllowClassPattern(String classNamePattern) {
synchronized (lock) {
if (allowClassPatterns == null)
allowClassPatterns = new HashMap<String, Pattern>();
allowClassPatterns.put(classNamePattern, Pattern.compile(classNamePattern));
clearClassCache();
}
}
/**
* Removes a class pattern that should be allowed to be created..
*
* @param classNamePattern The name of the class which can be a regular expression.
*/
public void removeAllowClassPattern(String classNamePattern) {
synchronized (lock) {
if (allowClassPatterns != null)
allowClassPatterns.remove(classNamePattern);
clearClassCache();
}
}
/**
* Adds a class pattern that should be disallowed to be created.
*
* @param classNamePattern The name of the class which can be a regular expression.
*/
public void addDisallowClassPattern(String classNamePattern) {
synchronized (lock) {
if (disallowClassPatterns == null)
disallowClassPatterns = new HashMap<String, Pattern>();
disallowClassPatterns.put(classNamePattern, Pattern.compile(classNamePattern));
clearClassCache();
}
}
/**
* Removes a class pattern that should be disallowed to be created..
*
* @param classNamePattern The name of the class which can be a regular expression.
*/
public void removeDisallowClassPattern(String classNamePattern) {
synchronized (lock) {
if (disallowClassPatterns != null)
disallowClassPatterns.remove(classNamePattern);
clearClassCache();
}
}
/**
* This method is meant to validate the assignment of a value to an index of
* an Array or List instance but this class only deals with class creations,
* therefore this method always returns true.
*
* @param instance The Array or List instance.
* @param index The index at which the value is being assigned.
* @param value The value that is assigned to the index.
* @return True.
*/
public boolean validateAssignment(Object instance, int index, Object value) {
return true;
}
/**
* This method is meant to validate the assignment of a property of an instance
* to a value but this class only deals with class creations, therefore this
* method always returns true.
*
* @param instance The instance with the property that is being assigned a new value.
* @param propertyName The name of the property that is being assigned.
* @param value The value that the property is being assigned to.
* @return True.
*/
public boolean validateAssignment(Object instance, String propertyName, Object value) {
return true;
}
/**
* Validates the creation of a class as indicated by the allow and disallow
* classes list. If the class is in both allow and disallow classes,
* disallow classes list take precedence.
*
* @param c The class that is being created.
* @return True if the creation is valid.
*/
public boolean validateCreation(Class<?> c) {
String className = c == null ? null : c.getName();
if (className == null)
return true;
// First, check against the encountered disallow-classes list.
if (disallowClasses != null && disallowClasses.contains(className))
return false;
// Then, check against the encountered allow-classes list.
if (allowClasses != null && allowClasses.contains(className))
return true;
// Otherwise, the class was encountered for the first time, need to
// go through the disallow and allow class patterns list.
// Disallow the class if there's a disallow-classes list, and the class is in that list.
if (disallowClassPatterns != null && !disallowClassPatterns.isEmpty()) {
for (Pattern pattern : disallowClassPatterns.values()) {
if (pattern.matcher(className).matches()) {
addDisallowClass(className);
return false;
}
}
}
// Disallow the class if there's an allowed-classes list, and the class is NOT in that list.
if (allowClassPatterns != null && !allowClassPatterns.isEmpty()) {
for (Pattern pattern : allowClassPatterns.values()) {
if (pattern.matcher(className).matches()) {
addAllowClass(className);
return true;
}
}
// Disallow the class as it's not in allow-classes list.
addDisallowClass(className);
return false;
}
// Otherwise allow the class.
addAllowClass(className);
return true;
}
/**
* {@inheritDoc}
*/
public void initialize(String id, ConfigMap properties) {
// Apply default allow classes
for (String defaultAllowClassPattern : DEFAULT_ALLOW_CLASSES) {
addAllowClassPattern(defaultAllowClassPattern);
}
if (properties == null || properties.size() == 0)
return;
// Process allow-classes.
ConfigMap allowedClassesMap = properties.getPropertyAsMap(PROPERTY_ALLOW_CLASSES, null);
if (allowedClassesMap != null && !allowedClassesMap.isEmpty()) {
List<?> names = allowedClassesMap.getPropertyAsList(PROPERTY_CLASS_ATTR, null);
if (names != null && !names.isEmpty()) {
for (Object element : names) {
String name = ((ConfigMap) element).getProperty(PROPERTY_NAME_ATTR);
addAllowClassPattern(name);
}
}
}
// Process disallow-classes.
ConfigMap disallowedClassesMap = properties.getPropertyAsMap(PROPERTY_DISALLOW_CLASSES, null);
if (disallowedClassesMap != null && !disallowedClassesMap.isEmpty()) {
List<?> names = disallowedClassesMap.getPropertyAsList(PROPERTY_CLASS_ATTR, null);
if (names != null && !names.isEmpty()) {
for (Object element : names) {
String name = ((ConfigMap) element).getProperty(PROPERTY_NAME_ATTR);
addDisallowClassPattern(name);
}
}
}
}
//--------------------------------------------------------------------------
//
// Protected Methods
//
//--------------------------------------------------------------------------
protected void addAllowClass(String className) {
synchronized (lock) {
if (allowClasses == null)
allowClasses = new HashSet<String>();
if (!allowClasses.contains(className))
allowClasses.add(className);
}
}
protected void addDisallowClass(String className) {
synchronized (lock) {
if (disallowClasses == null)
disallowClasses = new HashSet<String>();
if (!disallowClasses.contains(className))
disallowClasses.add(className);
}
}
protected void clearClassCache() {
if (allowClasses != null) {
allowClasses.clear();
}
if (disallowClasses != null) {
disallowClasses.clear();
}
}
}