You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by da...@apache.org on 2019/07/26 09:54:24 UTC

svn commit: r1863795 - in /felix/trunk/configadmin-plugins/interpolation/src: main/java/org/apache/felix/configadmin/plugin/interpolation/ test/java/org/apache/felix/configadmin/plugin/interpolation/

Author: davidb
Date: Fri Jul 26 09:54:23 2019
New Revision: 1863795

URL: http://svn.apache.org/viewvc?rev=1863795&view=rev
Log:
Rename substitution config admin plugin to interpolation.

Added:
    felix/trunk/configadmin-plugins/interpolation/src/main/java/org/apache/felix/configadmin/plugin/interpolation/InterpolationConfigurationPlugin.java
    felix/trunk/configadmin-plugins/interpolation/src/test/java/org/apache/felix/configadmin/plugin/interpolation/InterpolationConfigurationPluginTest.java
Removed:
    felix/trunk/configadmin-plugins/interpolation/src/main/java/org/apache/felix/configadmin/plugin/interpolation/K8SSecretsConfigurationPlugin.java
    felix/trunk/configadmin-plugins/interpolation/src/test/java/org/apache/felix/configadmin/plugin/interpolation/K8SSecretsConfigurationPluginTest.java
Modified:
    felix/trunk/configadmin-plugins/interpolation/src/main/java/org/apache/felix/configadmin/plugin/interpolation/Activator.java

Modified: felix/trunk/configadmin-plugins/interpolation/src/main/java/org/apache/felix/configadmin/plugin/interpolation/Activator.java
URL: http://svn.apache.org/viewvc/felix/trunk/configadmin-plugins/interpolation/src/main/java/org/apache/felix/configadmin/plugin/interpolation/Activator.java?rev=1863795&r1=1863794&r2=1863795&view=diff
==============================================================================
--- felix/trunk/configadmin-plugins/interpolation/src/main/java/org/apache/felix/configadmin/plugin/interpolation/Activator.java (original)
+++ felix/trunk/configadmin-plugins/interpolation/src/main/java/org/apache/felix/configadmin/plugin/interpolation/Activator.java Fri Jul 26 09:54:23 2019
@@ -29,7 +29,7 @@ public class Activator implements Bundle
     static final String DIR_PROPERTY = "org.apache.felix.configadmin.plugin.interpolation.dir";
     static final int PLUGIN_RANKING = 500;
 
-    static final Logger LOG = LoggerFactory.getLogger(K8SSecretsConfigurationPlugin.class);
+    static final Logger LOG = LoggerFactory.getLogger(InterpolationConfigurationPlugin.class);
 
     @Override
     public void start(BundleContext context) throws Exception {
@@ -39,7 +39,7 @@ public class Activator implements Bundle
             return;
         }
 
-        ConfigurationPlugin plugin = new K8SSecretsConfigurationPlugin(directory);
+        ConfigurationPlugin plugin = new InterpolationConfigurationPlugin(directory);
         Dictionary<String, Object> props = new Hashtable<>();
         props.put(ConfigurationPlugin.CM_RANKING, PLUGIN_RANKING);
         props.put(DIR_PROPERTY, directory);

