You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by kw...@apache.org on 2026/07/30 16:19:00 UTC
(sling-org-apache-sling-caconfig-impl) 01/01: SLING-13282 Support automatic decryption in Context-Aware Configurations
This is an automated email from the ASF dual-hosted git repository.
kwin pushed a commit to branch feature/encryption
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-caconfig-impl.git
commit 9ab87f1c3da0a69298db82af544d4f05d1d10cc3
Author: Konrad Windszus <kw...@apache.org>
AuthorDate: Thu Jul 30 18:18:48 2026 +0200
SLING-13282 Support automatic decryption in Context-Aware Configurations
Leverage Sling Commons Crypto
---
bnd.bnd | 1 +
pom.xml | 10 ++++-
.../caconfig/impl/ConfigurationBuilderImpl.java | 46 ++++++++++++++++------
.../caconfig/impl/ConfigurationResolverImpl.java | 6 +++
.../apache/sling/caconfig/impl/DecryptService.java | 38 ++++++++++++++++++
.../impl/metadata/AnnotationClassParser.java | 3 +-
6 files changed, 88 insertions(+), 16 deletions(-)
diff --git a/bnd.bnd b/bnd.bnd
index 6d91515..8c1caeb 100644
--- a/bnd.bnd
+++ b/bnd.bnd
@@ -1,3 +1,4 @@
Import-Package:\
org.apache.felix.webconsole;resolution:=optional,\
+ org.apache.sling.commons.crypto.*;resolution:=optional,\
*
diff --git a/pom.xml b/pom.xml
index ccb0e49..f4b0077 100644
--- a/pom.xml
+++ b/pom.xml
@@ -43,8 +43,8 @@
<properties>
<project.build.outputTimestamp>2025-03-12T12:14:50Z</project.build.outputTimestamp>
<sling.java.version>11</sling.java.version>
- <caconfig.api.version>1.2.0</caconfig.api.version>
- <caconfig.spi.version>1.4.0</caconfig.spi.version>
+ <caconfig.api.version>1.4.0-SNAPSHOT</caconfig.api.version>
+ <caconfig.spi.version>1.5.0-SNAPSHOT</caconfig.spi.version>
<!-- integration tests -->
<sling.starter.version>14</sling.starter.version>
<starter.min.bundles.count>200</starter.min.bundles.count>
@@ -86,6 +86,12 @@
<version>${caconfig.spi.version}</version>
<scope>compile</scope>
</dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.commons.crypto</artifactId>
+ <version>1.2.0</version>
+ <scope>compile</scope>
+ </dependency>
<dependency>
<groupId>jakarta.json</groupId>
<artifactId>jakarta.json-api</artifactId>
diff --git a/src/main/java/org/apache/sling/caconfig/impl/ConfigurationBuilderImpl.java b/src/main/java/org/apache/sling/caconfig/impl/ConfigurationBuilderImpl.java
index 9181ea0..bddcda4 100644
--- a/src/main/java/org/apache/sling/caconfig/impl/ConfigurationBuilderImpl.java
+++ b/src/main/java/org/apache/sling/caconfig/impl/ConfigurationBuilderImpl.java
@@ -25,6 +25,8 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
+import java.util.function.UnaryOperator;
import org.apache.commons.collections4.IteratorUtils;
import org.apache.commons.collections4.ResettableListIterator;
@@ -63,6 +65,7 @@ class ConfigurationBuilderImpl implements ConfigurationBuilder {
private final ConfigurationInheritanceStrategy configurationInheritanceStrategy;
private final ConfigurationOverrideMultiplexer configurationOverrideMultiplexer;
private final ConfigurationMetadataProvider configurationMetadataProvider;
+ private final Optional<UnaryOperator<String>> decryptOperator;
private final Collection<String> configBucketNames;
private final String configName;
@@ -76,6 +79,7 @@ class ConfigurationBuilderImpl implements ConfigurationBuilder {
final ConfigurationInheritanceStrategy configurationInheritanceStrategy,
final ConfigurationOverrideMultiplexer configurationOverrideMultiplexer,
final ConfigurationMetadataProvider configurationMetadataProvider,
+ final Optional<UnaryOperator<String>> decryptOperator,
final Collection<String> configBucketNames) {
this(
resource,
@@ -85,6 +89,7 @@ class ConfigurationBuilderImpl implements ConfigurationBuilder {
configurationInheritanceStrategy,
configurationOverrideMultiplexer,
configurationMetadataProvider,
+ decryptOperator,
configBucketNames,
null);
}
@@ -97,6 +102,7 @@ class ConfigurationBuilderImpl implements ConfigurationBuilder {
final ConfigurationInheritanceStrategy configurationInheritanceStrategy,
final ConfigurationOverrideMultiplexer configurationOverrideMultiplexer,
final ConfigurationMetadataProvider configurationMetadataProvider,
+ final Optional<UnaryOperator<String>> decryptOperator,
final Collection<String> configBucketNames,
final String configName) {
this.contentResource = resource;
@@ -106,6 +112,7 @@ class ConfigurationBuilderImpl implements ConfigurationBuilder {
this.configurationInheritanceStrategy = configurationInheritanceStrategy;
this.configurationOverrideMultiplexer = configurationOverrideMultiplexer;
this.configurationMetadataProvider = configurationMetadataProvider;
+ this.decryptOperator = decryptOperator;
this.configBucketNames = configBucketNames;
this.configName = configName;
}
@@ -121,6 +128,7 @@ class ConfigurationBuilderImpl implements ConfigurationBuilder {
configurationInheritanceStrategy,
configurationOverrideMultiplexer,
configurationMetadataProvider,
+ decryptOperator,
configBucketNames,
configName);
}
@@ -254,16 +262,16 @@ class ConfigurationBuilderImpl implements ConfigurationBuilder {
}
/**
- * Apply default values from configuration metadata (where no real data is present).
+ * Apply transformations on the underlying resource, e.g. to add default values from configuration metadata (where no real data is present) or to decrypt encrypted values.
* @param resource Resource
* @param configName Configuration name
- * @return null if no default values found, or a wrapped resource with added default properties.
+ * @return the resource with transformed properties.
*/
- private Resource applyDefaultValues(Resource resource, String configName) {
+ private Resource applyTransformations(Resource resource, String configName) {
if (resource == null) {
return null;
}
- Map<String, Object> updatedMap = applyDefaultValues(resource.getValueMap(), configName);
+ Map<String, Object> updatedMap = applyPropertyMetadata(resource.getValueMap(), configName);
if (updatedMap == null) {
return resource;
}
@@ -271,12 +279,12 @@ class ConfigurationBuilderImpl implements ConfigurationBuilder {
}
/**
- * Apply default values from configuration metadata (where no real data is present).
+ * Apply default values from configuration metadata (where no real data is present) and decrypt encrypted values.
* @param props Properties
* @param configName Configuration name
- * @return null if no default values found, or a new map with added default properties.
+ * @return null if no transformations applied, or a new map with transformed properties.
*/
- private Map<String, Object> applyDefaultValues(Map<String, Object> props, String configName) {
+ private Map<String, Object> applyPropertyMetadata(Map<String, Object> props, String configName) {
ConfigurationMetadata metadata = configurationMetadataProvider.getConfigurationMetadata(configName);
if (metadata == null) {
// probably a configuration list - remove item name from end
@@ -288,17 +296,29 @@ class ConfigurationBuilderImpl implements ConfigurationBuilder {
return null;
}
}
- Map<String, Object> updatedMap = new HashMap<>();
+ Map<String, Object> updatedMap = new HashMap<>(props);
+ boolean isTransformed = false;
for (PropertyMetadata<?> propertyMetadata :
metadata.getPropertyMetadata().values()) {
- if (propertyMetadata.getDefaultValue() != null) {
+ if (propertyMetadata.getDefaultValue() != null && !props.containsKey(propertyMetadata.getName())) {
updatedMap.put(propertyMetadata.getName(), propertyMetadata.getDefaultValue());
+ isTransformed = true;
+ }
+ if (propertyMetadata.isEncrypted() && props.containsKey(propertyMetadata.getName())) {
+ Object value = props.get(propertyMetadata.getName());
+ if (value instanceof String) {
+ String decryptedValue = decryptOperator
+ .map(operator -> operator.apply((String) value))
+ .orElseThrow(() -> new IllegalStateException(
+ "CryptoService not available to decrypt property " + propertyMetadata.getName()));
+ updatedMap.put(propertyMetadata.getName(), decryptedValue);
+ isTransformed = true;
+ }
}
}
- if (updatedMap.isEmpty()) {
+ if (!isTransformed) {
return null;
}
- updatedMap.putAll(props);
return updatedMap;
}
@@ -397,7 +417,7 @@ class ConfigurationBuilderImpl implements ConfigurationBuilder {
@Override
public ValueMap convert(Resource resource, Class<ValueMap> clazz, String configName, boolean isCollection) {
ValueMap props = ResourceUtil.getValueMap(resource);
- Map<String, Object> updatedMap = applyDefaultValues(props, configName);
+ Map<String, Object> updatedMap = applyPropertyMetadata(props, configName);
if (updatedMap != null) {
return new ValueMapDecorator(updatedMap);
} else {
@@ -439,7 +459,7 @@ class ConfigurationBuilderImpl implements ConfigurationBuilder {
if (resource == null || clazz == ConfigurationBuilder.class) {
return null;
}
- return applyDefaultValues(resource, configName).adaptTo(clazz);
+ return applyTransformations(resource, configName).adaptTo(clazz);
}
}
diff --git a/src/main/java/org/apache/sling/caconfig/impl/ConfigurationResolverImpl.java b/src/main/java/org/apache/sling/caconfig/impl/ConfigurationResolverImpl.java
index f0c3795..e341967 100644
--- a/src/main/java/org/apache/sling/caconfig/impl/ConfigurationResolverImpl.java
+++ b/src/main/java/org/apache/sling/caconfig/impl/ConfigurationResolverImpl.java
@@ -21,6 +21,7 @@ package org.apache.sling.caconfig.impl;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Optional;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.sling.api.resource.Resource;
@@ -36,6 +37,7 @@ import org.jetbrains.annotations.NotNull;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
import org.osgi.service.metatype.annotations.AttributeDefinition;
import org.osgi.service.metatype.annotations.Designate;
import org.osgi.service.metatype.annotations.ObjectClassDefinition;
@@ -63,6 +65,9 @@ public class ConfigurationResolverImpl implements ConfigurationResolver, Configu
@Reference
private ConfigurationMetadataProviderMultiplexer configurationMetadataProvider;
+ @Reference(cardinality = ReferenceCardinality.OPTIONAL)
+ private DecryptService decryptService;
+
@ObjectClassDefinition(
name = "Apache Sling Context-Aware Configuration Resolver",
description = "Getting context-aware configurations for a given resource context.")
@@ -99,6 +104,7 @@ public class ConfigurationResolverImpl implements ConfigurationResolver, Configu
configurationInheritanceStrategy,
configurationOverrideMultiplexer,
configurationMetadataProvider,
+ Optional.ofNullable(decryptService),
configBucketNames);
}
diff --git a/src/main/java/org/apache/sling/caconfig/impl/DecryptService.java b/src/main/java/org/apache/sling/caconfig/impl/DecryptService.java
new file mode 100644
index 0000000..2f40ead
--- /dev/null
+++ b/src/main/java/org/apache/sling/caconfig/impl/DecryptService.java
@@ -0,0 +1,38 @@
+/*
+ * 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.sling.caconfig.impl;
+
+import java.util.function.UnaryOperator;
+
+import org.apache.sling.commons.crypto.CryptoService;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+
+@Component(service = DecryptService.class)
+public class DecryptService implements UnaryOperator<String> {
+
+ @Reference(cardinality = ReferenceCardinality.OPTIONAL)
+ private CryptoService cryptoService;
+
+ @Override
+ public String apply(String t) {
+ return cryptoService.decrypt(t);
+ }
+}
diff --git a/src/main/java/org/apache/sling/caconfig/impl/metadata/AnnotationClassParser.java b/src/main/java/org/apache/sling/caconfig/impl/metadata/AnnotationClassParser.java
index f9ea4a9..050f66f 100644
--- a/src/main/java/org/apache/sling/caconfig/impl/metadata/AnnotationClassParser.java
+++ b/src/main/java/org/apache/sling/caconfig/impl/metadata/AnnotationClassParser.java
@@ -196,7 +196,8 @@ public final class AnnotationClassParser {
.label(emptyToNull(propertyAnnotation.label()))
.description(emptyToNull(propertyAnnotation.description()))
.properties(propsArrayToMap(propertyAnnotation.property()))
- .order(propertyAnnotation.order());
+ .order(propertyAnnotation.order())
+ .encrypted(propertyAnnotation.encrypted());
} else {
Map<String, String> emptyMap = Collections.emptyMap();
propertyMetadata.properties(emptyMap);