/*
* 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.brooklyn.core.config;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.brooklyn.util.core.config.ConfigBag;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
public final class Sanitizer {
/**
* Names that, if they appear anywhere in an attribute/config/field
* indicates that it may be private, so should not be logged etc.
*/
public static final List<String> SECRET_NAMES = ImmutableList.of(
"password",
"passwd",
"credential",
"secret",
"private",
"access.cert",
"access.key");
public static final Predicate<Object> IS_SECRET_PREDICATE = new IsSecretPredicate();
private static class IsSecretPredicate implements Predicate<Object> {
@Override
public boolean apply(Object name) {
String lowerName = name.toString().toLowerCase();
for (String secretName : SECRET_NAMES) {
if (lowerName.contains(secretName))
return true;
}
return false;
}
};
/**
* Kept only in case this anonymous inner class has made it into any persisted state.
*
* @deprecated since 0.7.0
*/
@Deprecated
@SuppressWarnings("unused")
private static final Predicate<Object> IS_SECRET_PREDICATE_DEPRECATED = new Predicate<Object>() {
@Override
public boolean apply(Object name) {
String lowerName = name.toString().toLowerCase();
for (String secretName : SECRET_NAMES) {
if (lowerName.contains(secretName))
return true;
}
return false;
}
};
public static Sanitizer newInstance(Predicate<Object> sanitizingNeededCheck) {
return new Sanitizer(sanitizingNeededCheck);
}
public static Sanitizer newInstance(){
return newInstance(IS_SECRET_PREDICATE);
}
public static Map<String, Object> sanitize(ConfigBag input) {
return sanitize(input.getAllConfig());
}
public static <K> Map<K, Object> sanitize(Map<K, ?> input) {
return sanitize(input, Sets.newHashSet());
}
static <K> Map<K, Object> sanitize(Map<K, ?> input, Set<Object> visited) {
return newInstance().apply(input, visited);
}
private Predicate<Object> predicate;
private Sanitizer(Predicate<Object> sanitizingNeededCheck) {
predicate = sanitizingNeededCheck;
}
public <K> Map<K, Object> apply(Map<K, ?> input) {
return apply(input, Sets.newHashSet());
}
private <K> Map<K, Object> apply(Map<K, ?> input, Set<Object> visited) {
Map<K, Object> result = Maps.newLinkedHashMap();
for (Map.Entry<K, ?> e : input.entrySet()) {
if (predicate.apply(e.getKey())){
result.put(e.getKey(), "xxxxxxxx");
continue;
}
// need to compare object reference, not equality since we may miss some.
// not a perfect identifier, but very low probability of collision.
if (visited.contains(System.identityHashCode(e.getValue()))) {
result.put(e.getKey(), e.getValue());
continue;
}
visited.add(System.identityHashCode(e.getValue()));
if (e.getValue() instanceof Map) {
result.put(e.getKey(), apply((Map<?, ?>) e.getValue(), visited));
} else if (e.getValue() instanceof List) {
result.put(e.getKey(), applyList((List<?>) e.getValue(), visited));
} else if (e.getValue() instanceof Set) {
result.put(e.getKey(), applySet((Set<?>) e.getValue(), visited));
} else {
result.put(e.getKey(), e.getValue());
}
}
return result;
}
private List<Object> applyIterable(Iterable<?> input, Set<Object> visited){
List<Object> result = Lists.newArrayList();
for(Object o : input){
if(visited.contains(System.identityHashCode(o))){
result.add(o);
continue;
}
visited.add(System.identityHashCode(o));
if (o instanceof Map) {
result.add(apply((Map<?, ?>) o, visited));
} else if (o instanceof List) {
result.add(applyList((List<?>) o, visited));
} else if (o instanceof Set) {
result.add(applySet((Set<?>) o, visited));
} else {
result.add(o);
}
}
return result;
}
private List<Object> applyList(List<?> input, Set<Object> visited) {
return applyIterable(input, visited);
}
private Set<Object> applySet(Set<?> input, Set<Object> visited) {
Set<Object> result = Sets.newLinkedHashSet();
result.addAll(applyIterable(input, visited));
return result;
}
}