Added: felix/trunk/configadmin-plugins/interpolation/src/main/java/org/apache/felix/configadmin/plugin/interpolation/InterpolationConfigurationPlugin.java
URL: http://svn.apache.org/viewvc/felix/trunk/configadmin-plugins/interpolation/src/main/java/org/apache/felix/configadmin/plugin/interpolation/InterpolationConfigurationPlugin.java?rev=1863795&view=auto
==============================================================================
--- felix/trunk/configadmin-plugins/interpolation/src/main/java/org/apache/felix/configadmin/plugin/interpolation/InterpolationConfigurationPlugin.java (added)
+++ felix/trunk/configadmin-plugins/interpolation/src/main/java/org/apache/felix/configadmin/plugin/interpolation/InterpolationConfigurationPlugin.java Fri Jul 26 09:54:23 2019
@@ -0,0 +1,131 @@
+/*
+ * 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.felix.configadmin.plugin.interpolation;
+
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.cm.ConfigurationPlugin;
+import org.slf4j.Logger;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.function.Function;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+class InterpolationConfigurationPlugin implements ConfigurationPlugin {
+    private static final String PREFIX = "$[";
+    private static final String SUFFIX = "]";
+
+    private static final String SECRET_PREFIX = PREFIX + "secret:";
+    private static final Pattern SECRET_PATTERN = createPattern(SECRET_PREFIX);
+    private static final String ENV_PREFIX = PREFIX + "env:";
+    private static final Pattern ENV_PATTERN = createPattern(ENV_PREFIX);
+
+    private static Pattern createPattern(String prefix) {
+        return Pattern.compile("\\Q" + prefix + "\\E.+\\Q" + SUFFIX + "\\E");
+    }
+
+    private final File directory;
+
+    InterpolationConfigurationPlugin(String dir) {
+        directory = new File(dir);
+        getLog().info("Configured directory for secrets: {}", dir);
+    }
+
+    private Logger getLog() {
+        return Activator.LOG;
+    }
+
+    @Override
+    public void modifyConfiguration(ServiceReference<?> reference, Dictionary<String, Object> properties) {
+        for (Enumeration<String> keys = properties.keys(); keys.hasMoreElements(); ) {
+            String key = keys.nextElement();
+            Object val = properties.get(key);
+            if (val instanceof String) {
+                String sv = (String) val;
+                int idx = sv.indexOf(PREFIX);
+                if (idx != -1) {
+                    String varStart = sv.substring(idx);
+                    Object pid = properties.get(Constants.SERVICE_PID);
+                    Object newVal = null;
+                    if (varStart.startsWith(SECRET_PREFIX)) {
+                        newVal = replaceVariablesFromFile(key, sv, pid);
+                    } else if (varStart.startsWith(ENV_PREFIX)) {
+                        newVal = replaceVariablesFromEnvironment(key, sv, pid);
+                    }
+
+                    if (newVal != null)
+                        properties.put(key, newVal);
+
+                    getLog().info("Replaced value of configuration property '{}' for PID {}", key, pid);
+                }
+            }
+        }
+    }
+
+    Object replaceVariablesFromEnvironment(final String key, final String value, final Object pid) {
+        return replaceVariables(ENV_PREFIX, ENV_PATTERN, key, value, pid, n -> System.getenv(n));
+    }
+
+    Object replaceVariablesFromFile(final String key, final String value, final Object pid) {
+        return replaceVariables(SECRET_PREFIX, SECRET_PATTERN, key, value, pid, n -> {
+            if (n.contains("..")) {
+                getLog().error("Illegal secret location: " + n + " Going up in the directory structure is not allowed");
+                return null;
+            }
+
+            File file = new File(directory, n);
+            if (!file.isFile()) {
+                getLog().warn("Cannot replace variable. Configured path is not a regular file: " + file);
+                return null;
+            }
+            byte[] bytes;
+            try {
+                bytes = Files.readAllBytes(file.toPath());
+            } catch (IOException e) {
+                getLog().error("Problem replacing configuration property '{}' for PID {} from file {}",
+                        key, pid, file, e);
+
+                return null;
+            }
+            return new String(bytes).trim();
+        });
+    }
+
+    Object replaceVariables(final String prefix, final Pattern pattern,
+            final String key, final String value, final Object pid,
+            final Function<String, String> valueSource) {
+        final Matcher m = pattern.matcher(value);
+        final StringBuffer sb = new StringBuffer();
+        while (m.find()) {
+            final String var = m.group();
+
+            final int len = var.length();
+            final String varName = var.substring(prefix.length(), len - SUFFIX.length());
+            String replacement = valueSource.apply(varName);
+            if (replacement != null)
+                m.appendReplacement(sb, Matcher.quoteReplacement(replacement));
+        }
+        m.appendTail(sb);
+
+        return sb.toString();
+    }
+}

Added: felix/trunk/configadmin-plugins/interpolation/src/test/java/org/apache/felix/configadmin/plugin/interpolation/InterpolationConfigurationPluginTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/configadmin-plugins/interpolation/src/test/java/org/apache/felix/configadmin/plugin/interpolation/InterpolationConfigurationPluginTest.java?rev=1863795&view=auto
==============================================================================
--- felix/trunk/configadmin-plugins/interpolation/src/test/java/org/apache/felix/configadmin/plugin/interpolation/InterpolationConfigurationPluginTest.java (added)
+++ felix/trunk/configadmin-plugins/interpolation/src/test/java/org/apache/felix/configadmin/plugin/interpolation/InterpolationConfigurationPluginTest.java Fri Jul 26 09:54:23 2019
@@ -0,0 +1,101 @@
+/*
+ * 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.felix.configadmin.plugin.interpolation;
+
+import org.apache.felix.configadmin.plugin.interpolation.InterpolationConfigurationPlugin;
+import org.junit.Test;
+import org.osgi.framework.Constants;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import static org.junit.Assert.assertEquals;
+
+public class InterpolationConfigurationPluginTest {
+    @Test
+    public void testModifyConfiguration() throws Exception {
+        String envUser = System.getenv("USER");
+        String userVar;
+        if (envUser == null) {
+            envUser = System.getenv("USERNAME"); // maybe we're on Windows
+            userVar = "USERNAME";
+        } else {
+            userVar = "USER";
+        }
+
+        String rf = getClass().getResource("/testfile").getFile();
+
+        InterpolationConfigurationPlugin plugin = new InterpolationConfigurationPlugin(
+                new File(rf).getParent());
+
+        Dictionary<String, Object> dict = new Hashtable<>();
+        dict.put("foo", "bar");
+        dict.put("replaced", "$[secret:testfile]");
+        dict.put("cur.user", "$[env:" + userVar + "]");
+        dict.put("intval", 999);
+        dict.put(Constants.SERVICE_PID, "my.service");
+        plugin.modifyConfiguration(null, dict);
+
+        assertEquals(5, dict.size());
+        assertEquals("bar", dict.get("foo"));
+        assertEquals("line1\nline2", dict.get("replaced"));
+        assertEquals(envUser, dict.get("cur.user"));
+        assertEquals("my.service", dict.get(Constants.SERVICE_PID));
+        assertEquals(999, dict.get("intval"));
+    }
+
+    @Test
+    public void testSubdirReplacement() throws Exception {
+        String rf = getClass().getResource("/sub/sub2/testfile2").getFile();
+
+        InterpolationConfigurationPlugin plugin = new InterpolationConfigurationPlugin(
+                new File(rf).getParentFile().getParent());
+
+        Dictionary<String, Object> dict = new Hashtable<>();
+        dict.put("substed", "$[secret:sub2/testfile2]");
+        dict.put("not1", "$[secret:../testfile]");
+        dict.put("not2", "$[secret:sub2/../../testfile.txt]");
+        plugin.modifyConfiguration(null, dict);
+
+        assertEquals(3, dict.size());
+        assertEquals("the_content", dict.get("substed"));
+        assertEquals("$[secret:../testfile]", dict.get("not1"));
+        assertEquals("$[secret:sub2/../../testfile.txt]", dict.get("not2"));
+    }
+
+    @Test
+    public void testReplacement() throws Exception {
+        String rf = getClass().getResource("/testfile.txt").getFile();
+        InterpolationConfigurationPlugin plugin = new InterpolationConfigurationPlugin(
+                new File(rf).getParent());
+
+        assertEquals("xxla la layy", plugin.replaceVariablesFromFile("akey", "xx$[secret:testfile.txt]yy", "apid"));
+        String doesNotReplace = "xx$[" + rf + "]yy";
+        assertEquals(doesNotReplace, plugin.replaceVariablesFromFile("akey", doesNotReplace, "apid"));
+    }
+
+    @Test
+    public void testNoReplacement() throws IOException {
+        String rf = getClass().getResource("/testfile.txt").getFile();
+        InterpolationConfigurationPlugin plugin = new InterpolationConfigurationPlugin(
+                new File(rf).getParent());
+
+        assertEquals("foo", plugin.replaceVariablesFromFile("akey", "foo", "apid"));
+    }
+}