Skip to content
This repository has been archived by the owner on Jul 19, 2024. It is now read-only.

Commit

Permalink
Merge pull request #311 from Azure/dev
Browse files Browse the repository at this point in the history
Dev into master
  • Loading branch information
rickle-msft authored May 22, 2018
2 parents b3822fd + d7ad398 commit 4195c32
Show file tree
Hide file tree
Showing 20 changed files with 447 additions and 13 deletions.
5 changes: 5 additions & 0 deletions ChangeLog.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
2018.05.22 Version 7.1.0
* Support for 2017-11-09 REST version. Please see our REST API documentation and blogs for information about the related added features.
* Added OAuth token support for authentication with HTTPS requests.
* Support for write-once read-many containers.

2018.02.05 Version 7.0.0
* Support for 2017-07-29 REST version. Please see our REST api documentation and blogs for information about the related added features.
* Added support for soft delete feature. If a delete retention policy is enabled through the set service properties API, then blobs or snapshots can be deleted softly and retained for a specified number of days, before being permanently removed by garbage collection.
Expand Down
3 changes: 3 additions & 0 deletions microsoft-azure-storage-test/res/TestConfigurations.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
<BlobServiceSecondaryEndpoint>http://[ACCOUNT]-secondary.blob.core.windows.net</BlobServiceSecondaryEndpoint>
<QueueServiceSecondaryEndpoint>http://[ACCOUNT]-secondary.queue.core.windows.net</QueueServiceSecondaryEndpoint>
<TableServiceSecondaryEndpoint>http://[ACCOUNT]-secondary.table.core.windows.net</TableServiceSecondaryEndpoint>
<ActiveDirectoryApplicationId>[APPLICATION_ID]</ActiveDirectoryApplicationId>
<ActiveDirectoryApplicationSecret>[APPLICATION_SECRET]</ActiveDirectoryApplicationSecret>
<ActiveDirectoryTenantId>[TENANT_ID]</ActiveDirectoryTenantId>
</TenantConfiguration>
</TenantConfigurations>
</TestConfigurations>
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package com.microsoft.azure.storage;

import com.microsoft.azure.storage.blob.CloudBlobClient;
import com.microsoft.azure.storage.queue.CloudQueueClient;
import org.junit.Test;
import org.junit.experimental.categories.Category;

import javax.naming.ServiceUnavailableException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.util.concurrent.ExecutionException;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;

