/*******************************************************************************
* Copyright (c) 2010-2014 SAP AG and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* SAP AG - initial API and implementation
*******************************************************************************/
package org.eclipse.skalli.services.configuration;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* Helper class to obfuscate fields of a given object that are annotated with {@link Protect}.
*/
public class Protector {
public static final String PROTECTION_VALUE_STRING = "******"; //$NON-NLS-1$
public static final char PROTECTION_VALUE_CHAR = '*';
/**
* Obfuscates all fields of the given object that are annotated with {@link Protect}.
* If a field is {@link Iterable iterable}, all entries of that field are obfuscated.
* Fields inherited from superclasses are obfuscated recursively, too.
*
* Obfuscation for string-like and character field types means that the field
* value is replaced with stars (either <tt>"********"</tt> or <tt>'*'</tt>,
* respectively).
* For primitive types (except <code>char</code) the field value is reset to the
* default value defined by the Java language (zero for numeric types,
* <code>false</code> for boolean types etc.).
* For all other types (including the wrapper types for the primitives like
* <code>Boolean</code> and <code>Integer</code>) the field value is replaced
* with <code>null</code>.
*
* @param obj the object to obfuscate.
* @param relevantClasses all classes referenced by or inherited from of the given object
* that should be included into the obfuscation. Classes not contained in the list will be
* ignored (e.g. JDK classes).
*
* @throws IllegalAccessException if any of the {@ Project protected}
* fields of the given object is inaccessible, e.g. because that field is either static or final (or both),
* or access control is enforced for that field.
*/
public static void protect(Object obj, Collection<Class<?>> relevantClasses) throws IllegalAccessException {
if (obj == null) {
return;
}
List<Class<?>> list = new ArrayList<Class<?>>();
if (relevantClasses != null) {
list.addAll(relevantClasses);
}
Class<?> clazz = obj.getClass();
list.add(clazz);
protect(obj, clazz, list);
}
private static void protect(Object obj, Class<? extends Object> clazz, Collection<Class<?>> relevantClasses)
throws IllegalAccessException {
if (obj instanceof Iterable) {
for (Object element : (Iterable<?>) obj) {
if (element != null) {
protect(element, element.getClass(), relevantClasses);
}
}
} else if (isAssignableFromAny(clazz, relevantClasses)) {
Class<?> superclass = clazz.getSuperclass();
if (superclass != null && superclass != Object.class) {
protect(obj, superclass, relevantClasses);
}
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
protectField(field, obj, relevantClasses);
}
}
}
private static boolean isAssignableFromAny(Class<? extends Object> clazz, Collection<Class<?>> relevantClasses) {
for (Class<?> relevatClass : relevantClasses) {
if (relevatClass.isAssignableFrom(clazz)) {
return true;
}
}
return false;
}
private static void protectField(Field field, Object obj, Collection<Class<?>> relevantClasses)
throws IllegalAccessException {
if (field.isAnnotationPresent(Protect.class)) {
protectValue(field, obj);
} else if (!field.getType().isPrimitive()) {
Object fieldValue = getFieldValue(field, obj);
if (fieldValue != null) {
protect(fieldValue, fieldValue.getClass(), relevantClasses);
}
}
}
private static Object getFieldValue(Field field, Object obj) throws IllegalAccessException {
field.setAccessible(true);
return field.get(obj);
}
private static void protectValue(Field field, Object obj)
throws IllegalArgumentException, IllegalAccessException {
field.setAccessible(true);
Class<?> fieldType = field.getType();
if (fieldType.equals(String.class)) {
field.set(obj, PROTECTION_VALUE_STRING);
} else if (fieldType.equals(char.class) || fieldType.equals(Character.class)) {
field.set(obj, PROTECTION_VALUE_CHAR);
} else if (fieldType.equals(boolean.class)) {
field.set(obj, false);
} else if (fieldType.equals(int.class)
|| fieldType.equals(long.class)
|| fieldType.equals(byte.class)
|| fieldType.equals(short.class)
|| fieldType.equals(float.class)
|| fieldType.equals(double.class)) {
field.set(obj, 0);
} else {
field.set(obj, null);
}
}
}