HADOOP-16730: ABFS: Support for Shared Access Signatures (SAS). Contributed by Sneha Vijayarajan.
This commit is contained in:
parent
cd2c6b1aac
commit
791270a2e5
|
@ -20,10 +20,10 @@ package org.apache.hadoop.fs.azurebfs;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.hadoop.classification.InterfaceAudience;
|
import org.apache.hadoop.classification.InterfaceAudience;
|
||||||
|
@ -40,15 +40,15 @@ import org.apache.hadoop.fs.azurebfs.contracts.exceptions.AzureBlobFileSystemExc
|
||||||
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.ConfigurationPropertyNotFoundException;
|
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.ConfigurationPropertyNotFoundException;
|
||||||
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.InvalidConfigurationValueException;
|
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.InvalidConfigurationValueException;
|
||||||
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.KeyProviderException;
|
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.KeyProviderException;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.SASTokenProviderException;
|
||||||
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.TokenAccessProviderException;
|
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.TokenAccessProviderException;
|
||||||
import org.apache.hadoop.fs.azurebfs.diagnostics.Base64StringConfigurationBasicValidator;
|
import org.apache.hadoop.fs.azurebfs.diagnostics.Base64StringConfigurationBasicValidator;
|
||||||
import org.apache.hadoop.fs.azurebfs.diagnostics.BooleanConfigurationBasicValidator;
|
import org.apache.hadoop.fs.azurebfs.diagnostics.BooleanConfigurationBasicValidator;
|
||||||
import org.apache.hadoop.fs.azurebfs.diagnostics.IntegerConfigurationBasicValidator;
|
import org.apache.hadoop.fs.azurebfs.diagnostics.IntegerConfigurationBasicValidator;
|
||||||
import org.apache.hadoop.fs.azurebfs.diagnostics.LongConfigurationBasicValidator;
|
import org.apache.hadoop.fs.azurebfs.diagnostics.LongConfigurationBasicValidator;
|
||||||
import org.apache.hadoop.fs.azurebfs.diagnostics.StringConfigurationBasicValidator;
|
import org.apache.hadoop.fs.azurebfs.diagnostics.StringConfigurationBasicValidator;
|
||||||
import org.apache.hadoop.fs.azurebfs.extensions.AbfsAuthorizationException;
|
|
||||||
import org.apache.hadoop.fs.azurebfs.extensions.AbfsAuthorizer;
|
|
||||||
import org.apache.hadoop.fs.azurebfs.extensions.CustomTokenProviderAdaptee;
|
import org.apache.hadoop.fs.azurebfs.extensions.CustomTokenProviderAdaptee;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.extensions.SASTokenProvider;
|
||||||
import org.apache.hadoop.fs.azurebfs.oauth2.AccessTokenProvider;
|
import org.apache.hadoop.fs.azurebfs.oauth2.AccessTokenProvider;
|
||||||
import org.apache.hadoop.fs.azurebfs.oauth2.ClientCredsTokenProvider;
|
import org.apache.hadoop.fs.azurebfs.oauth2.ClientCredsTokenProvider;
|
||||||
import org.apache.hadoop.fs.azurebfs.oauth2.CustomTokenProviderAdapter;
|
import org.apache.hadoop.fs.azurebfs.oauth2.CustomTokenProviderAdapter;
|
||||||
|
@ -170,9 +170,6 @@ public class AbfsConfiguration{
|
||||||
DefaultValue = DEFAULT_ENABLE_DELEGATION_TOKEN)
|
DefaultValue = DEFAULT_ENABLE_DELEGATION_TOKEN)
|
||||||
private boolean enableDelegationToken;
|
private boolean enableDelegationToken;
|
||||||
|
|
||||||
@StringConfigurationValidatorAnnotation(ConfigurationKey = ABFS_EXTERNAL_AUTHORIZATION_CLASS,
|
|
||||||
DefaultValue = "")
|
|
||||||
private String abfsExternalAuthorizationClass;
|
|
||||||
|
|
||||||
@BooleanConfigurationValidatorAnnotation(ConfigurationKey = FS_AZURE_ALWAYS_USE_HTTPS,
|
@BooleanConfigurationValidatorAnnotation(ConfigurationKey = FS_AZURE_ALWAYS_USE_HTTPS,
|
||||||
DefaultValue = DEFAULT_ENABLE_HTTPS)
|
DefaultValue = DEFAULT_ENABLE_HTTPS)
|
||||||
|
@ -217,6 +214,14 @@ public class AbfsConfiguration{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the Azure Storage account name corresponding to this instance of configuration.
|
||||||
|
* @return the Azure Storage account name
|
||||||
|
*/
|
||||||
|
public String getAccountName() {
|
||||||
|
return accountName;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Appends an account name to a configuration key yielding the
|
* Appends an account name to a configuration key yielding the
|
||||||
* account-specific form.
|
* account-specific form.
|
||||||
|
@ -436,7 +441,9 @@ public class AbfsConfiguration{
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean getCreateRemoteFileSystemDuringInitialization() {
|
public boolean getCreateRemoteFileSystemDuringInitialization() {
|
||||||
return this.createRemoteFileSystemDuringInitialization;
|
// we do not support creating the filesystem when AuthType is SAS
|
||||||
|
return this.createRemoteFileSystemDuringInitialization
|
||||||
|
&& this.getAuthType(this.accountName) != AuthType.SAS;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean getSkipUserGroupMetadataDuringInitialization() {
|
public boolean getSkipUserGroupMetadataDuringInitialization() {
|
||||||
|
@ -578,35 +585,32 @@ public class AbfsConfiguration{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getAbfsExternalAuthorizationClass() {
|
public SASTokenProvider getSASTokenProvider() throws AzureBlobFileSystemException {
|
||||||
return this.abfsExternalAuthorizationClass;
|
AuthType authType = getEnum(FS_AZURE_ACCOUNT_AUTH_TYPE_PROPERTY_NAME, AuthType.SharedKey);
|
||||||
}
|
if (authType != AuthType.SAS) {
|
||||||
|
throw new SASTokenProviderException(String.format(
|
||||||
public AbfsAuthorizer getAbfsAuthorizer() throws IOException {
|
"Invalid auth type: %s is being used, expecting SAS", authType));
|
||||||
String authClassName = getAbfsExternalAuthorizationClass();
|
}
|
||||||
AbfsAuthorizer authorizer = null;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (authClassName != null && !authClassName.isEmpty()) {
|
String configKey = FS_AZURE_SAS_TOKEN_PROVIDER_TYPE;
|
||||||
@SuppressWarnings("unchecked")
|
Class<? extends SASTokenProvider> sasTokenProviderClass =
|
||||||
Class<AbfsAuthorizer> authClass = (Class<AbfsAuthorizer>) rawConfig.getClassByName(authClassName);
|
getClass(configKey, null, SASTokenProvider.class);
|
||||||
authorizer = authClass.getConstructor(new Class[] {Configuration.class}).newInstance(rawConfig);
|
Preconditions.checkArgument(sasTokenProviderClass != null,
|
||||||
LOG.trace("Initializing {}", authClassName);
|
String.format("The configuration value for \"%s\" is invalid.", configKey));
|
||||||
authorizer.init();
|
|
||||||
LOG.trace("{} init complete", authClassName);
|
SASTokenProvider sasTokenProvider = ReflectionUtils
|
||||||
}
|
.newInstance(sasTokenProviderClass, rawConfig);
|
||||||
} catch (
|
Preconditions.checkArgument(sasTokenProvider != null,
|
||||||
IllegalAccessException
|
String.format("Failed to initialize %s", sasTokenProviderClass));
|
||||||
| InstantiationException
|
|
||||||
| ClassNotFoundException
|
LOG.trace("Initializing {}", sasTokenProviderClass.getName());
|
||||||
| IllegalArgumentException
|
sasTokenProvider.initialize(rawConfig, accountName);
|
||||||
| InvocationTargetException
|
LOG.trace("{} init complete", sasTokenProviderClass.getName());
|
||||||
| NoSuchMethodException
|
return sasTokenProvider;
|
||||||
| SecurityException
|
} catch (Exception e) {
|
||||||
| AbfsAuthorizationException e) {
|
throw new TokenAccessProviderException("Unable to load SAS token provider class: " + e, e);
|
||||||
throw new IOException(e);
|
|
||||||
}
|
}
|
||||||
return authorizer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void validateStorageAccountKeys() throws InvalidConfigurationValueException {
|
void validateStorageAccountKeys() throws InvalidConfigurationValueException {
|
||||||
|
|
|
@ -29,7 +29,6 @@ import java.net.URISyntaxException;
|
||||||
import java.util.Hashtable;
|
import java.util.Hashtable;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
@ -66,9 +65,8 @@ import org.apache.hadoop.fs.azurebfs.contracts.exceptions.AzureBlobFileSystemExc
|
||||||
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.FileSystemOperationUnhandledException;
|
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.FileSystemOperationUnhandledException;
|
||||||
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.InvalidUriAuthorityException;
|
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.InvalidUriAuthorityException;
|
||||||
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.InvalidUriException;
|
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.InvalidUriException;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.SASTokenProviderException;
|
||||||
import org.apache.hadoop.fs.azurebfs.contracts.services.AzureServiceErrorCode;
|
import org.apache.hadoop.fs.azurebfs.contracts.services.AzureServiceErrorCode;
|
||||||
import org.apache.hadoop.fs.azurebfs.extensions.AbfsAuthorizationException;
|
|
||||||
import org.apache.hadoop.fs.azurebfs.extensions.AbfsAuthorizer;
|
|
||||||
import org.apache.hadoop.fs.azurebfs.security.AbfsDelegationTokenManager;
|
import org.apache.hadoop.fs.azurebfs.security.AbfsDelegationTokenManager;
|
||||||
import org.apache.hadoop.fs.permission.AclEntry;
|
import org.apache.hadoop.fs.permission.AclEntry;
|
||||||
import org.apache.hadoop.fs.permission.AclStatus;
|
import org.apache.hadoop.fs.permission.AclStatus;
|
||||||
|
@ -96,7 +94,6 @@ public class AzureBlobFileSystem extends FileSystem {
|
||||||
|
|
||||||
private boolean delegationTokenEnabled = false;
|
private boolean delegationTokenEnabled = false;
|
||||||
private AbfsDelegationTokenManager delegationTokenManager;
|
private AbfsDelegationTokenManager delegationTokenManager;
|
||||||
private AbfsAuthorizer authorizer;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize(URI uri, Configuration configuration)
|
public void initialize(URI uri, Configuration configuration)
|
||||||
|
@ -139,9 +136,6 @@ public class AzureBlobFileSystem extends FileSystem {
|
||||||
|
|
||||||
AbfsClientThrottlingIntercept.initializeSingleton(abfsConfiguration.isAutoThrottlingEnabled());
|
AbfsClientThrottlingIntercept.initializeSingleton(abfsConfiguration.isAutoThrottlingEnabled());
|
||||||
|
|
||||||
// Initialize ABFS authorizer
|
|
||||||
//
|
|
||||||
this.authorizer = abfsConfiguration.getAbfsAuthorizer();
|
|
||||||
LOG.debug("Initializing AzureBlobFileSystem for {} complete", uri);
|
LOG.debug("Initializing AzureBlobFileSystem for {} complete", uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,7 +164,6 @@ public class AzureBlobFileSystem extends FileSystem {
|
||||||
LOG.debug("AzureBlobFileSystem.open path: {} bufferSize: {}", path, bufferSize);
|
LOG.debug("AzureBlobFileSystem.open path: {} bufferSize: {}", path, bufferSize);
|
||||||
|
|
||||||
Path qualifiedPath = makeQualified(path);
|
Path qualifiedPath = makeQualified(path);
|
||||||
performAbfsAuthCheck(FsAction.READ, qualifiedPath);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
InputStream inputStream = abfsStore.openFileForRead(qualifiedPath, statistics);
|
InputStream inputStream = abfsStore.openFileForRead(qualifiedPath, statistics);
|
||||||
|
@ -193,7 +186,6 @@ public class AzureBlobFileSystem extends FileSystem {
|
||||||
trailingPeriodCheck(f);
|
trailingPeriodCheck(f);
|
||||||
|
|
||||||
Path qualifiedPath = makeQualified(f);
|
Path qualifiedPath = makeQualified(f);
|
||||||
performAbfsAuthCheck(FsAction.WRITE, qualifiedPath);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
OutputStream outputStream = abfsStore.createFile(qualifiedPath, overwrite,
|
OutputStream outputStream = abfsStore.createFile(qualifiedPath, overwrite,
|
||||||
|
@ -256,7 +248,6 @@ public class AzureBlobFileSystem extends FileSystem {
|
||||||
bufferSize);
|
bufferSize);
|
||||||
|
|
||||||
Path qualifiedPath = makeQualified(f);
|
Path qualifiedPath = makeQualified(f);
|
||||||
performAbfsAuthCheck(FsAction.WRITE, qualifiedPath);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
OutputStream outputStream = abfsStore.openFileForWrite(qualifiedPath, false);
|
OutputStream outputStream = abfsStore.openFileForWrite(qualifiedPath, false);
|
||||||
|
@ -315,7 +306,6 @@ public class AzureBlobFileSystem extends FileSystem {
|
||||||
}
|
}
|
||||||
|
|
||||||
qualifiedDstPath = makeQualified(adjustedDst);
|
qualifiedDstPath = makeQualified(adjustedDst);
|
||||||
performAbfsAuthCheck(FsAction.READ_WRITE, qualifiedSrcPath, qualifiedDstPath);
|
|
||||||
|
|
||||||
abfsStore.rename(qualifiedSrcPath, qualifiedDstPath);
|
abfsStore.rename(qualifiedSrcPath, qualifiedDstPath);
|
||||||
return true;
|
return true;
|
||||||
|
@ -340,7 +330,6 @@ public class AzureBlobFileSystem extends FileSystem {
|
||||||
"AzureBlobFileSystem.delete path: {} recursive: {}", f.toString(), recursive);
|
"AzureBlobFileSystem.delete path: {} recursive: {}", f.toString(), recursive);
|
||||||
|
|
||||||
Path qualifiedPath = makeQualified(f);
|
Path qualifiedPath = makeQualified(f);
|
||||||
performAbfsAuthCheck(FsAction.WRITE, qualifiedPath);
|
|
||||||
|
|
||||||
if (f.isRoot()) {
|
if (f.isRoot()) {
|
||||||
if (!recursive) {
|
if (!recursive) {
|
||||||
|
@ -366,7 +355,6 @@ public class AzureBlobFileSystem extends FileSystem {
|
||||||
"AzureBlobFileSystem.listStatus path: {}", f.toString());
|
"AzureBlobFileSystem.listStatus path: {}", f.toString());
|
||||||
|
|
||||||
Path qualifiedPath = makeQualified(f);
|
Path qualifiedPath = makeQualified(f);
|
||||||
performAbfsAuthCheck(FsAction.READ, qualifiedPath);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
FileStatus[] result = abfsStore.listStatus(qualifiedPath);
|
FileStatus[] result = abfsStore.listStatus(qualifiedPath);
|
||||||
|
@ -416,7 +404,6 @@ public class AzureBlobFileSystem extends FileSystem {
|
||||||
}
|
}
|
||||||
|
|
||||||
Path qualifiedPath = makeQualified(f);
|
Path qualifiedPath = makeQualified(f);
|
||||||
performAbfsAuthCheck(FsAction.WRITE, qualifiedPath);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
abfsStore.createDirectory(qualifiedPath, permission == null ? FsPermission.getDirDefault() : permission,
|
abfsStore.createDirectory(qualifiedPath, permission == null ? FsPermission.getDirDefault() : permission,
|
||||||
|
@ -445,7 +432,6 @@ public class AzureBlobFileSystem extends FileSystem {
|
||||||
LOG.debug("AzureBlobFileSystem.getFileStatus path: {}", f);
|
LOG.debug("AzureBlobFileSystem.getFileStatus path: {}", f);
|
||||||
|
|
||||||
Path qualifiedPath = makeQualified(f);
|
Path qualifiedPath = makeQualified(f);
|
||||||
performAbfsAuthCheck(FsAction.READ, qualifiedPath);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return abfsStore.getFileStatus(qualifiedPath);
|
return abfsStore.getFileStatus(qualifiedPath);
|
||||||
|
@ -627,7 +613,6 @@ public class AzureBlobFileSystem extends FileSystem {
|
||||||
}
|
}
|
||||||
|
|
||||||
Path qualifiedPath = makeQualified(path);
|
Path qualifiedPath = makeQualified(path);
|
||||||
performAbfsAuthCheck(FsAction.WRITE, qualifiedPath);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
abfsStore.setOwner(qualifiedPath,
|
abfsStore.setOwner(qualifiedPath,
|
||||||
|
@ -657,9 +642,6 @@ public class AzureBlobFileSystem extends FileSystem {
|
||||||
throw new IllegalArgumentException("A valid name and value must be specified.");
|
throw new IllegalArgumentException("A valid name and value must be specified.");
|
||||||
}
|
}
|
||||||
|
|
||||||
Path qualifiedPath = makeQualified(path);
|
|
||||||
performAbfsAuthCheck(FsAction.READ_WRITE, qualifiedPath);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Hashtable<String, String> properties = abfsStore.getPathStatus(path);
|
Hashtable<String, String> properties = abfsStore.getPathStatus(path);
|
||||||
String xAttrName = ensureValidAttributeName(name);
|
String xAttrName = ensureValidAttributeName(name);
|
||||||
|
@ -693,9 +675,6 @@ public class AzureBlobFileSystem extends FileSystem {
|
||||||
throw new IllegalArgumentException("A valid name must be specified.");
|
throw new IllegalArgumentException("A valid name must be specified.");
|
||||||
}
|
}
|
||||||
|
|
||||||
Path qualifiedPath = makeQualified(path);
|
|
||||||
performAbfsAuthCheck(FsAction.READ, qualifiedPath);
|
|
||||||
|
|
||||||
byte[] value = null;
|
byte[] value = null;
|
||||||
try {
|
try {
|
||||||
Hashtable<String, String> properties = abfsStore.getPathStatus(path);
|
Hashtable<String, String> properties = abfsStore.getPathStatus(path);
|
||||||
|
@ -735,7 +714,6 @@ public class AzureBlobFileSystem extends FileSystem {
|
||||||
}
|
}
|
||||||
|
|
||||||
Path qualifiedPath = makeQualified(path);
|
Path qualifiedPath = makeQualified(path);
|
||||||
performAbfsAuthCheck(FsAction.WRITE, qualifiedPath);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
abfsStore.setPermission(qualifiedPath,
|
abfsStore.setPermission(qualifiedPath,
|
||||||
|
@ -771,7 +749,6 @@ public class AzureBlobFileSystem extends FileSystem {
|
||||||
}
|
}
|
||||||
|
|
||||||
Path qualifiedPath = makeQualified(path);
|
Path qualifiedPath = makeQualified(path);
|
||||||
performAbfsAuthCheck(FsAction.WRITE, qualifiedPath);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
abfsStore.modifyAclEntries(qualifiedPath,
|
abfsStore.modifyAclEntries(qualifiedPath,
|
||||||
|
@ -805,7 +782,6 @@ public class AzureBlobFileSystem extends FileSystem {
|
||||||
}
|
}
|
||||||
|
|
||||||
Path qualifiedPath = makeQualified(path);
|
Path qualifiedPath = makeQualified(path);
|
||||||
performAbfsAuthCheck(FsAction.WRITE, qualifiedPath);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
abfsStore.removeAclEntries(qualifiedPath, aclSpec);
|
abfsStore.removeAclEntries(qualifiedPath, aclSpec);
|
||||||
|
@ -831,7 +807,6 @@ public class AzureBlobFileSystem extends FileSystem {
|
||||||
}
|
}
|
||||||
|
|
||||||
Path qualifiedPath = makeQualified(path);
|
Path qualifiedPath = makeQualified(path);
|
||||||
performAbfsAuthCheck(FsAction.WRITE, qualifiedPath);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
abfsStore.removeDefaultAcl(qualifiedPath);
|
abfsStore.removeDefaultAcl(qualifiedPath);
|
||||||
|
@ -859,7 +834,6 @@ public class AzureBlobFileSystem extends FileSystem {
|
||||||
}
|
}
|
||||||
|
|
||||||
Path qualifiedPath = makeQualified(path);
|
Path qualifiedPath = makeQualified(path);
|
||||||
performAbfsAuthCheck(FsAction.WRITE, qualifiedPath);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
abfsStore.removeAcl(qualifiedPath);
|
abfsStore.removeAcl(qualifiedPath);
|
||||||
|
@ -894,7 +868,6 @@ public class AzureBlobFileSystem extends FileSystem {
|
||||||
}
|
}
|
||||||
|
|
||||||
Path qualifiedPath = makeQualified(path);
|
Path qualifiedPath = makeQualified(path);
|
||||||
performAbfsAuthCheck(FsAction.WRITE, qualifiedPath);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
abfsStore.setAcl(qualifiedPath, aclSpec);
|
abfsStore.setAcl(qualifiedPath, aclSpec);
|
||||||
|
@ -921,7 +894,6 @@ public class AzureBlobFileSystem extends FileSystem {
|
||||||
}
|
}
|
||||||
|
|
||||||
Path qualifiedPath = makeQualified(path);
|
Path qualifiedPath = makeQualified(path);
|
||||||
performAbfsAuthCheck(FsAction.READ, qualifiedPath);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return abfsStore.getAclStatus(qualifiedPath);
|
return abfsStore.getAclStatus(qualifiedPath);
|
||||||
|
@ -1107,6 +1079,8 @@ public class AzureBlobFileSystem extends FileSystem {
|
||||||
} else {
|
} else {
|
||||||
throw ere;
|
throw ere;
|
||||||
}
|
}
|
||||||
|
} else if (exception instanceof SASTokenProviderException) {
|
||||||
|
throw exception;
|
||||||
} else {
|
} else {
|
||||||
if (path == null) {
|
if (path == null) {
|
||||||
throw exception;
|
throw exception;
|
||||||
|
@ -1208,32 +1182,6 @@ public class AzureBlobFileSystem extends FileSystem {
|
||||||
return abfsStore.getIsNamespaceEnabled();
|
return abfsStore.getIsNamespaceEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Use ABFS authorizer to check if user is authorized to perform specific
|
|
||||||
* {@link FsAction} on specified {@link Path}s.
|
|
||||||
*
|
|
||||||
* @param action The {@link FsAction} being requested on the provided {@link Path}s.
|
|
||||||
* @param paths The absolute paths of the storage being accessed.
|
|
||||||
* @throws AbfsAuthorizationException on authorization failure.
|
|
||||||
* @throws IOException network problems or similar.
|
|
||||||
* @throws IllegalArgumentException if the required parameters are not provided.
|
|
||||||
*/
|
|
||||||
private void performAbfsAuthCheck(FsAction action, Path... paths)
|
|
||||||
throws AbfsAuthorizationException, IOException {
|
|
||||||
if (authorizer == null) {
|
|
||||||
LOG.debug("ABFS authorizer is not initialized. No authorization check will be performed.");
|
|
||||||
} else {
|
|
||||||
Preconditions.checkArgument(paths.length > 0, "no paths supplied for authorization check");
|
|
||||||
|
|
||||||
LOG.debug("Auth check for action: {} on paths: {}", action.toString(), Arrays.toString(paths));
|
|
||||||
if (!authorizer.isAuthorized(action, paths)) {
|
|
||||||
throw new AbfsAuthorizationException(
|
|
||||||
"User is not authorized for action " + action.toString()
|
|
||||||
+ " on paths: " + Arrays.toString(paths));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasPathCapability(final Path path, final String capability)
|
public boolean hasPathCapability(final Path path, final String capability)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
|
|
@ -71,6 +71,7 @@ import org.apache.hadoop.fs.azurebfs.contracts.exceptions.InvalidUriException;
|
||||||
import org.apache.hadoop.fs.azurebfs.contracts.services.AzureServiceErrorCode;
|
import org.apache.hadoop.fs.azurebfs.contracts.services.AzureServiceErrorCode;
|
||||||
import org.apache.hadoop.fs.azurebfs.contracts.services.ListResultEntrySchema;
|
import org.apache.hadoop.fs.azurebfs.contracts.services.ListResultEntrySchema;
|
||||||
import org.apache.hadoop.fs.azurebfs.contracts.services.ListResultSchema;
|
import org.apache.hadoop.fs.azurebfs.contracts.services.ListResultSchema;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.extensions.SASTokenProvider;
|
||||||
import org.apache.hadoop.fs.azurebfs.extensions.ExtensionHelper;
|
import org.apache.hadoop.fs.azurebfs.extensions.ExtensionHelper;
|
||||||
import org.apache.hadoop.fs.azurebfs.oauth2.AccessTokenProvider;
|
import org.apache.hadoop.fs.azurebfs.oauth2.AccessTokenProvider;
|
||||||
import org.apache.hadoop.fs.azurebfs.oauth2.IdentityTransformer;
|
import org.apache.hadoop.fs.azurebfs.oauth2.IdentityTransformer;
|
||||||
|
@ -1130,8 +1131,9 @@ public class AzureBlobFileSystemStore implements Closeable {
|
||||||
|
|
||||||
SharedKeyCredentials creds = null;
|
SharedKeyCredentials creds = null;
|
||||||
AccessTokenProvider tokenProvider = null;
|
AccessTokenProvider tokenProvider = null;
|
||||||
|
SASTokenProvider sasTokenProvider = null;
|
||||||
|
|
||||||
if (abfsConfiguration.getAuthType(accountName) == AuthType.SharedKey) {
|
if (authType == AuthType.SharedKey) {
|
||||||
LOG.trace("Fetching SharedKey credentials");
|
LOG.trace("Fetching SharedKey credentials");
|
||||||
int dotIndex = accountName.indexOf(AbfsHttpConstants.DOT);
|
int dotIndex = accountName.indexOf(AbfsHttpConstants.DOT);
|
||||||
if (dotIndex <= 0) {
|
if (dotIndex <= 0) {
|
||||||
|
@ -1140,6 +1142,9 @@ public class AzureBlobFileSystemStore implements Closeable {
|
||||||
}
|
}
|
||||||
creds = new SharedKeyCredentials(accountName.substring(0, dotIndex),
|
creds = new SharedKeyCredentials(accountName.substring(0, dotIndex),
|
||||||
abfsConfiguration.getStorageAccountKey());
|
abfsConfiguration.getStorageAccountKey());
|
||||||
|
} else if (authType == AuthType.SAS) {
|
||||||
|
LOG.trace("Fetching SAS token provider");
|
||||||
|
sasTokenProvider = abfsConfiguration.getSASTokenProvider();
|
||||||
} else {
|
} else {
|
||||||
LOG.trace("Fetching token provider");
|
LOG.trace("Fetching token provider");
|
||||||
tokenProvider = abfsConfiguration.getTokenProvider();
|
tokenProvider = abfsConfiguration.getTokenProvider();
|
||||||
|
@ -1148,9 +1153,15 @@ public class AzureBlobFileSystemStore implements Closeable {
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG.trace("Initializing AbfsClient for {}", baseUrl);
|
LOG.trace("Initializing AbfsClient for {}", baseUrl);
|
||||||
this.client = new AbfsClient(baseUrl, creds, abfsConfiguration,
|
if (tokenProvider != null) {
|
||||||
new ExponentialRetryPolicy(abfsConfiguration.getMaxIoRetries()),
|
this.client = new AbfsClient(baseUrl, creds, abfsConfiguration,
|
||||||
tokenProvider, abfsPerfTracker);
|
new ExponentialRetryPolicy(abfsConfiguration.getMaxIoRetries()),
|
||||||
|
tokenProvider, abfsPerfTracker);
|
||||||
|
} else {
|
||||||
|
this.client = new AbfsClient(baseUrl, creds, abfsConfiguration,
|
||||||
|
new ExponentialRetryPolicy(abfsConfiguration.getMaxIoRetries()),
|
||||||
|
sasTokenProvider, abfsPerfTracker);
|
||||||
|
}
|
||||||
LOG.trace("AbfsClient init complete");
|
LOG.trace("AbfsClient init complete");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -128,7 +128,8 @@ public final class ConfigurationKeys {
|
||||||
public static final String FS_AZURE_ENABLE_DELEGATION_TOKEN = "fs.azure.enable.delegation.token";
|
public static final String FS_AZURE_ENABLE_DELEGATION_TOKEN = "fs.azure.enable.delegation.token";
|
||||||
public static final String FS_AZURE_DELEGATION_TOKEN_PROVIDER_TYPE = "fs.azure.delegation.token.provider.type";
|
public static final String FS_AZURE_DELEGATION_TOKEN_PROVIDER_TYPE = "fs.azure.delegation.token.provider.type";
|
||||||
|
|
||||||
public static final String ABFS_EXTERNAL_AUTHORIZATION_CLASS = "abfs.external.authorization.class";
|
/** Key for SAS token provider **/
|
||||||
|
public static final String FS_AZURE_SAS_TOKEN_PROVIDER_TYPE = "fs.azure.sas.token.provider.type";
|
||||||
|
|
||||||
private ConfigurationKeys() {}
|
private ConfigurationKeys() {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,26 +16,23 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.apache.hadoop.fs.azurebfs.extensions;
|
package org.apache.hadoop.fs.azurebfs.contracts.exceptions;
|
||||||
|
|
||||||
import java.io.IOException;
|
import org.apache.hadoop.classification.InterfaceAudience;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception raised on ABFS Authorization failures.
|
* Thrown if there is an error instantiating the SASTokenProvider or getting
|
||||||
|
* a SAS token.
|
||||||
*/
|
*/
|
||||||
public class AbfsAuthorizationException extends IOException {
|
@InterfaceAudience.Private
|
||||||
|
public class SASTokenProviderException extends AzureBlobFileSystemException {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
public SASTokenProviderException(String message) {
|
||||||
|
|
||||||
public AbfsAuthorizationException(String message, Exception e) {
|
|
||||||
super(message, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
public AbfsAuthorizationException(String message) {
|
|
||||||
super(message);
|
super(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AbfsAuthorizationException(Throwable e) {
|
public SASTokenProviderException(String message, Throwable cause) {
|
||||||
super(e);
|
super(message);
|
||||||
|
initCause(cause);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,57 +0,0 @@
|
||||||
/**
|
|
||||||
* 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.azurebfs.extensions;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import org.apache.hadoop.classification.InterfaceAudience;
|
|
||||||
import org.apache.hadoop.classification.InterfaceStability;
|
|
||||||
import org.apache.hadoop.fs.Path;
|
|
||||||
import org.apache.hadoop.fs.permission.FsAction;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface to support authorization in Azure Blob File System.
|
|
||||||
*/
|
|
||||||
@InterfaceAudience.LimitedPrivate("authorization-subsystems")
|
|
||||||
@InterfaceStability.Unstable
|
|
||||||
public interface AbfsAuthorizer {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize authorizer for Azure Blob File System.
|
|
||||||
*
|
|
||||||
* @throws AbfsAuthorizationException if unable to initialize the authorizer.
|
|
||||||
* @throws IOException network problems or similar.
|
|
||||||
* @throws IllegalArgumentException if the required parameters are not provided.
|
|
||||||
*/
|
|
||||||
void init() throws AbfsAuthorizationException, IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the provided {@link FsAction} is allowed on the provided {@link Path}s.
|
|
||||||
*
|
|
||||||
* @param action the {@link FsAction} being requested on the provided {@link Path}s.
|
|
||||||
* @param absolutePaths The absolute paths of the storage being accessed.
|
|
||||||
* @return true if authorized, otherwise false.
|
|
||||||
* @throws AbfsAuthorizationException on authorization failure.
|
|
||||||
* @throws IOException network problems or similar.
|
|
||||||
* @throws IllegalArgumentException if the required parameters are not provided.
|
|
||||||
*/
|
|
||||||
boolean isAuthorized(FsAction action, Path... absolutePaths)
|
|
||||||
throws AbfsAuthorizationException, IOException;
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
/**
|
||||||
|
* 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.azurebfs.extensions;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.apache.hadoop.classification.InterfaceAudience;
|
||||||
|
import org.apache.hadoop.classification.InterfaceStability;
|
||||||
|
import org.apache.hadoop.conf.Configuration;
|
||||||
|
import org.apache.hadoop.security.AccessControlException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface to support SAS authorization.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.LimitedPrivate("authorization-subsystems")
|
||||||
|
@InterfaceStability.Unstable
|
||||||
|
public interface SASTokenProvider {
|
||||||
|
|
||||||
|
String CONCAT_SOURCE_OPERATION = "concat-source";
|
||||||
|
String CONCAT_TARGET_OPERATION = "concat-target";
|
||||||
|
String CREATEFILE_OPERATION = "create";
|
||||||
|
String DELETE_OPERATION = "delete";
|
||||||
|
String EXECUTE_OPERATION = "execute";
|
||||||
|
String GETACL_OPERATION = "getaclstatus";
|
||||||
|
String GETFILESTATUS_OPERATION = "getfilestatus";
|
||||||
|
String LISTSTATUS_OPERATION = "liststatus";
|
||||||
|
String MKDIR_OPERATION = "mkdir";
|
||||||
|
String READ_OPERATION = "read";
|
||||||
|
String RENAME_SOURCE_OPERATION = "rename-source";
|
||||||
|
String RENAME_DESTINATION_OPERATION = "rename-destination";
|
||||||
|
String SETACL_OPERATION = "setacl";
|
||||||
|
String SETOWNER_OPERATION = "setowner";
|
||||||
|
String SETPERMISSION_OPERATION = "setpermission";
|
||||||
|
String APPEND_OPERATION = "write";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize authorizer for Azure Blob File System.
|
||||||
|
* @param configuration Configuration object
|
||||||
|
* @param accountName Account Name
|
||||||
|
* @throws IOException network problems or similar.
|
||||||
|
*/
|
||||||
|
void initialize(Configuration configuration, String accountName)
|
||||||
|
throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invokes the authorizer to obtain a SAS token.
|
||||||
|
*
|
||||||
|
* @param account the name of the storage account.
|
||||||
|
* @param fileSystem the name of the fileSystem.
|
||||||
|
* @param path the file or directory path.
|
||||||
|
* @param operation the operation to be performed on the path.
|
||||||
|
* @return a SAS token to perform the request operation.
|
||||||
|
* @throws IOException if there is a network error.
|
||||||
|
* @throws AccessControlException if access is denied.
|
||||||
|
*/
|
||||||
|
String getSASToken(String account, String fileSystem, String path,
|
||||||
|
String operation) throws IOException, AccessControlException;
|
||||||
|
}
|
|
@ -38,7 +38,9 @@ import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.AzureBlobFileSystemException;
|
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.AzureBlobFileSystemException;
|
||||||
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.InvalidUriException;
|
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.InvalidUriException;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.SASTokenProviderException;
|
||||||
import org.apache.hadoop.fs.azurebfs.extensions.ExtensionHelper;
|
import org.apache.hadoop.fs.azurebfs.extensions.ExtensionHelper;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.extensions.SASTokenProvider;
|
||||||
import org.apache.hadoop.fs.azurebfs.AbfsConfiguration;
|
import org.apache.hadoop.fs.azurebfs.AbfsConfiguration;
|
||||||
import org.apache.hadoop.fs.azurebfs.oauth2.AccessTokenProvider;
|
import org.apache.hadoop.fs.azurebfs.oauth2.AccessTokenProvider;
|
||||||
import org.apache.hadoop.io.IOUtils;
|
import org.apache.hadoop.io.IOUtils;
|
||||||
|
@ -62,13 +64,14 @@ public class AbfsClient implements Closeable {
|
||||||
private final String userAgent;
|
private final String userAgent;
|
||||||
private final AbfsPerfTracker abfsPerfTracker;
|
private final AbfsPerfTracker abfsPerfTracker;
|
||||||
|
|
||||||
private final AccessTokenProvider tokenProvider;
|
private final String accountName;
|
||||||
|
private final AuthType authType;
|
||||||
|
private AccessTokenProvider tokenProvider;
|
||||||
|
private SASTokenProvider sasTokenProvider;
|
||||||
|
|
||||||
|
private AbfsClient(final URL baseUrl, final SharedKeyCredentials sharedKeyCredentials,
|
||||||
public AbfsClient(final URL baseUrl, final SharedKeyCredentials sharedKeyCredentials,
|
|
||||||
final AbfsConfiguration abfsConfiguration,
|
final AbfsConfiguration abfsConfiguration,
|
||||||
final ExponentialRetryPolicy exponentialRetryPolicy,
|
final ExponentialRetryPolicy exponentialRetryPolicy,
|
||||||
final AccessTokenProvider tokenProvider,
|
|
||||||
final AbfsPerfTracker abfsPerfTracker) {
|
final AbfsPerfTracker abfsPerfTracker) {
|
||||||
this.baseUrl = baseUrl;
|
this.baseUrl = baseUrl;
|
||||||
this.sharedKeyCredentials = sharedKeyCredentials;
|
this.sharedKeyCredentials = sharedKeyCredentials;
|
||||||
|
@ -76,6 +79,8 @@ public class AbfsClient implements Closeable {
|
||||||
this.filesystem = baseUrlString.substring(baseUrlString.lastIndexOf(FORWARD_SLASH) + 1);
|
this.filesystem = baseUrlString.substring(baseUrlString.lastIndexOf(FORWARD_SLASH) + 1);
|
||||||
this.abfsConfiguration = abfsConfiguration;
|
this.abfsConfiguration = abfsConfiguration;
|
||||||
this.retryPolicy = exponentialRetryPolicy;
|
this.retryPolicy = exponentialRetryPolicy;
|
||||||
|
this.accountName = abfsConfiguration.getAccountName().substring(0, abfsConfiguration.getAccountName().indexOf(AbfsHttpConstants.DOT));
|
||||||
|
this.authType = abfsConfiguration.getAuthType(accountName);
|
||||||
|
|
||||||
String sslProviderName = null;
|
String sslProviderName = null;
|
||||||
|
|
||||||
|
@ -93,10 +98,27 @@ public class AbfsClient implements Closeable {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.userAgent = initializeUserAgent(abfsConfiguration, sslProviderName);
|
this.userAgent = initializeUserAgent(abfsConfiguration, sslProviderName);
|
||||||
this.tokenProvider = tokenProvider;
|
|
||||||
this.abfsPerfTracker = abfsPerfTracker;
|
this.abfsPerfTracker = abfsPerfTracker;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AbfsClient(final URL baseUrl, final SharedKeyCredentials sharedKeyCredentials,
|
||||||
|
final AbfsConfiguration abfsConfiguration,
|
||||||
|
final ExponentialRetryPolicy exponentialRetryPolicy,
|
||||||
|
final AccessTokenProvider tokenProvider,
|
||||||
|
final AbfsPerfTracker abfsPerfTracker) {
|
||||||
|
this(baseUrl, sharedKeyCredentials, abfsConfiguration, exponentialRetryPolicy, abfsPerfTracker);
|
||||||
|
this.tokenProvider = tokenProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AbfsClient(final URL baseUrl, final SharedKeyCredentials sharedKeyCredentials,
|
||||||
|
final AbfsConfiguration abfsConfiguration,
|
||||||
|
final ExponentialRetryPolicy exponentialRetryPolicy,
|
||||||
|
final SASTokenProvider sasTokenProvider,
|
||||||
|
final AbfsPerfTracker abfsPerfTracker) {
|
||||||
|
this(baseUrl, sharedKeyCredentials, abfsConfiguration, exponentialRetryPolicy, abfsPerfTracker);
|
||||||
|
this.sasTokenProvider = sasTokenProvider;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
if (tokenProvider instanceof Closeable) {
|
if (tokenProvider instanceof Closeable) {
|
||||||
|
@ -191,6 +213,7 @@ public class AbfsClient implements Closeable {
|
||||||
abfsUriQueryBuilder.addQuery(QUERY_PARAM_CONTINUATION, continuation);
|
abfsUriQueryBuilder.addQuery(QUERY_PARAM_CONTINUATION, continuation);
|
||||||
abfsUriQueryBuilder.addQuery(QUERY_PARAM_MAXRESULTS, String.valueOf(listMaxResults));
|
abfsUriQueryBuilder.addQuery(QUERY_PARAM_MAXRESULTS, String.valueOf(listMaxResults));
|
||||||
abfsUriQueryBuilder.addQuery(HttpQueryParams.QUERY_PARAM_UPN, String.valueOf(abfsConfiguration.isUpnUsed()));
|
abfsUriQueryBuilder.addQuery(HttpQueryParams.QUERY_PARAM_UPN, String.valueOf(abfsConfiguration.isUpnUsed()));
|
||||||
|
appendSASTokenToQuery(relativePath, SASTokenProvider.LISTSTATUS_OPERATION, abfsUriQueryBuilder);
|
||||||
|
|
||||||
final URL url = createRequestUrl(abfsUriQueryBuilder.toString());
|
final URL url = createRequestUrl(abfsUriQueryBuilder.toString());
|
||||||
final AbfsRestOperation op = new AbfsRestOperation(
|
final AbfsRestOperation op = new AbfsRestOperation(
|
||||||
|
@ -255,6 +278,11 @@ public class AbfsClient implements Closeable {
|
||||||
final AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder();
|
final AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder();
|
||||||
abfsUriQueryBuilder.addQuery(QUERY_PARAM_RESOURCE, isFile ? FILE : DIRECTORY);
|
abfsUriQueryBuilder.addQuery(QUERY_PARAM_RESOURCE, isFile ? FILE : DIRECTORY);
|
||||||
|
|
||||||
|
String operation = isFile
|
||||||
|
? SASTokenProvider.CREATEFILE_OPERATION
|
||||||
|
: SASTokenProvider.MKDIR_OPERATION;
|
||||||
|
appendSASTokenToQuery(path, operation, abfsUriQueryBuilder);
|
||||||
|
|
||||||
final URL url = createRequestUrl(path, abfsUriQueryBuilder.toString());
|
final URL url = createRequestUrl(path, abfsUriQueryBuilder.toString());
|
||||||
final AbfsRestOperation op = new AbfsRestOperation(
|
final AbfsRestOperation op = new AbfsRestOperation(
|
||||||
AbfsRestOperationType.CreatePath,
|
AbfsRestOperationType.CreatePath,
|
||||||
|
@ -266,16 +294,24 @@ public class AbfsClient implements Closeable {
|
||||||
return op;
|
return op;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AbfsRestOperation renamePath(final String source, final String destination, final String continuation)
|
public AbfsRestOperation renamePath(String source, final String destination, final String continuation)
|
||||||
throws AzureBlobFileSystemException {
|
throws AzureBlobFileSystemException {
|
||||||
final List<AbfsHttpHeader> requestHeaders = createDefaultHeaders();
|
final List<AbfsHttpHeader> requestHeaders = createDefaultHeaders();
|
||||||
|
|
||||||
final String encodedRenameSource = urlEncode(FORWARD_SLASH + this.getFileSystem() + source);
|
String encodedRenameSource = urlEncode(FORWARD_SLASH + this.getFileSystem() + source);
|
||||||
|
if (authType == AuthType.SAS) {
|
||||||
|
final AbfsUriQueryBuilder srcQueryBuilder = new AbfsUriQueryBuilder();
|
||||||
|
appendSASTokenToQuery(source, SASTokenProvider.RENAME_SOURCE_OPERATION, srcQueryBuilder);
|
||||||
|
encodedRenameSource += srcQueryBuilder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG.trace("Rename source queryparam added {}", encodedRenameSource);
|
||||||
requestHeaders.add(new AbfsHttpHeader(X_MS_RENAME_SOURCE, encodedRenameSource));
|
requestHeaders.add(new AbfsHttpHeader(X_MS_RENAME_SOURCE, encodedRenameSource));
|
||||||
requestHeaders.add(new AbfsHttpHeader(IF_NONE_MATCH, STAR));
|
requestHeaders.add(new AbfsHttpHeader(IF_NONE_MATCH, STAR));
|
||||||
|
|
||||||
final AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder();
|
final AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder();
|
||||||
abfsUriQueryBuilder.addQuery(QUERY_PARAM_CONTINUATION, continuation);
|
abfsUriQueryBuilder.addQuery(QUERY_PARAM_CONTINUATION, continuation);
|
||||||
|
appendSASTokenToQuery(destination, SASTokenProvider.RENAME_DESTINATION_OPERATION, abfsUriQueryBuilder);
|
||||||
|
|
||||||
final URL url = createRequestUrl(destination, abfsUriQueryBuilder.toString());
|
final URL url = createRequestUrl(destination, abfsUriQueryBuilder.toString());
|
||||||
final AbfsRestOperation op = new AbfsRestOperation(
|
final AbfsRestOperation op = new AbfsRestOperation(
|
||||||
|
@ -299,6 +335,7 @@ public class AbfsClient implements Closeable {
|
||||||
final AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder();
|
final AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder();
|
||||||
abfsUriQueryBuilder.addQuery(QUERY_PARAM_ACTION, APPEND_ACTION);
|
abfsUriQueryBuilder.addQuery(QUERY_PARAM_ACTION, APPEND_ACTION);
|
||||||
abfsUriQueryBuilder.addQuery(QUERY_PARAM_POSITION, Long.toString(position));
|
abfsUriQueryBuilder.addQuery(QUERY_PARAM_POSITION, Long.toString(position));
|
||||||
|
appendSASTokenToQuery(path, SASTokenProvider.APPEND_OPERATION, abfsUriQueryBuilder);
|
||||||
|
|
||||||
final URL url = createRequestUrl(path, abfsUriQueryBuilder.toString());
|
final URL url = createRequestUrl(path, abfsUriQueryBuilder.toString());
|
||||||
final AbfsRestOperation op = new AbfsRestOperation(
|
final AbfsRestOperation op = new AbfsRestOperation(
|
||||||
|
@ -324,6 +361,7 @@ public class AbfsClient implements Closeable {
|
||||||
abfsUriQueryBuilder.addQuery(QUERY_PARAM_POSITION, Long.toString(position));
|
abfsUriQueryBuilder.addQuery(QUERY_PARAM_POSITION, Long.toString(position));
|
||||||
abfsUriQueryBuilder.addQuery(QUERY_PARAM_RETAIN_UNCOMMITTED_DATA, String.valueOf(retainUncommittedData));
|
abfsUriQueryBuilder.addQuery(QUERY_PARAM_RETAIN_UNCOMMITTED_DATA, String.valueOf(retainUncommittedData));
|
||||||
abfsUriQueryBuilder.addQuery(QUERY_PARAM_CLOSE, String.valueOf(isClose));
|
abfsUriQueryBuilder.addQuery(QUERY_PARAM_CLOSE, String.valueOf(isClose));
|
||||||
|
appendSASTokenToQuery(path, SASTokenProvider.APPEND_OPERATION, abfsUriQueryBuilder);
|
||||||
|
|
||||||
final URL url = createRequestUrl(path, abfsUriQueryBuilder.toString());
|
final URL url = createRequestUrl(path, abfsUriQueryBuilder.toString());
|
||||||
final AbfsRestOperation op = new AbfsRestOperation(
|
final AbfsRestOperation op = new AbfsRestOperation(
|
||||||
|
@ -365,6 +403,7 @@ public class AbfsClient implements Closeable {
|
||||||
|
|
||||||
final AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder();
|
final AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder();
|
||||||
abfsUriQueryBuilder.addQuery(HttpQueryParams.QUERY_PARAM_UPN, String.valueOf(abfsConfiguration.isUpnUsed()));
|
abfsUriQueryBuilder.addQuery(HttpQueryParams.QUERY_PARAM_UPN, String.valueOf(abfsConfiguration.isUpnUsed()));
|
||||||
|
appendSASTokenToQuery(path, SASTokenProvider.GETFILESTATUS_OPERATION, abfsUriQueryBuilder);
|
||||||
|
|
||||||
final URL url = createRequestUrl(path, abfsUriQueryBuilder.toString());
|
final URL url = createRequestUrl(path, abfsUriQueryBuilder.toString());
|
||||||
final AbfsRestOperation op = new AbfsRestOperation(
|
final AbfsRestOperation op = new AbfsRestOperation(
|
||||||
|
@ -385,6 +424,7 @@ public class AbfsClient implements Closeable {
|
||||||
requestHeaders.add(new AbfsHttpHeader(IF_MATCH, eTag));
|
requestHeaders.add(new AbfsHttpHeader(IF_MATCH, eTag));
|
||||||
|
|
||||||
final AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder();
|
final AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder();
|
||||||
|
appendSASTokenToQuery(path, SASTokenProvider.READ_OPERATION, abfsUriQueryBuilder);
|
||||||
|
|
||||||
final URL url = createRequestUrl(path, abfsUriQueryBuilder.toString());
|
final URL url = createRequestUrl(path, abfsUriQueryBuilder.toString());
|
||||||
|
|
||||||
|
@ -409,6 +449,7 @@ public class AbfsClient implements Closeable {
|
||||||
final AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder();
|
final AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder();
|
||||||
abfsUriQueryBuilder.addQuery(QUERY_PARAM_RECURSIVE, String.valueOf(recursive));
|
abfsUriQueryBuilder.addQuery(QUERY_PARAM_RECURSIVE, String.valueOf(recursive));
|
||||||
abfsUriQueryBuilder.addQuery(QUERY_PARAM_CONTINUATION, continuation);
|
abfsUriQueryBuilder.addQuery(QUERY_PARAM_CONTINUATION, continuation);
|
||||||
|
appendSASTokenToQuery(path, SASTokenProvider.DELETE_OPERATION, abfsUriQueryBuilder);
|
||||||
|
|
||||||
final URL url = createRequestUrl(path, abfsUriQueryBuilder.toString());
|
final URL url = createRequestUrl(path, abfsUriQueryBuilder.toString());
|
||||||
final AbfsRestOperation op = new AbfsRestOperation(
|
final AbfsRestOperation op = new AbfsRestOperation(
|
||||||
|
@ -438,6 +479,7 @@ public class AbfsClient implements Closeable {
|
||||||
|
|
||||||
final AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder();
|
final AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder();
|
||||||
abfsUriQueryBuilder.addQuery(HttpQueryParams.QUERY_PARAM_ACTION, AbfsHttpConstants.SET_ACCESS_CONTROL);
|
abfsUriQueryBuilder.addQuery(HttpQueryParams.QUERY_PARAM_ACTION, AbfsHttpConstants.SET_ACCESS_CONTROL);
|
||||||
|
appendSASTokenToQuery(path, SASTokenProvider.SETOWNER_OPERATION, abfsUriQueryBuilder);
|
||||||
|
|
||||||
final URL url = createRequestUrl(path, abfsUriQueryBuilder.toString());
|
final URL url = createRequestUrl(path, abfsUriQueryBuilder.toString());
|
||||||
final AbfsRestOperation op = new AbfsRestOperation(
|
final AbfsRestOperation op = new AbfsRestOperation(
|
||||||
|
@ -462,6 +504,7 @@ public class AbfsClient implements Closeable {
|
||||||
|
|
||||||
final AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder();
|
final AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder();
|
||||||
abfsUriQueryBuilder.addQuery(HttpQueryParams.QUERY_PARAM_ACTION, AbfsHttpConstants.SET_ACCESS_CONTROL);
|
abfsUriQueryBuilder.addQuery(HttpQueryParams.QUERY_PARAM_ACTION, AbfsHttpConstants.SET_ACCESS_CONTROL);
|
||||||
|
appendSASTokenToQuery(path, SASTokenProvider.SETPERMISSION_OPERATION, abfsUriQueryBuilder);
|
||||||
|
|
||||||
final URL url = createRequestUrl(path, abfsUriQueryBuilder.toString());
|
final URL url = createRequestUrl(path, abfsUriQueryBuilder.toString());
|
||||||
final AbfsRestOperation op = new AbfsRestOperation(
|
final AbfsRestOperation op = new AbfsRestOperation(
|
||||||
|
@ -494,6 +537,7 @@ public class AbfsClient implements Closeable {
|
||||||
|
|
||||||
final AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder();
|
final AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder();
|
||||||
abfsUriQueryBuilder.addQuery(HttpQueryParams.QUERY_PARAM_ACTION, AbfsHttpConstants.SET_ACCESS_CONTROL);
|
abfsUriQueryBuilder.addQuery(HttpQueryParams.QUERY_PARAM_ACTION, AbfsHttpConstants.SET_ACCESS_CONTROL);
|
||||||
|
appendSASTokenToQuery(path, SASTokenProvider.SETACL_OPERATION, abfsUriQueryBuilder);
|
||||||
|
|
||||||
final URL url = createRequestUrl(path, abfsUriQueryBuilder.toString());
|
final URL url = createRequestUrl(path, abfsUriQueryBuilder.toString());
|
||||||
final AbfsRestOperation op = new AbfsRestOperation(
|
final AbfsRestOperation op = new AbfsRestOperation(
|
||||||
|
@ -516,6 +560,7 @@ public class AbfsClient implements Closeable {
|
||||||
final AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder();
|
final AbfsUriQueryBuilder abfsUriQueryBuilder = createDefaultUriQueryBuilder();
|
||||||
abfsUriQueryBuilder.addQuery(HttpQueryParams.QUERY_PARAM_ACTION, AbfsHttpConstants.GET_ACCESS_CONTROL);
|
abfsUriQueryBuilder.addQuery(HttpQueryParams.QUERY_PARAM_ACTION, AbfsHttpConstants.GET_ACCESS_CONTROL);
|
||||||
abfsUriQueryBuilder.addQuery(HttpQueryParams.QUERY_PARAM_UPN, String.valueOf(useUPN));
|
abfsUriQueryBuilder.addQuery(HttpQueryParams.QUERY_PARAM_UPN, String.valueOf(useUPN));
|
||||||
|
appendSASTokenToQuery(path, SASTokenProvider.GETACL_OPERATION, abfsUriQueryBuilder);
|
||||||
|
|
||||||
final URL url = createRequestUrl(path, abfsUriQueryBuilder.toString());
|
final URL url = createRequestUrl(path, abfsUriQueryBuilder.toString());
|
||||||
final AbfsRestOperation op = new AbfsRestOperation(
|
final AbfsRestOperation op = new AbfsRestOperation(
|
||||||
|
@ -550,6 +595,34 @@ public class AbfsClient implements Closeable {
|
||||||
return op;
|
return op;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If configured for SAS AuthType, appends SAS token to queryBuilder
|
||||||
|
* @param path
|
||||||
|
* @param operation
|
||||||
|
* @param queryBuilder
|
||||||
|
* @throws SASTokenProviderException
|
||||||
|
*/
|
||||||
|
private void appendSASTokenToQuery(String path, String operation, AbfsUriQueryBuilder queryBuilder) throws SASTokenProviderException {
|
||||||
|
if (this.authType == AuthType.SAS) {
|
||||||
|
try {
|
||||||
|
LOG.trace("Fetch SAS token for {} on {}", operation, path);
|
||||||
|
String sasToken = sasTokenProvider.getSASToken(this.accountName,
|
||||||
|
this.filesystem, path, operation);
|
||||||
|
if ((sasToken == null) || sasToken.isEmpty()) {
|
||||||
|
throw new UnsupportedOperationException("SASToken received is empty or null");
|
||||||
|
}
|
||||||
|
|
||||||
|
queryBuilder.setSASToken(sasToken);
|
||||||
|
LOG.trace("SAS token fetch complete for {} on {}", operation, path);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw new SASTokenProviderException(String.format("Failed to acquire a SAS token for %s on %s due to %s",
|
||||||
|
operation,
|
||||||
|
path,
|
||||||
|
ex.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private URL createRequestUrl(final String query) throws AzureBlobFileSystemException {
|
private URL createRequestUrl(final String query) throws AzureBlobFileSystemException {
|
||||||
return createRequestUrl(EMPTY_STRING, query);
|
return createRequestUrl(EMPTY_STRING, query);
|
||||||
}
|
}
|
||||||
|
@ -600,6 +673,10 @@ public class AbfsClient implements Closeable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AuthType getAuthType() {
|
||||||
|
return authType;
|
||||||
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
String initializeUserAgent(final AbfsConfiguration abfsConfiguration,
|
String initializeUserAgent(final AbfsConfiguration abfsConfiguration,
|
||||||
final String sslProviderName) {
|
final String sslProviderName) {
|
||||||
|
@ -634,4 +711,9 @@ public class AbfsClient implements Closeable {
|
||||||
URL getBaseUrl() {
|
URL getBaseUrl() {
|
||||||
return baseUrl;
|
return baseUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public SASTokenProvider getSasTokenProvider() {
|
||||||
|
return this.sasTokenProvider;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -160,18 +160,26 @@ public class AbfsRestOperation {
|
||||||
// initialize the HTTP request and open the connection
|
// initialize the HTTP request and open the connection
|
||||||
httpOperation = new AbfsHttpOperation(url, method, requestHeaders);
|
httpOperation = new AbfsHttpOperation(url, method, requestHeaders);
|
||||||
|
|
||||||
// sign the HTTP request
|
switch(client.getAuthType()) {
|
||||||
if (client.getAccessToken() == null) {
|
case Custom:
|
||||||
LOG.debug("Signing request with shared key");
|
case OAuth:
|
||||||
// sign the HTTP request
|
LOG.debug("Authenticating request with OAuth2 access token");
|
||||||
client.getSharedKeyCredentials().signRequest(
|
httpOperation.getConnection().setRequestProperty(HttpHeaderConfigurations.AUTHORIZATION,
|
||||||
httpOperation.getConnection(),
|
client.getAccessToken());
|
||||||
hasRequestBody ? bufferLength : 0);
|
break;
|
||||||
} else {
|
case SAS:
|
||||||
LOG.debug("Authenticating request with OAuth2 access token");
|
// do nothing; the SAS token should already be appended to the query string
|
||||||
httpOperation.getConnection().setRequestProperty(HttpHeaderConfigurations.AUTHORIZATION,
|
break;
|
||||||
client.getAccessToken());
|
case SharedKey:
|
||||||
|
// sign the HTTP request
|
||||||
|
LOG.debug("Signing request with shared key");
|
||||||
|
// sign the HTTP request
|
||||||
|
client.getSharedKeyCredentials().signRequest(
|
||||||
|
httpOperation.getConnection(),
|
||||||
|
hasRequestBody ? bufferLength : 0);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// dump the headers
|
// dump the headers
|
||||||
AbfsIoUtils.dumpHeadersToDebugLog("Request Headers",
|
AbfsIoUtils.dumpHeadersToDebugLog("Request Headers",
|
||||||
httpOperation.getConnection().getRequestProperties());
|
httpOperation.getConnection().getRequestProperties());
|
||||||
|
|
|
@ -29,6 +29,7 @@ import org.apache.hadoop.fs.azurebfs.contracts.exceptions.AzureBlobFileSystemExc
|
||||||
*/
|
*/
|
||||||
public class AbfsUriQueryBuilder {
|
public class AbfsUriQueryBuilder {
|
||||||
private Map<String, String> parameters;
|
private Map<String, String> parameters;
|
||||||
|
private String sasToken = null;
|
||||||
|
|
||||||
public AbfsUriQueryBuilder() {
|
public AbfsUriQueryBuilder() {
|
||||||
this.parameters = new HashMap<>();
|
this.parameters = new HashMap<>();
|
||||||
|
@ -40,6 +41,10 @@ public class AbfsUriQueryBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setSASToken(final String sasToken) {
|
||||||
|
this.sasToken = sasToken;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
|
@ -59,6 +64,16 @@ public class AbfsUriQueryBuilder {
|
||||||
throw new IllegalArgumentException("Query string param is not encode-able: " + entry.getKey() + "=" + entry.getValue());
|
throw new IllegalArgumentException("Query string param is not encode-able: " + entry.getKey() + "=" + entry.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// append SAS Token
|
||||||
|
if (sasToken != null) {
|
||||||
|
if (first) {
|
||||||
|
sb.append(AbfsHttpConstants.QUESTION_MARK);
|
||||||
|
} else {
|
||||||
|
sb.append(AbfsHttpConstants.AND_MARK);
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.append(sasToken);
|
||||||
|
}
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -23,5 +23,6 @@ package org.apache.hadoop.fs.azurebfs.services;
|
||||||
public enum AuthType {
|
public enum AuthType {
|
||||||
SharedKey,
|
SharedKey,
|
||||||
OAuth,
|
OAuth,
|
||||||
Custom
|
Custom,
|
||||||
|
SAS
|
||||||
}
|
}
|
||||||
|
|
|
@ -626,7 +626,7 @@ points for third-parties to integrate their authentication and authorization
|
||||||
services into the ABFS client.
|
services into the ABFS client.
|
||||||
|
|
||||||
* `CustomDelegationTokenManager` : adds ability to issue Hadoop Delegation Tokens.
|
* `CustomDelegationTokenManager` : adds ability to issue Hadoop Delegation Tokens.
|
||||||
* `AbfsAuthorizer` permits client-side authorization of file operations.
|
* `SASTokenProvider`: allows for custom provision of Azure Storage Shared Access Signature (SAS) tokens.
|
||||||
* `CustomTokenProviderAdaptee`: allows for custom provision of
|
* `CustomTokenProviderAdaptee`: allows for custom provision of
|
||||||
Azure OAuth tokens.
|
Azure OAuth tokens.
|
||||||
* `KeyProvider`.
|
* `KeyProvider`.
|
||||||
|
|
|
@ -39,6 +39,7 @@ import org.apache.hadoop.fs.azure.AzureNativeFileSystemStore;
|
||||||
import org.apache.hadoop.fs.azure.NativeAzureFileSystem;
|
import org.apache.hadoop.fs.azure.NativeAzureFileSystem;
|
||||||
import org.apache.hadoop.fs.azure.metrics.AzureFileSystemInstrumentation;
|
import org.apache.hadoop.fs.azure.metrics.AzureFileSystemInstrumentation;
|
||||||
import org.apache.hadoop.fs.azurebfs.constants.FileSystemUriSchemes;
|
import org.apache.hadoop.fs.azurebfs.constants.FileSystemUriSchemes;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.constants.AbfsHttpConstants;
|
||||||
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.AbfsRestOperationException;
|
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.AbfsRestOperationException;
|
||||||
import org.apache.hadoop.fs.azurebfs.utils.UriUtils;
|
import org.apache.hadoop.fs.azurebfs.utils.UriUtils;
|
||||||
import org.apache.hadoop.fs.contract.ContractTestUtils;
|
import org.apache.hadoop.fs.contract.ContractTestUtils;
|
||||||
|
@ -73,6 +74,7 @@ public abstract class AbstractAbfsIntegrationTest extends
|
||||||
private String accountName;
|
private String accountName;
|
||||||
private String testUrl;
|
private String testUrl;
|
||||||
private AuthType authType;
|
private AuthType authType;
|
||||||
|
private boolean useConfiguredFileSystem = false;
|
||||||
|
|
||||||
protected AbstractAbfsIntegrationTest() throws Exception {
|
protected AbstractAbfsIntegrationTest() throws Exception {
|
||||||
fileSystemName = TEST_CONTAINER_PREFIX + UUID.randomUUID().toString();
|
fileSystemName = TEST_CONTAINER_PREFIX + UUID.randomUUID().toString();
|
||||||
|
@ -134,7 +136,9 @@ public abstract class AbstractAbfsIntegrationTest extends
|
||||||
createFileSystem();
|
createFileSystem();
|
||||||
|
|
||||||
// Only live account without namespace support can run ABFS&WASB compatibility tests
|
// Only live account without namespace support can run ABFS&WASB compatibility tests
|
||||||
if (!isIPAddress && !abfs.getIsNamespaceEnabled()) {
|
if (!isIPAddress
|
||||||
|
&& (abfsConfig.getAuthType(accountName) != AuthType.SAS)
|
||||||
|
&& !abfs.getIsNamespaceEnabled()) {
|
||||||
final URI wasbUri = new URI(abfsUrlToWasbUrl(getTestUrl()));
|
final URI wasbUri = new URI(abfsUrlToWasbUrl(getTestUrl()));
|
||||||
final AzureNativeFileSystemStore azureNativeFileSystemStore =
|
final AzureNativeFileSystemStore azureNativeFileSystemStore =
|
||||||
new AzureNativeFileSystemStore();
|
new AzureNativeFileSystemStore();
|
||||||
|
@ -167,19 +171,21 @@ public abstract class AbstractAbfsIntegrationTest extends
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final AzureBlobFileSystemStore abfsStore = abfs.getAbfsStore();
|
// Delete all uniquely created filesystem from the account
|
||||||
abfsStore.deleteFilesystem();
|
if (!useConfiguredFileSystem) {
|
||||||
|
final AzureBlobFileSystemStore abfsStore = abfs.getAbfsStore();
|
||||||
|
abfsStore.deleteFilesystem();
|
||||||
|
|
||||||
AbfsRestOperationException ex = intercept(
|
AbfsRestOperationException ex = intercept(AbfsRestOperationException.class,
|
||||||
AbfsRestOperationException.class,
|
new Callable<Hashtable<String, String>>() {
|
||||||
new Callable<Hashtable<String, String>>() {
|
@Override
|
||||||
@Override
|
public Hashtable<String, String> call() throws Exception {
|
||||||
public Hashtable<String, String> call() throws Exception {
|
return abfsStore.getFilesystemProperties();
|
||||||
return abfsStore.getFilesystemProperties();
|
}
|
||||||
}
|
});
|
||||||
});
|
if (FILE_SYSTEM_NOT_FOUND.getStatusCode() != ex.getStatusCode()) {
|
||||||
if (FILE_SYSTEM_NOT_FOUND.getStatusCode() != ex.getStatusCode()) {
|
LOG.warn("Deleted test filesystem may still exist: {}", abfs, ex);
|
||||||
LOG.warn("Deleted test filesystem may still exist: {}", abfs, ex);
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOG.warn("During cleanup: {}", e, e);
|
LOG.warn("During cleanup: {}", e, e);
|
||||||
|
@ -189,6 +195,32 @@ public abstract class AbstractAbfsIntegrationTest extends
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void loadConfiguredFileSystem() throws Exception {
|
||||||
|
// disable auto-creation of filesystem
|
||||||
|
abfsConfig.setBoolean(AZURE_CREATE_REMOTE_FILESYSTEM_DURING_INITIALIZATION,
|
||||||
|
false);
|
||||||
|
|
||||||
|
// AbstractAbfsIntegrationTest always uses a new instance of FileSystem,
|
||||||
|
// need to disable that and force filesystem provided in test configs.
|
||||||
|
String[] authorityParts =
|
||||||
|
(new URI(rawConfig.get(FS_AZURE_CONTRACT_TEST_URI))).getRawAuthority().split(
|
||||||
|
AbfsHttpConstants.AZURE_DISTRIBUTED_FILE_SYSTEM_AUTHORITY_DELIMITER, 2);
|
||||||
|
this.fileSystemName = authorityParts[0];
|
||||||
|
|
||||||
|
// Reset URL with configured filesystem
|
||||||
|
final String abfsUrl = this.getFileSystemName() + "@" + this.getAccountName();
|
||||||
|
URI defaultUri = null;
|
||||||
|
|
||||||
|
defaultUri = new URI(abfsScheme, abfsUrl, null, null, null);
|
||||||
|
|
||||||
|
this.testUrl = defaultUri.toString();
|
||||||
|
abfsConfig.set(CommonConfigurationKeysPublic.FS_DEFAULT_NAME_KEY,
|
||||||
|
defaultUri.toString());
|
||||||
|
|
||||||
|
useConfiguredFileSystem = true;
|
||||||
|
}
|
||||||
|
|
||||||
public AzureBlobFileSystem getFileSystem() throws IOException {
|
public AzureBlobFileSystem getFileSystem() throws IOException {
|
||||||
return abfs;
|
return abfs;
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,7 +62,6 @@ public class ITestAbfsIdentityTransformer extends AbstractAbfsScaleTest{
|
||||||
|
|
||||||
public ITestAbfsIdentityTransformer() throws Exception {
|
public ITestAbfsIdentityTransformer() throws Exception {
|
||||||
super();
|
super();
|
||||||
UserGroupInformation.reset();
|
|
||||||
userGroupInfo = UserGroupInformation.getCurrentUser();
|
userGroupInfo = UserGroupInformation.getCurrentUser();
|
||||||
localUser = userGroupInfo.getShortUserName();
|
localUser = userGroupInfo.getShortUserName();
|
||||||
localGroup = userGroupInfo.getPrimaryGroupName();
|
localGroup = userGroupInfo.getPrimaryGroupName();
|
||||||
|
|
|
@ -18,53 +18,106 @@
|
||||||
|
|
||||||
package org.apache.hadoop.fs.azurebfs;
|
package org.apache.hadoop.fs.azurebfs;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.junit.Assume;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.apache.hadoop.conf.Configuration;
|
||||||
|
|
||||||
import org.apache.hadoop.fs.Path;
|
import org.apache.hadoop.fs.Path;
|
||||||
import org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys;
|
import org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys;
|
||||||
import org.apache.hadoop.fs.azurebfs.extensions.AbfsAuthorizationException;
|
import org.apache.hadoop.fs.azurebfs.constants.TestConfigurationKeys;
|
||||||
import org.apache.hadoop.fs.permission.AclEntry;
|
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.SASTokenProviderException;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.TokenAccessProviderException;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.extensions.MockSASTokenProvider;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.services.AuthType;
|
||||||
import org.apache.hadoop.fs.permission.FsAction;
|
import org.apache.hadoop.fs.permission.FsAction;
|
||||||
import org.apache.hadoop.fs.permission.FsPermission;
|
import org.apache.hadoop.fs.permission.FsPermission;
|
||||||
|
|
||||||
import static org.apache.hadoop.fs.azurebfs.extensions.MockAbfsAuthorizer.*;
|
import static org.apache.hadoop.fs.azurebfs.constants.TestConfigurationKeys.MOCK_SASTOKENPROVIDER_FAIL_INIT;
|
||||||
|
import static org.apache.hadoop.fs.azurebfs.constants.TestConfigurationKeys.MOCK_SASTOKENPROVIDER_RETURN_EMPTY_SAS_TOKEN;
|
||||||
import static org.apache.hadoop.fs.azurebfs.utils.AclTestHelpers.aclEntry;
|
import static org.apache.hadoop.fs.azurebfs.utils.AclTestHelpers.aclEntry;
|
||||||
import static org.apache.hadoop.fs.permission.AclEntryScope.ACCESS;
|
import static org.apache.hadoop.fs.permission.AclEntryScope.ACCESS;
|
||||||
import static org.apache.hadoop.fs.permission.AclEntryType.GROUP;
|
import static org.apache.hadoop.fs.permission.AclEntryType.GROUP;
|
||||||
import static org.apache.hadoop.test.LambdaTestUtils.intercept;
|
import static org.apache.hadoop.test.LambdaTestUtils.intercept;
|
||||||
import static org.junit.Assume.assumeTrue;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test Perform Authorization Check operation
|
* Test Perform Authorization Check operation
|
||||||
*/
|
*/
|
||||||
public class ITestAzureBlobFileSystemAuthorization extends AbstractAbfsIntegrationTest {
|
public class ITestAzureBlobFileSystemAuthorization extends AbstractAbfsIntegrationTest {
|
||||||
|
|
||||||
private static final Path TEST_READ_ONLY_FILE_PATH_0 = new Path(TEST_READ_ONLY_FILE_0);
|
private static final String TEST_AUTHZ_CLASS = "org.apache.hadoop.fs.azurebfs.extensions.MockSASTokenProvider";
|
||||||
private static final Path TEST_READ_ONLY_FOLDER_PATH = new Path(TEST_READ_ONLY_FOLDER);
|
private static final String TEST_ERR_AUTHZ_CLASS = "org.apache.hadoop.fs.azurebfs.extensions.MockErrorSASTokenProvider";
|
||||||
private static final Path TEST_WRITE_ONLY_FILE_PATH_0 = new Path(TEST_WRITE_ONLY_FILE_0);
|
|
||||||
private static final Path TEST_WRITE_ONLY_FILE_PATH_1 = new Path(TEST_WRITE_ONLY_FILE_1);
|
|
||||||
private static final Path TEST_READ_WRITE_FILE_PATH_0 = new Path(TEST_READ_WRITE_FILE_0);
|
|
||||||
private static final Path TEST_READ_WRITE_FILE_PATH_1 = new Path(TEST_READ_WRITE_FILE_1);
|
|
||||||
private static final Path TEST_WRITE_ONLY_FOLDER_PATH = new Path(TEST_WRITE_ONLY_FOLDER);
|
|
||||||
private static final Path TEST_WRITE_THEN_READ_ONLY_PATH = new Path(TEST_WRITE_THEN_READ_ONLY);
|
|
||||||
private static final String TEST_AUTHZ_CLASS = "org.apache.hadoop.fs.azurebfs.extensions.MockAbfsAuthorizer";
|
|
||||||
private static final String TEST_USER = UUID.randomUUID().toString();
|
private static final String TEST_USER = UUID.randomUUID().toString();
|
||||||
private static final String TEST_GROUP = UUID.randomUUID().toString();
|
private static final String TEST_GROUP = UUID.randomUUID().toString();
|
||||||
private static final String BAR = UUID.randomUUID().toString();
|
private static final String BAR = UUID.randomUUID().toString();
|
||||||
|
|
||||||
public ITestAzureBlobFileSystemAuthorization() throws Exception {
|
public ITestAzureBlobFileSystemAuthorization() throws Exception {
|
||||||
|
// The mock SAS token provider relies on the account key to generate SAS.
|
||||||
|
Assume.assumeTrue(this.getAuthType() == AuthType.SharedKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setup() throws Exception {
|
public void setup() throws Exception {
|
||||||
this.getConfiguration().set(ConfigurationKeys.ABFS_EXTERNAL_AUTHORIZATION_CLASS, TEST_AUTHZ_CLASS);
|
boolean isHNSEnabled = this.getConfiguration().getBoolean(
|
||||||
|
TestConfigurationKeys.FS_AZURE_TEST_NAMESPACE_ENABLED_ACCOUNT, false);
|
||||||
|
Assume.assumeTrue(isHNSEnabled);
|
||||||
|
loadConfiguredFileSystem();
|
||||||
|
this.getConfiguration().set(ConfigurationKeys.FS_AZURE_SAS_TOKEN_PROVIDER_TYPE, TEST_AUTHZ_CLASS);
|
||||||
|
this.getConfiguration().set(ConfigurationKeys.FS_AZURE_ACCOUNT_AUTH_TYPE_PROPERTY_NAME, "SAS");
|
||||||
super.setup();
|
super.setup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSASTokenProviderInitializeException() throws Exception {
|
||||||
|
final AzureBlobFileSystem fs = this.getFileSystem();
|
||||||
|
|
||||||
|
final AzureBlobFileSystem testFs = new AzureBlobFileSystem();
|
||||||
|
Configuration testConfig = this.getConfiguration().getRawConfiguration();
|
||||||
|
testConfig.set(ConfigurationKeys.FS_AZURE_SAS_TOKEN_PROVIDER_TYPE, TEST_ERR_AUTHZ_CLASS);
|
||||||
|
testConfig.set(MOCK_SASTOKENPROVIDER_FAIL_INIT, "true");
|
||||||
|
|
||||||
|
intercept(TokenAccessProviderException.class,
|
||||||
|
()-> {
|
||||||
|
testFs.initialize(fs.getUri(), this.getConfiguration().getRawConfiguration());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSASTokenProviderEmptySASToken() throws Exception {
|
||||||
|
final AzureBlobFileSystem fs = this.getFileSystem();
|
||||||
|
|
||||||
|
final AzureBlobFileSystem testFs = new AzureBlobFileSystem();
|
||||||
|
Configuration testConfig = this.getConfiguration().getRawConfiguration();
|
||||||
|
testConfig.set(ConfigurationKeys.FS_AZURE_SAS_TOKEN_PROVIDER_TYPE, TEST_ERR_AUTHZ_CLASS);
|
||||||
|
testConfig.set(MOCK_SASTOKENPROVIDER_RETURN_EMPTY_SAS_TOKEN, "true");
|
||||||
|
|
||||||
|
testFs.initialize(fs.getUri(),
|
||||||
|
this.getConfiguration().getRawConfiguration());
|
||||||
|
intercept(SASTokenProviderException.class,
|
||||||
|
() -> {
|
||||||
|
testFs.create(new org.apache.hadoop.fs.Path("/testFile"));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSASTokenProviderNullSASToken() throws Exception {
|
||||||
|
final AzureBlobFileSystem fs = this.getFileSystem();
|
||||||
|
|
||||||
|
final AzureBlobFileSystem testFs = new AzureBlobFileSystem();
|
||||||
|
Configuration testConfig = this.getConfiguration().getRawConfiguration();
|
||||||
|
testConfig.set(ConfigurationKeys.FS_AZURE_SAS_TOKEN_PROVIDER_TYPE, TEST_ERR_AUTHZ_CLASS);
|
||||||
|
|
||||||
|
testFs.initialize(fs.getUri(), this.getConfiguration().getRawConfiguration());
|
||||||
|
intercept(SASTokenProviderException.class,
|
||||||
|
()-> {
|
||||||
|
testFs.create(new org.apache.hadoop.fs.Path("/testFile"));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOpenFileWithInvalidPath() throws Exception {
|
public void testOpenFileWithInvalidPath() throws Exception {
|
||||||
final AzureBlobFileSystem fs = this.getFileSystem();
|
final AzureBlobFileSystem fs = this.getFileSystem();
|
||||||
|
@ -76,291 +129,232 @@ public class ITestAzureBlobFileSystemAuthorization extends AbstractAbfsIntegrati
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOpenFileAuthorized() throws Exception {
|
public void testOpenFileAuthorized() throws Exception {
|
||||||
final AzureBlobFileSystem fs = this.getFileSystem();
|
runTest(FileSystemOperations.Open, false);
|
||||||
fs.create(TEST_WRITE_THEN_READ_ONLY_PATH).close();
|
|
||||||
fs.open(TEST_WRITE_THEN_READ_ONLY_PATH).close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOpenFileUnauthorized() throws Exception {
|
public void testOpenFileUnauthorized() throws Exception {
|
||||||
final AzureBlobFileSystem fs = this.getFileSystem();
|
runTest(FileSystemOperations.Open, true);
|
||||||
fs.create(TEST_WRITE_ONLY_FILE_PATH_0).close();
|
|
||||||
intercept(AbfsAuthorizationException.class,
|
|
||||||
()-> {
|
|
||||||
fs.open(TEST_WRITE_ONLY_FILE_PATH_0).close();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCreateFileAuthorized() throws Exception {
|
public void testCreateFileAuthorized() throws Exception {
|
||||||
final AzureBlobFileSystem fs = this.getFileSystem();
|
runTest(FileSystemOperations.CreatePath, false);
|
||||||
fs.create(TEST_WRITE_ONLY_FILE_PATH_0).close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCreateFileUnauthorized() throws Exception {
|
public void testCreateFileUnauthorized() throws Exception {
|
||||||
final AzureBlobFileSystem fs = this.getFileSystem();
|
runTest(FileSystemOperations.CreatePath, true);
|
||||||
intercept(AbfsAuthorizationException.class,
|
|
||||||
()-> {
|
|
||||||
fs.create(TEST_READ_ONLY_FILE_PATH_0).close();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAppendFileAuthorized() throws Exception {
|
public void testAppendFileAuthorized() throws Exception {
|
||||||
final AzureBlobFileSystem fs = this.getFileSystem();
|
runTest(FileSystemOperations.Append, false);
|
||||||
fs.create(TEST_WRITE_ONLY_FILE_PATH_0).close();
|
|
||||||
fs.append(TEST_WRITE_ONLY_FILE_PATH_0).close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAppendFileUnauthorized() throws Exception {
|
public void testAppendFileUnauthorized() throws Exception {
|
||||||
final AzureBlobFileSystem fs = this.getFileSystem();
|
runTest(FileSystemOperations.Append, true);
|
||||||
fs.create(TEST_WRITE_THEN_READ_ONLY_PATH).close();
|
|
||||||
intercept(AbfsAuthorizationException.class,
|
|
||||||
()-> {
|
|
||||||
fs.append(TEST_WRITE_THEN_READ_ONLY_PATH).close();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRenameAuthorized() throws Exception {
|
public void testRenameAuthorized() throws Exception {
|
||||||
final AzureBlobFileSystem fs = this.getFileSystem();
|
runTest(FileSystemOperations.RenamePath, false);
|
||||||
fs.rename(TEST_READ_WRITE_FILE_PATH_0, TEST_READ_WRITE_FILE_PATH_1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRenameUnauthorized() throws Exception {
|
public void testRenameUnauthorized() throws Exception {
|
||||||
final AzureBlobFileSystem fs = this.getFileSystem();
|
runTest(FileSystemOperations.RenamePath, true);
|
||||||
intercept(AbfsAuthorizationException.class,
|
|
||||||
()-> {
|
|
||||||
fs.rename(TEST_WRITE_ONLY_FILE_PATH_0, TEST_WRITE_ONLY_FILE_PATH_1);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDeleteFileAuthorized() throws Exception {
|
public void testDeleteFileAuthorized() throws Exception {
|
||||||
final AzureBlobFileSystem fs = this.getFileSystem();
|
runTest(FileSystemOperations.DeletePath, false);
|
||||||
fs.create(TEST_WRITE_ONLY_FILE_PATH_0).close();
|
|
||||||
fs.delete(TEST_WRITE_ONLY_FILE_PATH_0, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDeleteFileUnauthorized() throws Exception {
|
public void testDeleteFileUnauthorized() throws Exception {
|
||||||
final AzureBlobFileSystem fs = this.getFileSystem();
|
runTest(FileSystemOperations.DeletePath, true);
|
||||||
fs.create(TEST_WRITE_THEN_READ_ONLY_PATH).close();
|
|
||||||
intercept(AbfsAuthorizationException.class,
|
|
||||||
()-> {
|
|
||||||
fs.delete(TEST_WRITE_THEN_READ_ONLY_PATH, false);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testListStatusAuthorized() throws Exception {
|
public void testListStatusAuthorized() throws Exception {
|
||||||
final AzureBlobFileSystem fs = getFileSystem();
|
runTest(FileSystemOperations.ListPaths, false);
|
||||||
fs.create(TEST_WRITE_THEN_READ_ONLY_PATH).close();
|
|
||||||
fs.listStatus(TEST_WRITE_THEN_READ_ONLY_PATH);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testListStatusUnauthorized() throws Exception {
|
public void testListStatusUnauthorized() throws Exception {
|
||||||
final AzureBlobFileSystem fs = getFileSystem();
|
runTest(FileSystemOperations.ListPaths, true);
|
||||||
fs.create(TEST_WRITE_ONLY_FILE_PATH_0).close();
|
|
||||||
intercept(AbfsAuthorizationException.class,
|
|
||||||
()-> {
|
|
||||||
fs.listStatus(TEST_WRITE_ONLY_FILE_PATH_0);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMkDirsAuthorized() throws Exception {
|
public void testMkDirsAuthorized() throws Exception {
|
||||||
final AzureBlobFileSystem fs = getFileSystem();
|
runTest(FileSystemOperations.Mkdir, false);
|
||||||
fs.mkdirs(TEST_WRITE_ONLY_FOLDER_PATH, new FsPermission(FsAction.ALL, FsAction.NONE, FsAction.NONE));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMkDirsUnauthorized() throws Exception {
|
public void testMkDirsUnauthorized() throws Exception {
|
||||||
final AzureBlobFileSystem fs = getFileSystem();
|
runTest(FileSystemOperations.Mkdir, true);
|
||||||
intercept(AbfsAuthorizationException.class,
|
|
||||||
()-> {
|
|
||||||
fs.mkdirs(TEST_READ_ONLY_FOLDER_PATH, new FsPermission(FsAction.ALL, FsAction.NONE, FsAction.NONE));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetFileStatusAuthorized() throws Exception {
|
public void testGetFileStatusAuthorized() throws Exception {
|
||||||
final AzureBlobFileSystem fs = getFileSystem();
|
runTest(FileSystemOperations.GetPathStatus, false);
|
||||||
fs.create(TEST_WRITE_THEN_READ_ONLY_PATH).close();
|
|
||||||
fs.getFileStatus(TEST_WRITE_THEN_READ_ONLY_PATH);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetFileStatusUnauthorized() throws Exception {
|
public void testGetFileStatusUnauthorized() throws Exception {
|
||||||
final AzureBlobFileSystem fs = getFileSystem();
|
runTest(FileSystemOperations.GetPathStatus, true);
|
||||||
fs.create(TEST_WRITE_ONLY_FILE_PATH_0).close();
|
|
||||||
intercept(AbfsAuthorizationException.class,
|
|
||||||
()-> {
|
|
||||||
fs.getFileStatus(TEST_WRITE_ONLY_FILE_PATH_0);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSetOwnerAuthorized() throws Exception {
|
|
||||||
final AzureBlobFileSystem fs = getFileSystem();
|
|
||||||
assumeTrue("This test case only runs when namespace is enabled", fs.getIsNamespaceEnabled());
|
|
||||||
fs.create(TEST_WRITE_ONLY_FILE_PATH_0).close();
|
|
||||||
fs.setOwner(TEST_WRITE_ONLY_FILE_PATH_0, TEST_USER, TEST_GROUP);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSetOwnerUnauthorized() throws Exception {
|
public void testSetOwnerUnauthorized() throws Exception {
|
||||||
final AzureBlobFileSystem fs = getFileSystem();
|
runTest(FileSystemOperations.SetOwner, true);
|
||||||
assumeTrue("This test case only runs when namespace is enabled", fs.getIsNamespaceEnabled());
|
|
||||||
fs.create(TEST_WRITE_THEN_READ_ONLY_PATH).close();
|
|
||||||
intercept(AbfsAuthorizationException.class,
|
|
||||||
()-> {
|
|
||||||
fs.setOwner(TEST_WRITE_THEN_READ_ONLY_PATH, TEST_USER, TEST_GROUP);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSetPermissionAuthorized() throws Exception {
|
|
||||||
final AzureBlobFileSystem fs = getFileSystem();
|
|
||||||
assumeTrue("This test case only runs when namespace is enabled", fs.getIsNamespaceEnabled());
|
|
||||||
fs.create(TEST_WRITE_ONLY_FILE_PATH_0).close();
|
|
||||||
fs.setPermission(TEST_WRITE_ONLY_FILE_PATH_0, new FsPermission(FsAction.ALL, FsAction.NONE, FsAction.NONE));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSetPermissionUnauthorized() throws Exception {
|
public void testSetPermissionUnauthorized() throws Exception {
|
||||||
final AzureBlobFileSystem fs = getFileSystem();
|
runTest(FileSystemOperations.SetPermissions, true);
|
||||||
assumeTrue("This test case only runs when namespace is enabled", fs.getIsNamespaceEnabled());
|
|
||||||
fs.create(TEST_WRITE_THEN_READ_ONLY_PATH).close();
|
|
||||||
intercept(AbfsAuthorizationException.class,
|
|
||||||
()-> {
|
|
||||||
fs.setPermission(TEST_WRITE_THEN_READ_ONLY_PATH, new FsPermission(FsAction.ALL, FsAction.NONE, FsAction.NONE));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testModifyAclEntriesAuthorized() throws Exception {
|
|
||||||
final AzureBlobFileSystem fs = getFileSystem();
|
|
||||||
assumeTrue("This test case only runs when namespace is enabled", fs.getIsNamespaceEnabled());
|
|
||||||
fs.create(TEST_WRITE_ONLY_FILE_PATH_0).close();
|
|
||||||
List<AclEntry> aclSpec = Arrays.asList(aclEntry(ACCESS, GROUP, BAR, FsAction.ALL));
|
|
||||||
fs.modifyAclEntries(TEST_WRITE_ONLY_FILE_PATH_0, aclSpec);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testModifyAclEntriesUnauthorized() throws Exception {
|
public void testModifyAclEntriesUnauthorized() throws Exception {
|
||||||
final AzureBlobFileSystem fs = getFileSystem();
|
runTest(FileSystemOperations.ModifyAclEntries, true);
|
||||||
assumeTrue("This test case only runs when namespace is enabled", fs.getIsNamespaceEnabled());
|
|
||||||
fs.create(TEST_WRITE_THEN_READ_ONLY_PATH).close();
|
|
||||||
List<AclEntry> aclSpec = Arrays.asList(aclEntry(ACCESS, GROUP, BAR, FsAction.ALL));
|
|
||||||
intercept(AbfsAuthorizationException.class,
|
|
||||||
()-> {
|
|
||||||
fs.modifyAclEntries(TEST_WRITE_THEN_READ_ONLY_PATH, aclSpec);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testRemoveAclEntriesAuthorized() throws Exception {
|
|
||||||
final AzureBlobFileSystem fs = getFileSystem();
|
|
||||||
assumeTrue("This test case only runs when namespace is enabled", fs.getIsNamespaceEnabled());
|
|
||||||
fs.create(TEST_WRITE_ONLY_FILE_PATH_0).close();
|
|
||||||
List<AclEntry> aclSpec = Arrays.asList(aclEntry(ACCESS, GROUP, BAR, FsAction.ALL));
|
|
||||||
fs.removeAclEntries(TEST_WRITE_ONLY_FILE_PATH_0, aclSpec);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRemoveAclEntriesUnauthorized() throws Exception {
|
public void testRemoveAclEntriesUnauthorized() throws Exception {
|
||||||
final AzureBlobFileSystem fs = getFileSystem();
|
runTest(FileSystemOperations.RemoveAclEntries, true);
|
||||||
assumeTrue("This test case only runs when namespace is enabled", fs.getIsNamespaceEnabled());
|
|
||||||
fs.create(TEST_WRITE_THEN_READ_ONLY_PATH).close();
|
|
||||||
List<AclEntry> aclSpec = Arrays.asList(aclEntry(ACCESS, GROUP, BAR, FsAction.ALL));
|
|
||||||
intercept(AbfsAuthorizationException.class,
|
|
||||||
()-> {
|
|
||||||
fs.removeAclEntries(TEST_WRITE_THEN_READ_ONLY_PATH, aclSpec);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testRemoveDefaultAclAuthorized() throws Exception {
|
|
||||||
final AzureBlobFileSystem fs = getFileSystem();
|
|
||||||
assumeTrue("This test case only runs when namespace is enabled", fs.getIsNamespaceEnabled());
|
|
||||||
fs.create(TEST_WRITE_ONLY_FILE_PATH_0).close();
|
|
||||||
fs.removeDefaultAcl(TEST_WRITE_ONLY_FILE_PATH_0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRemoveDefaultAclUnauthorized() throws Exception {
|
public void testRemoveDefaultAclUnauthorized() throws Exception {
|
||||||
final AzureBlobFileSystem fs = getFileSystem();
|
runTest(FileSystemOperations.RemoveDefaultAcl, true);
|
||||||
assumeTrue("This test case only runs when namespace is enabled", fs.getIsNamespaceEnabled());
|
|
||||||
fs.create(TEST_WRITE_THEN_READ_ONLY_PATH).close();
|
|
||||||
intercept(AbfsAuthorizationException.class,
|
|
||||||
()-> {
|
|
||||||
fs.removeDefaultAcl(TEST_WRITE_THEN_READ_ONLY_PATH);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testRemoveAclAuthorized() throws Exception {
|
|
||||||
final AzureBlobFileSystem fs = getFileSystem();
|
|
||||||
assumeTrue("This test case only runs when namespace is enabled", fs.getIsNamespaceEnabled());
|
|
||||||
fs.create(TEST_WRITE_ONLY_FILE_PATH_0).close();
|
|
||||||
fs.removeAcl(TEST_WRITE_ONLY_FILE_PATH_0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRemoveAclUnauthorized() throws Exception {
|
public void testRemoveAclUnauthorized() throws Exception {
|
||||||
final AzureBlobFileSystem fs = getFileSystem();
|
runTest(FileSystemOperations.RemoveAcl, true);
|
||||||
assumeTrue("This test case only runs when namespace is enabled", fs.getIsNamespaceEnabled());
|
|
||||||
fs.create(TEST_WRITE_THEN_READ_ONLY_PATH).close();
|
|
||||||
intercept(AbfsAuthorizationException.class,
|
|
||||||
()-> {
|
|
||||||
fs.removeAcl(TEST_WRITE_THEN_READ_ONLY_PATH);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSetAclAuthorized() throws Exception {
|
|
||||||
final AzureBlobFileSystem fs = getFileSystem();
|
|
||||||
assumeTrue("This test case only runs when namespace is enabled", fs.getIsNamespaceEnabled());
|
|
||||||
fs.create(TEST_WRITE_ONLY_FILE_PATH_0).close();
|
|
||||||
List<AclEntry> aclSpec = Arrays.asList(aclEntry(ACCESS, GROUP, BAR, FsAction.ALL));
|
|
||||||
fs.setAcl(TEST_WRITE_ONLY_FILE_PATH_0, aclSpec);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSetAclUnauthorized() throws Exception {
|
public void testSetAclUnauthorized() throws Exception {
|
||||||
final AzureBlobFileSystem fs = getFileSystem();
|
runTest(FileSystemOperations.SetAcl, true);
|
||||||
assumeTrue("This test case only runs when namespace is enabled", fs.getIsNamespaceEnabled());
|
|
||||||
fs.create(TEST_WRITE_THEN_READ_ONLY_PATH).close();
|
|
||||||
List<AclEntry> aclSpec = Arrays.asList(aclEntry(ACCESS, GROUP, BAR, FsAction.ALL));
|
|
||||||
intercept(AbfsAuthorizationException.class,
|
|
||||||
()-> {
|
|
||||||
fs.setAcl(TEST_WRITE_THEN_READ_ONLY_PATH, aclSpec);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetAclStatusAuthorized() throws Exception {
|
public void testGetAclStatusAuthorized() throws Exception {
|
||||||
final AzureBlobFileSystem fs = getFileSystem();
|
runTest(FileSystemOperations.GetAcl, false);
|
||||||
assumeTrue("This test case only runs when namespace is enabled", fs.getIsNamespaceEnabled());
|
|
||||||
fs.create(TEST_WRITE_THEN_READ_ONLY_PATH).close();
|
|
||||||
List<AclEntry> aclSpec = Arrays.asList(aclEntry(ACCESS, GROUP, BAR, FsAction.ALL));
|
|
||||||
fs.getAclStatus(TEST_WRITE_THEN_READ_ONLY_PATH);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetAclStatusUnauthorized() throws Exception {
|
public void testGetAclStatusUnauthorized() throws Exception {
|
||||||
|
runTest(FileSystemOperations.GetAcl, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void runTest(FileSystemOperations testOp,
|
||||||
|
boolean expectAbfsAuthorizationException) throws Exception {
|
||||||
final AzureBlobFileSystem fs = getFileSystem();
|
final AzureBlobFileSystem fs = getFileSystem();
|
||||||
assumeTrue("This test case only runs when namespace is enabled", fs.getIsNamespaceEnabled());
|
|
||||||
fs.create(TEST_WRITE_ONLY_FILE_PATH_0).close();
|
Path reqPath = new Path("requestPath"
|
||||||
List<AclEntry> aclSpec = Arrays.asList(aclEntry(ACCESS, GROUP, BAR, FsAction.ALL));
|
+ UUID.randomUUID().toString()
|
||||||
intercept(AbfsAuthorizationException.class,
|
+ (expectAbfsAuthorizationException ? "unauthorized":""));
|
||||||
()-> {
|
|
||||||
fs.getAclStatus(TEST_WRITE_ONLY_FILE_PATH_0);
|
getMockSASTokenProvider(fs).setSkipAuthorizationForTestSetup(true);
|
||||||
});
|
if ((testOp != FileSystemOperations.CreatePath)
|
||||||
|
&& (testOp != FileSystemOperations.Mkdir)) {
|
||||||
|
fs.create(reqPath).close();
|
||||||
|
fs.getFileStatus(reqPath);
|
||||||
|
}
|
||||||
|
getMockSASTokenProvider(fs).setSkipAuthorizationForTestSetup(false);
|
||||||
|
|
||||||
|
// Test Operation
|
||||||
|
if (expectAbfsAuthorizationException) {
|
||||||
|
intercept(SASTokenProviderException.class, () -> {
|
||||||
|
executeOp(reqPath, fs, testOp);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
executeOp(reqPath, fs, testOp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void executeOp(Path reqPath, AzureBlobFileSystem fs,
|
||||||
|
FileSystemOperations op) throws IOException, IOException {
|
||||||
|
|
||||||
|
|
||||||
|
switch (op) {
|
||||||
|
case ListPaths:
|
||||||
|
fs.listStatus(reqPath);
|
||||||
|
break;
|
||||||
|
case CreatePath:
|
||||||
|
fs.create(reqPath);
|
||||||
|
break;
|
||||||
|
case RenamePath:
|
||||||
|
fs.rename(reqPath,
|
||||||
|
new Path("renameDest" + UUID.randomUUID().toString()));
|
||||||
|
break;
|
||||||
|
case GetAcl:
|
||||||
|
fs.getAclStatus(reqPath);
|
||||||
|
break;
|
||||||
|
case GetPathStatus:
|
||||||
|
fs.getFileStatus(reqPath);
|
||||||
|
break;
|
||||||
|
case SetAcl:
|
||||||
|
fs.setAcl(reqPath, Arrays
|
||||||
|
.asList(aclEntry(ACCESS, GROUP, BAR, FsAction.ALL)));
|
||||||
|
break;
|
||||||
|
case SetOwner:
|
||||||
|
fs.setOwner(reqPath, TEST_USER, TEST_GROUP);
|
||||||
|
break;
|
||||||
|
case SetPermissions:
|
||||||
|
fs.setPermission(reqPath,
|
||||||
|
new FsPermission(FsAction.ALL, FsAction.NONE, FsAction.NONE));
|
||||||
|
break;
|
||||||
|
case Append:
|
||||||
|
fs.append(reqPath);
|
||||||
|
break;
|
||||||
|
case ReadFile:
|
||||||
|
fs.open(reqPath);
|
||||||
|
break;
|
||||||
|
case Open:
|
||||||
|
fs.open(reqPath);
|
||||||
|
break;
|
||||||
|
case DeletePath:
|
||||||
|
fs.delete(reqPath, false);
|
||||||
|
break;
|
||||||
|
case Mkdir:
|
||||||
|
fs.mkdirs(reqPath,
|
||||||
|
new FsPermission(FsAction.ALL, FsAction.NONE, FsAction.NONE));
|
||||||
|
break;
|
||||||
|
case RemoveAclEntries:
|
||||||
|
fs.removeAclEntries(reqPath, Arrays
|
||||||
|
.asList(aclEntry(ACCESS, GROUP, BAR, FsAction.ALL)));
|
||||||
|
break;
|
||||||
|
case ModifyAclEntries:
|
||||||
|
fs.modifyAclEntries(reqPath, Arrays
|
||||||
|
.asList(aclEntry(ACCESS, GROUP, BAR, FsAction.ALL)));
|
||||||
|
break;
|
||||||
|
case RemoveAcl:
|
||||||
|
fs.removeAcl(reqPath);
|
||||||
|
break;
|
||||||
|
case RemoveDefaultAcl:
|
||||||
|
fs.removeDefaultAcl(reqPath);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException("Unexpected value: " + op);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private MockSASTokenProvider getMockSASTokenProvider(AzureBlobFileSystem fs)
|
||||||
|
throws Exception {
|
||||||
|
return ((MockSASTokenProvider) fs.getAbfsStore().getClient().getSasTokenProvider());
|
||||||
|
}
|
||||||
|
|
||||||
|
enum FileSystemOperations {
|
||||||
|
None, ListPaths, CreatePath, RenamePath, GetAcl, GetPathStatus, SetAcl,
|
||||||
|
SetOwner, SetPermissions, Append, ReadFile, DeletePath, Mkdir,
|
||||||
|
RemoveAclEntries, RemoveDefaultAcl, RemoveAcl, ModifyAclEntries,
|
||||||
|
Open
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,9 @@ public final class TestConfigurationKeys {
|
||||||
|
|
||||||
public static final String FS_AZURE_BLOB_FS_CHECKACCESS_TEST_USER_GUID = "fs.azure.check.access.testuser.guid";
|
public static final String FS_AZURE_BLOB_FS_CHECKACCESS_TEST_USER_GUID = "fs.azure.check.access.testuser.guid";
|
||||||
|
|
||||||
|
public static final String MOCK_SASTOKENPROVIDER_FAIL_INIT = "mock.sastokenprovider.fail.init";
|
||||||
|
public static final String MOCK_SASTOKENPROVIDER_RETURN_EMPTY_SAS_TOKEN = "mock.sastokenprovider.return.empty.sasToken";
|
||||||
|
|
||||||
public static final String TEST_CONFIGURATION_FILE_NAME = "azure-test.xml";
|
public static final String TEST_CONFIGURATION_FILE_NAME = "azure-test.xml";
|
||||||
public static final String TEST_CONTAINER_PREFIX = "abfs-testcontainer-";
|
public static final String TEST_CONTAINER_PREFIX = "abfs-testcontainer-";
|
||||||
public static final int TEST_TIMEOUT = 15 * 60 * 1000;
|
public static final int TEST_TIMEOUT = 15 * 60 * 1000;
|
||||||
|
|
|
@ -1,87 +0,0 @@
|
||||||
/**
|
|
||||||
* 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.azurebfs.extensions;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import org.apache.hadoop.conf.Configuration;
|
|
||||||
import org.apache.hadoop.fs.Path;
|
|
||||||
import org.apache.hadoop.fs.permission.FsAction;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A mock Azure Blob File System Authorization Implementation
|
|
||||||
*/
|
|
||||||
public class MockAbfsAuthorizer implements AbfsAuthorizer {
|
|
||||||
|
|
||||||
public static final String TEST_READ_ONLY_FILE_0 = "readOnlyFile0";
|
|
||||||
public static final String TEST_READ_ONLY_FILE_1 = "readOnlyFile1";
|
|
||||||
public static final String TEST_READ_ONLY_FOLDER = "readOnlyFolder";
|
|
||||||
public static final String TEST_WRITE_ONLY_FILE_0 = "writeOnlyFile0";
|
|
||||||
public static final String TEST_WRITE_ONLY_FILE_1 = "writeOnlyFile1";
|
|
||||||
public static final String TEST_WRITE_ONLY_FOLDER = "writeOnlyFolder";
|
|
||||||
public static final String TEST_READ_WRITE_FILE_0 = "readWriteFile0";
|
|
||||||
public static final String TEST_READ_WRITE_FILE_1 = "readWriteFile1";
|
|
||||||
public static final String TEST_WRITE_THEN_READ_ONLY = "writeThenReadOnlyFile";
|
|
||||||
private Configuration conf;
|
|
||||||
private Set<Path> readOnlyPaths = new HashSet<Path>();
|
|
||||||
private Set<Path> writeOnlyPaths = new HashSet<Path>();
|
|
||||||
private Set<Path> readWritePaths = new HashSet<Path>();
|
|
||||||
private int writeThenReadOnly = 0;
|
|
||||||
public MockAbfsAuthorizer(Configuration conf) {
|
|
||||||
this.conf = conf;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void init() throws AbfsAuthorizationException, IOException {
|
|
||||||
readOnlyPaths.add(new Path(TEST_READ_ONLY_FILE_0));
|
|
||||||
readOnlyPaths.add(new Path(TEST_READ_ONLY_FILE_1));
|
|
||||||
readOnlyPaths.add(new Path(TEST_READ_ONLY_FOLDER));
|
|
||||||
writeOnlyPaths.add(new Path(TEST_WRITE_ONLY_FILE_0));
|
|
||||||
writeOnlyPaths.add(new Path(TEST_WRITE_ONLY_FILE_1));
|
|
||||||
writeOnlyPaths.add(new Path(TEST_WRITE_ONLY_FOLDER));
|
|
||||||
readWritePaths.add(new Path(TEST_READ_WRITE_FILE_0));
|
|
||||||
readWritePaths.add(new Path(TEST_READ_WRITE_FILE_1));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isAuthorized(FsAction action, Path... absolutePaths) throws AbfsAuthorizationException, IOException {
|
|
||||||
Set<Path> paths = new HashSet<Path>();
|
|
||||||
for (Path path : absolutePaths) {
|
|
||||||
paths.add(new Path(path.getName()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (action.equals(FsAction.READ) && Stream.concat(readOnlyPaths.stream(), readWritePaths.stream()).collect(Collectors.toSet()).containsAll(paths)) {
|
|
||||||
return true;
|
|
||||||
} else if (action.equals(FsAction.READ) && paths.contains(new Path(TEST_WRITE_THEN_READ_ONLY)) && writeThenReadOnly == 1) {
|
|
||||||
return true;
|
|
||||||
} else if (action.equals(FsAction.WRITE)
|
|
||||||
&& Stream.concat(writeOnlyPaths.stream(), readWritePaths.stream()).collect(Collectors.toSet()).containsAll(paths)) {
|
|
||||||
return true;
|
|
||||||
} else if (action.equals(FsAction.WRITE) && paths.contains(new Path(TEST_WRITE_THEN_READ_ONLY)) && writeThenReadOnly == 0) {
|
|
||||||
writeThenReadOnly = 1;
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return action.equals(FsAction.READ_WRITE) && readWritePaths.containsAll(paths);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
/**
|
||||||
|
* 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.azurebfs.extensions;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.apache.hadoop.conf.Configuration;
|
||||||
|
|
||||||
|
import static org.apache.hadoop.fs.azurebfs.constants.TestConfigurationKeys.MOCK_SASTOKENPROVIDER_FAIL_INIT;
|
||||||
|
import static org.apache.hadoop.fs.azurebfs.constants.TestConfigurationKeys.MOCK_SASTOKENPROVIDER_RETURN_EMPTY_SAS_TOKEN;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A mock SAS token provider to test error conditions.
|
||||||
|
*/
|
||||||
|
public class MockErrorSASTokenProvider implements SASTokenProvider {
|
||||||
|
Configuration config = null;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize(org.apache.hadoop.conf.Configuration configuration, String accountName) {
|
||||||
|
boolean throwExceptionAtInit = configuration.getBoolean(MOCK_SASTOKENPROVIDER_FAIL_INIT, false);
|
||||||
|
if (throwExceptionAtInit) {
|
||||||
|
throw new RuntimeException("MockSASTokenProvider initialize exception");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.config = configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns null SAS token query or Empty if returnEmptySASToken is set.
|
||||||
|
* @param accountName
|
||||||
|
* @param fileSystem the name of the fileSystem.
|
||||||
|
* @param path the file or directory path.
|
||||||
|
* @param operation the operation to be performed on the path.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getSASToken(String accountName, String fileSystem, String path,
|
||||||
|
String operation) {
|
||||||
|
boolean returnEmptySASTokenQuery = this.config.getBoolean(
|
||||||
|
MOCK_SASTOKENPROVIDER_RETURN_EMPTY_SAS_TOKEN, false);
|
||||||
|
|
||||||
|
if (returnEmptySASTokenQuery) {
|
||||||
|
return "";
|
||||||
|
} else { return null; }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
/**
|
||||||
|
* 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.azurebfs.extensions;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.apache.hadoop.conf.Configuration;
|
||||||
|
import org.apache.hadoop.security.AccessControlException;
|
||||||
|
|
||||||
|
import org.apache.hadoop.fs.azurebfs.AbfsConfiguration;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.utils.Base64;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.utils.SASGenerator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A mock SAS token provider implementation
|
||||||
|
*/
|
||||||
|
public class MockSASTokenProvider implements SASTokenProvider {
|
||||||
|
|
||||||
|
private byte[] accountKey;
|
||||||
|
private SASGenerator generator;
|
||||||
|
private boolean skipAuthorizationForTestSetup = false;
|
||||||
|
|
||||||
|
// For testing we use a container SAS for all operations.
|
||||||
|
private String generateSAS(byte[] accountKey, String accountName, String fileSystemName) {
|
||||||
|
return generator.getContainerSASWithFullControl(accountName, fileSystemName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize(Configuration configuration, String accountName) throws IOException {
|
||||||
|
try {
|
||||||
|
AbfsConfiguration abfsConfig = new AbfsConfiguration(configuration, accountName);
|
||||||
|
accountKey = Base64.decode(abfsConfig.getStorageAccountKey());
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw new IOException(ex);
|
||||||
|
}
|
||||||
|
generator = new SASGenerator(accountKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invokes the authorizer to obtain a SAS token.
|
||||||
|
*
|
||||||
|
* @param accountName the name of the storage account.
|
||||||
|
* @param fileSystem the name of the fileSystem.
|
||||||
|
* @param path the file or directory path.
|
||||||
|
* @param operation the operation to be performed on the path.
|
||||||
|
* @return a SAS token to perform the request operation.
|
||||||
|
* @throws IOException if there is a network error.
|
||||||
|
* @throws AccessControlException if access is denied.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getSASToken(String accountName, String fileSystem, String path,
|
||||||
|
String operation) throws IOException, AccessControlException {
|
||||||
|
if (!isSkipAuthorizationForTestSetup() && path.contains("unauthorized")) {
|
||||||
|
throw new AccessControlException(
|
||||||
|
"The user is not authorized to perform this operation.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return generateSAS(accountKey, accountName, fileSystem);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSkipAuthorizationForTestSetup() {
|
||||||
|
return skipAuthorizationForTestSetup;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSkipAuthorizationForTestSetup(
|
||||||
|
boolean skipAuthorizationForTestSetup) {
|
||||||
|
this.skipAuthorizationForTestSetup = skipAuthorizationForTestSetup;
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,10 +21,12 @@ package org.apache.hadoop.fs.azurebfs.services;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import org.apache.hadoop.fs.azurebfs.contracts.exceptions.AzureBlobFileSystemException;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.apache.hadoop.fs.azurebfs.AbfsConfiguration;
|
import org.apache.hadoop.fs.azurebfs.AbfsConfiguration;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.oauth2.AccessTokenProvider;
|
||||||
import org.apache.hadoop.conf.Configuration;
|
import org.apache.hadoop.conf.Configuration;
|
||||||
import org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys;
|
import org.apache.hadoop.fs.azurebfs.constants.ConfigurationKeys;
|
||||||
import org.apache.hadoop.security.ssl.DelegatingSSLSocketFactory;
|
import org.apache.hadoop.security.ssl.DelegatingSSLSocketFactory;
|
||||||
|
@ -36,14 +38,15 @@ import org.apache.hadoop.util.VersionInfo;
|
||||||
*/
|
*/
|
||||||
public final class TestAbfsClient {
|
public final class TestAbfsClient {
|
||||||
|
|
||||||
private final String accountName = "bogusAccountName";
|
private final String accountName = "bogusAccountName.dfs.core.windows.net";
|
||||||
|
|
||||||
private void validateUserAgent(String expectedPattern,
|
private void validateUserAgent(String expectedPattern,
|
||||||
URL baseUrl,
|
URL baseUrl,
|
||||||
AbfsConfiguration config,
|
AbfsConfiguration config,
|
||||||
boolean includeSSLProvider) {
|
boolean includeSSLProvider)
|
||||||
|
throws AzureBlobFileSystemException {
|
||||||
AbfsClient client = new AbfsClient(baseUrl, null,
|
AbfsClient client = new AbfsClient(baseUrl, null,
|
||||||
config, null, null, null);
|
config, null, (AccessTokenProvider) null, null);
|
||||||
String sslProviderName = null;
|
String sslProviderName = null;
|
||||||
if (includeSSLProvider) {
|
if (includeSSLProvider) {
|
||||||
sslProviderName = DelegatingSSLSocketFactory.getDefaultFactory().getProviderName();
|
sslProviderName = DelegatingSSLSocketFactory.getDefaultFactory().getProviderName();
|
||||||
|
|
|
@ -0,0 +1,129 @@
|
||||||
|
/**
|
||||||
|
* 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.azurebfs.utils;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.util.Locale;
|
||||||
|
import javax.crypto.Mac;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
|
||||||
|
import org.apache.hadoop.fs.azurebfs.constants.AbfsHttpConstants;
|
||||||
|
import org.apache.hadoop.fs.azurebfs.services.AbfsUriQueryBuilder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test container SAS generator.
|
||||||
|
*/
|
||||||
|
public class SASGenerator {
|
||||||
|
|
||||||
|
private static final String HMAC_SHA256 = "HmacSHA256";
|
||||||
|
private static final int TOKEN_START_PERIOD_IN_SECONDS = 5 * 60;
|
||||||
|
private static final int TOKEN_EXPIRY_PERIOD_IN_SECONDS = 24 * 60 * 60;
|
||||||
|
public static final DateTimeFormatter ISO_8601_UTC_DATE_FORMATTER =
|
||||||
|
DateTimeFormatter
|
||||||
|
.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.ROOT)
|
||||||
|
.withZone(ZoneId.of("UTC"));
|
||||||
|
private Mac hmacSha256;
|
||||||
|
private byte[] key;
|
||||||
|
|
||||||
|
public SASGenerator(byte[] key) {
|
||||||
|
this.key = key;
|
||||||
|
initializeMac();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getContainerSASWithFullControl(String accountName, String containerName) {
|
||||||
|
String sp = "rcwdl";
|
||||||
|
String sv = "2018-11-09";
|
||||||
|
String sr = "c";
|
||||||
|
String st = ISO_8601_UTC_DATE_FORMATTER.format(Instant.now().minusSeconds(TOKEN_START_PERIOD_IN_SECONDS));
|
||||||
|
String se =
|
||||||
|
ISO_8601_UTC_DATE_FORMATTER.format(Instant.now().plusSeconds(TOKEN_EXPIRY_PERIOD_IN_SECONDS));
|
||||||
|
|
||||||
|
String signature = computeSignatureForSAS(sp, st, se, sv, "c",
|
||||||
|
accountName, containerName);
|
||||||
|
|
||||||
|
AbfsUriQueryBuilder qb = new AbfsUriQueryBuilder();
|
||||||
|
qb.addQuery("sp", sp);
|
||||||
|
qb.addQuery("st", st);
|
||||||
|
qb.addQuery("se", se);
|
||||||
|
qb.addQuery("sv", sv);
|
||||||
|
qb.addQuery("sr", sr);
|
||||||
|
qb.addQuery("sig", signature);
|
||||||
|
return qb.toString().substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String computeSignatureForSAS(String sp, String st,
|
||||||
|
String se, String sv, String sr, String accountName, String containerName) {
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(sp);
|
||||||
|
sb.append("\n");
|
||||||
|
sb.append(st);
|
||||||
|
sb.append("\n");
|
||||||
|
sb.append(se);
|
||||||
|
sb.append("\n");
|
||||||
|
// canonicalized resource
|
||||||
|
sb.append("/blob/");
|
||||||
|
sb.append(accountName);
|
||||||
|
sb.append("/");
|
||||||
|
sb.append(containerName);
|
||||||
|
sb.append("\n");
|
||||||
|
sb.append("\n"); // si
|
||||||
|
sb.append("\n"); // sip
|
||||||
|
sb.append("\n"); // spr
|
||||||
|
sb.append(sv);
|
||||||
|
sb.append("\n");
|
||||||
|
sb.append(sr);
|
||||||
|
sb.append("\n");
|
||||||
|
sb.append("\n"); // - For optional : rscc - ResponseCacheControl
|
||||||
|
sb.append("\n"); // - For optional : rscd - ResponseContentDisposition
|
||||||
|
sb.append("\n"); // - For optional : rsce - ResponseContentEncoding
|
||||||
|
sb.append("\n"); // - For optional : rscl - ResponseContentLanguage
|
||||||
|
sb.append("\n"); // - For optional : rsct - ResponseContentType
|
||||||
|
|
||||||
|
String stringToSign = sb.toString();
|
||||||
|
return computeHmac256(stringToSign);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeMac() {
|
||||||
|
// Initializes the HMAC-SHA256 Mac and SecretKey.
|
||||||
|
try {
|
||||||
|
hmacSha256 = Mac.getInstance(HMAC_SHA256);
|
||||||
|
hmacSha256.init(new SecretKeySpec(key, HMAC_SHA256));
|
||||||
|
} catch (final Exception e) {
|
||||||
|
throw new IllegalArgumentException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String computeHmac256(final String stringToSign) {
|
||||||
|
byte[] utf8Bytes;
|
||||||
|
try {
|
||||||
|
utf8Bytes = stringToSign.getBytes(AbfsHttpConstants.UTF_8);
|
||||||
|
} catch (final UnsupportedEncodingException e) {
|
||||||
|
throw new IllegalArgumentException(e);
|
||||||
|
}
|
||||||
|
byte[] hmac;
|
||||||
|
synchronized (this) {
|
||||||
|
hmac = hmacSha256.doFinal(utf8Bytes);
|
||||||
|
}
|
||||||
|
return Base64.encode(hmac);
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,7 +30,7 @@
|
||||||
|
|
||||||
<property>
|
<property>
|
||||||
<name>fs.azure.test.namespace.enabled</name>
|
<name>fs.azure.test.namespace.enabled</name>
|
||||||
<value>false</value>
|
<value>true</value>
|
||||||
</property>
|
</property>
|
||||||
|
|
||||||
<property>
|
<property>
|
||||||
|
|
Loading…
Reference in New Issue