Last active
July 13, 2019 16:08
-
-
Save EliasRanz/0b3f95e3b786df69413c7120d800f0ac to your computer and use it in GitHub Desktop.
A helper class that allows you to store Google Credential files in Amazon S3, and loads it into context for Google's SDKs.
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
import java.lang.reflect.Field; | |
import java.util.Collections; | |
import java.util.Map; | |
public class SystemEnvironmentUtil { | |
public static void setEnv(Map<String, String> newenv) throws ClassNotFoundException, IllegalAccessException, NoSuchFieldException{ | |
try { | |
Class<?> processEnvironmentClass = Class.forName("java.lang.ProcessEnvironment"); | |
Field theEnvironmentField = processEnvironmentClass.getDeclaredField("theEnvironment"); | |
theEnvironmentField.setAccessible(true); | |
Map<String, String> env = (Map<String, String>) theEnvironmentField.get(null); | |
env.putAll(newenv); | |
Field theCaseInsensitiveEnvironmentField = processEnvironmentClass.getDeclaredField("theCaseInsensitiveEnvironment"); | |
theCaseInsensitiveEnvironmentField.setAccessible(true); | |
Map<String, String> cienv = (Map<String, String>) theCaseInsensitiveEnvironmentField.get(null); | |
cienv.putAll(newenv); | |
} catch (NoSuchFieldException e) { | |
Class[] classes = Collections.class.getDeclaredClasses(); | |
Map<String, String> env = System.getenv(); | |
for(Class cl : classes) { | |
if("java.util.Collections$UnmodifiableMap".equals(cl.getName())) { | |
Field field = cl.getDeclaredField("m"); | |
field.setAccessible(true); | |
Object obj = field.get(env); | |
Map<String, String> map = (Map<String, String>) obj; | |
map.clear(); | |
map.putAll(newenv); | |
} | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Goal
I have a file stored in Amazon S3 that stores my credentials for my Google Application that's utilized by Dialogflow for Natural Language Processing. I wanted to configure a Spring Bean that allowed me to just
@Autowired
the credential provider from the Dialogflow SDK. The application is running on spring-boot.Problem
Locally when I run the application I get the following exception thrown from the Google SDK.
Since the file isn't stored on disc, I have to download the file and then load it into the system's environment variables. This is the important part, because at Runtime the file doesn't exist, and Google is expecting it to be loaded at the system level, not the Java process level so it fails saying it can't find the file.
Solution
Courtesy of this stackoverflow post I was able to use their Reflection method to grab the System that is running the Java process and then adding an additional property to the environment, which then allowed Google's SDK to receive the credentials correctly. When I load the credentials into the DIalogflow SDK I simply just do it with the following...
Then I can make authenticated calls no problem.
Additional Code
AWSConfig.java
Receives it's credentials from
~/.aws/credentials
on OSX.GoogleConfig.java
Closing
If you have comments/questions let me know, or if you can think of a better method then let me know in the comments below as I'd love to be able to improve the approach that I am doing.
Note that since we're modifying things at the system level, you need to be extra careful with security so that you don't add a vulnerability onto the host system. E.g. you'll want to make sure that it's private, and you're explicitly telling it what to do rather than dynamically allowing it to make system level changes.
A solution for that would be to restrict access to the class through a private method in your config class instead of an external utility class. That would mean moving
SystemEnvironmentUtil#setEnv()
to the class that you're invoking it from, alternatively making the utility class package private so that only your configuration classes can access it. In reality most the time the only thing that might need it is a configuration class, which is specific to your application, so it can be private. You can expose things like a String field that contains your path to assets, which your configuration utilizes to something like a Builder class, if you really need to add customizations.