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

Commit d7ad398

Browse files
authored
Merge pull request #310 from rickle-msft/nov17
Nov17
2 parents 2d82ab7 + 3c77585 commit d7ad398

20 files changed

+447
-13
lines changed

ChangeLog.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
2018.05.22 Version 7.1.0
2+
* Support for 2017-11-09 REST version. Please see our REST API documentation and blogs for information about the related added features.
3+
* Added OAuth token support for authentication with HTTPS requests.
4+
* Support for write-once read-many containers.
5+
16
2018.02.05 Version 7.0.0
27
* Support for 2017-07-29 REST version. Please see our REST api documentation and blogs for information about the related added features.
38
* 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.

microsoft-azure-storage-test/res/TestConfigurations.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@
2626
<BlobServiceSecondaryEndpoint>http://[ACCOUNT]-secondary.blob.core.windows.net</BlobServiceSecondaryEndpoint>
2727
<QueueServiceSecondaryEndpoint>http://[ACCOUNT]-secondary.queue.core.windows.net</QueueServiceSecondaryEndpoint>
2828
<TableServiceSecondaryEndpoint>http://[ACCOUNT]-secondary.table.core.windows.net</TableServiceSecondaryEndpoint>
29+
<ActiveDirectoryApplicationId>[APPLICATION_ID]</ActiveDirectoryApplicationId>
30+
<ActiveDirectoryApplicationSecret>[APPLICATION_SECRET]</ActiveDirectoryApplicationSecret>
31+
<ActiveDirectoryTenantId>[TENANT_ID]</ActiveDirectoryTenantId>
2932
</TenantConfiguration>
3033
</TenantConfigurations>
3134
</TestConfigurations>
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package com.microsoft.azure.storage;
2+
3+
import com.microsoft.azure.storage.blob.CloudBlobClient;
4+
import com.microsoft.azure.storage.queue.CloudQueueClient;
5+
import org.junit.Test;
6+
import org.junit.experimental.categories.Category;
7+
8+
import javax.naming.ServiceUnavailableException;
9+
import java.net.MalformedURLException;
10+
import java.net.URISyntaxException;
11+
import java.util.concurrent.ExecutionException;
12+
13+
import static org.junit.Assert.assertEquals;
14+
import static org.junit.Assert.fail;
15+
16+
/**
17+
* OAuth tests.
18+
*/
19+
public class OAuthTests {
20+
/**
21+
* Test OAuth with respect to blob client
22+
*/
23+
@Test
24+
@Category({ TestRunners.DevFabricTests.class, TestRunners.DevStoreTests.class, TestRunners.CloudTests.class })
25+
public void testOAuthTokenWithBlobClient() throws StorageException, URISyntaxException, MalformedURLException, InterruptedException, ExecutionException, ServiceUnavailableException {
26+
// get the standard test account
27+
CloudStorageAccount account = TestHelper.getAccount();
28+
29+
// create a token credential and replace the account's credential
30+
StorageCredentialsToken storageCredentialsToken = new StorageCredentialsToken(TestHelper.getAccountName(), TestHelper.generateOAuthToken());
31+
account.setCredentials(storageCredentialsToken);
32+
33+
// test to make sure authentication is working
34+
CloudBlobClient blobClient = account.createCloudBlobClient();
35+
blobClient.downloadServiceProperties();
36+
37+
// change the token to see it fail
38+
try {
39+
storageCredentialsToken.updateToken("BLA");
40+
blobClient.downloadServiceProperties();
41+
fail();
42+
} catch (StorageException e) {
43+
assertEquals("AuthenticationFailed", e.getErrorCode());
44+
}
45+
46+
// change the token again to see it succeed
47+
storageCredentialsToken.updateToken(TestHelper.generateOAuthToken());
48+
blobClient.downloadServiceProperties();
49+
}
50+
51+
/**
52+
* Test OAuth with respect to queue client
53+
*/
54+
@Test
55+
@Category({ TestRunners.DevFabricTests.class, TestRunners.DevStoreTests.class, TestRunners.CloudTests.class })
56+
public void testOAuthTokenWithQueueClient() throws StorageException, URISyntaxException, MalformedURLException, InterruptedException, ExecutionException, ServiceUnavailableException {
57+
// get the standard test account
58+
CloudStorageAccount account = TestHelper.getAccount();
59+
60+
// create a token credential and replace the account's credential
61+
StorageCredentialsToken storageCredentialsToken = new StorageCredentialsToken(TestHelper.getAccountName(), TestHelper.generateOAuthToken());
62+
account.setCredentials(storageCredentialsToken);
63+
64+
// test to make sure authentication is working
65+
CloudQueueClient queueClient = account.createCloudQueueClient();
66+
queueClient.downloadServiceProperties();
67+
68+
// change the token to see it fail
69+
try {
70+
storageCredentialsToken.updateToken("BLA");
71+
queueClient.downloadServiceProperties();
72+
fail();
73+
} catch (StorageException e) {
74+
assertEquals("AuthenticationFailed", e.getErrorCode());
75+
}
76+
77+
// change the token again to see it succeed
78+
storageCredentialsToken.updateToken(TestHelper.generateOAuthToken());
79+
queueClient.downloadServiceProperties();
80+
}
81+
}

