You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by ro...@apache.org on 2021/05/09 19:42:09 UTC

[felix-dev] branch master updated: [FELIX-6414] Interpolation plugin support multiple secrets directories

This is an automated email from the ASF dual-hosted git repository.

rotty3000 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/felix-dev.git


The following commit(s) were added to refs/heads/master by this push:
     new afa3924  [FELIX-6414] Interpolation plugin support multiple secrets directories
afa3924 is described below

commit afa392414c8939e3f4dd73939621fb22aefbf47c
Author: Raymond Augé <ro...@apache.org>
AuthorDate: Sun May 9 15:41:41 2021 -0400

    [FELIX-6414] Interpolation plugin support multiple secrets directories
    
    Signed-off-by: Raymond Augé <ro...@apache.org>
---
 configadmin-plugins/interpolation/README.md        |  4 +--
 .../InterpolationConfigurationPlugin.java          | 27 ++++++++++++------
 .../InterpolationConfigurationPluginTest.java      | 32 +++++++++++++++++-----
 .../interpolation/src/test/resources/foo.bar       |  1 +
 .../src/test/resources/{ => other}/testfile.txt    |  0
 5 files changed, 46 insertions(+), 18 deletions(-)

diff --git a/configadmin-plugins/interpolation/README.md b/configadmin-plugins/interpolation/README.md
index 868ea0f..d6605fc 100644
--- a/configadmin-plugins/interpolation/README.md
+++ b/configadmin-plugins/interpolation/README.md
@@ -109,12 +109,12 @@ In case of the Apache Felix ConfigAdmin implementation, this can be achieved by
 
 ### Secrets lookup
 
-In order to look up secrets on the filesystem, the plugin must be provided with the directory
+In order to look up secrets on the filesystem, the plugin must be provided with the directories
 where these can be found.
 
 This is done through the following property:
 
-* `org.apache.felix.configadmin.plugin.interpolation.secretsdir`: specify the directory where the files used for the file-based interpolation, such as Kubernetes secrets, are mounted.
+* `org.apache.felix.configadmin.plugin.interpolation.secretsdir`: specify a comma-separated (`,`) list of directories where the files used for the file-based interpolation, such as Kubernetes secrets, are mounted.
 
 If the property is not present, the plugin will function, but without being able to replace values based on secrets.
 
diff --git a/configadmin-plugins/interpolation/src/main/java/org/apache/felix/configadmin/plugin/interpolation/InterpolationConfigurationPlugin.java b/configadmin-plugins/interpolation/src/main/java/org/apache/felix/configadmin/plugin/interpolation/InterpolationConfigurationPlugin.java
index 97656ae..4974848 100644
--- a/configadmin-plugins/interpolation/src/main/java/org/apache/felix/configadmin/plugin/interpolation/InterpolationConfigurationPlugin.java
+++ b/configadmin-plugins/interpolation/src/main/java/org/apache/felix/configadmin/plugin/interpolation/InterpolationConfigurationPlugin.java
@@ -16,17 +16,22 @@
  */
 package org.apache.felix.configadmin.plugin.interpolation;
 
+import static java.util.stream.Collectors.toList;
+
 import java.io.File;
 import java.io.IOException;
 import java.nio.charset.Charset;
 import java.nio.file.Files;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Dictionary;
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
@@ -91,17 +96,17 @@ class InterpolationConfigurationPlugin implements ConfigurationPlugin {
     }
 
     private final BundleContext context;
