From b134e814067ec8abd39e05166691acbe78e27822 Mon Sep 17 00:00:00 2001 From: Steve Date: Tue, 26 Sep 2023 13:38:22 +0200 Subject: [PATCH] Add Access-Control-Allow-Credentials header Fixes #415 --- Dockerfile | 1 + README.md | 1 + .../gaul/s3proxy/CrossOriginResourceSharing.java | 14 ++++++++++++-- src/main/java/org/gaul/s3proxy/S3Proxy.java | 6 +++++- .../java/org/gaul/s3proxy/S3ProxyConstants.java | 2 ++ src/main/java/org/gaul/s3proxy/S3ProxyHandler.java | 3 +++ src/main/resources/run-docker-container.sh | 1 + .../CrossOriginResourceSharingResponseTest.java | 3 +++ .../CrossOriginResourceSharingRuleTest.java | 11 +++++++++-- 9 files changed, 37 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index cac39f7f..32942c85 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,6 +21,7 @@ ENV \ S3PROXY_CORS_ALLOW_ORIGINS="" \ S3PROXY_CORS_ALLOW_METHODS="" \ S3PROXY_CORS_ALLOW_HEADERS="" \ + S3PROXY_CORS_ALLOW_CREDENTIAL="" \ S3PROXY_IGNORE_UNKNOWN_HEADERS="false" \ S3PROXY_ENCRYPTED_BLOBSTORE="" \ S3PROXY_ENCRYPTED_BLOBSTORE_PASSWORD="" \ diff --git a/README.md b/README.md index ab13d096..b2f2724f 100644 --- a/README.md +++ b/README.md @@ -140,6 +140,7 @@ file (and corresponding ENV variables for Docker): s3proxy.cors-allow-origins=https://example\.com https://.+\.example\.com https://example\.cloud s3proxy.cors-allow-methods=GET PUT s3proxy.cors-allow-headers=Accept Content-Type +s3proxy.cors-allow-credential=true ``` CORS cannot be configured per bucket. `s3proxy.cors-allow-all=true` will accept any origin and header. diff --git a/src/main/java/org/gaul/s3proxy/CrossOriginResourceSharing.java b/src/main/java/org/gaul/s3proxy/CrossOriginResourceSharing.java index 7bbb0d52..b6278e71 100644 --- a/src/main/java/org/gaul/s3proxy/CrossOriginResourceSharing.java +++ b/src/main/java/org/gaul/s3proxy/CrossOriginResourceSharing.java @@ -40,6 +40,7 @@ public final class CrossOriginResourceSharing { private static final String HEADER_VALUE_SEPARATOR = ", "; private static final String ALLOW_ANY_ORIGIN = "*"; private static final String ALLOW_ANY_HEADER = "*"; + private static final String ALLOW_CREDENTIALS = "true"; private static final Logger logger = LoggerFactory.getLogger( CrossOriginResourceSharing.class); @@ -50,16 +51,18 @@ public final class CrossOriginResourceSharing { private final Set allowedOrigins; private final Set allowedMethods; private final Set allowedHeaders; + private final String allowCredentials; public CrossOriginResourceSharing() { // CORS Allow all this(Lists.newArrayList(ALLOW_ANY_ORIGIN), SUPPORTED_METHODS, - Lists.newArrayList(ALLOW_ANY_HEADER)); + Lists.newArrayList(ALLOW_ANY_HEADER), ""); } public CrossOriginResourceSharing(Collection allowedOrigins, Collection allowedMethods, - Collection allowedHeaders) { + Collection allowedHeaders, + String allowCredentials) { Set allowedPattern = new HashSet(); boolean anyOriginAllowed = false; @@ -92,9 +95,12 @@ public CrossOriginResourceSharing(Collection allowedOrigins, this.allowedHeadersRaw = Joiner.on(HEADER_VALUE_SEPARATOR).join( this.allowedHeaders); + this.allowCredentials = allowCredentials; + logger.info("CORS allowed origins: {}", allowedOrigins); logger.info("CORS allowed methods: {}", allowedMethods); logger.info("CORS allowed headers: {}", allowedHeaders); + logger.info("CORS allow credentials: {}", allowCredentials); } public String getAllowedMethods() { @@ -166,6 +172,10 @@ public boolean isEveryHeaderAllowed(String headers) { return result; } + public boolean isAllowCredentials() { + return ALLOW_CREDENTIALS.equals(allowCredentials); + } + @Override public boolean equals(Object object) { if (this == object) { diff --git a/src/main/java/org/gaul/s3proxy/S3Proxy.java b/src/main/java/org/gaul/s3proxy/S3Proxy.java index 1dd4494f..af173cfa 100644 --- a/src/main/java/org/gaul/s3proxy/S3Proxy.java +++ b/src/main/java/org/gaul/s3proxy/S3Proxy.java @@ -267,6 +267,9 @@ public static Builder fromProperties(Properties properties) S3ProxyConstants.PROPERTY_CORS_ALLOW_METHODS, ""); String corsAllowHeaders = properties.getProperty( S3ProxyConstants.PROPERTY_CORS_ALLOW_HEADERS, ""); + String allowCredentials = properties.getProperty( + S3ProxyConstants.PROPERTY_CORS_ALLOW_CREDENTIAL, ""); + Splitter splitter = Splitter.on(" ").trimResults() .omitEmptyStrings(); @@ -285,7 +288,8 @@ public static Builder fromProperties(Properties properties) builder.corsRules(new CrossOriginResourceSharing( Lists.newArrayList(splitter.split(corsAllowOrigins)), Lists.newArrayList(splitter.split(corsAllowMethods)), - Lists.newArrayList(splitter.split(corsAllowHeaders)))); + Lists.newArrayList(splitter.split(corsAllowHeaders)), + allowCredentials)); } String jettyMaxThreads = properties.getProperty( diff --git a/src/main/java/org/gaul/s3proxy/S3ProxyConstants.java b/src/main/java/org/gaul/s3proxy/S3ProxyConstants.java index 9936343a..9e0f71af 100644 --- a/src/main/java/org/gaul/s3proxy/S3ProxyConstants.java +++ b/src/main/java/org/gaul/s3proxy/S3ProxyConstants.java @@ -40,6 +40,8 @@ public final class S3ProxyConstants { "s3proxy.cors-allow-methods"; public static final String PROPERTY_CORS_ALLOW_HEADERS = "s3proxy.cors-allow-headers"; + public static final String PROPERTY_CORS_ALLOW_CREDENTIAL = + "s3proxy.cors-allow-credential"; public static final String PROPERTY_CREDENTIAL = "s3proxy.credential"; public static final String PROPERTY_IGNORE_UNKNOWN_HEADERS = diff --git a/src/main/java/org/gaul/s3proxy/S3ProxyHandler.java b/src/main/java/org/gaul/s3proxy/S3ProxyHandler.java index b4755d07..3396ebc7 100644 --- a/src/main/java/org/gaul/s3proxy/S3ProxyHandler.java +++ b/src/main/java/org/gaul/s3proxy/S3ProxyHandler.java @@ -2994,6 +2994,9 @@ private void addCorsResponseHeader(HttpServletRequest request, corsRules.getAllowedOrigin(corsOrigin)); response.addHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, corsRules.getAllowedMethods()); + if (corsRules.isAllowCredentials()) { + response.addHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true"); + } } } diff --git a/src/main/resources/run-docker-container.sh b/src/main/resources/run-docker-container.sh index 2326d8c6..d70dd57b 100755 --- a/src/main/resources/run-docker-container.sh +++ b/src/main/resources/run-docker-container.sh @@ -15,6 +15,7 @@ exec java \ -Ds3proxy.cors-allow-origins="${S3PROXY_CORS_ALLOW_ORIGINS}" \ -Ds3proxy.cors-allow-methods="${S3PROXY_CORS_ALLOW_METHODS}" \ -Ds3proxy.cors-allow-headers="${S3PROXY_CORS_ALLOW_HEADERS}" \ + -Ds3proxy.cors-allow-credential="${S3PROXY_CORS_ALLOW_CREDENTIAL}" \ -Ds3proxy.ignore-unknown-headers="${S3PROXY_IGNORE_UNKNOWN_HEADERS}" \ -Ds3proxy.encrypted-blobstore="${S3PROXY_ENCRYPTED_BLOBSTORE}" \ -Ds3proxy.encrypted-blobstore-password="${S3PROXY_ENCRYPTED_BLOBSTORE_PASSWORD}" \ diff --git a/src/test/java/org/gaul/s3proxy/CrossOriginResourceSharingResponseTest.java b/src/test/java/org/gaul/s3proxy/CrossOriginResourceSharingResponseTest.java index 1c33f33a..c9d0c83d 100644 --- a/src/test/java/org/gaul/s3proxy/CrossOriginResourceSharingResponseTest.java +++ b/src/test/java/org/gaul/s3proxy/CrossOriginResourceSharingResponseTest.java @@ -303,6 +303,9 @@ public void testCorsPreflightPublicRead() throws Exception { assertThat(response.getFirstHeader( HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS).getValue()) .isEqualTo("Accept, Content-Type"); + assertThat(response.getFirstHeader( + HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS)) + .isNull(); } @Test diff --git a/src/test/java/org/gaul/s3proxy/CrossOriginResourceSharingRuleTest.java b/src/test/java/org/gaul/s3proxy/CrossOriginResourceSharingRuleTest.java index 0fb0ca48..6c147af8 100644 --- a/src/test/java/org/gaul/s3proxy/CrossOriginResourceSharingRuleTest.java +++ b/src/test/java/org/gaul/s3proxy/CrossOriginResourceSharingRuleTest.java @@ -38,9 +38,10 @@ public void setUp() throws Exception { "https://.+\\.example\\.com", "https://example\\.cloud"), Lists.newArrayList("GET", "PUT"), - Lists.newArrayList("Accept", "Content-Type")); + Lists.newArrayList("Accept", "Content-Type"), + "true"); // CORS disabled - corsOff = new CrossOriginResourceSharing(null, null, null); + corsOff = new CrossOriginResourceSharing(null, null, null, null); } @Test @@ -174,4 +175,10 @@ public void testCorsCfgHeader() throws Exception { assertThat(corsCfg.isEveryHeaderAllowed(probe)) .as("check '%s' as header", probe).isTrue(); } + + @Test + public void testAllowCredentials() { + assertThat(corsOff.isAllowCredentials()).isFalse(); + assertThat(corsCfg.isAllowCredentials()).isTrue(); + } }