microsoft-azure-storage-test/src/com/microsoft/azure/storage/Tenant.java

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ public class Tenant {
3232
private Integer fileHttpsPortOverride = null;
3333
private Integer queueHttpsPortOverride = null;
3434
private Integer tableHttpsPortOverride = null;
35+
private String activeDirectoryApplicationId;
36+
private String activeDirectoryApplicationSecret;
37+
private String activeDirectoryTenantId;
3538

3639
public String getTenantName() {
3740
return this.tenantName;
@@ -92,7 +95,31 @@ public Integer getQueueHttpsPortOverride() {
9295
public Integer getTableHttpsPortOverride() {
9396
return this.tableHttpsPortOverride;
9497
}
95-
98+
99+
public String getActiveDirectoryApplicationId() {
100+
return this.activeDirectoryApplicationId;
101+
}
102+
103+
public String getActiveDirectoryApplicationSecret() {
104+
return this.activeDirectoryApplicationSecret;
105+
}
106+
107+
public String getActiveDirectoryTenantId() {
108+
return this.activeDirectoryTenantId;
109+
}
110+
111+
public void setActiveDirectoryApplicationId(String activeDirectoryApplicationId) {
112+
this.activeDirectoryApplicationId = activeDirectoryApplicationId;
113+
}
114+
115+
public void setActiveDirectoryApplicationSecret(String activeDirectoryApplicationSecret) {
116+
this.activeDirectoryApplicationSecret = activeDirectoryApplicationSecret;
117+
}
118+
119+
public void setActiveDirectoryTenantId(String activeDirectoryTenantId) {
120+
this.activeDirectoryTenantId = activeDirectoryTenantId;
121+
}
122+
96123
void setTenantName(String tenantName) {
97124
this.tenantName = tenantName;
98125
}

microsoft-azure-storage-test/src/com/microsoft/azure/storage/TestHelper.java

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.io.File;
2222
import java.io.IOException;
2323
import java.io.InputStream;
24+
import java.net.MalformedURLException;
2425
import java.net.URI;
2526
import java.net.URISyntaxException;
2627
import java.net.URL;
@@ -32,14 +33,22 @@
3233
import java.util.ArrayList;
3334
import java.util.Arrays;
3435
import java.util.Random;
36+
import java.util.concurrent.ExecutionException;
37+
import java.util.concurrent.ExecutorService;
38+
import java.util.concurrent.Executors;
39+
import java.util.concurrent.Future;
3540

3641
import javax.crypto.KeyGenerator;
3742
import javax.crypto.NoSuchPaddingException;
3843
import javax.crypto.SecretKey;
44+
import javax.naming.ServiceUnavailableException;
3945
import javax.xml.parsers.DocumentBuilder;
4046
import javax.xml.parsers.DocumentBuilderFactory;
4147
import javax.xml.parsers.ParserConfigurationException;
4248

49+
import com.microsoft.aad.adal4j.AuthenticationContext;
50+
import com.microsoft.aad.adal4j.AuthenticationResult;
51+
import com.microsoft.aad.adal4j.ClientCredential;
4352
import org.junit.AssumptionViolatedException;
4453
import org.w3c.dom.DOMException;
4554
import org.w3c.dom.Document;
@@ -67,6 +76,47 @@ public class TestHelper {
6776
private final static boolean enableFiddler = false;
6877
private final static boolean requireSecondaryEndpoint = false;
6978

79+
public static String generateOAuthToken() throws MalformedURLException, InterruptedException, ExecutionException, ServiceUnavailableException, StorageException {
80+
// if the test configuration has not been read in yet, trigger a getAccount to read in the information
81+
if(tenant == null) {
82+
getAccount();
83+
}
84+
85+
// this boiler plate code is from the ADAL sample
86+
String authority = String.format("https://login.microsoftonline.com/%s/oauth2/token",
87+
tenant.getActiveDirectoryTenantId());
88+
ClientCredential credential = new ClientCredential(tenant.getActiveDirectoryApplicationId(),
89+
tenant.getActiveDirectoryApplicationSecret());
90+
91+
ExecutorService service = null;
92+
AuthenticationResult result;
93+
94+
try {
95+
service = Executors.newFixedThreadPool(1);
96+
AuthenticationContext context = new AuthenticationContext(authority, true, service);
97+
Future<AuthenticationResult> future = context.acquireToken("https://storage.azure.com", credential, null);
98+
result = future.get();
99+
} finally {
100+
if(service != null) {
101+
service.shutdown();
102+
}
103+
}
104+
105+
if (result == null) {
106+
throw new ServiceUnavailableException("authentication result was null");
107+
}
108+
return result.getAccessToken();
109+
}
110+
111+
public static String getAccountName() throws StorageException {
112+
// if the test configuration has not been read in yet, trigger a getAccount to read in the information
113+
if(tenant == null) {
114+
getAccount();
115+
}
116+
117+
return tenant.getAccountName();
118+
}
119+
70120
public static CloudBlobClient createCloudBlobClient() throws StorageException {
71121
CloudBlobClient client = getAccount().createCloudBlobClient();
72122
return client;
@@ -305,7 +355,7 @@ public static void verifyServiceStats(ServiceStats stats) {
305355
}
306356
}
307357

308-
private static CloudStorageAccount getAccount() throws StorageException {
358+
public static CloudStorageAccount getAccount() throws StorageException {
309359
// Only do this the first time TestBase is called as storage account is static
310360
if (account == null) {
311361
//enable fiddler
@@ -502,6 +552,15 @@ else if (name.equals("TableHttpsPortOverride")) {
502552
else if (name.equals("FileHttpsPortOverride")) {
503553
tenant.setFileHttpsPortOverride(Integer.parseInt(node.getTextContent()));
504554
}
555+
else if (name.equals("ActiveDirectoryApplicationId")) {
556+
tenant.setActiveDirectoryApplicationId(node.getTextContent());
557+
}
558+
else if (name.equals("ActiveDirectoryApplicationSecret")) {
559+
tenant.setActiveDirectoryApplicationSecret(node.getTextContent());
560+
}
561+
else if (name.equals("ActiveDirectoryTenantId")) {
562+
tenant.setActiveDirectoryTenantId(node.getTextContent());
563+
}
505564
else if (!premiumBlob){
506565
throw new IllegalArgumentException(String.format(
507566
"Invalid child of TenantConfiguration with name: %s", name));

microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/CloudBlobClientTests.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ public void testListContainers() throws StorageException, URISyntaxException {
7777
for (final CloudBlobContainer container : segment.getResults()) {
7878
container.downloadAttributes();
7979
assertEquals(CloudBlobContainer.class, container.getClass());
80+
assertNotNull(container.getProperties().hasImmutabilityPolicy());
81+
assertNotNull(container.getProperties().hasLegalHold());
82+
assertFalse(container.getProperties().hasImmutabilityPolicy());
83+
assertFalse(container.getProperties().hasLegalHold());
8084
containerList.remove(container.getName());
8185
}
8286

microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/CloudBlobContainerTests.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,10 @@ public void testCloudBlobContainerExists() throws StorageException {
316316
this.container.create();
317317
assertTrue(this.container.exists());
318318
assertNotNull(this.container.getProperties().getEtag());
319+
assertNotNull(this.container.getProperties().hasImmutabilityPolicy());
320+
assertNotNull(this.container.getProperties().hasLegalHold());
321+
assertFalse(this.container.getProperties().hasImmutabilityPolicy());
322+
assertFalse(this.container.getProperties().hasLegalHold());
319323

320324
this.container.delete();
321325
assertFalse(this.container.exists());
@@ -549,6 +553,8 @@ public void testCloudBlobContainerListBlobs() throws StorageException, IOExcepti
549553
EnumSet.noneOf(BlobListingDetails.class), 150, token, null, null);
550554
for (ListBlobItem blob : result.getResults()) {
551555
assertEquals(CloudBlockBlob.class, blob.getClass());
556+
assertNotNull(((CloudBlockBlob)blob).getProperties().getCreatedTime());
557+
assertTrue(((CloudBlockBlob)blob).getProperties().getCreatedTime().before(new Date()));
552558
assertTrue(blobNames.remove(((CloudBlockBlob) blob).getName()));
553559
}
554560
token = result.getContinuationToken();

microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/CloudBlockBlobTests.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ public void testBlockBlobCreate() throws StorageException, URISyntaxException, I
9999
// Create again (should succeed)
100100
blob.uploadText("text");
101101
assertTrue(blob.exists());
102+
assertNotNull(blob.getProperties().getCreatedTime());
103+
assertTrue(blob.getProperties().getCreatedTime().before(new Date()));
102104

103105
// Create again, specifying not to if it already exists
104106
// This should fail

microsoft-azure-storage/src/com/microsoft/azure/storage/CloudStorageAccount.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ public final class CloudStorageAccount {
4646
*/
4747
protected static final String ACCOUNT_KEY_NAME = "AccountKey";
4848

49+
/**
50+
* Represents the setting name for the token credential.
51+
*/
52+
protected static final String ACCOUNT_TOKEN_NAME = "AccountToken";
53+
4954
/**
5055
* Represents the setting name for the account name.
5156
*/

microsoft-azure-storage/src/com/microsoft/azure/storage/Constants.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,11 @@ public static class HeaderConstants {
314314
*/
315315
public static final String AUTHORIZATION = "Authorization";
316316

317+
/**
318+
* The keyword used for bearer token authorization.
319+
*/
320+
public static final String BEARER = "Bearer";
321+
317322
/**
318323
* The format string for specifying ranges with only begin offset.
319324
*/
@@ -650,7 +655,7 @@ public static class HeaderConstants {
650655
/**
651656
* The current storage version header value.
652657
*/
653-
public static final String TARGET_STORAGE_VERSION = "2017-07-29";
658+
public static final String TARGET_STORAGE_VERSION = "2017-11-09";
654659

655660
/**
656661
* The header that specifies the next visible time for a queue message.

0 commit comments

Comments
 (0)