/**
* OAuth tests.
*/
public class OAuthTests {
/**
* Test OAuth with respect to blob client
*/
@Test
@Category({ TestRunners.DevFabricTests.class, TestRunners.DevStoreTests.class, TestRunners.CloudTests.class })
public void testOAuthTokenWithBlobClient() throws StorageException, URISyntaxException, MalformedURLException, InterruptedException, ExecutionException, ServiceUnavailableException {
// get the standard test account
CloudStorageAccount account = TestHelper.getAccount();

// create a token credential and replace the account's credential
StorageCredentialsToken storageCredentialsToken = new StorageCredentialsToken(TestHelper.getAccountName(), TestHelper.generateOAuthToken());
account.setCredentials(storageCredentialsToken);

// test to make sure authentication is working
CloudBlobClient blobClient = account.createCloudBlobClient();
blobClient.downloadServiceProperties();

// change the token to see it fail
try {
storageCredentialsToken.updateToken("BLA");
blobClient.downloadServiceProperties();
fail();
} catch (StorageException e) {
assertEquals("AuthenticationFailed", e.getErrorCode());
}

// change the token again to see it succeed
storageCredentialsToken.updateToken(TestHelper.generateOAuthToken());
blobClient.downloadServiceProperties();
}

/**
* Test OAuth with respect to queue client
*/
@Test
@Category({ TestRunners.DevFabricTests.class, TestRunners.DevStoreTests.class, TestRunners.CloudTests.class })
public void testOAuthTokenWithQueueClient() throws StorageException, URISyntaxException, MalformedURLException, InterruptedException, ExecutionException, ServiceUnavailableException {
// get the standard test account
CloudStorageAccount account = TestHelper.getAccount();

// create a token credential and replace the account's credential
StorageCredentialsToken storageCredentialsToken = new StorageCredentialsToken(TestHelper.getAccountName(), TestHelper.generateOAuthToken());
account.setCredentials(storageCredentialsToken);

// test to make sure authentication is working
CloudQueueClient queueClient = account.createCloudQueueClient();
queueClient.downloadServiceProperties();

// change the token to see it fail
try {
storageCredentialsToken.updateToken("BLA");
queueClient.downloadServiceProperties();
fail();
} catch (StorageException e) {
assertEquals("AuthenticationFailed", e.getErrorCode());
}

// change the token again to see it succeed
storageCredentialsToken.updateToken(TestHelper.generateOAuthToken());
queueClient.downloadServiceProperties();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ public class Tenant {
private Integer fileHttpsPortOverride = null;
private Integer queueHttpsPortOverride = null;
private Integer tableHttpsPortOverride = null;
private String activeDirectoryApplicationId;
private String activeDirectoryApplicationSecret;
private String activeDirectoryTenantId;

public String getTenantName() {
return this.tenantName;
Expand Down Expand Up @@ -92,7 +95,31 @@ public Integer getQueueHttpsPortOverride() {
public Integer getTableHttpsPortOverride() {
return this.tableHttpsPortOverride;
}


public String getActiveDirectoryApplicationId() {
return this.activeDirectoryApplicationId;
}

public String getActiveDirectoryApplicationSecret() {
return this.activeDirectoryApplicationSecret;
}

public String getActiveDirectoryTenantId() {
return this.activeDirectoryTenantId;
}

public void setActiveDirectoryApplicationId(String activeDirectoryApplicationId) {
this.activeDirectoryApplicationId = activeDirectoryApplicationId;
}

public void setActiveDirectoryApplicationSecret(String activeDirectoryApplicationSecret) {
this.activeDirectoryApplicationSecret = activeDirectoryApplicationSecret;
}

public void setActiveDirectoryTenantId(String activeDirectoryTenantId) {
this.activeDirectoryTenantId = activeDirectoryTenantId;
}

void setTenantName(String tenantName) {
this.tenantName = tenantName;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
Expand All @@ -32,14 +33,22 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.naming.ServiceUnavailableException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import com.microsoft.aad.adal4j.AuthenticationContext;
import com.microsoft.aad.adal4j.AuthenticationResult;
import com.microsoft.aad.adal4j.ClientCredential;
import org.junit.AssumptionViolatedException;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
Expand Down Expand Up @@ -67,6 +76,47 @@ public class TestHelper {
private final static boolean enableFiddler = false;
private final static boolean requireSecondaryEndpoint = false;

public static String generateOAuthToken() throws MalformedURLException, InterruptedException, ExecutionException, ServiceUnavailableException, StorageException {
// if the test configuration has not been read in yet, trigger a getAccount to read in the information
if(tenant == null) {
getAccount();
}

// this boiler plate code is from the ADAL sample
String authority = String.format("https://login.microsoftonline.com/%s/oauth2/token",
tenant.getActiveDirectoryTenantId());
ClientCredential credential = new ClientCredential(tenant.getActiveDirectoryApplicationId(),
tenant.getActiveDirectoryApplicationSecret());

ExecutorService service = null;
AuthenticationResult result;

try {
service = Executors.newFixedThreadPool(1);
AuthenticationContext context = new AuthenticationContext(authority, true, service);
Future<AuthenticationResult> future = context.acquireToken("https://storage.azure.com", credential, null);
result = future.get();
} finally {
if(service != null) {
service.shutdown();
}
}

if (result == null) {
throw new ServiceUnavailableException("authentication result was null");
}
return result.getAccessToken();
}

public static String getAccountName() throws StorageException {
// if the test configuration has not been read in yet, trigger a getAccount to read in the information
if(tenant == null) {
getAccount();
}

return tenant.getAccountName();
}

public static CloudBlobClient createCloudBlobClient() throws StorageException {
CloudBlobClient client = getAccount().createCloudBlobClient();
return client;
Expand Down Expand Up @@ -305,7 +355,7 @@ public static void verifyServiceStats(ServiceStats stats) {
}
}

private static CloudStorageAccount getAccount() throws StorageException {
public static CloudStorageAccount getAccount() throws StorageException {
// Only do this the first time TestBase is called as storage account is static
if (account == null) {
//enable fiddler
Expand Down Expand Up @@ -502,6 +552,15 @@ else if (name.equals("TableHttpsPortOverride")) {
else if (name.equals("FileHttpsPortOverride")) {
tenant.setFileHttpsPortOverride(Integer.parseInt(node.getTextContent()));
}
else if (name.equals("ActiveDirectoryApplicationId")) {
tenant.setActiveDirectoryApplicationId(node.getTextContent());
}
else if (name.equals("ActiveDirectoryApplicationSecret")) {
tenant.setActiveDirectoryApplicationSecret(node.getTextContent());
}
else if (name.equals("ActiveDirectoryTenantId")) {
tenant.setActiveDirectoryTenantId(node.getTextContent());
}
else if (!premiumBlob){
throw new IllegalArgumentException(String.format(
"Invalid child of TenantConfiguration with name: %s", name));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ public void testListContainers() throws StorageException, URISyntaxException {
for (final CloudBlobContainer container : segment.getResults()) {
container.downloadAttributes();
assertEquals(CloudBlobContainer.class, container.getClass());
assertNotNull(container.getProperties().hasImmutabilityPolicy());
assertNotNull(container.getProperties().hasLegalHold());
assertFalse(container.getProperties().hasImmutabilityPolicy());
assertFalse(container.getProperties().hasLegalHold());
containerList.remove(container.getName());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,10 @@ public void testCloudBlobContainerExists() throws StorageException {
this.container.create();
assertTrue(this.container.exists());
assertNotNull(this.container.getProperties().getEtag());
assertNotNull(this.container.getProperties().hasImmutabilityPolicy());
assertNotNull(this.container.getProperties().hasLegalHold());
assertFalse(this.container.getProperties().hasImmutabilityPolicy());
assertFalse(this.container.getProperties().hasLegalHold());

this.container.delete();
assertFalse(this.container.exists());
Expand Down Expand Up @@ -549,6 +553,8 @@ public void testCloudBlobContainerListBlobs() throws StorageException, IOExcepti
EnumSet.noneOf(BlobListingDetails.class), 150, token, null, null);
for (ListBlobItem blob : result.getResults()) {
assertEquals(CloudBlockBlob.class, blob.getClass());
assertNotNull(((CloudBlockBlob)blob).getProperties().getCreatedTime());
assertTrue(((CloudBlockBlob)blob).getProperties().getCreatedTime().before(new Date()));
assertTrue(blobNames.remove(((CloudBlockBlob) blob).getName()));
}
token = result.getContinuationToken();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ public void testBlockBlobCreate() throws StorageException, URISyntaxException, I
// Create again (should succeed)
blob.uploadText("text");
assertTrue(blob.exists());
assertNotNull(blob.getProperties().getCreatedTime());
assertTrue(blob.getProperties().getCreatedTime().before(new Date()));

// Create again, specifying not to if it already exists
// This should fail
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ public final class CloudStorageAccount {
*/
protected static final String ACCOUNT_KEY_NAME = "AccountKey";

/**
* Represents the setting name for the token credential.
*/
protected static final String ACCOUNT_TOKEN_NAME = "AccountToken";

/**
* Represents the setting name for the account name.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,11 @@ public static class HeaderConstants {
*/
public static final String AUTHORIZATION = "Authorization";

/**
* The keyword used for bearer token authorization.
*/
public static final String BEARER = "Bearer";

/**
* The format string for specifying ranges with only begin offset.
*/
Expand Down Expand Up @@ -650,7 +655,7 @@ public static class HeaderConstants {
/**
* The current storage version header value.
*/
public static final String TARGET_STORAGE_VERSION = "2017-07-29";
public static final String TARGET_STORAGE_VERSION = "2017-11-09";

/**
* The header that specifies the next visible time for a queue message.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

/**
* Represents a set of credentials used to authenticate access to a Microsoft Azure storage account. This is the base
* class for the {@link StorageCredentialsAccountAndKey} and {@link StorageCredentialsSharedAccessSignature} classes.
* class for the {@link StorageCredentialsAccountAndKey}, {@link StorageCredentialsToken}, and {@link StorageCredentialsSharedAccessSignature} classes.
*/
public abstract class StorageCredentials {

Expand Down
Loading

0 comments on commit 4195c32

Please sign in to comment.