/**
* 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.azure;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.contract.ContractTestUtils;
import org.apache.http.*;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.Mockito;
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import static org.apache.hadoop.fs.azure.AzureNativeFileSystemStore.KEY_USE_SECURE_MODE;
/**
* Test class to hold all WasbRemoteCallHelper tests
*/
public class TestWasbRemoteCallHelper
extends AbstractWasbTestBase {
@Override
protected AzureBlobStorageTestAccount createTestAccount() throws Exception {
Configuration conf = new Configuration();
conf.set(NativeAzureFileSystem.KEY_AZURE_AUTHORIZATION, "true");
conf.set(RemoteWasbAuthorizerImpl.KEY_REMOTE_AUTH_SERVICE_URL, "http://localhost/");
return AzureBlobStorageTestAccount.create(conf);
}
@Before
public void beforeMethod() {
boolean useSecureMode = fs.getConf().getBoolean(KEY_USE_SECURE_MODE, false);
boolean useAuthorization = fs.getConf().getBoolean(NativeAzureFileSystem.KEY_AZURE_AUTHORIZATION, false);
Assume.assumeTrue("Test valid when both SecureMode and Authorization are enabled .. skipping",
useSecureMode && useAuthorization);
Assume.assumeTrue(
useSecureMode && useAuthorization
);
}
@Rule
public ExpectedException expectedEx = ExpectedException.none();
/**
* Test invalid status-code
* @throws Throwable
*/
@Test // (expected = WasbAuthorizationException.class)
public void testInvalidStatusCode() throws Throwable {
setupExpectations();
// set up mocks
HttpClient mockHttpClient = Mockito.mock(HttpClient.class);
HttpResponse mockHttpResponse = Mockito.mock(HttpResponse.class);
Mockito.when(mockHttpClient.execute(Mockito.<HttpGet>any())).thenReturn(mockHttpResponse);
Mockito.when(mockHttpResponse.getStatusLine()).thenReturn(newStatusLine(999));
// finished setting up mocks
performop(mockHttpClient);
}
/**
* Test invalid Content-Type
* @throws Throwable
*/
@Test // (expected = WasbAuthorizationException.class)
public void testInvalidContentType() throws Throwable {
setupExpectations();
// set up mocks
HttpClient mockHttpClient = Mockito.mock(HttpClient.class);
HttpResponse mockHttpResponse = Mockito.mock(HttpResponse.class);
Mockito.when(mockHttpClient.execute(Mockito.<HttpGet>any())).thenReturn(mockHttpResponse);
Mockito.when(mockHttpResponse.getStatusLine()).thenReturn(newStatusLine(200));
Mockito.when(mockHttpResponse.getFirstHeader("Content-Type"))
.thenReturn(newHeader("Content-Type", "text/plain"));
// finished setting up mocks
performop(mockHttpClient);
}
/**
* Test missing Content-Length
* @throws Throwable
*/
@Test // (expected = WasbAuthorizationException.class)
public void testMissingContentLength() throws Throwable {
setupExpectations();
// set up mocks
HttpClient mockHttpClient = Mockito.mock(HttpClient.class);
HttpResponse mockHttpResponse = Mockito.mock(HttpResponse.class);
Mockito.when(mockHttpClient.execute(Mockito.<HttpGet>any())).thenReturn(mockHttpResponse);
Mockito.when(mockHttpResponse.getStatusLine()).thenReturn(newStatusLine(200));
Mockito.when(mockHttpResponse.getFirstHeader("Content-Type"))
.thenReturn(newHeader("Content-Type", "application/json"));
// finished setting up mocks
performop(mockHttpClient);
}
/**
* Test Content-Length exceeds max
* @throws Throwable
*/
@Test // (expected = WasbAuthorizationException.class)
public void testContentLengthExceedsMax() throws Throwable {
setupExpectations();
// set up mocks
HttpClient mockHttpClient = Mockito.mock(HttpClient.class);
HttpResponse mockHttpResponse = Mockito.mock(HttpResponse.class);
Mockito.when(mockHttpClient.execute(Mockito.<HttpGet>any())).thenReturn(mockHttpResponse);
Mockito.when(mockHttpResponse.getStatusLine()).thenReturn(newStatusLine(200));
Mockito.when(mockHttpResponse.getFirstHeader("Content-Type"))
.thenReturn(newHeader("Content-Type", "application/json"));
Mockito.when(mockHttpResponse.getFirstHeader("Content-Length"))
.thenReturn(newHeader("Content-Length", "2048"));
// finished setting up mocks
performop(mockHttpClient);
}
/**
* Test invalid Content-Length value
* @throws Throwable
*/
@Test // (expected = WasbAuthorizationException.class)
public void testInvalidContentLengthValue() throws Throwable {
setupExpectations();
// set up mocks
HttpClient mockHttpClient = Mockito.mock(HttpClient.class);
HttpResponse mockHttpResponse = Mockito.mock(HttpResponse.class);
Mockito.when(mockHttpClient.execute(Mockito.<HttpGet>any())).thenReturn(mockHttpResponse);
Mockito.when(mockHttpResponse.getStatusLine()).thenReturn(newStatusLine(200));
Mockito.when(mockHttpResponse.getFirstHeader("Content-Type"))
.thenReturn(newHeader("Content-Type", "application/json"));
Mockito.when(mockHttpResponse.getFirstHeader("Content-Length"))
.thenReturn(newHeader("Content-Length", "20abc48"));
// finished setting up mocks
performop(mockHttpClient);
}
/**
* Test valid JSON response
* @throws Throwable
*/
@Test
public void testValidJSONResponse() throws Throwable {
// set up mocks
HttpClient mockHttpClient = Mockito.mock(HttpClient.class);
HttpResponse mockHttpResponse = Mockito.mock(HttpResponse.class);
HttpEntity mockHttpEntity = Mockito.mock(HttpEntity.class);
Mockito.when(mockHttpClient.execute(Mockito.<HttpGet>any())).thenReturn(mockHttpResponse);
Mockito.when(mockHttpResponse.getStatusLine()).thenReturn(newStatusLine(200));
Mockito.when(mockHttpResponse.getFirstHeader("Content-Type"))
.thenReturn(newHeader("Content-Type", "application/json"));
Mockito.when(mockHttpResponse.getFirstHeader("Content-Length"))
.thenReturn(newHeader("Content-Length", "1024"));
Mockito.when(mockHttpResponse.getEntity()).thenReturn(mockHttpEntity);
Mockito.when(mockHttpEntity.getContent())
.thenReturn(new ByteArrayInputStream(validJsonResponse().getBytes(StandardCharsets.UTF_8)))
.thenReturn(new ByteArrayInputStream(validJsonResponse().getBytes(StandardCharsets.UTF_8)))
.thenReturn(new ByteArrayInputStream(validJsonResponse().getBytes(StandardCharsets.UTF_8)));
// finished setting up mocks
performop(mockHttpClient);
}
/**
* Test malformed JSON response
* @throws Throwable
*/
@Test // (expected = WasbAuthorizationException.class)
public void testMalFormedJSONResponse() throws Throwable {
expectedEx.expect(WasbAuthorizationException.class);
expectedEx.expectMessage("com.fasterxml.jackson.core.JsonParseException: Unexpected end-of-input in FIELD_NAME");
// set up mocks
HttpClient mockHttpClient = Mockito.mock(HttpClient.class);
HttpResponse mockHttpResponse = Mockito.mock(HttpResponse.class);
HttpEntity mockHttpEntity = Mockito.mock(HttpEntity.class);
Mockito.when(mockHttpClient.execute(Mockito.<HttpGet>any())).thenReturn(mockHttpResponse);
Mockito.when(mockHttpResponse.getStatusLine()).thenReturn(newStatusLine(200));
Mockito.when(mockHttpResponse.getFirstHeader("Content-Type"))
.thenReturn(newHeader("Content-Type", "application/json"));
Mockito.when(mockHttpResponse.getFirstHeader("Content-Length"))
.thenReturn(newHeader("Content-Length", "1024"));
Mockito.when(mockHttpResponse.getEntity()).thenReturn(mockHttpEntity);
Mockito.when(mockHttpEntity.getContent())
.thenReturn(new ByteArrayInputStream(malformedJsonResponse().getBytes(StandardCharsets.UTF_8)));
// finished setting up mocks
performop(mockHttpClient);
}
/**
* Test valid JSON response failure response code
* @throws Throwable
*/
@Test // (expected = WasbAuthorizationException.class)
public void testFailureCodeJSONResponse() throws Throwable {
expectedEx.expect(WasbAuthorizationException.class);
expectedEx.expectMessage("Remote authorization service encountered an error Unauthorized");
// set up mocks
HttpClient mockHttpClient = Mockito.mock(HttpClient.class);
HttpResponse mockHttpResponse = Mockito.mock(HttpResponse.class);
HttpEntity mockHttpEntity = Mockito.mock(HttpEntity.class);
Mockito.when(mockHttpClient.execute(Mockito.<HttpGet>any())).thenReturn(mockHttpResponse);
Mockito.when(mockHttpResponse.getStatusLine()).thenReturn(newStatusLine(200));
Mockito.when(mockHttpResponse.getFirstHeader("Content-Type"))
.thenReturn(newHeader("Content-Type", "application/json"));
Mockito.when(mockHttpResponse.getFirstHeader("Content-Length"))
.thenReturn(newHeader("Content-Length", "1024"));
Mockito.when(mockHttpResponse.getEntity()).thenReturn(mockHttpEntity);
Mockito.when(mockHttpEntity.getContent())
.thenReturn(new ByteArrayInputStream(failureCodeJsonResponse().getBytes(StandardCharsets.UTF_8)));
// finished setting up mocks
performop(mockHttpClient);
}
private void setupExpectations() {
expectedEx.expect(WasbAuthorizationException.class);
expectedEx.expectMessage("org.apache.hadoop.fs.azure.WasbRemoteCallException: " +
"http://localhost/CHECK_AUTHORIZATION?wasb_absolute_path=%2Ftest.dat&" +
"operation_type=write&delegation_token:Encountered IOException while making remote call");
}
private void performop(HttpClient mockHttpClient) throws Throwable {
AzureBlobStorageTestAccount testAccount = createTestAccount();
NativeAzureFileSystem fs = testAccount.getFileSystem();
Path testPath = new Path("/", "test.dat");
RemoteWasbAuthorizerImpl authorizer = new RemoteWasbAuthorizerImpl();
authorizer.init(fs.getConf());
WasbRemoteCallHelper mockWasbRemoteCallHelper = new WasbRemoteCallHelper();
mockWasbRemoteCallHelper.updateHttpClient(mockHttpClient);
authorizer.updateWasbRemoteCallHelper(mockWasbRemoteCallHelper);
fs.updateWasbAuthorizer(authorizer);
fs.create(testPath);
ContractTestUtils.assertPathExists(fs, "testPath was not created", testPath);
fs.delete(testPath, false);
}
private String validJsonResponse() {
return new String(
"{\"responseCode\": 0, \"authorizationResult\": true, \"responseMessage\": \"Authorized\"}"
);
}
private String malformedJsonResponse() {
return new String(
"{\"responseCode\": 0, \"authorizationResult\": true, \"responseMessage\":"
);
}
private String failureCodeJsonResponse() {
return new String(
"{\"responseCode\": 1, \"authorizationResult\": false, \"responseMessage\": \"Unauthorized\"}"
);
}
private StatusLine newStatusLine(final int statusCode) {
return new StatusLine() {
@Override
public ProtocolVersion getProtocolVersion() {
return new ProtocolVersion("HTTP", 1, 1);
}
@Override
public int getStatusCode() {
return statusCode;
}
@Override
public String getReasonPhrase() {
return "Reason Phrase";
}
};
}
private Header newHeader(final String name, final String value) {
return new Header() {
@Override
public String getName() {
return name;
}
@Override
public String getValue() {
return value;
}
@Override
public HeaderElement[] getElements() throws ParseException {
return new HeaderElement[0];
}
};
}
}