All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.kiwiproject.json.PropertyMaskingSafePropertyWriter Maven / Gradle / Ivy

Go to download

Kiwi is a utility library. We really like Google's Guava, and also use Apache Commons. But if they don't have something we need, and we think it is useful, this is where we put it.

There is a newer version: 4.5.2
Show newest version
package org.kiwiproject.json;

import static java.util.stream.Collectors.toUnmodifiableList;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j;

import java.util.List;
import java.util.Objects;
import java.util.regex.Pattern;

/**
 * Writes properties "safely" and masks sensitive properties such as passwords. This property writer will attempt
 * to write out the value, but if an exception is thrown, it will instead write a pre-configured value in place
 * of the actual value.
 * 

* This writer also allows masking (hiding the true value) of certain fields based on their name by writing out * asterisks instead of the true value. *

* NOTE: Generally masking should be used only on String fields, because otherwise the type in the resulting JSON will * be different than the source type. For example, if a class has a "secretNumber" of type "int" and it is masked, the * resulting JSON contains a String instead of an int, which will likely cause problems if a downstream system reads * the JSON expecting an int. For such cases, consider using {@link com.fasterxml.jackson.annotation.JsonView} instead. *

* Note that jackson-core and jackson-databind must be available at runtime. */ @Slf4j public class PropertyMaskingSafePropertyWriter extends BeanPropertyWriter { private final List hiddenFieldPatterns; private final String maskedFieldReplacementText; private final String serializationErrorReplacementText; /** * Construct new instance wrapping the given {@link BeanPropertyWriter} using the given list of (String) * regular expressions that define the properties which should be masked. *

* Default values for masked fields and serialization errors are used. The default values are * defined by {@link PropertyMaskingOptions}. * * @param base the base or delegate {@link BeanPropertyWriter} to use * @param maskedFieldRegexps list containing regular expressions that define the properties to mask */ public PropertyMaskingSafePropertyWriter(BeanPropertyWriter base, List maskedFieldRegexps) { this(base, PropertyMaskingOptions.builder() .maskedFieldRegexps(maskedFieldRegexps) .build()); } /** * Construct new instance wrapping the given {@link BeanPropertyWriter} using the given * {@link PropertyMaskingOptions} to define properties to be masked, as well as replacement text for * masked fields and serialization errors. * * @param base the base or delegate {@link BeanPropertyWriter} to use * @param options the options to use * @see PropertyMaskingOptions */ public PropertyMaskingSafePropertyWriter(BeanPropertyWriter base, PropertyMaskingOptions options) { super(base); this.hiddenFieldPatterns = convertToPatterns(options.getMaskedFieldRegexps()); this.maskedFieldReplacementText = options.getMaskedFieldReplacementText(); this.serializationErrorReplacementText = options.getSerializationErrorReplacementText(); } private static List convertToPatterns(List maskedFieldRegexps) { return maskedFieldRegexps.stream() .filter(Objects::nonNull) .map(regex -> Pattern.compile(regex, Pattern.CASE_INSENSITIVE)) .collect(toUnmodifiableList()); } @Override public void serializeAsField(Object bean, JsonGenerator gen, SerializerProvider prov) { var propertyName = getName(); try { LOG.trace("Using custom serializer for field: {}", propertyName); if (matchesExclusionPatterns(propertyName)) { writeReplacementText(gen, propertyName, maskedFieldReplacementText); } else { super.serializeAsField(bean, gen, prov); } } catch (Exception e) { LOG.debug("Unable to serialize: {}, of {} instance, exception {}: {}", propertyName, bean.getClass().getName(), e.getClass().getName(), e.getMessage()); LOG.trace("Exception serializing field: {}", propertyName, e); writeReplacementText(gen, propertyName, serializationErrorReplacementText); } } private boolean matchesExclusionPatterns(String name) { return hiddenFieldPatterns.stream().anyMatch(pattern -> pattern.matcher(name).find()); } @VisibleForTesting static void writeReplacementText(JsonGenerator gen, String name, String text) { LOG.trace("Setting field '{}' to: {}", name, text); try { gen.writeFieldName(name); gen.writeString(text); } catch (Exception e) { LOG.error("Failed to serialize replacement value for field: {}", name, e); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy