blob: 95d44cca61090e5b7649ed1a10d12997f5eb4f27 [file] [log] [blame]
/**
* 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.FileStatus;
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;
import static org.apache.hadoop.fs.s3a.S3ATestUtils.assumeS3GuardState;
/**
* 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();
// Skip in the case of S3Guard with DynamoDB because it cannot get
// credentials for its own use if they're only in S3 URLs
assumeS3GuardState(false, conf);
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);
assumeS3GuardState(false, conf);
URI original = new URI(fsname);
URI testURI = createUriWithEmbeddedSecrets(original, "user", "//");
conf.set(TEST_FS_S3A_NAME, testURI.toString());
try {
fs = S3ATestUtils.createTestFileSystem(conf);
FileStatus 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;
}
}