-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
JAVAFICATION: Move env/secret variable substitution to java
This is a partial port that required writing our own version of ruby's gsub. Java has `String#replaceAll`, but that doesn't let you pass in a function to do the replacement.
- Loading branch information
Showing
8 changed files
with
244 additions
and
76 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
42 changes: 42 additions & 0 deletions
42
logstash-core/src/main/java/org/logstash/common/SubstitutionVariables.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package org.logstash.common; | ||
|
||
import org.apache.logging.log4j.LogManager; | ||
import org.apache.logging.log4j.Logger; | ||
import org.logstash.secret.SecretIdentifier; | ||
import org.logstash.secret.store.SecretStore; | ||
import org.logstash.secret.store.SecretStoreFactory; | ||
|
||
import java.util.Map; | ||
import java.util.regex.Pattern; | ||
|
||
public class SubstitutionVariables { | ||
private static final Logger logger = LogManager.getLogger(SubstitutionVariables.class); | ||
|
||
private static Pattern SUBSTITUTION_PLACEHOLDER_REGEX = Pattern.compile("\\$\\{(?<name>[a-zA-Z_.][a-zA-Z0-9_.]*)(:(?<default>[^}]*))?\\}"); | ||
|
||
static String replacePlaceholders(final String input, | ||
final Map<String, String> environmentVariables, | ||
final SecretStore secretStore) { | ||
|
||
return Util.gsub(input, SUBSTITUTION_PLACEHOLDER_REGEX, (matchResult) -> { | ||
String name = matchResult.group(1); | ||
String defaultValue = matchResult.group(3); | ||
|
||
if (secretStore != null) { | ||
byte[] secretValue = secretStore.retrieveSecret(new SecretIdentifier(name)); | ||
if (secretValue != null) return new String(secretValue); | ||
} | ||
|
||
String envValue = environmentVariables.getOrDefault(name, defaultValue); | ||
if (envValue != null) return envValue; | ||
|
||
String errMsg = String.format( | ||
"Cannot evaluate `%s`. Replacement variable `%s` is not defined in a Logstash secret store " + | ||
"or as an Environment entry and there is no default value given.", | ||
matchResult.group(0), name); | ||
throw new MissingSubstitutionVariableError(); | ||
}); | ||
} | ||
|
||
static class MissingSubstitutionVariableError extends RuntimeException {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
51 changes: 51 additions & 0 deletions
51
logstash-core/src/test/java/org/logstash/common/SubstitutionVariablesTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package org.logstash.common; | ||
|
||
import org.junit.Test; | ||
import org.logstash.secret.MemoryStore; | ||
import org.logstash.secret.SecretIdentifier; | ||
import org.logstash.secret.store.SecretStore; | ||
|
||
import java.util.Collections; | ||
|
||
import static org.junit.Assert.*; | ||
|
||
public class SubstitutionVariablesTest { | ||
@Test | ||
public void substituteDefaultTest() { | ||
assertEquals( | ||
"Some bar Text", | ||
SubstitutionVariables.replacePlaceholders("Some ${foo:bar} Text", Collections.emptyMap(), null) | ||
); | ||
} | ||
|
||
@Test | ||
public void substituteEnvMatchTest() { | ||
assertEquals( | ||
"Some env Text", | ||
SubstitutionVariables.replacePlaceholders( | ||
"Some ${foo:bar} Text", | ||
Collections.singletonMap("foo", "env"), | ||
null | ||
) | ||
); | ||
} | ||
|
||
@Test | ||
public void substituteSecretMatchTest() { | ||
SecretStore secretStore = new MemoryStore(); | ||
SecretIdentifier identifier = new SecretIdentifier("foo"); | ||
String secretValue = "SuperSekret"; | ||
secretStore.persistSecret(identifier, secretValue.getBytes()); | ||
|
||
assertEquals( | ||
"Some " + secretValue + " Text", | ||
SubstitutionVariables.replacePlaceholders( | ||
"Some ${foo:bar} Text", | ||
// Tests precedence over the env as well | ||
Collections.singletonMap("foo", "env"), | ||
secretStore | ||
) | ||
); | ||
} | ||
|
||
} |
37 changes: 37 additions & 0 deletions
37
logstash-core/src/test/java/org/logstash/common/UtilTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package org.logstash.common; | ||
|
||
import org.junit.Test; | ||
|
||
import static org.junit.Assert.*; | ||
|
||
public class UtilTest { | ||
|
||
@Test | ||
public void gsubSimple() { | ||
assertEquals( | ||
"fooREPLACEbarREPLACEbaz", | ||
Util.gsub( | ||
"fooXbarXbaz", | ||
"X", | ||
(mRes) -> "REPLACE" | ||
) | ||
); | ||
} | ||
|
||
@Test | ||
public void gsubGroups() { | ||
assertEquals( | ||
"fooYbarZbaz", | ||
Util.gsub( | ||
"foo${Y}bar${Z}baz", | ||
"\\$\\{(.)\\}", (mRes) -> mRes.group(1))); | ||
} | ||
|
||
@Test | ||
public void gsubNoMatch() { | ||
assertEquals( | ||
"foobarbaz", | ||
Util.gsub("foobarbaz", "XXX", (mRes) -> "") | ||
); | ||
} | ||
} |
64 changes: 64 additions & 0 deletions
64
logstash-core/src/test/java/org/logstash/secret/MemoryStore.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
package org.logstash.secret; | ||
|
||
import org.logstash.secret.store.SecretStore; | ||
import org.logstash.secret.store.SecureConfig; | ||
|
||
import java.nio.ByteBuffer; | ||
import java.nio.charset.StandardCharsets; | ||
import java.util.Collection; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
|
||
import static org.logstash.secret.store.SecretStoreFactory.LOGSTASH_MARKER; | ||
|
||
/** | ||
* Valid alternate implementation of secret store | ||
*/ | ||
public class MemoryStore implements SecretStore { | ||
|
||
Map<SecretIdentifier, ByteBuffer> secrets = new HashMap(1); | ||
|
||
public MemoryStore() { | ||
persistSecret(LOGSTASH_MARKER, LOGSTASH_MARKER.getKey().getBytes(StandardCharsets.UTF_8)); | ||
} | ||
|
||
@Override | ||
public SecretStore create(SecureConfig secureConfig) { | ||
return this; | ||
} | ||
|
||
@Override | ||
public void delete(SecureConfig secureConfig) { | ||
secrets.clear(); | ||
} | ||
|
||
@Override | ||
public SecretStore load(SecureConfig secureConfig) { | ||
return this; | ||
} | ||
|
||
@Override | ||
public boolean exists(SecureConfig secureConfig) { | ||
return true; | ||
} | ||
|
||
@Override | ||
public Collection<SecretIdentifier> list() { | ||
return secrets.keySet(); | ||
} | ||
|
||
@Override | ||
public void persistSecret(SecretIdentifier id, byte[] secret) { | ||
secrets.put(id, ByteBuffer.wrap(secret)); | ||
} | ||
|
||
@Override | ||
public void purgeSecret(SecretIdentifier id) { | ||
secrets.remove(id); | ||
} | ||
|
||
@Override | ||
public byte[] retrieveSecret(SecretIdentifier id) { | ||
return secrets.get(id).array(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters