diff --git a/src/main/java/org/gaul/s3proxy/AuthenticationType.java b/src/main/java/org/gaul/s3proxy/AuthenticationType.java new file mode 100644 index 0000000..5d3cad1 --- /dev/null +++ b/src/main/java/org/gaul/s3proxy/AuthenticationType.java @@ -0,0 +1,31 @@ +/* + * Copyright 2014-2016 Andrew Gaul + * + * Licensed 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.gaul.s3proxy; + +import com.google.common.base.CaseFormat; + +public enum AuthenticationType { + AWS_V2, + AWS_V4, + AWS_V2_OR_V4, + NONE; + + static AuthenticationType fromString(String string) { + return AuthenticationType.valueOf(CaseFormat.LOWER_HYPHEN.to( + CaseFormat.UPPER_UNDERSCORE, string)); + } +} diff --git a/src/main/java/org/gaul/s3proxy/Main.java b/src/main/java/org/gaul/s3proxy/Main.java index df8e505..e031db4 100644 --- a/src/main/java/org/gaul/s3proxy/Main.java +++ b/src/main/java/org/gaul/s3proxy/Main.java @@ -84,10 +84,10 @@ public final class Main { S3ProxyConstants.PROPERTY_ENDPOINT); String secureEndpoint = properties.getProperty( S3ProxyConstants.PROPERTY_SECURE_ENDPOINT); - String s3ProxyAuthorization = properties.getProperty( + String s3ProxyAuthorizationString = properties.getProperty( S3ProxyConstants.PROPERTY_AUTHORIZATION); if ((s3ProxyEndpointString == null && secureEndpoint == null) || - s3ProxyAuthorization == null) { + s3ProxyAuthorizationString == null) { System.err.println("Properties file must contain:\n" + S3ProxyConstants.PROPERTY_AUTHORIZATION + "\n" + "and one of\n" + @@ -96,9 +96,14 @@ public final class Main { System.exit(1); } + AuthenticationType s3ProxyAuthorization = + AuthenticationType.fromString(s3ProxyAuthorizationString); String localIdentity = null; String localCredential = null; - if (s3ProxyAuthorization.equalsIgnoreCase("aws-v2")) { + switch (s3ProxyAuthorization) { + case AWS_V2: + case AWS_V4: + case AWS_V2_OR_V4: localIdentity = properties.getProperty( S3ProxyConstants.PROPERTY_IDENTITY); localCredential = properties.getProperty( @@ -107,12 +112,15 @@ public final class Main { System.err.println("Must specify both " + S3ProxyConstants.PROPERTY_IDENTITY + " and " + S3ProxyConstants.PROPERTY_CREDENTIAL + - " when using aws-v2 authentication"); + " when using authentication"); System.exit(1); } - } else if (!s3ProxyAuthorization.equalsIgnoreCase("none")) { + break; + case NONE: + break; + default: System.err.println(S3ProxyConstants.PROPERTY_AUTHORIZATION + - " must be aws-v2 or none, was: " + s3ProxyAuthorization); + " invalid value, was: " + s3ProxyAuthorization); System.exit(1); } @@ -171,7 +179,8 @@ public final class Main { s3ProxyBuilder.secureEndpoint(new URI(secureEndpoint)); } if (localIdentity != null || localCredential != null) { - s3ProxyBuilder.awsAuthentication(localIdentity, + s3ProxyBuilder.awsAuthentication( + s3ProxyAuthorization, localIdentity, localCredential); } if (keyStorePath != null || keyStorePassword != null) { diff --git a/src/main/java/org/gaul/s3proxy/S3AuthorizationHeader.java b/src/main/java/org/gaul/s3proxy/S3AuthorizationHeader.java index 4162da3..b576f24 100644 --- a/src/main/java/org/gaul/s3proxy/S3AuthorizationHeader.java +++ b/src/main/java/org/gaul/s3proxy/S3AuthorizationHeader.java @@ -30,6 +30,7 @@ final class S3AuthorizationHeader { // TODO: these fields should have accessors // CHECKSTYLE:OFF + final AuthenticationType authenticationType; final String hmacAlgorithm; final String hashAlgorithm; final String region; @@ -41,7 +42,7 @@ final class S3AuthorizationHeader { S3AuthorizationHeader(String header) { if (header.startsWith("AWS ")) { - // AWS v2 header + authenticationType = AuthenticationType.AWS_V2; hmacAlgorithm = null; hashAlgorithm = null; region = null; @@ -58,7 +59,7 @@ final class S3AuthorizationHeader { identity = identityTuple[0]; signature = identityTuple[1]; } else if (header.startsWith("AWS4-HMAC")) { - // AWS v4 header + authenticationType = AuthenticationType.AWS_V4; signature = extractSignature(header); int credentialIndex = header.indexOf(CREDENTIAL_FIELD); diff --git a/src/main/java/org/gaul/s3proxy/S3Proxy.java b/src/main/java/org/gaul/s3proxy/S3Proxy.java index cdcb6a3..6957b5f 100644 --- a/src/main/java/org/gaul/s3proxy/S3Proxy.java +++ b/src/main/java/org/gaul/s3proxy/S3Proxy.java @@ -101,7 +101,8 @@ public final class S3Proxy { } else { listenHTTPS = false; } - handler = new S3ProxyHandlerJetty(builder.blobStore, builder.identity, + handler = new S3ProxyHandlerJetty(builder.blobStore, + builder.authenticationType, builder.identity, builder.credential, Optional.fromNullable(builder.virtualHost), builder.v4MaxNonChunkedRequestSize, builder.ignoreUnknownHeaders, builder.corsAllowAll); @@ -112,6 +113,8 @@ public final class S3Proxy { private BlobStore blobStore; private URI endpoint; private URI secureEndpoint; + private AuthenticationType authenticationType = + AuthenticationType.NONE; private String identity; private String credential; private String keyStorePath; @@ -143,7 +146,9 @@ public final class S3Proxy { return this; } - public Builder awsAuthentication(String identity, String credential) { + public Builder awsAuthentication(AuthenticationType authenticationType, + String identity, String credential) { + this.authenticationType = authenticationType; this.identity = requireNonNull(identity); this.credential = requireNonNull(credential); return this; @@ -223,5 +228,4 @@ public final class S3Proxy { public void setBlobStoreLocator(BlobStoreLocator lookup) { handler.getHandler().setBlobStoreLocator(lookup); } - } diff --git a/src/main/java/org/gaul/s3proxy/S3ProxyHandler.java b/src/main/java/org/gaul/s3proxy/S3ProxyHandler.java index e6524d9..d894649 100644 --- a/src/main/java/org/gaul/s3proxy/S3ProxyHandler.java +++ b/src/main/java/org/gaul/s3proxy/S3ProxyHandler.java @@ -195,6 +195,7 @@ public class S3ProxyHandler { private static final int B2_PUT_BLOB_BUFFER_SIZE = 1024 * 1024; private final boolean anonymousIdentity; + private final AuthenticationType authenticationType; private final Optional virtualHost; private final long v4MaxNonChunkedRequestSize; private final boolean ignoreUnknownHeaders; @@ -216,7 +217,8 @@ public class S3ProxyHandler { .expireAfterWrite(10, TimeUnit.MINUTES) .build(); - public S3ProxyHandler(final BlobStore blobStore, final String identity, + public S3ProxyHandler(final BlobStore blobStore, + AuthenticationType authenticationType, final String identity, final String credential, Optional virtualHost, long v4MaxNonChunkedRequestSize, boolean ignoreUnknownHeaders, boolean corsAllowAll) { @@ -244,6 +246,7 @@ public class S3ProxyHandler { } }; } + this.authenticationType = authenticationType; this.virtualHost = requireNonNull(virtualHost); this.v4MaxNonChunkedRequestSize = v4MaxNonChunkedRequestSize; this.ignoreUnknownHeaders = ignoreUnknownHeaders; @@ -385,6 +388,30 @@ public class S3ProxyHandler { } } + switch (authHeader.authenticationType) { + case AWS_V2: + switch (authenticationType) { + case AWS_V2: + case AWS_V2_OR_V4: + break; + default: + throw new S3Exception(S3ErrorCode.INVALID_ARGUMENT); + } + break; + case AWS_V4: + switch (authenticationType) { + case AWS_V4: + case AWS_V2_OR_V4: + break; + default: + throw new S3Exception(S3ErrorCode.INVALID_ARGUMENT); + } + break; + default: + throw new IllegalArgumentException("Unhandled type: " + + authHeader.authenticationType); + } + String expectedSignature = null; if (authHeader.hmacAlgorithm == null) { expectedSignature = createAuthorizationSignature(request, diff --git a/src/main/java/org/gaul/s3proxy/S3ProxyHandlerJetty.java b/src/main/java/org/gaul/s3proxy/S3ProxyHandlerJetty.java index 8e52240..9d356bb 100644 --- a/src/main/java/org/gaul/s3proxy/S3ProxyHandlerJetty.java +++ b/src/main/java/org/gaul/s3proxy/S3ProxyHandlerJetty.java @@ -40,13 +40,14 @@ import org.jclouds.util.Throwables2; final class S3ProxyHandlerJetty extends AbstractHandler { private final S3ProxyHandler handler; - S3ProxyHandlerJetty(final BlobStore blobStore, final String identity, + S3ProxyHandlerJetty(final BlobStore blobStore, + AuthenticationType authenticationType, final String identity, final String credential, Optional virtualHost, long v4MaxNonChunkedRequestSize, boolean ignoreUnknownHeaders, boolean corsAllowAll) { - handler = new S3ProxyHandler(blobStore, identity, credential, - virtualHost, v4MaxNonChunkedRequestSize, ignoreUnknownHeaders, - corsAllowAll); + handler = new S3ProxyHandler(blobStore, authenticationType, identity, + credential, virtualHost, v4MaxNonChunkedRequestSize, + ignoreUnknownHeaders, corsAllowAll); } @Override diff --git a/src/test/java/org/gaul/s3proxy/TestUtils.java b/src/test/java/org/gaul/s3proxy/TestUtils.java index b03983e..3a26d32 100644 --- a/src/test/java/org/gaul/s3proxy/TestUtils.java +++ b/src/test/java/org/gaul/s3proxy/TestUtils.java @@ -160,6 +160,10 @@ final class TestUtils { } String endpoint = info.getProperties().getProperty( Constants.PROPERTY_ENDPOINT); + String s3ProxyAuthorizationString = info.getProperties().getProperty( + S3ProxyConstants.PROPERTY_AUTHORIZATION); + AuthenticationType s3ProxyAuthorization = + AuthenticationType.fromString(s3ProxyAuthorizationString); info.s3Identity = info.getProperties().getProperty( S3ProxyConstants.PROPERTY_IDENTITY); info.s3Credential = info.getProperties().getProperty( @@ -193,8 +197,8 @@ final class TestUtils { s3ProxyBuilder.secureEndpoint(new URI(secureEndpoint)); } if (info.getS3Identity() != null || info.getS3Credential() != null) { - s3ProxyBuilder.awsAuthentication(info.getS3Identity(), - info.getS3Credential()); + s3ProxyBuilder.awsAuthentication(s3ProxyAuthorization, + info.getS3Identity(), info.getS3Credential()); } if (keyStorePath != null || keyStorePassword != null) { s3ProxyBuilder.keyStore( diff --git a/src/test/resources/s3proxy.conf b/src/test/resources/s3proxy.conf index cea10c8..ac627e4 100644 --- a/src/test/resources/s3proxy.conf +++ b/src/test/resources/s3proxy.conf @@ -1,7 +1,7 @@ s3proxy.endpoint=http://127.0.0.1:0 s3proxy.secure-endpoint=https://127.0.0.1:0 -# authorization must be aws-v2 or none -s3proxy.authorization=aws-v2 +# authorization must be aws-v2, aws-v4, aws-v2-or-v4, or none +s3proxy.authorization=aws-v2-or-v4 s3proxy.identity=local-identity s3proxy.credential=local-credential s3proxy.keystore-path=keystore.jks