From b82a835dd38df4e55fa95ffc37558d377591a9bd Mon Sep 17 00:00:00 2001 From: "janek.lehr" Date: Thu, 31 Jan 2019 22:26:06 -0600 Subject: [PATCH] Replace mem data store with file data store Use a file-based data store to enable persisting the credential store beyond server restarts. --- Dockerfile | 1 + .../proxy/CachingGoogleAuthCodeFlow.java | 14 ++++---- .../nexus/proxy/ProxyDataStoreFactory.java | 33 +++++++++++++++++++ .../CloudIamAuthNexusProxyVerticleTests.java | 3 +- 4 files changed, 42 insertions(+), 9 deletions(-) create mode 100644 src/main/java/com/travelaudience/nexus/proxy/ProxyDataStoreFactory.java diff --git a/Dockerfile b/Dockerfile index 67edaa9..45b775b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,6 +14,7 @@ ENV CLOUD_IAM_AUTH_ENABLED "false" ENV JWT_REQUIRES_MEMBERSHIP_VERIFICATION "true" ENV KEYSTORE_PATH "keystore.jceks" ENV KEYSTORE_PASS "safe#passw0rd!" +ENV CREDENTIAL_STORE_PATH "/var/lib/nexus-proxy" ENV NEXUS_DOCKER_HOST "containers.example.com" ENV NEXUS_HTTP_HOST "nexus.example.com" ENV NEXUS_RUT_HEADER "X-Forwarded-User" diff --git a/src/main/java/com/travelaudience/nexus/proxy/CachingGoogleAuthCodeFlow.java b/src/main/java/com/travelaudience/nexus/proxy/CachingGoogleAuthCodeFlow.java index 2d6b79d..4e1be0f 100644 --- a/src/main/java/com/travelaudience/nexus/proxy/CachingGoogleAuthCodeFlow.java +++ b/src/main/java/com/travelaudience/nexus/proxy/CachingGoogleAuthCodeFlow.java @@ -1,9 +1,5 @@ package com.travelaudience.nexus.proxy; -import static com.google.api.services.cloudresourcemanager.CloudResourceManagerScopes.CLOUD_PLATFORM_READ_ONLY; -import static com.google.api.services.oauth2.Oauth2Scopes.USERINFO_EMAIL; -import static java.util.concurrent.TimeUnit.MILLISECONDS; - import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; import com.google.api.client.auth.oauth2.Credential; @@ -13,8 +9,6 @@ import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.json.JsonFactory; import com.google.api.client.json.jackson2.JacksonFactory; -import com.google.api.client.util.store.DataStoreFactory; -import com.google.api.client.util.store.MemoryDataStoreFactory; import com.google.api.services.cloudresourcemanager.CloudResourceManager; import com.google.api.services.cloudresourcemanager.model.Organization; import com.google.common.collect.ImmutableSet; @@ -26,13 +20,16 @@ import java.util.List; import java.util.Set; +import static com.google.api.services.cloudresourcemanager.CloudResourceManagerScopes.CLOUD_PLATFORM_READ_ONLY; +import static com.google.api.services.oauth2.Oauth2Scopes.USERINFO_EMAIL; +import static java.util.concurrent.TimeUnit.MILLISECONDS; + /** * Wraps {@link GoogleAuthorizationCodeFlow} caching authorization results and providing unchecked methods. */ public class CachingGoogleAuthCodeFlow { private static final Logger LOGGER = LoggerFactory.getLogger(CachingGoogleAuthCodeFlow.class); - private static final DataStoreFactory DATA_STORE_FACTORY = new MemoryDataStoreFactory(); private static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport(); private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance(); private static final Set SCOPES = ImmutableSet.of(CLOUD_PLATFORM_READ_ONLY, USERINFO_EMAIL); @@ -47,6 +44,7 @@ private CachingGoogleAuthCodeFlow(final int authCacheTtl, final String clientSecret, final String organizationId, final String redirectUri) throws IOException { + this.authCache = Caffeine.newBuilder() .maximumSize(4096) .expireAfterWrite(authCacheTtl, MILLISECONDS) @@ -58,7 +56,7 @@ private CachingGoogleAuthCodeFlow(final int authCacheTtl, clientSecret, SCOPES ).setDataStoreFactory( - DATA_STORE_FACTORY + ProxyDataStoreFactory.getDefaultInstance() ).setAccessType( "offline" ).setApprovalPrompt( diff --git a/src/main/java/com/travelaudience/nexus/proxy/ProxyDataStoreFactory.java b/src/main/java/com/travelaudience/nexus/proxy/ProxyDataStoreFactory.java new file mode 100644 index 0000000..95065c6 --- /dev/null +++ b/src/main/java/com/travelaudience/nexus/proxy/ProxyDataStoreFactory.java @@ -0,0 +1,33 @@ +package com.travelaudience.nexus.proxy; + +import com.google.api.client.util.store.DataStoreFactory; +import com.google.api.client.util.store.FileDataStoreFactory; + +import java.io.File; +import java.io.IOException; + +/** + * Provides a thread-safe way to get a singleton {@link DataStoreFactory} instance + */ +class ProxyDataStoreFactory { + + private static final String CREDENTIAL_STORE_PATH = System.getenv("CREDENTIAL_STORE_PATH"); + + private static final Object mutex = new Object(); + private static volatile DataStoreFactory instance; + + + static DataStoreFactory getDefaultInstance() throws IOException { + DataStoreFactory result = instance; + if (result == null) { + synchronized (mutex) { + result = instance; + if (result == null) + instance = result = new FileDataStoreFactory(new File(CREDENTIAL_STORE_PATH)); + } + } + + return result; + } + +} diff --git a/src/test/java/com/travelaudience/nexus/proxy/CloudIamAuthNexusProxyVerticleTests.java b/src/test/java/com/travelaudience/nexus/proxy/CloudIamAuthNexusProxyVerticleTests.java index 2417be1..fc22dec 100644 --- a/src/test/java/com/travelaudience/nexus/proxy/CloudIamAuthNexusProxyVerticleTests.java +++ b/src/test/java/com/travelaudience/nexus/proxy/CloudIamAuthNexusProxyVerticleTests.java @@ -29,7 +29,7 @@ @RunWith(PowerMockRunner.class) @PowerMockRunnerDelegate(VertxUnitRunner.class) -@PrepareForTest(CloudIamAuthNexusProxyVerticle.class) +@PrepareForTest({CloudIamAuthNexusProxyVerticle.class, ProxyDataStoreFactory.class}) public class CloudIamAuthNexusProxyVerticleTests { private static final String HOST = "localhost"; private static final int PORT = findRandomUnusedPort(); @@ -47,6 +47,7 @@ public class CloudIamAuthNexusProxyVerticleTests { put("CLOUD_IAM_AUTH_ENABLED", "true"); put("KEYSTORE_PATH", "keystore.jceks"); put("KEYSTORE_PASS", "safe#passw0rd!"); + put("CREDENTIAL_STORE_PATH", "build/temp"); put("NEXUS_DOCKER_HOST", "containers.example.com"); put("NEXUS_HTTP_HOST", "nexus.example.com"); put("NEXUS_RUT_HEADER", "X-Forwarded-User");