-    private final File directory;
+    private final List<File> directory;
 
     private final Charset encodingCharset;
 
     InterpolationConfigurationPlugin(BundleContext bc, String dir, String fileEncoding) {
         context = bc;
         if (dir != null) {
-            directory = new File(dir);
+            directory = Stream.of(dir.split("\\s*,\\s*")).map(File::new).collect(toList());
             getLog().info("Configured directory for secrets: {}", dir);
         } else {
-            directory = null;
+            directory = Collections.emptyList();
         }
         if (fileEncoding == null) {
             encodingCharset = Charset.defaultCharset();
@@ -188,7 +193,7 @@ class InterpolationConfigurationPlugin implements ConfigurationPlugin {
     }
 
     String getVariableFromFile(final String key, final String name, final Object pid) {
-        if (directory == null) {
+        if (directory.isEmpty()) {
             getLog().warn("Cannot replace property value {} for PID {}. No directory configured via framework property " +
                     Activator.DIR_PROPERTY, key, pid);
             return null;
@@ -199,17 +204,21 @@ class InterpolationConfigurationPlugin implements ConfigurationPlugin {
             return null;
         }
 
-        File file = new File(directory, name);
-        if (!file.isFile()) {
-            getLog().warn("Cannot replace variable. Configured path is not a regular file: " + file);
+        List<File> files = directory.stream().map(d -> new File(d, name)).filter(File::exists).collect(toList());
+        if (files.stream().noneMatch(File::isFile)) {
+            getLog().warn("Cannot replace variable. Configured paths are not regular files: " + files);
             return null;
         }
 
-        if (!file.getAbsolutePath().startsWith(directory.getAbsolutePath())) {
+        if (files.stream().map(File::getAbsolutePath).noneMatch(s -> directory.stream().anyMatch(dir -> s.startsWith(dir.getAbsolutePath())))) {
             getLog().error("Illegal secret location: " + name + " Going out the directory structure is not allowed");
             return null;
         }
 
+        File file = files.stream().findFirst().orElseThrow(
+            () -> new IllegalStateException(
+                "Something went terribly wrong. This should not be possible."));
+
         byte[] bytes;
         try {
             bytes = Files.readAllBytes(file.toPath());
@@ -253,7 +262,7 @@ class InterpolationConfigurationPlugin implements ConfigurationPlugin {
      * the delimiter (avoiding splitting), unless that backslash is preceded by
      * another backslash, in which case the two backslashes are replaced by a single
      * one and the value is split after the backslash.
-     * 
+     *
      * @param value     The value to split
      * @param delimiter The delimiter
      * @return The resulting array
diff --git a/configadmin-plugins/interpolation/src/test/java/org/apache/felix/configadmin/plugin/interpolation/InterpolationConfigurationPluginTest.java b/configadmin-plugins/interpolation/src/test/java/org/apache/felix/configadmin/plugin/interpolation/InterpolationConfigurationPluginTest.java
index 7a38573..888f0a5 100644
--- a/configadmin-plugins/interpolation/src/test/java/org/apache/felix/configadmin/plugin/interpolation/InterpolationConfigurationPluginTest.java
+++ b/configadmin-plugins/interpolation/src/test/java/org/apache/felix/configadmin/plugin/interpolation/InterpolationConfigurationPluginTest.java
@@ -114,9 +114,10 @@ public class InterpolationConfigurationPluginTest {
 
     @Test
     public void testReplacement() throws Exception {
-        String rf = getClass().getResource("/testfile.txt").getFile();
+        String rf = getClass().getResource("/other/testfile.txt").getFile();
+        File file = new File(rf);
         InterpolationConfigurationPlugin plugin = new InterpolationConfigurationPlugin(null,
-                new File(rf).getParent(), null);
+                file.getParent() + "," + file.getParentFile().getParent(), null);
 
         assertEquals("xxla la layy", plugin.replace("akey", "xx$[secret:testfile.txt]yy", "apid"));
         String doesNotReplace = "xx$[" + rf + "]yy";
@@ -125,9 +126,10 @@ public class InterpolationConfigurationPluginTest {
 
     @Test
     public void testNoReplacement() throws IOException {
-        String rf = getClass().getResource("/testfile.txt").getFile();
+        String rf = getClass().getResource("/other/testfile.txt").getFile();
+        File file = new File(rf);
         InterpolationConfigurationPlugin plugin = new InterpolationConfigurationPlugin(null,
-                new File(rf).getParent(), null);
+                file.getParent() + "," + file.getParentFile().getParent(), null);
 
         assertEquals("foo", plugin.replace("akey", "foo", "apid"));
     }
@@ -187,9 +189,10 @@ public class InterpolationConfigurationPluginTest {
     public void testMultiplePlaceholders() throws Exception {
         BundleContext bc = Mockito.mock(BundleContext.class);
         Mockito.when(bc.getProperty("foo.bar")).thenReturn("hello there");
-        String rf = getClass().getResource("/testfile.txt").getFile();
-        InterpolationConfigurationPlugin plugin = new InterpolationConfigurationPlugin(bc, new File(rf).getParent(),
-                null);
+        String rf = getClass().getResource("/other/testfile.txt").getFile();
+        File file = new File(rf);
+        InterpolationConfigurationPlugin plugin = new InterpolationConfigurationPlugin(bc,
+                file.getParent() + "," + file.getParentFile().getParent(), null);
 
         assertEquals("xxhello thereyyhello therezz",
                 plugin.replace("akey", "xx$[prop:foo.bar]yy$[prop:foo.bar]zz", "apid"));
@@ -199,6 +202,21 @@ public class InterpolationConfigurationPluginTest {
     }
 
     @Test
+    public void testMultipleDirectories() throws Exception {
+        BundleContext bc = Mockito.mock(BundleContext.class);
+        String rf = getClass().getResource("/other/testfile.txt").getFile();
+        File file = new File(rf);
+        InterpolationConfigurationPlugin plugin = new InterpolationConfigurationPlugin(bc,
+                file.getParent() + "," + file.getParentFile().getParent(), null);
+
+        assertEquals("xxhello thereyyhello therezz",
+                plugin.replace("akey", "xx$[secret:foo.bar]yy$[secret:foo.bar]zz", "apid"));
+
+        assertEquals("xxla la layyhello therezz",
+                plugin.replace("akey", "xx$[secret:testfile.txt]yy$[secret:foo.bar]zz", "apid"));
+    }
+
+    @Test
     public void testNestedPlaceholders() throws Exception {
         BundleContext bc = Mockito.mock(BundleContext.class);
         Mockito.when(bc.getProperty("foo.bar")).thenReturn("hello there");
diff --git a/configadmin-plugins/interpolation/src/test/resources/foo.bar b/configadmin-plugins/interpolation/src/test/resources/foo.bar
new file mode 100644
index 0000000..d2aeeec
--- /dev/null
+++ b/configadmin-plugins/interpolation/src/test/resources/foo.bar
@@ -0,0 +1 @@
+hello there
\ No newline at end of file
diff --git a/configadmin-plugins/interpolation/src/test/resources/testfile.txt b/configadmin-plugins/interpolation/src/test/resources/other/testfile.txt
similarity index 100%
rename from configadmin-plugins/interpolation/src/test/resources/testfile.txt
rename to configadmin-plugins/interpolation/src/test/resources/other/testfile.txt