/** * 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.fs.s3a; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.IOUtils; import org.junit.After; import org.junit.Assert; import org.junit.Assume; import org.junit.Rule; import org.junit.Test; import org.junit.rules.Timeout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URLEncoder; import java.nio.file.AccessDeniedException; import static org.apache.hadoop.fs.s3a.S3ATestConstants.TEST_FS_S3A_NAME; /** * Tests that credentials can go into the URL. This includes a valid * set, and a check that an invalid set do at least get stripped out * of the final URI */ public class ITestS3ACredentialsInURL extends Assert { private S3AFileSystem fs; private static final Logger LOG = LoggerFactory.getLogger(ITestS3ACredentialsInURL.class); @Rule public Timeout testTimeout = new Timeout(30 * 60 * 1000); @After public void teardown() { IOUtils.closeStream(fs); } /** * Test instantiation. * @throws Throwable */ @Test public void testInstantiateFromURL() throws Throwable { Configuration conf = new Configuration(); String accessKey = conf.get(Constants.ACCESS_KEY); String secretKey = conf.get(Constants.SECRET_KEY); String fsname = conf.getTrimmed(TEST_FS_S3A_NAME, ""); Assume.assumeNotNull(fsname, accessKey, secretKey); URI original = new URI(fsname); URI secretsURI = createUriWithEmbeddedSecrets(original, accessKey, secretKey); if (secretKey.contains("/")) { assertTrue("test URI encodes the / symbol", secretsURI.toString(). contains("%252F")); } if (secretKey.contains("+")) { assertTrue("test URI encodes the + symbol", secretsURI.toString(). contains("%252B")); } assertFalse("Does not contain secrets", original.equals(secretsURI)); conf.set(TEST_FS_S3A_NAME, secretsURI.toString()); conf.unset(Constants.ACCESS_KEY); conf.unset(Constants.SECRET_KEY); fs = S3ATestUtils.createTestFileSystem(conf); String fsURI = fs.getUri().toString(); assertFalse("FS URI contains a @ symbol", fsURI.contains("@")); assertFalse("FS URI contains a % symbol", fsURI.contains("%")); if (!original.toString().startsWith(fsURI)) { fail("Filesystem URI does not match original"); } validate("original path", new Path(original)); validate("bare path", new Path("/")); validate("secrets path", new Path(secretsURI)); } private void validate(String text, Path path) throws IOException { try { fs.canonicalizeUri(path.toUri()); fs.checkPath(path); assertTrue(text + " Not a directory", fs.getFileStatus(new Path("/")).isDirectory()); fs.globStatus(path); } catch (AssertionError e) { throw e; } catch (Exception e) { LOG.debug("{} failure: {}", text, e, e); fail(text + " Test failed"); } } /** * Set up some invalid credentials, verify login is rejected. * @throws Throwable */ @Test public void testInvalidCredentialsFail() throws Throwable { Configuration conf = new Configuration(); String fsname = conf.getTrimmed(TEST_FS_S3A_NAME, ""); Assume.assumeNotNull(fsname); URI original = new URI(fsname); URI testURI = createUriWithEmbeddedSecrets(original, "user", "//"); conf.set(TEST_FS_S3A_NAME, testURI.toString()); fs = S3ATestUtils.createTestFileSystem(conf); try { S3AFileStatus status = fs.getFileStatus(new Path("/")); fail("Expected an AccessDeniedException, got " + status); } catch (AccessDeniedException e) { // expected } } private URI createUriWithEmbeddedSecrets(URI original, String accessKey, String secretKey) throws UnsupportedEncodingException { String encodedSecretKey = URLEncoder.encode(secretKey, "UTF-8"); String formattedString = String.format("%s://%s:%s@%s/%s/", original.getScheme(), accessKey, encodedSecretKey, original.getHost(), original.getPath()); URI testURI; try { testURI = new Path(formattedString).toUri(); } catch (IllegalArgumentException e) { // inner cause is stripped to keep any secrets out of stack traces throw new IllegalArgumentException("Could not encode Path"); } return testURI; } }