diff --git a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/VirtualHostStyleFilter.java b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/VirtualHostStyleFilter.java index f1d52a6283..d4e7547907 100644 --- a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/VirtualHostStyleFilter.java +++ b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/VirtualHostStyleFilter.java @@ -34,6 +34,7 @@ import com.google.common.annotations.VisibleForTesting; import org.apache.hadoop.fs.InvalidRequestException; import org.apache.hadoop.hdds.conf.OzoneConfiguration; +import org.apache.hadoop.ozone.s3.header.AuthenticationHeaderParser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -52,11 +53,18 @@ public class VirtualHostStyleFilter implements ContainerRequestFilter { @Inject private OzoneConfiguration conf; + + @Inject + private AuthenticationHeaderParser authenticationHeaderParser; + private String[] domains; @Override public void filter(ContainerRequestContext requestContext) throws IOException { + + authenticationHeaderParser.setAuthHeader(requestContext.getHeaderString( + HttpHeaders.AUTHORIZATION)); domains = conf.getTrimmedStrings(OZONE_S3G_DOMAIN_NAME); if (domains.length == 0) { @@ -140,4 +148,9 @@ private String getDomainName(String host) { return match; } + @VisibleForTesting + public void setAuthenticationHeaderParser(AuthenticationHeaderParser parser) { + this.authenticationHeaderParser = parser; + } + } diff --git a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/BucketEndpoint.java b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/BucketEndpoint.java index 453cecce13..fca251d9bb 100644 --- a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/BucketEndpoint.java +++ b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/BucketEndpoint.java @@ -36,7 +36,6 @@ import java.io.InputStream; import java.time.Instant; import java.util.Iterator; -import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response.ResponseBuilder; import org.apache.hadoop.ozone.client.OzoneBucket; @@ -151,7 +150,7 @@ public Response list( public Response put(@PathParam("bucket") String bucketName, @Context HttpHeaders httpHeaders) throws IOException, OS3Exception { - String userName = parseUsername(httpHeaders); + String userName = getAuthenticationHeaderParser().getAccessKeyID(); String location = createS3Bucket(userName, bucketName); diff --git a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/EndpointBase.java b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/EndpointBase.java index 148bf69076..a5af7f13e0 100644 --- a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/EndpointBase.java +++ b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/EndpointBase.java @@ -19,8 +19,6 @@ import javax.inject.Inject; import javax.ws.rs.NotFoundException; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.HttpHeaders; import java.io.IOException; import org.apache.hadoop.ozone.client.OzoneBucket; @@ -28,8 +26,7 @@ import org.apache.hadoop.ozone.client.OzoneVolume; import org.apache.hadoop.ozone.s3.exception.OS3Exception; import org.apache.hadoop.ozone.s3.exception.S3ErrorTable; -import org.apache.hadoop.ozone.s3.header.AuthorizationHeaderV2; -import org.apache.hadoop.ozone.s3.header.AuthorizationHeaderV4; +import org.apache.hadoop.ozone.s3.header.AuthenticationHeaderParser; import com.google.common.annotations.VisibleForTesting; import org.slf4j.Logger; @@ -46,6 +43,9 @@ public class EndpointBase { @Inject private OzoneClient client; + @Inject + private AuthenticationHeaderParser authenticationHeaderParser; + protected OzoneBucket getBucket(String volumeName, String bucketName) throws IOException { return getVolume(volumeName).getBucket(bucketName); @@ -172,43 +172,17 @@ public String getOzoneBucketName(String s3BucketName) throws IOException { return client.getObjectStore().getOzoneBucketName(s3BucketName); } - /** - * Retrieve the username based on the authorization header. - * - * @param httpHeaders - * @return Identified username - * @throws OS3Exception - */ - public String parseUsername( - @Context HttpHeaders httpHeaders) throws OS3Exception { - String auth = httpHeaders.getHeaderString("Authorization"); - LOG.info("Auth header string {}", auth); + public AuthenticationHeaderParser getAuthenticationHeaderParser() { + return authenticationHeaderParser; + } - if (auth == null) { - // In this case, adding resource as Authorization, need to revisit in - // future if it needs to be changed. - throw S3ErrorTable - .newError(S3ErrorTable.MALFORMED_HEADER, "Authorization"); - } - - String userName; - if (auth.startsWith("AWS4")) { - LOG.info("V4 Header {}", auth); - AuthorizationHeaderV4 authorizationHeader = new AuthorizationHeaderV4( - auth); - userName = authorizationHeader.getAccessKeyID().toLowerCase(); - } else { - LOG.info("V2 Header {}", auth); - AuthorizationHeaderV2 authorizationHeader = new AuthorizationHeaderV2( - auth); - userName = authorizationHeader.getAccessKeyID().toLowerCase(); - } - return userName; + @VisibleForTesting + public void setAuthenticationHeaderParser(AuthenticationHeaderParser parser) { + this.authenticationHeaderParser = parser; } @VisibleForTesting public void setClient(OzoneClient ozoneClient) { this.client = ozoneClient; } - } diff --git a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/RootEndpoint.java b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/RootEndpoint.java index 4de26f3878..1b0e8768a7 100644 --- a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/RootEndpoint.java +++ b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/RootEndpoint.java @@ -20,8 +20,6 @@ import javax.ws.rs.GET; import javax.ws.rs.NotFoundException; import javax.ws.rs.Path; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.HttpHeaders; import java.io.IOException; import java.time.Instant; import java.util.Iterator; @@ -50,12 +48,12 @@ public class RootEndpoint extends EndpointBase { * for more details. */ @GET - public ListBucketResponse get(@Context HttpHeaders headers) + public ListBucketResponse get() throws OS3Exception, IOException { OzoneVolume volume; ListBucketResponse response = new ListBucketResponse(); - String volumeName = "s3" + parseUsername(headers).toLowerCase(); + String volumeName = "s3" + getAuthenticationHeaderParser().getAccessKeyID(); try { //TODO: we need a specific s3bucketlist endpoint instead // of reimplement the naming convention here diff --git a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/header/AuthenticationHeaderParser.java b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/header/AuthenticationHeaderParser.java new file mode 100644 index 0000000000..1ca50cba25 --- /dev/null +++ b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/header/AuthenticationHeaderParser.java @@ -0,0 +1,61 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.hadoop.ozone.s3.header; + +import org.apache.hadoop.ozone.s3.exception.OS3Exception; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.enterprise.context.RequestScoped; + +/** + * Authentication Header parser to parse HttpHeader Authentication. + */ +@RequestScoped +public class AuthenticationHeaderParser { + + private final static Logger LOG = LoggerFactory.getLogger( + AuthenticationHeaderParser.class); + + private String authHeader; + private String accessKeyID; + + public void parse() throws OS3Exception { + if (authHeader.startsWith("AWS4")) { + LOG.debug("V4 Header {}", authHeader); + AuthorizationHeaderV4 authorizationHeader = new AuthorizationHeaderV4( + authHeader); + accessKeyID = authorizationHeader.getAccessKeyID().toLowerCase(); + } else { + LOG.debug("V2 Header {}", authHeader); + AuthorizationHeaderV2 authorizationHeader = new AuthorizationHeaderV2( + authHeader); + accessKeyID = authorizationHeader.getAccessKeyID().toLowerCase(); + } + } + + public String getAccessKeyID() throws OS3Exception { + parse(); + return accessKeyID; + } + + public void setAuthHeader(String header) { + this.authHeader = header; + } +} diff --git a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/TestVirtualHostStyleFilter.java b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/TestVirtualHostStyleFilter.java index 3fe6e1a50e..1b706da9b5 100644 --- a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/TestVirtualHostStyleFilter.java +++ b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/TestVirtualHostStyleFilter.java @@ -19,6 +19,7 @@ import org.apache.hadoop.fs.InvalidRequestException; import org.apache.hadoop.hdds.conf.OzoneConfiguration; +import org.apache.hadoop.ozone.s3.header.AuthenticationHeaderParser; import org.apache.hadoop.test.GenericTestUtils; import org.glassfish.jersey.internal.PropertiesDelegate; import org.glassfish.jersey.server.ContainerRequest; @@ -41,6 +42,7 @@ public class TestVirtualHostStyleFilter { private static OzoneConfiguration conf; private static String s3HttpAddr; + private AuthenticationHeaderParser authenticationHeaderParser; @Before public void setup() { @@ -48,6 +50,8 @@ public void setup() { s3HttpAddr = "localhost:9878"; conf.set(S3GatewayConfigKeys.OZONE_S3G_HTTP_ADDRESS_KEY, s3HttpAddr); conf.set(S3GatewayConfigKeys.OZONE_S3G_DOMAIN_NAME, s3HttpAddr); + authenticationHeaderParser = new AuthenticationHeaderParser(); + authenticationHeaderParser.setAuthHeader("AWS ozone:scret"); } /** @@ -99,6 +103,8 @@ public void testVirtualHostStyle() throws Exception { VirtualHostStyleFilter virtualHostStyleFilter = new VirtualHostStyleFilter(); virtualHostStyleFilter.setConfiguration(conf); + virtualHostStyleFilter.setAuthenticationHeaderParser( + authenticationHeaderParser); ContainerRequest containerRequest = createContainerRequest("mybucket" + ".localhost:9878", "/myfile", null, true); @@ -114,6 +120,8 @@ public void testPathStyle() throws Exception { VirtualHostStyleFilter virtualHostStyleFilter = new VirtualHostStyleFilter(); virtualHostStyleFilter.setConfiguration(conf); + virtualHostStyleFilter.setAuthenticationHeaderParser( + authenticationHeaderParser); ContainerRequest containerRequest = createContainerRequest(s3HttpAddr, "/mybucket/myfile", null, false); @@ -130,6 +138,8 @@ public void testVirtualHostStyleWithCreateBucketRequest() throws Exception { VirtualHostStyleFilter virtualHostStyleFilter = new VirtualHostStyleFilter(); virtualHostStyleFilter.setConfiguration(conf); + virtualHostStyleFilter.setAuthenticationHeaderParser( + authenticationHeaderParser); ContainerRequest containerRequest = createContainerRequest("mybucket" + ".localhost:9878", null, null, true); @@ -145,6 +155,8 @@ public void testVirtualHostStyleWithQueryParams() throws Exception { VirtualHostStyleFilter virtualHostStyleFilter = new VirtualHostStyleFilter(); virtualHostStyleFilter.setConfiguration(conf); + virtualHostStyleFilter.setAuthenticationHeaderParser( + authenticationHeaderParser); ContainerRequest containerRequest = createContainerRequest("mybucket" + ".localhost:9878", null, "?prefix=bh", true); @@ -169,6 +181,8 @@ public void testVirtualHostStyleWithNoMatchingDomain() throws Exception { VirtualHostStyleFilter virtualHostStyleFilter = new VirtualHostStyleFilter(); virtualHostStyleFilter.setConfiguration(conf); + virtualHostStyleFilter.setAuthenticationHeaderParser( + authenticationHeaderParser); ContainerRequest containerRequest = createContainerRequest("mybucket" + ".localhost:9999", null, null, true); @@ -187,6 +201,8 @@ public void testIncorrectVirtualHostStyle() throws VirtualHostStyleFilter virtualHostStyleFilter = new VirtualHostStyleFilter(); virtualHostStyleFilter.setConfiguration(conf); + virtualHostStyleFilter.setAuthenticationHeaderParser( + authenticationHeaderParser); ContainerRequest containerRequest = createContainerRequest("mybucket" + "localhost:9878", null, null, true); diff --git a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestRootList.java b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestRootList.java index 65fdf4e0cd..80cad3ace5 100644 --- a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestRootList.java +++ b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestRootList.java @@ -20,7 +20,6 @@ package org.apache.hadoop.ozone.s3.endpoint; -import javax.ws.rs.core.HttpHeaders; import org.apache.hadoop.ozone.client.ObjectStore; import org.apache.hadoop.ozone.client.OzoneClientStub; @@ -28,21 +27,21 @@ import org.apache.commons.lang3.RandomStringUtils; import static org.junit.Assert.assertEquals; + +import org.apache.hadoop.ozone.s3.header.AuthenticationHeaderParser; import org.junit.Before; import org.junit.Test; -import org.mockito.Mockito; -import static org.mockito.Mockito.when; /** * This class test HeadBucket functionality. */ public class TestRootList { - private String volumeName = "vol1"; private OzoneClientStub clientStub; private ObjectStore objectStoreStub; private OzoneVolume volumeStub; private RootEndpoint rootEndpoint; + private String userName = "ozone"; @Before public void setup() throws Exception { @@ -50,21 +49,24 @@ public void setup() throws Exception { //Create client stub and object store stub. clientStub = new OzoneClientStub(); objectStoreStub = clientStub.getObjectStore(); - objectStoreStub.createVolume("s3key"); - volumeStub = objectStoreStub.getVolume("s3key"); + String volumeName = "s3" + userName; + objectStoreStub.createVolume(volumeName); + volumeStub = objectStoreStub.getVolume(volumeName); // Create HeadBucket and setClient to OzoneClientStub rootEndpoint = new RootEndpoint(); rootEndpoint.setClient(clientStub); + + AuthenticationHeaderParser parser = new AuthenticationHeaderParser(); + parser.setAuthHeader("AWS " + userName +":secret"); + rootEndpoint.setAuthenticationHeaderParser(parser); } @Test public void testListBucket() throws Exception { - HttpHeaders headers = Mockito.mock(HttpHeaders.class); - when(headers.getHeaderString("Authorization")).thenReturn("AWS key:secret"); // List operation should success even there is no bucket. - ListBucketResponse response = rootEndpoint.get(headers); + ListBucketResponse response = rootEndpoint.get(); assertEquals(0, response.getBucketsNum()); String bucketBaseName = "bucket-"; @@ -72,7 +74,7 @@ public void testListBucket() throws Exception { volumeStub.createBucket( bucketBaseName + RandomStringUtils.randomNumeric(3)); } - response = rootEndpoint.get(headers); + response = rootEndpoint.get(); assertEquals(10, response.getBucketsNum()); }