blob: 0e2fd50dbef7797d9d09fa969ba8944b62660823 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.fs.azure;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.InvalidKeyException;
import java.util.Calendar;
import java.util.Date;
import java.util.EnumSet;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.TimeZone;
import org.apache.hadoop.conf.Configuration;
import com.microsoft.azure.storage.CloudStorageAccount;
import com.microsoft.azure.storage.SharedAccessAccountPermissions;
import com.microsoft.azure.storage.SharedAccessAccountPolicy;
import com.microsoft.azure.storage.SharedAccessAccountResourceType;
import com.microsoft.azure.storage.SharedAccessAccountService;
import com.microsoft.azure.storage.StorageCredentialsAccountAndKey;
import com.microsoft.azure.storage.StorageCredentialsSharedAccessSignature;
import com.microsoft.azure.storage.StorageException;
import com.microsoft.azure.storage.blob.CloudBlobClient;
import com.microsoft.azure.storage.blob.CloudBlobContainer;
import com.microsoft.azure.storage.blob.CloudBlockBlob;
/***
* Local SAS Key Generation implementation. This class resides in
* the same address space as the WASB driver.
*
* This class gets typically used for testing purposes.
*
*/
public class LocalSASKeyGeneratorImpl extends SASKeyGeneratorImpl {
/**
* Map to cache CloudStorageAccount instances.
*/
private Map<String, CloudStorageAccount> storageAccountMap;
private CachingAuthorizer<CachedSASKeyEntry, URI> cache;
private static final int HOURS_IN_DAY = 24;
public LocalSASKeyGeneratorImpl(Configuration conf) {
super(conf);
storageAccountMap = new HashMap<String, CloudStorageAccount>();
cache = new CachingAuthorizer<>(getSasKeyExpiryPeriod(), "SASKEY");
cache.init(conf);
}
/**
* Implementation to generate SAS Key for a container
*/
@Override
public URI getContainerSASUri(String accountName, String container)
throws SASKeyGenerationException {
try {
CachedSASKeyEntry cacheKey = new CachedSASKeyEntry(accountName, container, "/");
URI cacheResult = cache.get(cacheKey);
if (cacheResult != null) {
return cacheResult;
}
CloudStorageAccount account =
getSASKeyBasedStorageAccountInstance(accountName);
CloudBlobClient client = account.createCloudBlobClient();
URI sasKey = client.getCredentials().transformUri(
client.getContainerReference(container).getUri());
cache.put(cacheKey, sasKey);
return sasKey;
} catch (StorageException stoEx) {
throw new SASKeyGenerationException("Encountered StorageException while"
+ " generating SAS Key for container " + container + " inside "
+ "storage account " + accountName, stoEx);
} catch (URISyntaxException uriSyntaxEx) {
throw new SASKeyGenerationException("Encountered URISyntaxException while"
+ " generating SAS Key for container " + container + " inside storage"
+ " account " + accountName, uriSyntaxEx);
}
}
/**
* Helper method that creates a CloudStorageAccount instance based on
* SAS key for accountName
*
* @param accountName Storage Account Name
* @return CloudStorageAccount instance created using SAS key for
* the Storage Account.
* @throws SASKeyGenerationException
*/
private CloudStorageAccount getSASKeyBasedStorageAccountInstance(
String accountName) throws SASKeyGenerationException {
try {
String accountNameWithoutDomain =
getAccountNameWithoutDomain(accountName);
CloudStorageAccount account =
getStorageAccountInstance(accountNameWithoutDomain,
AzureNativeFileSystemStore.getAccountKeyFromConfiguration(
accountName, getConf()));
return new CloudStorageAccount(
new StorageCredentialsSharedAccessSignature(
account.generateSharedAccessSignature(
getDefaultAccountAccessPolicy())), false,
account.getEndpointSuffix(), accountNameWithoutDomain);
} catch (KeyProviderException keyProviderEx) {
throw new SASKeyGenerationException("Encountered KeyProviderException"
+ " while retrieving Storage key from configuration for account "
+ accountName, keyProviderEx);
} catch (InvalidKeyException invalidKeyEx) {
throw new SASKeyGenerationException("Encoutered InvalidKeyException "
+ "while generating Account level SAS key for account" + accountName,
invalidKeyEx);
} catch(StorageException storeEx) {
throw new SASKeyGenerationException("Encoutered StorageException while "
+ "generating Account level SAS key for account" + accountName,
storeEx);
} catch(URISyntaxException uriSyntaxEx) {
throw new SASKeyGenerationException("Encountered URISyntaxException for"
+ " account " + accountName, uriSyntaxEx);
}
}
/**
* Implementation for generation of Relative Path Blob SAS Uri.
*/
@Override
public URI getRelativeBlobSASUri(String accountName, String container,
String relativePath) throws SASKeyGenerationException {
CloudBlobContainer sc = null;
CloudBlobClient client = null;
CachedSASKeyEntry cacheKey = null;
try {
cacheKey = new CachedSASKeyEntry(accountName, container, relativePath);
URI cacheResult = cache.get(cacheKey);
if (cacheResult != null) {
return cacheResult;
}
CloudStorageAccount account =
getSASKeyBasedStorageAccountInstance(accountName);
client = account.createCloudBlobClient();
sc = client.getContainerReference(container);
} catch (URISyntaxException uriSyntaxEx) {
throw new SASKeyGenerationException("Encountered URISyntaxException "
+ "while getting container references for container " + container
+ " inside storage account : " + accountName, uriSyntaxEx);
} catch (StorageException stoEx) {
throw new SASKeyGenerationException("Encountered StorageException while "
+ "getting container references for container " + container
+ " inside storage account : " + accountName, stoEx);
}
CloudBlockBlob blob = null;
try {
blob = sc.getBlockBlobReference(relativePath);
} catch (URISyntaxException uriSyntaxEx) {
throw new SASKeyGenerationException("Encountered URISyntaxException while "
+ "getting Block Blob references for container " + container
+ " inside storage account : " + accountName, uriSyntaxEx);
} catch (StorageException stoEx) {
throw new SASKeyGenerationException("Encountered StorageException while "
+ "getting Block Blob references for container " + container
+ " inside storage account : " + accountName, stoEx);
}
try {
URI sasKey = client.getCredentials().transformUri(blob.getUri());
cache.put(cacheKey, sasKey);
return sasKey;
} catch (StorageException stoEx) {
throw new SASKeyGenerationException("Encountered StorageException while "
+ "generating SAS key for Blob: " + relativePath + " inside "
+ "container : " + container + " in Storage Account : " + accountName,
stoEx);
} catch (URISyntaxException uriSyntaxEx) {
throw new SASKeyGenerationException("Encountered URISyntaxException "
+ "while generating SAS key for Blob: " + relativePath + " inside "
+ "container: " + container + " in Storage Account : " + accountName,
uriSyntaxEx);
}
}
/**
* Helper method that creates CloudStorageAccount Instance using the
* storage account key.
* @param accountName Name of the storage account
* @param accountKey Storage Account key
* @return CloudStorageAccount instance for the storage account.
* @throws SASKeyGenerationException
*/
private CloudStorageAccount getStorageAccountInstance(String accountName,
String accountKey) throws SASKeyGenerationException {
if (!storageAccountMap.containsKey(accountName)) {
CloudStorageAccount account = null;
try {
account =
new CloudStorageAccount(new StorageCredentialsAccountAndKey(
accountName, accountKey));
} catch (URISyntaxException uriSyntaxEx) {
throw new SASKeyGenerationException("Encountered URISyntaxException "
+ "for account " + accountName, uriSyntaxEx);
}
storageAccountMap.put(accountName, account);
}
return storageAccountMap.get(accountName);
}
/**
* Helper method that returns the Storage account name without
* the domain name suffix.
* @param fullAccountName Storage account name with domain name suffix
* @return String
*/
private String getAccountNameWithoutDomain(String fullAccountName) {
StringTokenizer tokenizer = new StringTokenizer(fullAccountName, ".");
return tokenizer.nextToken();
}
/**
* Helper method to generate Access Policy for the Storage Account SAS Key
* @return SharedAccessAccountPolicy
*/
private SharedAccessAccountPolicy getDefaultAccountAccessPolicy() {
SharedAccessAccountPolicy ap =
new SharedAccessAccountPolicy();
Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
cal.setTime(new Date());
cal.add(Calendar.HOUR, (int) getSasKeyExpiryPeriod() * HOURS_IN_DAY);
ap.setSharedAccessExpiryTime(cal.getTime());
ap.setPermissions(getDefaultAccoutSASKeyPermissions());
ap.setResourceTypes(EnumSet.of(SharedAccessAccountResourceType.CONTAINER,
SharedAccessAccountResourceType.OBJECT));
ap.setServices(EnumSet.of(SharedAccessAccountService.BLOB));
return ap;
}
private EnumSet<SharedAccessAccountPermissions> getDefaultAccoutSASKeyPermissions() {
return EnumSet.of(SharedAccessAccountPermissions.ADD,
SharedAccessAccountPermissions.CREATE,
SharedAccessAccountPermissions.DELETE,
SharedAccessAccountPermissions.LIST,
SharedAccessAccountPermissions.READ,
SharedAccessAccountPermissions.UPDATE,
SharedAccessAccountPermissions.WRITE);
}
}