/*
* RHQ Management Platform
* Copyright 2011, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.rhq.core.util.obfuscation;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.prefs.BackingStoreException;
import java.util.prefs.NodeChangeListener;
import java.util.prefs.PreferenceChangeListener;
import java.util.prefs.Preferences;
import java.util.regex.Pattern;
/**
* @author Stefan Negrea
*
*/
public class ObfuscatedPreferences extends Preferences {
@Retention(RetentionPolicy.RUNTIME)
public static @interface Restricted {
}
private Preferences actualPreferences;
private Set<String> restrictedPreferences = new HashSet<String>();
private Set<String> userRestrictedPreferences = new HashSet<String>();
@SuppressWarnings("rawtypes")
public ObfuscatedPreferences(Preferences actualPreferences, Class classz) {
this.actualPreferences = actualPreferences;
for (Field field : classz.getFields()) {
Restricted restricted = field.getAnnotation(Restricted.class);
if (restricted != null) {
try {
String restrictedProperty = field.get(classz).toString();
restrictedPreferences.add(restrictedProperty);
this.get(restrictedProperty, null);
} catch (Exception e) {
//nothing to do, the field is just not accesible
}
}
}
try{
for (String key : actualPreferences.keys()) {
if (!restrictedPreferences.contains(key)) {
String storedValue = actualPreferences.get(key, null);
if (storedValue != null && RestrictedFormat.isRestrictedFormat(storedValue)) {
userRestrictedPreferences.add(key);
}
}
}
} catch (Exception e) {
//nothing to do since this was just an exploration to see if the
//user requested any other properties to be restricted
}
}
@Override
public void put(String key, String value) {
if (restrictedPreferences.contains(key) || userRestrictedPreferences.contains(key)) {
try {
if (RestrictedFormat.isRestrictedFormat(value)) {
value = RestrictedFormat.retrieveValue(value);
value = PicketBoxObfuscator.decode(value);
} else {
throw new Exception("Value not in a retricted format");
}
} catch (Exception e) {
try {
value = PicketBoxObfuscator.decode(value);
} catch (Exception e2) {
//nothing to do, the value was not obfuscated
}
}
try{
value = PicketBoxObfuscator.encode(value);
} catch (Exception ex2) {
//an error occurred during encoding so just store
//the value as is
}
actualPreferences.put(key, RestrictedFormat.formatValue(value));
} else {
if (RestrictedFormat.isRestrictedFormat(value)) {
userRestrictedPreferences.add(key);
value = RestrictedFormat.retrieveValue(value);
try {
PicketBoxObfuscator.decode(value);
} catch (Exception e) {
value = PicketBoxObfuscator.encode(value);
}
actualPreferences.put(key, RestrictedFormat.formatValue(value));
} else {
actualPreferences.put(key, value);
}
}
}
@Override
public String get(String key, String def) {
String value = actualPreferences.get(key, null);
if (value == null) {
return def;
}
if (restrictedPreferences.contains(key) || userRestrictedPreferences.contains(key)) {
try {
if (RestrictedFormat.isRestrictedFormat(value)) {
value = RestrictedFormat.retrieveValue(value);
return PicketBoxObfuscator.decode(value);
} else {
throw new Exception("Value not in a restricted format");
}
} catch (Exception ex) {
this.put(key, value);
return value;
}
} else {
return value;
}
}
@Override
public void remove(String key) {
actualPreferences.remove(key);
}
@Override
public void clear() throws BackingStoreException {
actualPreferences.clear();
}
@Override
public void putInt(String key, int value) {
actualPreferences.putInt(key, value);
}
@Override
public int getInt(String key, int def) {
return actualPreferences.getInt(key, def);
}
@Override
public void putLong(String key, long value) {
actualPreferences.putLong(key, value);
}
@Override
public long getLong(String key, long def) {
return actualPreferences.getLong(key, def);
}
@Override
public void putBoolean(String key, boolean value) {
actualPreferences.putBoolean(key, value);
}
@Override
public boolean getBoolean(String key, boolean def) {
return actualPreferences.getBoolean(key, def);
}
@Override
public void putFloat(String key, float value) {
actualPreferences.putFloat(key, value);
}
@Override
public float getFloat(String key, float def) {
return actualPreferences.getFloat(key, def);
}
@Override
public void putDouble(String key, double value) {
actualPreferences.putDouble(key, value);
}
@Override
public double getDouble(String key, double def) {
return actualPreferences.getDouble(key, def);
}
@Override
public void putByteArray(String key, byte[] value) {
actualPreferences.putByteArray(key, value);
}
@Override
public byte[] getByteArray(String key, byte[] def) {
return actualPreferences.getByteArray(key, def);
}
@Override
public String[] keys() throws BackingStoreException {
return actualPreferences.keys();
}
@Override
public String[] childrenNames() throws BackingStoreException {
return actualPreferences.childrenNames();
}
@Override
public Preferences parent() {
return actualPreferences.parent();
}
@Override
public Preferences node(String pathName) {
return actualPreferences.node(pathName);
}
@Override
public boolean nodeExists(String pathName) throws BackingStoreException {
return actualPreferences.nodeExists(pathName);
}
@Override
public void removeNode() throws BackingStoreException {
actualPreferences.removeNode();
}
@Override
public String name() {
return actualPreferences.name();
}
@Override
public String absolutePath() {
return actualPreferences.absolutePath();
}
@Override
public boolean isUserNode() {
return actualPreferences.isUserNode();
}
@Override
public String toString() {
return actualPreferences.toString();
}
@Override
public void flush() throws BackingStoreException {
actualPreferences.flush();
}
@Override
public void sync() throws BackingStoreException {
actualPreferences.sync();
}
@Override
public void addPreferenceChangeListener(PreferenceChangeListener pcl) {
actualPreferences.addPreferenceChangeListener(pcl);
}
@Override
public void removePreferenceChangeListener(PreferenceChangeListener pcl) {
actualPreferences.removePreferenceChangeListener(pcl);
}
@Override
public void addNodeChangeListener(NodeChangeListener ncl) {
actualPreferences.addNodeChangeListener(ncl);
}
@Override
public void removeNodeChangeListener(NodeChangeListener ncl) {
actualPreferences.removeNodeChangeListener(ncl);
}
@Override
public void exportNode(OutputStream os) throws IOException, BackingStoreException {
actualPreferences.exportNode(os);
}
@Override
public void exportSubtree(OutputStream os) throws IOException, BackingStoreException {
actualPreferences.exportSubtree(os);
}
public static class RestrictedFormat {
private static final Pattern RESTRICTED_PATTERN = Pattern.compile("RESTRICTED::.*", Pattern.CASE_INSENSITIVE);
private static final String RESTRICTED_FORMAT = "RESTRICTED::%s";
/**
* Checks if a property value is in a restricted format.
*
* @param str string to check
* @return
*/
public static boolean isRestrictedFormat(String str) {
return str != null && RESTRICTED_PATTERN.matcher(str).matches();
}
/**
* Retrieves the actual value from a restricted format string.
*
* @param value
* @return
*/
public static String retrieveValue(String value) {
if (!isRestrictedFormat(value)) {
return null;
}
StringTokenizer tokenizer = new StringTokenizer(value, "::");
if (!tokenizer.hasMoreTokens()) {
return null;
}
tokenizer.nextToken();
if (tokenizer.hasMoreTokens()) {
return tokenizer.nextToken();
}
return null;
}
/**
* Formats a value in the proper restricted format.
*
* @param value
* @return
*/
public static String formatValue(String value) {
return String.format(RESTRICTED_FORMAT, value);
}
}
}