|
53 | 53 |
|
54 | 54 | import org.springframework.beans.BeanUtils;
|
55 | 55 | import org.springframework.beans.BeansException;
|
| 56 | +import org.springframework.boot.actuate.endpoint.SanitizableData; |
56 | 57 | import org.springframework.boot.actuate.endpoint.Sanitizer;
|
| 58 | +import org.springframework.boot.actuate.endpoint.SanitizingFunction; |
57 | 59 | import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
58 | 60 | import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
59 | 61 | import org.springframework.boot.actuate.endpoint.annotation.Selector;
|
|
64 | 66 | import org.springframework.boot.context.properties.bind.Name;
|
65 | 67 | import org.springframework.boot.context.properties.source.ConfigurationProperty;
|
66 | 68 | import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
|
| 69 | +import org.springframework.boot.context.properties.source.ConfigurationPropertySource; |
67 | 70 | import org.springframework.boot.origin.Origin;
|
68 | 71 | import org.springframework.context.ApplicationContext;
|
69 | 72 | import org.springframework.context.ApplicationContextAware;
|
|
73 | 76 | import org.springframework.core.annotation.MergedAnnotation;
|
74 | 77 | import org.springframework.core.annotation.MergedAnnotations;
|
75 | 78 | import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;
|
| 79 | +import org.springframework.core.env.PropertySource; |
76 | 80 | import org.springframework.util.ClassUtils;
|
77 | 81 | import org.springframework.util.StringUtils;
|
78 | 82 |
|
@@ -100,12 +104,20 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext
|
100 | 104 |
|
101 | 105 | private static final String CONFIGURATION_PROPERTIES_FILTER_ID = "configurationPropertiesFilter";
|
102 | 106 |
|
103 |
| - private final Sanitizer sanitizer = new Sanitizer(); |
| 107 | + private final Sanitizer sanitizer; |
104 | 108 |
|
105 | 109 | private ApplicationContext context;
|
106 | 110 |
|
107 | 111 | private ObjectMapper objectMapper;
|
108 | 112 |
|
| 113 | + public ConfigurationPropertiesReportEndpoint() { |
| 114 | + this(Collections.emptyList()); |
| 115 | + } |
| 116 | + |
| 117 | + public ConfigurationPropertiesReportEndpoint(Iterable<SanitizingFunction> sanitizingFunctions) { |
| 118 | + this.sanitizer = new Sanitizer(sanitizingFunctions); |
| 119 | + } |
| 120 | + |
109 | 121 | @Override
|
110 | 122 | public void setApplicationContext(ApplicationContext context) throws BeansException {
|
111 | 123 | this.context = context;
|
@@ -236,26 +248,63 @@ else if (value instanceof List) {
|
236 | 248 | map.put(key, sanitize(qualifiedKey, (List<Object>) value));
|
237 | 249 | }
|
238 | 250 | else {
|
239 |
| - value = this.sanitizer.sanitize(key, value); |
240 |
| - value = this.sanitizer.sanitize(qualifiedKey, value); |
241 |
| - map.put(key, value); |
| 251 | + map.put(key, sanitizeWithPropertySourceIfPresent(qualifiedKey, value)); |
242 | 252 | }
|
243 | 253 | });
|
244 | 254 | return map;
|
245 | 255 | }
|
246 | 256 |
|
| 257 | + private Object sanitizeWithPropertySourceIfPresent(String qualifiedKey, Object value) { |
| 258 | + ConfigurationPropertyName currentName = getCurrentName(qualifiedKey); |
| 259 | + ConfigurationProperty candidate = getCandidate(currentName); |
| 260 | + PropertySource<?> propertySource = getPropertySource(candidate); |
| 261 | + if (propertySource != null) { |
| 262 | + SanitizableData data = new SanitizableData(propertySource, qualifiedKey, value); |
| 263 | + return this.sanitizer.sanitize(data); |
| 264 | + } |
| 265 | + SanitizableData data = new SanitizableData(null, qualifiedKey, value); |
| 266 | + return this.sanitizer.sanitize(data); |
| 267 | + } |
| 268 | + |
| 269 | + private PropertySource<?> getPropertySource(ConfigurationProperty configurationProperty) { |
| 270 | + if (configurationProperty == null) { |
| 271 | + return null; |
| 272 | + } |
| 273 | + ConfigurationPropertySource source = configurationProperty.getSource(); |
| 274 | + Object underlyingSource = (source != null) ? source.getUnderlyingSource() : null; |
| 275 | + return (underlyingSource instanceof PropertySource<?>) ? (PropertySource<?>) underlyingSource : null; |
| 276 | + } |
| 277 | + |
| 278 | + private ConfigurationPropertyName getCurrentName(String qualifiedKey) { |
| 279 | + return ConfigurationPropertyName.adapt(qualifiedKey, '.'); |
| 280 | + } |
| 281 | + |
| 282 | + private ConfigurationProperty getCandidate(ConfigurationPropertyName currentName) { |
| 283 | + BoundConfigurationProperties bound = BoundConfigurationProperties.get(this.context); |
| 284 | + if (bound == null) { |
| 285 | + return null; |
| 286 | + } |
| 287 | + ConfigurationProperty candidate = bound.get(currentName); |
| 288 | + if (candidate == null && currentName.isLastElementIndexed()) { |
| 289 | + candidate = bound.get(currentName.chop(currentName.getNumberOfElements() - 1)); |
| 290 | + } |
| 291 | + return candidate; |
| 292 | + } |
| 293 | + |
247 | 294 | @SuppressWarnings("unchecked")
|
248 | 295 | private List<Object> sanitize(String prefix, List<Object> list) {
|
249 | 296 | List<Object> sanitized = new ArrayList<>();
|
| 297 | + int index = 0; |
250 | 298 | for (Object item : list) {
|
| 299 | + String name = prefix + "[" + index++ + "]"; |
251 | 300 | if (item instanceof Map) {
|
252 |
| - sanitized.add(sanitize(prefix, (Map<String, Object>) item)); |
| 301 | + sanitized.add(sanitize(name, (Map<String, Object>) item)); |
253 | 302 | }
|
254 | 303 | else if (item instanceof List) {
|
255 |
| - sanitized.add(sanitize(prefix, (List<Object>) item)); |
| 304 | + sanitized.add(sanitize(name, (List<Object>) item)); |
256 | 305 | }
|
257 | 306 | else {
|
258 |
| - sanitized.add(this.sanitizer.sanitize(prefix, item)); |
| 307 | + sanitized.add(sanitizeWithPropertySourceIfPresent(name, item)); |
259 | 308 | }
|
260 | 309 | }
|
261 | 310 | return sanitized;
|
@@ -299,24 +348,22 @@ else if (item instanceof List) {
|
299 | 348 | }
|
300 | 349 |
|
301 | 350 | private Map<String, Object> applyInput(String qualifiedKey) {
|
302 |
| - BoundConfigurationProperties bound = BoundConfigurationProperties.get(this.context); |
303 |
| - if (bound == null) { |
304 |
| - return Collections.emptyMap(); |
305 |
| - } |
306 |
| - ConfigurationPropertyName currentName = ConfigurationPropertyName.adapt(qualifiedKey, '.'); |
307 |
| - ConfigurationProperty candidate = bound.get(currentName); |
308 |
| - if (candidate == null && currentName.isLastElementIndexed()) { |
309 |
| - candidate = bound.get(currentName.chop(currentName.getNumberOfElements() - 1)); |
310 |
| - } |
311 |
| - return (candidate != null) ? getInput(currentName.toString(), candidate) : Collections.emptyMap(); |
| 351 | + ConfigurationPropertyName currentName = getCurrentName(qualifiedKey); |
| 352 | + ConfigurationProperty candidate = getCandidate(currentName); |
| 353 | + PropertySource<?> propertySource = getPropertySource(candidate); |
| 354 | + if (propertySource != null) { |
| 355 | + Object value = stringifyIfNecessary(candidate.getValue()); |
| 356 | + SanitizableData data = new SanitizableData(propertySource, currentName.toString(), value); |
| 357 | + return getInput(candidate, this.sanitizer.sanitize(data)); |
| 358 | + } |
| 359 | + return Collections.emptyMap(); |
312 | 360 | }
|
313 | 361 |
|
314 |
| - private Map<String, Object> getInput(String property, ConfigurationProperty candidate) { |
| 362 | + private Map<String, Object> getInput(ConfigurationProperty candidate, Object sanitizedValue) { |
315 | 363 | Map<String, Object> input = new LinkedHashMap<>();
|
316 |
| - Object value = stringifyIfNecessary(candidate.getValue()); |
317 | 364 | Origin origin = Origin.from(candidate);
|
318 | 365 | List<Origin> originParents = Origin.parentsFrom(candidate);
|
319 |
| - input.put("value", this.sanitizer.sanitize(property, value)); |
| 366 | + input.put("value", sanitizedValue); |
320 | 367 | input.put("origin", (origin != null) ? origin.toString() : "none");
|
321 | 368 | if (!originParents.isEmpty()) {
|
322 | 369 | input.put("originParents", originParents.stream().map(Object::toString).toArray(String[]::new));
|
|
0 commit comments