From eabe17d26b5553a0e040458c1f0505221d495ea3 Mon Sep 17 00:00:00 2001 From: Emily Gerner Date: Mon, 5 Oct 2015 13:01:00 -0700 Subject: [PATCH] Java Storage Client Library 4.0.0 --- BreakingChanges.txt | 9 + ChangeLog.txt | 11 + README.md | 2 +- microsoft-azure-storage-samples/pom.xml | 2 +- .../res/simplelogger.properties | 3 + .../azure/storage/AccountSasTests.java | 1669 +++++++++++++++++ .../microsoft/azure/storage/LoggerTests.java | 23 - .../azure/storage/ServicePropertiesTests.java | 173 +- .../azure/storage/StorageAccountTests.java | 4 +- .../com/microsoft/azure/storage/Tenant.java | 36 + .../microsoft/azure/storage/TestHelper.java | 131 +- .../microsoft/azure/storage/TestRunners.java | 5 +- .../analytics/CloudAnalyticsClientTests.java | 50 +- .../azure/storage/blob/BlobTestHelper.java | 87 +- .../storage/blob/CloudBlobContainerTests.java | 26 +- .../azure/storage/blob/SasTests.java | 343 +++- .../storage/file/CloudFileShareTests.java | 11 +- .../azure/storage/file/FileSasTests.java | 301 ++- .../azure/storage/file/FileTestHelper.java | 12 +- .../table/TableBatchOperationTests.java | 138 +- .../azure/storage/table/TableClientTests.java | 27 +- .../azure/storage/table/TableDateTests.java | 72 - .../storage/table/TableEscapingTests.java | 8 - .../storage/table/TableOperationTests.java | 142 +- .../azure/storage/table/TableQueryTests.java | 67 +- .../storage/table/TableSerializerTests.java | 55 - .../azure/storage/table/TableTests.java | 69 - .../azure/storage/CloudStorageAccount.java | 161 +- .../microsoft/azure/storage/Constants.java | 58 +- .../microsoft/azure/storage/Credentials.java | 157 -- .../com/microsoft/azure/storage/IPRange.java | 113 ++ .../azure/storage/ServiceClient.java | 11 +- .../SharedAccessAccountPermissions.java | 129 ++ .../storage/SharedAccessAccountPolicy.java | 202 ++ .../SharedAccessAccountResourceType.java | 103 + .../storage/SharedAccessAccountService.java | 108 ++ .../azure/storage/SharedAccessPolicy.java | 6 +- .../azure/storage/SharedAccessProtocols.java | 41 + .../azure/storage/StorageCredentials.java | 42 +- .../StorageCredentialsAccountAndKey.java | 124 +- .../storage/StorageCredentialsAnonymous.java | 5 +- ...orageCredentialsSharedAccessSignature.java | 26 +- .../microsoft/azure/storage/StorageKey.java | 212 --- .../analytics/CloudAnalyticsClient.java | 70 +- .../storage/analytics/StorageService.java | 5 + .../azure/storage/blob/CloudAppendBlob.java | 29 +- .../azure/storage/blob/CloudBlob.java | 298 +-- .../azure/storage/blob/CloudBlobClient.java | 1 - .../storage/blob/CloudBlobContainer.java | 142 +- .../storage/blob/CloudBlobDirectory.java | 37 +- .../azure/storage/blob/CloudBlockBlob.java | 128 +- .../azure/storage/blob/CloudPageBlob.java | 128 +- .../blob/SharedAccessBlobPermissions.java | 12 +- .../storage/blob/SharedAccessBlobPolicy.java | 43 +- .../azure/storage/core/BaseRequest.java | 70 - .../azure/storage/core/Canonicalizer.java | 59 - .../JsonUtilities.java} | 17 +- .../azure/storage/core/PathUtility.java | 34 - .../com/microsoft/azure/storage/core/SR.java | 5 +- .../core/SharedAccessSignatureHelper.java | 483 +++-- .../core/StorageCredentialsHelper.java | 101 +- .../microsoft/azure/storage/core/Utility.java | 74 - .../azure/storage/file/CloudFile.java | 176 +- .../azure/storage/file/CloudFileClient.java | 1 - .../storage/file/CloudFileDirectory.java | 45 +- .../azure/storage/file/CloudFileShare.java | 109 +- .../storage/file/FileServiceProperties.java | 55 +- .../file/SharedAccessFilePermissions.java | 7 +- .../storage/file/SharedAccessFilePolicy.java | 19 +- .../azure/storage/queue/CloudQueue.java | 92 +- .../azure/storage/queue/CloudQueueClient.java | 6 +- .../azure/storage/table/CloudTable.java | 103 +- .../azure/storage/table/CloudTableClient.java | 4 - .../storage/table/DeserializationHelper.java | 216 --- .../storage/table/DynamicTableEntity.java | 29 - .../azure/storage/table/MimeHelper.java | 32 +- .../azure/storage/table/ODataConstants.java | 120 -- .../storage/table/QueryTableOperation.java | 8 +- .../table/SharedAccessTablePolicy.java | 2 +- .../storage/table/TableBatchOperation.java | 7 - .../azure/storage/table/TableConstants.java | 26 - .../storage/table/TableDeserializer.java | 661 +------ .../azure/storage/table/TableEntity.java | 5 +- .../storage/table/TableEntitySerializer.java | 299 +-- .../azure/storage/table/TableOperation.java | 23 +- .../storage/table/TablePayloadFormat.java | 9 - .../azure/storage/table/TableRequest.java | 23 +- .../azure/storage/table/TableResult.java | 1 - .../storage/table/TableServiceEntity.java | 5 +- .../table/TableStorageErrorDeserializer.java | 315 +--- pom.xml | 20 +- 91 files changed, 4781 insertions(+), 4347 deletions(-) create mode 100644 microsoft-azure-storage-test/res/simplelogger.properties create mode 100644 microsoft-azure-storage-test/src/com/microsoft/azure/storage/AccountSasTests.java delete mode 100644 microsoft-azure-storage/src/com/microsoft/azure/storage/Credentials.java create mode 100644 microsoft-azure-storage/src/com/microsoft/azure/storage/IPRange.java create mode 100644 microsoft-azure-storage/src/com/microsoft/azure/storage/SharedAccessAccountPermissions.java create mode 100644 microsoft-azure-storage/src/com/microsoft/azure/storage/SharedAccessAccountPolicy.java create mode 100644 microsoft-azure-storage/src/com/microsoft/azure/storage/SharedAccessAccountResourceType.java create mode 100644 microsoft-azure-storage/src/com/microsoft/azure/storage/SharedAccessAccountService.java create mode 100644 microsoft-azure-storage/src/com/microsoft/azure/storage/SharedAccessProtocols.java delete mode 100644 microsoft-azure-storage/src/com/microsoft/azure/storage/StorageKey.java rename microsoft-azure-storage/src/com/microsoft/azure/storage/{table/ODataUtilities.java => core/JsonUtilities.java} (84%) delete mode 100644 microsoft-azure-storage/src/com/microsoft/azure/storage/table/DeserializationHelper.java diff --git a/BreakingChanges.txt b/BreakingChanges.txt index e6c261914..c3d74ad6a 100644 --- a/BreakingChanges.txt +++ b/BreakingChanges.txt @@ -1,3 +1,12 @@ +Changes in 4.0.0 + +TABLE + * Removed deprecated table AtomPub support. + +OTHER + * Removed deprecated constructors which take service clients in favor of constructors which take credentials. + * Removed deprecated Credentials and StorageKey classes. Please use the appropriate methods on StorageCredentialsAccountAndKey instead. + Changes in 3.0.0 BLOB diff --git a/ChangeLog.txt b/ChangeLog.txt index 32b5f1813..c52a79de2 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1,3 +1,14 @@ +2015.10.05 Version 4.0.0 + * Removed deprecated table AtomPub support. + * Removed deprecated constructors which take service clients in favor of constructors which take credentials. + * Added support for "Add" permissions on Blob SAS. + * Added support for "Create" permissions on Blob and File SAS. + * Added support for IP Restricted SAS and Protocol SAS. + * Added support for Account SAS to all services. + * Added support for Minute and Hour Metrics to FileServiceProperties and added support for File Metrics to CloudAnalyticsClient. + * Removed deprecated startCopyFromBlob() on CloudBlob. Use startCopy() instead. + * Removed deprecated Credentials and StorageKey classes. Please use the appropriate methods on StorageCredentialsAccountAndKey instead. + 2015.09.16 Version 3.1.0 * Fixed a bug in table where a select on a non-existent field resulted in a null reference exception if the corresponding field in the TableEntity was not nullable. * Fixed a bug in table where JsonParser was automatically closing the response stream before it was completely drained causing socket exhaustion. diff --git a/README.md b/README.md index 7cd924ebc..61ead327a 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ To get the binaries of this library as distributed by Microsoft, ready for use w com.microsoft.azure azure-storage - 3.1.0 + 4.0.0 ``` diff --git a/microsoft-azure-storage-samples/pom.xml b/microsoft-azure-storage-samples/pom.xml index 5dc443adc..8306b5cc2 100644 --- a/microsoft-azure-storage-samples/pom.xml +++ b/microsoft-azure-storage-samples/pom.xml @@ -26,7 +26,7 @@ com.microsoft.azure azure-storage - 3.1.0 + 4.0.0 \ No newline at end of file diff --git a/microsoft-azure-storage-test/res/simplelogger.properties b/microsoft-azure-storage-test/res/simplelogger.properties new file mode 100644 index 000000000..33af116bc --- /dev/null +++ b/microsoft-azure-storage-test/res/simplelogger.properties @@ -0,0 +1,3 @@ + org.slf4j.simpleLogger.defaultLogLevel = trace + org.slf4j.simpleLogger.log.limited = error + org.slf4j.simpleLogger.showThreadName = false \ No newline at end of file diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/AccountSasTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/AccountSasTests.java new file mode 100644 index 000000000..95c53ba1d --- /dev/null +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/AccountSasTests.java @@ -0,0 +1,1669 @@ +/** + * Copyright Microsoft Corporation + * + * Licensed 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 com.microsoft.azure.storage; + +import static org.junit.Assert.*; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.UnknownHostException; +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.NoSuchElementException; +import java.util.TimeZone; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +import com.microsoft.azure.storage.StorageException; +import com.microsoft.azure.storage.TestHelper; +import com.microsoft.azure.storage.TestRunners.CloudTests; +import com.microsoft.azure.storage.TestRunners.DevFabricTests; +import com.microsoft.azure.storage.TestRunners.DevStoreTests; +import com.microsoft.azure.storage.blob.BlobTestHelper; +import com.microsoft.azure.storage.blob.BlobType; +import com.microsoft.azure.storage.blob.CloudAppendBlob; +import com.microsoft.azure.storage.blob.CloudBlobClient; +import com.microsoft.azure.storage.blob.CloudBlobContainer; +import com.microsoft.azure.storage.core.SR; +import com.microsoft.azure.storage.file.CloudFile; +import com.microsoft.azure.storage.file.CloudFileClient; +import com.microsoft.azure.storage.file.CloudFileShare; +import com.microsoft.azure.storage.file.FileTestHelper; +import com.microsoft.azure.storage.queue.CloudQueue; +import com.microsoft.azure.storage.queue.CloudQueueClient; +import com.microsoft.azure.storage.queue.CloudQueueMessage; +import com.microsoft.azure.storage.queue.MessageUpdateFields; +import com.microsoft.azure.storage.queue.QueueTestHelper; +import com.microsoft.azure.storage.table.CloudTable; +import com.microsoft.azure.storage.table.CloudTableClient; +import com.microsoft.azure.storage.table.DynamicTableEntity; +import com.microsoft.azure.storage.table.EntityProperty; +import com.microsoft.azure.storage.table.TableOperation; +import com.microsoft.azure.storage.table.TableTestHelper; + +@Category({ DevFabricTests.class, DevStoreTests.class, CloudTests.class }) +public class AccountSasTests { + private static final String DOES_NOT_EXIST_ERROR_MESSAGE = "The specified resource does not exist."; + private static final String ENUMERATION_ERROR_MESSAGE = + "An error occurred while enumerating the result, check the original exception for details."; + private static final String INVALID_PERMISSION_MESSAGE = + "This request is not authorized to perform this operation using this permission."; + private static final String INVALID_RESOURCE_TYPE_MESSAGE = + "This request is not authorized to perform this operation using this resource type."; + private static final String INVALID_SERVICE_MESSAGE = + "This request is not authorized to perform this operation using this service."; + private static final String QUERY_PARAM_MISSING_MESSAGE = + "Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature."; + + private static final int ADD_CODE = 0x1; + private static final int CREATE_CODE = 0x2; + private static final int DELETE_CODE = 0x4; + private static final int LIST_CODE = 0x8; + private static final int PROCESS_CODE = 0x10; + private static final int READ_CODE = 0x20; + private static final int UPDATE_CODE = 0x40; + private static final int WRITE_CODE = 0x80; + private static final int OBJECT_CODE = 0x100; + private static final int CONTAINER_CODE = 0x200; + private static final int SERVICE_CODE = 0x400; + + // 0x7ff + private static final int FULL_PERMS_CODE = + ADD_CODE | CREATE_CODE | DELETE_CODE | LIST_CODE | PROCESS_CODE | READ_CODE | + UPDATE_CODE | WRITE_CODE | OBJECT_CODE | CONTAINER_CODE | SERVICE_CODE; + private static final int EMPTY_PERMS_CODE = 0x0; + + private CloudBlobClient blobClient; + private CloudBlobContainer blobContainer; + + private CloudFileClient fileClient; + private CloudFileShare fileShare; + + private CloudQueueClient queueClient; + private CloudQueue queueQueue; + + private CloudTableClient tableClient; + private CloudTable tableTable; + + + @Before + public void accountSasTestMethodSetup() throws URISyntaxException, StorageException, IOException { + this.blobClient = TestHelper.createCloudBlobClient(); + this.blobContainer = BlobTestHelper.getRandomContainerReference(); + + this.fileClient = TestHelper.createCloudFileClient(); + this.fileShare = FileTestHelper.getRandomShareReference(); + + this.queueClient = TestHelper.createCloudQueueClient(); + this.queueQueue = QueueTestHelper.getRandomQueueReference(); + + this.tableClient = TestHelper.createCloudTableClient(); + this.tableTable = TableTestHelper.getRandomTableReference(); + } + + @After + public void accountSasTestMethodTearDown() throws StorageException, URISyntaxException { + this.blobContainer.deleteIfExists(); + this.fileShare.deleteIfExists(); + this.queueQueue.deleteIfExists(); + this.tableTable.deleteIfExists(); + } + + @Test + @Category({ DevFabricTests.class, DevStoreTests.class, CloudTests.class }) + public void testInvalidIP() throws InvalidKeyException, StorageException, URISyntaxException, IOException { + // Arbitrary non-IP string + String ip = "not an IP"; + try { + new IPRange(ip); + fail("Invalid IP address should throw"); + } + catch (IllegalArgumentException ex) { + assertEquals(UnknownHostException.class, ex.getCause().getClass()); + assertEquals(String.format(SR.INVALID_IP_ADDRESS, ip), ex.getMessage()); + } + + // IPv6 Address + ip = "2001:0db8:85a3:0000:0000:8a2e:0370:7334"; + try { + new IPRange(ip); + fail("Invalid IP address should throw"); + } + catch (IllegalArgumentException ex) { + assertEquals(ClassCastException.class, ex.getCause().getClass()); + assertEquals(String.format(SR.INVALID_IP_ADDRESS, ip), ex.getMessage()); + } + } + + @Test + @Category({ DevFabricTests.class, DevStoreTests.class, CloudTests.class }) + public void testBlobServiceAccountSas() throws InvalidKeyException, StorageException, URISyntaxException, IOException { + // A file policy should not work on blobs or containers + try { + testBlobAccountSas(this.blobContainer, false, + generatePolicy(AccountSasTests.FULL_PERMS_CODE, SharedAccessAccountService.FILE, null, null)); + fail(); + } + catch (StorageException ex) { + assertEquals(AccountSasTests.INVALID_SERVICE_MESSAGE, ex.getMessage()); + } + + // A queue policy should not work on blobs or containers + try { + testBlobAccountSas(this.blobContainer, false, + generatePolicy(AccountSasTests.FULL_PERMS_CODE, SharedAccessAccountService.QUEUE, null, null)); + fail(); + } + catch (StorageException ex) { + assertEquals(AccountSasTests.INVALID_SERVICE_MESSAGE, ex.getMessage()); + } + + // A table policy should not work on blobs or containers + try { + testBlobAccountSas(this.blobContainer, false, + generatePolicy(AccountSasTests.FULL_PERMS_CODE, SharedAccessAccountService.TABLE, null, null)); + fail(); + } + catch (StorageException ex) { + assertEquals(AccountSasTests.INVALID_SERVICE_MESSAGE, ex.getMessage()); + } + } + + @Test + @Category({ DevFabricTests.class, DevStoreTests.class, CloudTests.class }) + public void testBlobIPAccountSas() throws InvalidKeyException, StorageException, URISyntaxException, IOException { + + IPRange allIP = new IPRange("0.0.0.0", "255.255.255.255"); + IPRange noneIP = new IPRange("0.0.0.0"); + + // Ensure access attempt from invalid IP fails + IPRange sourceIP = null; + try { + testBlobAccountSas(this.blobContainer, false, + generatePolicy(AccountSasTests.FULL_PERMS_CODE, SharedAccessAccountService.BLOB, noneIP, null)); + fail(); + } + catch (StorageException ex) { + assertEquals(HttpURLConnection.HTTP_FORBIDDEN, ex.getHttpStatusCode()); + + final String[] words = ex.getMessage().split(" "); + // final word + String lastWord = words[words.length - 1]; + // strip trailing period + lastWord = lastWord.substring(0, lastWord.length() - 1); + + sourceIP = new IPRange(lastWord); + } + finally { + this.blobContainer.deleteIfExists(); + } + + // Ensure access attempt from the single allowed IP succeeds + this.blobContainer = BlobTestHelper.getRandomContainerReference(); + testBlobAccountSas(this.blobContainer, false, + generatePolicy(AccountSasTests.FULL_PERMS_CODE, SharedAccessAccountService.BLOB, sourceIP, null)); + + // Ensure access attempt from one of many valid IPs succeeds + this.blobContainer = BlobTestHelper.getRandomContainerReference(); + testBlobAccountSas(this.blobContainer, false, + generatePolicy(AccountSasTests.FULL_PERMS_CODE, SharedAccessAccountService.BLOB, allIP, null)); + } + + @Test + @Category({ DevFabricTests.class, DevStoreTests.class, CloudTests.class }) + public void testBlobProtocolAccountSas() + throws InvalidKeyException, StorageException, URISyntaxException, IOException { + + // Ensure attempt from http fails against HTTPS_ONLY + try { + testBlobAccountSas(this.blobContainer, false, generatePolicy( + AccountSasTests.FULL_PERMS_CODE, SharedAccessAccountService.BLOB, null, SharedAccessProtocols.HTTPS_ONLY)); + fail(); + } + catch (IllegalArgumentException ex) { + assertEquals(SR.CANNOT_TRANSFORM_NON_HTTPS_URI_WITH_HTTPS_ONLY_CREDENTIALS, ex.getMessage()); + } + finally { + this.blobContainer.deleteIfExists(); + } + + // Ensure attempt from https succeeds against HTTPS_ONLY + this.blobContainer = BlobTestHelper.getRandomContainerReference(); + testBlobAccountSas(this.blobContainer, true, generatePolicy( + AccountSasTests.FULL_PERMS_CODE, SharedAccessAccountService.BLOB, null, SharedAccessProtocols.HTTPS_ONLY)); + + // Ensure attempts from both https and http succeed against HTTPS_HTTP + this.blobContainer = BlobTestHelper.getRandomContainerReference(); + testBlobAccountSas(this.blobContainer, true, generatePolicy( + AccountSasTests.FULL_PERMS_CODE, SharedAccessAccountService.BLOB, null, SharedAccessProtocols.HTTPS_HTTP)); + + this.blobContainer = BlobTestHelper.getRandomContainerReference(); + testBlobAccountSas(this.blobContainer, false, generatePolicy( + AccountSasTests.FULL_PERMS_CODE, SharedAccessAccountService.BLOB, null, SharedAccessProtocols.HTTPS_HTTP)); + } + + @Test + @Category({ DevFabricTests.class, DevStoreTests.class, CloudTests.class }) + public void testBlobAccountSasCombinations() + throws InvalidKeyException, StorageException, URISyntaxException, IOException { + // Test full and empty permissions + testBlobAccountSas(false, AccountSasTests.FULL_PERMS_CODE); + testBlobAccountSas(false, AccountSasTests.EMPTY_PERMS_CODE); + + // Test each individual permission + testBlobAccountSas(false, AccountSasTests.ADD_CODE); + testBlobAccountSas(false, AccountSasTests.CREATE_CODE); + testBlobAccountSas(false, AccountSasTests.DELETE_CODE); + testBlobAccountSas(false, AccountSasTests.LIST_CODE); + testBlobAccountSas(false, AccountSasTests.READ_CODE); + testBlobAccountSas(false, AccountSasTests.WRITE_CODE); + testBlobAccountSas(false, AccountSasTests.OBJECT_CODE); + testBlobAccountSas(false, AccountSasTests.CONTAINER_CODE); + testBlobAccountSas(false, AccountSasTests.SERVICE_CODE); + + // Test an arbitrary combination of permissions. + final int bits = AccountSasTests.OBJECT_CODE | AccountSasTests.SERVICE_CODE | AccountSasTests.READ_CODE | + AccountSasTests.CREATE_CODE | AccountSasTests.DELETE_CODE; + testBlobAccountSas(false, bits); + } + + @Test + @Category({ DevFabricTests.class, DevStoreTests.class, CloudTests.class }) + public void testFileServiceAccountSas() throws InvalidKeyException, StorageException, URISyntaxException, IOException { + // A blob policy should not work on files or shares + try { + testFileAccountSas(this.fileShare, false, + generatePolicy(AccountSasTests.FULL_PERMS_CODE, SharedAccessAccountService.BLOB, null, null)); + fail(); + } + catch (StorageException ex) { + assertEquals(AccountSasTests.INVALID_SERVICE_MESSAGE, ex.getMessage()); + } + + // A queue policy should not work on files or shares + try { + testFileAccountSas(this.fileShare, false, + generatePolicy(AccountSasTests.FULL_PERMS_CODE, SharedAccessAccountService.QUEUE, null, null)); + fail(); + } + catch (StorageException ex) { + assertEquals(AccountSasTests.INVALID_SERVICE_MESSAGE, ex.getMessage()); + } + + // A table policy should not work on files or shares + try { + testFileAccountSas(this.fileShare, false, + generatePolicy(AccountSasTests.FULL_PERMS_CODE, SharedAccessAccountService.TABLE, null, null)); + fail(); + } + catch (StorageException ex) { + assertEquals(AccountSasTests.INVALID_SERVICE_MESSAGE, ex.getMessage()); + } + } + + @Test + @Category({ DevFabricTests.class, DevStoreTests.class, CloudTests.class }) + public void testFileIPAccountSas() throws InvalidKeyException, StorageException, URISyntaxException, IOException { + + IPRange allIP = new IPRange("0.0.0.0", "255.255.255.255"); + IPRange noneIP = new IPRange("0.0.0.0"); + + // Ensure access attempt from invalid IP fails + IPRange sourceIP = null; + try { + testFileAccountSas(this.fileShare, false, + generatePolicy(AccountSasTests.FULL_PERMS_CODE, SharedAccessAccountService.FILE, noneIP, null)); + fail(); + } + catch (StorageException ex) { + assertEquals(HttpURLConnection.HTTP_FORBIDDEN, ex.getHttpStatusCode()); + + final String[] words = ex.getMessage().split(" "); + // final word + String lastWord = words[words.length - 1]; + // strip trailing period + lastWord = lastWord.substring(0, lastWord.length() - 1); + + sourceIP = new IPRange(lastWord); + } + finally { + this.fileShare.deleteIfExists(); + } + + // Ensure access attempt from the single allowed IP succeeds + this.fileShare = FileTestHelper.getRandomShareReference(); + testFileAccountSas(this.fileShare, false, + generatePolicy(AccountSasTests.FULL_PERMS_CODE, SharedAccessAccountService.FILE, sourceIP, null)); + + // Ensure access attempt from one of many valid IPs succeeds + this.fileShare = FileTestHelper.getRandomShareReference(); + testFileAccountSas(this.fileShare, false, + generatePolicy(AccountSasTests.FULL_PERMS_CODE, SharedAccessAccountService.FILE, allIP, null)); + } + + @Test + @Category({ DevFabricTests.class, DevStoreTests.class, CloudTests.class }) + public void testFileProtocolAccountSas() + throws InvalidKeyException, StorageException, URISyntaxException, IOException { + + // Ensure attempt from http fails against HTTPS_ONLY + try { + testFileAccountSas(this.fileShare, false, generatePolicy( + AccountSasTests.FULL_PERMS_CODE, SharedAccessAccountService.FILE, null, SharedAccessProtocols.HTTPS_ONLY)); + fail(); + } + catch (IllegalArgumentException ex) { + assertEquals(SR.CANNOT_TRANSFORM_NON_HTTPS_URI_WITH_HTTPS_ONLY_CREDENTIALS, ex.getMessage()); + } + finally { + this.fileShare.deleteIfExists(); + } + + // Ensure attempt from https succeeds against HTTPS_ONLY + this.fileShare = FileTestHelper.getRandomShareReference(); + testFileAccountSas(this.fileShare, true, generatePolicy( + AccountSasTests.FULL_PERMS_CODE, SharedAccessAccountService.FILE, null, SharedAccessProtocols.HTTPS_ONLY)); + + // Ensure attempts from both https and http succeed against HTTPS_HTTP + this.fileShare = FileTestHelper.getRandomShareReference(); + testFileAccountSas(this.fileShare, true, generatePolicy( + AccountSasTests.FULL_PERMS_CODE, SharedAccessAccountService.FILE, null, SharedAccessProtocols.HTTPS_HTTP)); + + this.fileShare = FileTestHelper.getRandomShareReference(); + testFileAccountSas(this.fileShare, false, generatePolicy( + AccountSasTests.FULL_PERMS_CODE, SharedAccessAccountService.FILE, null, SharedAccessProtocols.HTTPS_HTTP)); + } + + @Test + @Category({ DevFabricTests.class, DevStoreTests.class, CloudTests.class }) + public void testFileAccountSasCombinations() + throws InvalidKeyException, StorageException, URISyntaxException, IOException { + // Test full and empty permissions + testFileAccountSas(false, AccountSasTests.FULL_PERMS_CODE); + testFileAccountSas(false, AccountSasTests.EMPTY_PERMS_CODE); + + // Test each individual permission + testFileAccountSas(false, AccountSasTests.CREATE_CODE); + testFileAccountSas(false, AccountSasTests.DELETE_CODE); + testFileAccountSas(false, AccountSasTests.LIST_CODE); + testFileAccountSas(false, AccountSasTests.READ_CODE); + testFileAccountSas(false, AccountSasTests.WRITE_CODE); + testFileAccountSas(false, AccountSasTests.OBJECT_CODE); + testFileAccountSas(false, AccountSasTests.CONTAINER_CODE); + testFileAccountSas(false, AccountSasTests.SERVICE_CODE); + + // Test an arbitrary combination of permissions. + final int bits = AccountSasTests.OBJECT_CODE | AccountSasTests.SERVICE_CODE | AccountSasTests.READ_CODE | + AccountSasTests.CREATE_CODE | AccountSasTests.DELETE_CODE; + testFileAccountSas(false, bits); + } + + @Test + @Category({ DevFabricTests.class, DevStoreTests.class, CloudTests.class }) + public void testQueueServiceAccountSas() + throws InvalidKeyException, StorageException, URISyntaxException, IOException, InterruptedException { + + // A blob policy should not work on queues + try { + testQueueAccountSas(this.queueQueue, false, + generatePolicy(AccountSasTests.FULL_PERMS_CODE, SharedAccessAccountService.BLOB, null, null)); + fail(); + } + catch (StorageException ex) { + assertEquals(AccountSasTests.INVALID_SERVICE_MESSAGE, ex.getMessage()); + } + + // A file policy should not work on queues + try { + testQueueAccountSas(this.queueQueue, false, + generatePolicy(AccountSasTests.FULL_PERMS_CODE, SharedAccessAccountService.FILE, null, null)); + fail(); + } + catch (StorageException ex) { + assertEquals(AccountSasTests.INVALID_SERVICE_MESSAGE, ex.getMessage()); + } + + // A table policy should not work on queues + try { + testQueueAccountSas(this.queueQueue, false, + generatePolicy(AccountSasTests.FULL_PERMS_CODE, SharedAccessAccountService.TABLE, null, null)); + fail(); + } + catch (StorageException ex) { + assertEquals(AccountSasTests.INVALID_SERVICE_MESSAGE, ex.getMessage()); + } + } + + @Test + @Category({ DevFabricTests.class, DevStoreTests.class, CloudTests.class }) + public void testQueueIPAccountSas() + throws InvalidKeyException, StorageException, URISyntaxException, IOException, InterruptedException { + + IPRange allIP = new IPRange("0.0.0.0", "255.255.255.255"); + IPRange noneIP = new IPRange("0.0.0.0"); + + // Ensure access attempt from invalid IP fails + IPRange sourceIP = null; + try { + testQueueAccountSas(this.queueQueue, false, + generatePolicy(AccountSasTests.FULL_PERMS_CODE, SharedAccessAccountService.QUEUE, noneIP, null)); + fail(); + } + catch (StorageException ex) { + assertEquals(HttpURLConnection.HTTP_FORBIDDEN, ex.getHttpStatusCode()); + + final String[] words = ex.getMessage().split(" "); + // final word + String lastWord = words[words.length - 1]; + // strip trailing period + lastWord = lastWord.substring(0, lastWord.length() - 1); + + sourceIP = new IPRange(lastWord); + } + finally { + this.queueQueue.deleteIfExists(); + } + + // Ensure access attempt from the single allowed IP succeeds + this.queueQueue = QueueTestHelper.getRandomQueueReference(); + testQueueAccountSas(this.queueQueue, false, + generatePolicy(AccountSasTests.FULL_PERMS_CODE, SharedAccessAccountService.QUEUE, sourceIP, null)); + + // Ensure access attempt from one of many valid IPs succeeds + this.queueQueue = QueueTestHelper.getRandomQueueReference(); + testQueueAccountSas(this.queueQueue, false, + generatePolicy(AccountSasTests.FULL_PERMS_CODE, SharedAccessAccountService.QUEUE, allIP, null)); + } + + @Test + @Category({ DevFabricTests.class, DevStoreTests.class, CloudTests.class }) + public void testQueueProtocolAccountSas() + throws InvalidKeyException, StorageException, URISyntaxException, IOException, InterruptedException { + + // Ensure attempt from http fails against HTTPS_ONLY + try { + testQueueAccountSas(this.queueQueue, false, generatePolicy( + AccountSasTests.FULL_PERMS_CODE, SharedAccessAccountService.QUEUE, null, SharedAccessProtocols.HTTPS_ONLY)); + fail(); + } + catch (IllegalArgumentException ex) { + assertEquals(SR.CANNOT_TRANSFORM_NON_HTTPS_URI_WITH_HTTPS_ONLY_CREDENTIALS, ex.getMessage()); + } + finally { + this.queueQueue.deleteIfExists(); + } + + // Ensure attempt from https succeeds against HTTPS_ONLY + this.queueQueue = QueueTestHelper.getRandomQueueReference(); + testQueueAccountSas(this.queueQueue, true, generatePolicy( + AccountSasTests.FULL_PERMS_CODE, SharedAccessAccountService.QUEUE, null, SharedAccessProtocols.HTTPS_ONLY)); + + // Ensure attempts from both https and http succeed against HTTPS_HTTP + this.queueQueue = QueueTestHelper.getRandomQueueReference(); + testQueueAccountSas(this.queueQueue, true, generatePolicy( + AccountSasTests.FULL_PERMS_CODE, SharedAccessAccountService.QUEUE, null, SharedAccessProtocols.HTTPS_HTTP)); + + this.queueQueue = QueueTestHelper.getRandomQueueReference(); + testQueueAccountSas(this.queueQueue, false, generatePolicy( + AccountSasTests.FULL_PERMS_CODE, SharedAccessAccountService.QUEUE, null, SharedAccessProtocols.HTTPS_HTTP)); + } + + @Test + @Category({ DevFabricTests.class, DevStoreTests.class, CloudTests.class }) + public void testQueueAccountSasCombinations() + throws InvalidKeyException, StorageException, URISyntaxException, IOException, InterruptedException { + // Test full and empty permissions + testQueueAccountSas(false, AccountSasTests.FULL_PERMS_CODE); + testQueueAccountSas(false, AccountSasTests.EMPTY_PERMS_CODE); + + // Test each individual permission + testQueueAccountSas(false, AccountSasTests.ADD_CODE); + testQueueAccountSas(false, AccountSasTests.CREATE_CODE); + testQueueAccountSas(false, AccountSasTests.DELETE_CODE); + testQueueAccountSas(false, AccountSasTests.LIST_CODE); + testQueueAccountSas(false, AccountSasTests.PROCESS_CODE); + testQueueAccountSas(false, AccountSasTests.READ_CODE); + testQueueAccountSas(false, AccountSasTests.UPDATE_CODE); + testQueueAccountSas(false, AccountSasTests.WRITE_CODE); + testQueueAccountSas(false, AccountSasTests.OBJECT_CODE); + testQueueAccountSas(false, AccountSasTests.CONTAINER_CODE); + testQueueAccountSas(false, AccountSasTests.SERVICE_CODE); + + // Test an arbitrary combination of permissions. + final int bits = AccountSasTests.OBJECT_CODE | AccountSasTests.SERVICE_CODE | AccountSasTests.READ_CODE | + AccountSasTests.CREATE_CODE | AccountSasTests.DELETE_CODE; + testQueueAccountSas(false, bits); + } + + @Test + @Category({ DevFabricTests.class, DevStoreTests.class, CloudTests.class }) + public void testTableServiceAccountSas() throws InvalidKeyException, StorageException, URISyntaxException, IOException { + // A blob policy should not work on tables + try { + testTableAccountSas(this.tableTable, false, + generatePolicy(AccountSasTests.FULL_PERMS_CODE, SharedAccessAccountService.BLOB, null, null)); + fail(); + } + catch (StorageException ex) { + assertEquals(AccountSasTests.INVALID_SERVICE_MESSAGE, ex.getMessage()); + } + + // A file policy should not work on tables + try { + testTableAccountSas(this.tableTable, false, + generatePolicy(AccountSasTests.FULL_PERMS_CODE, SharedAccessAccountService.FILE, null, null)); + fail(); + } + catch (StorageException ex) { + assertEquals(AccountSasTests.INVALID_SERVICE_MESSAGE, ex.getMessage()); + } + + // A queue policy should not work on tables + try { + testTableAccountSas(this.tableTable, false, + generatePolicy(AccountSasTests.FULL_PERMS_CODE, SharedAccessAccountService.QUEUE, null, null)); + fail(); + } + catch (StorageException ex) { + assertEquals(AccountSasTests.INVALID_SERVICE_MESSAGE, ex.getMessage()); + } + } + + @Test + @Category({ DevFabricTests.class, DevStoreTests.class, CloudTests.class }) + public void testTableIPAccountSas() throws InvalidKeyException, StorageException, URISyntaxException, IOException { + + IPRange allIP = new IPRange("0.0.0.0", "255.255.255.255"); + IPRange noneIP = new IPRange("0.0.0.0"); + + // Ensure access attempt from invalid IP fails + IPRange sourceIP = null; + try { + testTableAccountSas(this.tableTable, false, + generatePolicy(AccountSasTests.FULL_PERMS_CODE, SharedAccessAccountService.TABLE, noneIP, null)); + fail(); + } + catch (StorageException ex) { + assertEquals(HttpURLConnection.HTTP_FORBIDDEN, ex.getHttpStatusCode()); + + final String[] words = ex.getMessage().split(" "); + // final word + String lastWord = words[words.length - 1]; + // strip trailing period + lastWord = lastWord.substring(0, lastWord.length() - 1); + + sourceIP = new IPRange(lastWord); + } + finally { + this.tableTable.deleteIfExists(); + } + + // Ensure access attempt from the single allowed IP succeeds + this.tableTable = TableTestHelper.getRandomTableReference(); + testTableAccountSas(this.tableTable, false, + generatePolicy(AccountSasTests.FULL_PERMS_CODE, SharedAccessAccountService.TABLE, sourceIP, null)); + + // Ensure access attempt from one of many valid IPs succeeds + this.tableTable = TableTestHelper.getRandomTableReference(); + testTableAccountSas(this.tableTable, false, + generatePolicy(AccountSasTests.FULL_PERMS_CODE, SharedAccessAccountService.TABLE, allIP, null)); + } + + @Test + @Category({ DevFabricTests.class, DevStoreTests.class, CloudTests.class }) + public void testTableProtocolAccountSas() + throws InvalidKeyException, StorageException, URISyntaxException, IOException { + + // Ensure attempt from http fails against HTTPS_ONLY + try { + testTableAccountSas(this.tableTable, false, generatePolicy( + AccountSasTests.FULL_PERMS_CODE, SharedAccessAccountService.TABLE, null, SharedAccessProtocols.HTTPS_ONLY)); + fail(); + } + catch (IllegalArgumentException ex) { + assertEquals(SR.CANNOT_TRANSFORM_NON_HTTPS_URI_WITH_HTTPS_ONLY_CREDENTIALS, ex.getMessage()); + } + finally { + this.tableTable.deleteIfExists(); + } + + // Ensure attempt from https succeeds against HTTPS_ONLY + this.tableTable = TableTestHelper.getRandomTableReference(); + testTableAccountSas(this.tableTable, true, generatePolicy( + AccountSasTests.FULL_PERMS_CODE, SharedAccessAccountService.TABLE, null, SharedAccessProtocols.HTTPS_ONLY)); + + // Ensure attempts from both https and http succeed against HTTPS_HTTP + this.tableTable = TableTestHelper.getRandomTableReference(); + testTableAccountSas(this.tableTable, true, generatePolicy( + AccountSasTests.FULL_PERMS_CODE, SharedAccessAccountService.TABLE, null, SharedAccessProtocols.HTTPS_HTTP)); + + this.tableTable = TableTestHelper.getRandomTableReference(); + testTableAccountSas(this.tableTable, false, generatePolicy( + AccountSasTests.FULL_PERMS_CODE, SharedAccessAccountService.TABLE, null, SharedAccessProtocols.HTTPS_HTTP)); + } + + @Test + @Category({ DevFabricTests.class, DevStoreTests.class, CloudTests.class }) + public void testTableAccountSasCombinations() + throws InvalidKeyException, StorageException, URISyntaxException, IOException { + // Test full and empty permissions + testTableAccountSas(false, AccountSasTests.FULL_PERMS_CODE); + testTableAccountSas(false, AccountSasTests.EMPTY_PERMS_CODE); + + // Test each individual permission + testTableAccountSas(false, AccountSasTests.ADD_CODE); + testTableAccountSas(false, AccountSasTests.CREATE_CODE); + testTableAccountSas(false, AccountSasTests.DELETE_CODE); + testTableAccountSas(false, AccountSasTests.LIST_CODE); + testTableAccountSas(false, AccountSasTests.READ_CODE); + testTableAccountSas(false, AccountSasTests.UPDATE_CODE); + testTableAccountSas(false, AccountSasTests.WRITE_CODE); + testTableAccountSas(false, AccountSasTests.OBJECT_CODE); + testTableAccountSas(false, AccountSasTests.CONTAINER_CODE); + testTableAccountSas(false, AccountSasTests.SERVICE_CODE); + + // Test an arbitrary combination of permissions. + final int bits = AccountSasTests.OBJECT_CODE | AccountSasTests.SERVICE_CODE | AccountSasTests.READ_CODE | + AccountSasTests.CREATE_CODE | AccountSasTests.DELETE_CODE; + testTableAccountSas(false, bits); + } + + + + private void testBlobAccountSas(final boolean useHttps, final int bits) + throws InvalidKeyException, StorageException, URISyntaxException, IOException { + SharedAccessAccountPolicy policy = generatePolicy(bits, SharedAccessAccountService.BLOB, null, null); + this.blobContainer = this.blobClient.getContainerReference("blobtest" + bits); + + try { + testBlobAccountSas(this.blobContainer, useHttps, policy); + } + catch (StorageException ex) { + if (bits < AccountSasTests.OBJECT_CODE || + bits % AccountSasTests.OBJECT_CODE == AccountSasTests.EMPTY_PERMS_CODE) { + // Expected failure if permissions or resource type is empty. + assertEquals(AccountSasTests.QUERY_PARAM_MISSING_MESSAGE, ex.getMessage()); + } + else { + throw ex; + } + } + finally { + this.blobContainer.deleteIfExists(); + } + } + + private void testBlobAccountSas(CloudBlobContainer container, boolean useHttps, SharedAccessAccountPolicy policy) + throws InvalidKeyException, StorageException, URISyntaxException, IOException { + + assertNotEquals(null, container); + assertNotEquals(null, policy); + assertFalse(container.exists()); + + final CloudBlobClient sasClient = TestHelper.createCloudBlobClient(policy, useHttps); + URI sasUri = sasClient.getContainerReference(container.getName()).getUri(); + sasUri = sasClient.getCredentials().transformUri(sasUri); + final CloudBlobContainer sasContainer = new CloudBlobContainer(sasUri); + + // Test creating the container + if (policy.getResourceTypes().contains(SharedAccessAccountResourceType.CONTAINER) && + (policy.getPermissions().contains(SharedAccessAccountPermissions.CREATE) || + policy.getPermissions().contains(SharedAccessAccountPermissions.WRITE))) { + + sasContainer.create(); + } + else { + try { + sasContainer.create(); + fail(); + } + catch (StorageException ex) { + if (AccountSasTests.QUERY_PARAM_MISSING_MESSAGE.equals(ex.getMessage())) { + throw ex; + } + + if (policy.getResourceTypes().contains(SharedAccessAccountResourceType.CONTAINER)) { + assertMessagesMatch(AccountSasTests.INVALID_PERMISSION_MESSAGE, ex); + } + else { + assertMessagesMatch(AccountSasTests.INVALID_RESOURCE_TYPE_MESSAGE, ex); + } + container.create(); + } + } + + assertTrue(container.exists()); + + // Test listing the containers on the client + if (policy.getResourceTypes().contains(SharedAccessAccountResourceType.SERVICE) && + (policy.getPermissions().contains(SharedAccessAccountPermissions.LIST))) { + + assertEquals(sasContainer.getName(), + sasClient.listContainers(sasContainer.getName()).iterator().next().getName()); + } + else { + try { + sasClient.listContainers(sasContainer.getName()).iterator().next(); + fail(); + } + catch (NoSuchElementException ex) { + assertEquals(AccountSasTests.ENUMERATION_ERROR_MESSAGE, ex.getMessage()); + assertEquals(sasContainer.getName(), + this.blobClient.listContainers(sasContainer.getName()).iterator().next().getName()); + } + } + + if (policy.getResourceTypes().contains(SharedAccessAccountResourceType.OBJECT)) { + + // Test creating a new blob + CloudAppendBlob blob = null; + CloudAppendBlob sasBlob = null; + if (policy.getPermissions().contains(SharedAccessAccountPermissions.CREATE) || + policy.getPermissions().contains(SharedAccessAccountPermissions.WRITE)) { + + sasBlob = (CloudAppendBlob) BlobTestHelper.uploadNewBlob( + sasContainer, BlobType.APPEND_BLOB, null, 0, null); + blob = container.getAppendBlobReference(sasBlob.getName()); + } + else { + try { + sasBlob = (CloudAppendBlob) BlobTestHelper.uploadNewBlob( + sasContainer, BlobType.APPEND_BLOB, null, 0, null); + fail(); + } + catch (StorageException ex) { + assertMessagesMatch(AccountSasTests.INVALID_PERMISSION_MESSAGE, ex); + blob = (CloudAppendBlob) BlobTestHelper.uploadNewBlob( + container, BlobType.APPEND_BLOB, null, 0, null); + sasBlob = (CloudAppendBlob) BlobTestHelper.getBlobReference( + BlobType.APPEND_BLOB, sasContainer, blob.getName()); + } + } + + assertTrue(blob.exists()); + + // Test uploading data to the blob + final int length = 512; + ByteArrayInputStream sourceStream = BlobTestHelper.getRandomDataStream(length); + if (policy.getPermissions().contains(SharedAccessAccountPermissions.ADD) || + policy.getPermissions().contains(SharedAccessAccountPermissions.WRITE)) { + + sasBlob.appendBlock(sourceStream, length); + } + else { + try { + sasBlob.appendBlock(sourceStream, length); + fail(); + } + catch (StorageException ex) { + assertMessagesMatch(AccountSasTests.INVALID_PERMISSION_MESSAGE, ex); + sourceStream = BlobTestHelper.getRandomDataStream(length); + blob.appendBlock(sourceStream, length); + } + } + + // Test downloading data from the blob + final ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + if (policy.getPermissions().contains(SharedAccessAccountPermissions.READ)) { + sasBlob.download(outStream); + } + else { + try { + sasBlob.download(outStream); + fail(); + } + catch (StorageException ex) { + assertMessagesMatch(AccountSasTests.INVALID_PERMISSION_MESSAGE, ex); + blob.download(outStream); + } + } + + TestHelper.assertStreamsAreEqual(sourceStream, new ByteArrayInputStream(outStream.toByteArray())); + + if (policy.getPermissions().contains(SharedAccessAccountPermissions.DELETE)) { + sasBlob.delete(); + } + else { + try { + sasBlob.delete(); + fail(); + } + catch (StorageException ex) { + assertMessagesMatch(AccountSasTests.INVALID_PERMISSION_MESSAGE, ex); + blob.delete(); + } + } + + assertFalse(blob.exists()); + } + else { + try { + BlobTestHelper.uploadNewBlob(sasContainer, BlobType.APPEND_BLOB, null, 0, null); + fail(); + } + catch (StorageException ex) { + assertMessagesMatch(AccountSasTests.INVALID_RESOURCE_TYPE_MESSAGE, ex); + BlobTestHelper.uploadNewBlob(container, BlobType.APPEND_BLOB, null, 0, null); + } + } + + // Test deleting the container + if (policy.getResourceTypes().contains(SharedAccessAccountResourceType.CONTAINER) && + policy.getPermissions().contains(SharedAccessAccountPermissions.DELETE)) { + + sasContainer.delete(); + } + else { + try { + sasContainer.delete(); + fail(); + } + catch (StorageException ex) { + if (policy.getResourceTypes().contains(SharedAccessAccountResourceType.CONTAINER)) { + assertMessagesMatch(AccountSasTests.INVALID_PERMISSION_MESSAGE, ex); + } + else { + assertMessagesMatch(AccountSasTests.INVALID_RESOURCE_TYPE_MESSAGE, ex); + } + container.delete(); + } + } + } + + private void testFileAccountSas(final boolean useHttps, final int bits) + throws InvalidKeyException, StorageException, URISyntaxException, IOException { + SharedAccessAccountPolicy policy = generatePolicy(bits, SharedAccessAccountService.FILE, null, null); + this.fileShare = this.fileClient.getShareReference("filetest" + bits); + + try { + testFileAccountSas(this.fileShare, useHttps, policy); + } + catch (StorageException ex) { + if (bits < AccountSasTests.OBJECT_CODE || + bits % AccountSasTests.OBJECT_CODE == AccountSasTests.EMPTY_PERMS_CODE) { + // Expected failure if permissions or resource type is empty. + assertEquals(AccountSasTests.QUERY_PARAM_MISSING_MESSAGE, ex.getMessage()); + } + else { + throw ex; + } + } + finally { + this.fileShare.deleteIfExists(); + } + } + + private void testFileAccountSas(CloudFileShare share, boolean useHttps, SharedAccessAccountPolicy policy) + throws InvalidKeyException, StorageException, URISyntaxException, IOException { + + assertNotEquals(null, share); + assertNotEquals(null, policy); + assertFalse(share.exists()); + + final CloudFileClient sasClient = TestHelper.createCloudFileClient(policy, useHttps); + URI sasUri = sasClient.getShareReference(share.getName()).getUri(); + sasUri = sasClient.getCredentials().transformUri(sasUri); + final CloudFileShare sasShare = new CloudFileShare(sasUri); + + // Test creating the share + if (policy.getResourceTypes().contains(SharedAccessAccountResourceType.CONTAINER) && + (policy.getPermissions().contains(SharedAccessAccountPermissions.CREATE) || + policy.getPermissions().contains(SharedAccessAccountPermissions.WRITE))) { + + sasShare.create(); + } + else { + try { + sasShare.create(); + fail(); + } + catch (StorageException ex) { + if (AccountSasTests.QUERY_PARAM_MISSING_MESSAGE.equals(ex.getMessage())) { + throw ex; + } + + if (policy.getResourceTypes().contains(SharedAccessAccountResourceType.CONTAINER)) { + assertMessagesMatch(AccountSasTests.INVALID_PERMISSION_MESSAGE, ex); + } + else { + assertMessagesMatch(AccountSasTests.INVALID_RESOURCE_TYPE_MESSAGE, ex); + } + share.create(); + } + } + + assertTrue(share.exists()); + + // Test listing the shares on the client + if (policy.getResourceTypes().contains(SharedAccessAccountResourceType.SERVICE) && + (policy.getPermissions().contains(SharedAccessAccountPermissions.LIST))) { + + assertEquals(sasShare.getName(), sasClient.listShares(sasShare.getName()).iterator().next().getName()); + } + else { + try { + sasClient.listShares(sasShare.getName()).iterator().next(); + fail(); + } + catch (NoSuchElementException ex) { + assertEquals(AccountSasTests.ENUMERATION_ERROR_MESSAGE, ex.getMessage()); + assertEquals(sasShare.getName(), + this.fileClient.listShares(sasShare.getName()).iterator().next().getName()); + } + } + + final int length = 512; + if (policy.getResourceTypes().contains(SharedAccessAccountResourceType.OBJECT)) { + + // Test creating a new file + CloudFile file = null; + CloudFile sasFile = null; + + sasFile = sasShare.getRootDirectoryReference().getFileReference(FileTestHelper.generateRandomFileName()); + if (policy.getPermissions().contains(SharedAccessAccountPermissions.CREATE) || + policy.getPermissions().contains(SharedAccessAccountPermissions.WRITE)) { + + sasFile.create(length); + file = share.getRootDirectoryReference().getFileReference(sasFile.getName()); + } + else { + try { + sasFile.create(length); + fail(); + } + catch (StorageException ex) { + assertMessagesMatch(AccountSasTests.INVALID_PERMISSION_MESSAGE, ex); + file = FileTestHelper.uploadNewFile(share, FileTestHelper.getRandomDataStream(0), length, null); + sasFile = sasShare.getRootDirectoryReference().getFileReference(file.getName()); + } + } + + assertTrue(file.exists()); + + //Test writing data to a file + final ByteArrayInputStream sourcestream = FileTestHelper.getRandomDataStream(length); + if (policy.getPermissions().contains(SharedAccessAccountPermissions.WRITE)) { + sasFile.upload(sourcestream, length); + } + else { + try { + sasFile.upload(sourcestream, length); + fail(); + } + catch (StorageException ex) { + assertMessagesMatch(AccountSasTests.INVALID_PERMISSION_MESSAGE, ex); + file.upload(sourcestream, length); + } + } + + // Test downloading data from the file + final ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + if (policy.getPermissions().contains(SharedAccessAccountPermissions.READ)) { + sasFile.download(outStream); + } + else { + try { + sasFile.download(outStream); + fail(); + } + catch (StorageException ex) { + assertMessagesMatch(AccountSasTests.INVALID_PERMISSION_MESSAGE, ex); + file.download(outStream); + } + } + + TestHelper.assertStreamsAreEqual(sourcestream, new ByteArrayInputStream(outStream.toByteArray())); + + if (policy.getPermissions().contains(SharedAccessAccountPermissions.DELETE)) { + sasFile.delete(); + } + else { + try { + sasFile.delete(); + fail(); + } + catch (StorageException ex) { + assertMessagesMatch(AccountSasTests.INVALID_PERMISSION_MESSAGE, ex); + file.delete(); + } + } + + assertFalse(file.exists()); + } + else { + try { + FileTestHelper.uploadNewFile(sasShare, FileTestHelper.getRandomDataStream(0), length, null); + fail(); + } + catch (StorageException ex) { + assertMessagesMatch(AccountSasTests.INVALID_RESOURCE_TYPE_MESSAGE, ex); + FileTestHelper.uploadNewFile(share, FileTestHelper.getRandomDataStream(0), length, null); + } + } + + // Test deleting the share + if (policy.getResourceTypes().contains(SharedAccessAccountResourceType.CONTAINER) && + policy.getPermissions().contains(SharedAccessAccountPermissions.DELETE)) { + + sasShare.delete(); + } + else { + try { + sasShare.delete(); + fail(); + } + catch (StorageException ex) { + if (policy.getResourceTypes().contains(SharedAccessAccountResourceType.CONTAINER)) { + assertMessagesMatch(AccountSasTests.INVALID_PERMISSION_MESSAGE, ex); + } + else { + assertMessagesMatch(AccountSasTests.INVALID_RESOURCE_TYPE_MESSAGE, ex); + } + share.delete(); + } + } + } + + private void testQueueAccountSas(final boolean useHttps, final int bits) + throws InvalidKeyException, StorageException, URISyntaxException, IOException, InterruptedException { + SharedAccessAccountPolicy policy = generatePolicy(bits, SharedAccessAccountService.QUEUE, null, null); + this.queueQueue = this.queueClient.getQueueReference("queuetest" + bits); + + try { + testQueueAccountSas(this.queueQueue, useHttps, policy); + } + catch (StorageException ex) { + if (bits < AccountSasTests.OBJECT_CODE || + bits % AccountSasTests.OBJECT_CODE == AccountSasTests.EMPTY_PERMS_CODE) { + // Expected failure if permissions or resource type is empty. + assertEquals(AccountSasTests.QUERY_PARAM_MISSING_MESSAGE, ex.getMessage()); + } + else { + throw ex; + } + } + finally { + this.queueQueue.deleteIfExists(); + } + } + + private void testQueueAccountSas(CloudQueue queue, boolean useHttps, SharedAccessAccountPolicy policy) + throws InvalidKeyException, StorageException, URISyntaxException, IOException, InterruptedException { + + assertNotEquals(null, policy); + assertNotEquals(null, queue); + assertFalse(queue.exists()); + + final CloudQueueClient sasClient = TestHelper.createCloudQueueClient(policy, useHttps); + URI sasUri = sasClient.getQueueReference(queue.getName()).getUri(); + sasUri = sasClient.getCredentials().transformUri(sasUri); + final CloudQueue sasQueue = new CloudQueue(sasUri); + + final String key = "testkey"; + final String value = "testvalue"; + + if (policy.getResourceTypes().contains(SharedAccessAccountResourceType.CONTAINER)) { + // Test creating the queue + if (policy.getPermissions().contains(SharedAccessAccountPermissions.CREATE) || + policy.getPermissions().contains(SharedAccessAccountPermissions.WRITE)) { + + sasQueue.create(); + } + else { + try { + sasQueue.create(); + fail(); + } + catch (StorageException ex) { + if (AccountSasTests.QUERY_PARAM_MISSING_MESSAGE.equals(ex.getMessage())) { + throw ex; + } + + if (policy.getResourceTypes().contains(SharedAccessAccountResourceType.CONTAINER)) { + assertMessagesMatch(AccountSasTests.INVALID_PERMISSION_MESSAGE, ex); + } + else { + assertMessagesMatch(AccountSasTests.INVALID_RESOURCE_TYPE_MESSAGE, ex); + } + queue.create(); + } + } + + // Test queue metadata + queue.setMetadata(new HashMap()); + queue.getMetadata().put(key, Constants.ID); + queue.uploadMetadata(); + + sasQueue.setMetadata(new HashMap()); + if (policy.getPermissions().contains(SharedAccessAccountPermissions.WRITE)) { + sasQueue.getMetadata().put(key, value); + sasQueue.uploadMetadata(); + } + else { + try { + sasQueue.getMetadata().put(key, value); + sasQueue.uploadMetadata(); + fail(); + } + catch (StorageException ex) { + assertMessagesMatch(AccountSasTests.INVALID_PERMISSION_MESSAGE, ex); + + queue.getMetadata().put(key, value); + queue.uploadMetadata(); + } + } + + queue.downloadAttributes(); + assertEquals(value, queue.getMetadata().get(key)); + } + else { + try { + sasQueue.create(); + fail(); + } + catch (StorageException ex) { + if (AccountSasTests.QUERY_PARAM_MISSING_MESSAGE.equals(ex.getMessage())) { + throw ex; + } + + if (policy.getResourceTypes().contains(SharedAccessAccountResourceType.CONTAINER)) { + assertMessagesMatch(AccountSasTests.INVALID_PERMISSION_MESSAGE, ex); + } + else { + assertMessagesMatch(AccountSasTests.INVALID_RESOURCE_TYPE_MESSAGE, ex); + } + queue.create(); + } + } + + assertTrue(queue.exists()); + + // Test listing the queues on the client + if (policy.getResourceTypes().contains(SharedAccessAccountResourceType.SERVICE) && + (policy.getPermissions().contains(SharedAccessAccountPermissions.LIST))) { + + assertEquals(sasQueue.getName(), sasClient.listQueues(sasQueue.getName()).iterator().next().getName()); + } + else { + try { + sasClient.listQueues(sasQueue.getName()).iterator().next(); + fail(); + } + catch (NoSuchElementException ex) { + assertEquals(AccountSasTests.ENUMERATION_ERROR_MESSAGE, ex.getMessage()); + assertEquals(sasQueue.getName(), + this.queueClient.listQueues(sasQueue.getName()).iterator().next().getName()); + } + } + + if (policy.getResourceTypes().contains(SharedAccessAccountResourceType.OBJECT)) { + // Test inserting a message into a queue + if (policy.getPermissions().contains(SharedAccessAccountPermissions.ADD)) { + sasQueue.addMessage(new CloudQueueMessage(value)); + } + else { + try { + sasQueue.addMessage(new CloudQueueMessage(value)); + fail(); + } + catch (StorageException ex) { + assertMessagesMatch(AccountSasTests.INVALID_PERMISSION_MESSAGE, ex); + queue.addMessage(new CloudQueueMessage(value)); + } + } + + // Test peeking at a message in the queue + CloudQueueMessage message = null; + if (policy.getPermissions().contains(SharedAccessAccountPermissions.READ)) { + message = sasQueue.peekMessage(); + } + else { + try { + sasQueue.peekMessage(); + fail(); + } + catch (StorageException ex) { + assertMessagesMatch(AccountSasTests.INVALID_PERMISSION_MESSAGE, ex); + message = queue.peekMessage(); + } + } + + assertEquals(value, message.getMessageContentAsString()); + + // Test getting a message from the queue + if (policy.getPermissions().contains(SharedAccessAccountPermissions.PROCESS_MESSAGES)) { + message = sasQueue.retrieveMessage(); + } + else { + try { + sasQueue.retrieveMessage(); + fail(); + } + catch (StorageException ex) { + assertMessagesMatch(AccountSasTests.INVALID_PERMISSION_MESSAGE, ex); + message = queue.retrieveMessage(); + } + } + + // Test updating a message in the queue + message.setMessageContent(key); + OperationContext oc = new OperationContext(); + if (policy.getPermissions().contains(SharedAccessAccountPermissions.UPDATE)) { + sasQueue.updateMessage(message, 1, EnumSet.of(MessageUpdateFields.CONTENT), null, oc); + } + else { + try { + sasQueue.updateMessage(message, 1, EnumSet.of(MessageUpdateFields.CONTENT), null, oc); + fail(); + } + catch (StorageException ex) { + assertMessagesMatch(AccountSasTests.INVALID_PERMISSION_MESSAGE, ex); + queue.updateMessage(message, 1, EnumSet.of(MessageUpdateFields.CONTENT), null, oc); + } + } + + assertEquals(oc.getLastResult().getStatusCode(), HttpURLConnection.HTTP_NO_CONTENT); + Thread.sleep(1000); + message = queue.peekMessage(); + assertEquals(key, message.getMessageContentAsString()); + + // Test clearing all messages from the queue. + queue.addMessage(new CloudQueueMessage(key)); + queue.addMessage(new CloudQueueMessage(value)); + if (policy.getPermissions().contains(SharedAccessAccountPermissions.DELETE)) { + sasQueue.clear(); + } + else { + try { + sasQueue.clear(); + fail(); + } + catch (StorageException ex) { + assertMessagesMatch(AccountSasTests.INVALID_PERMISSION_MESSAGE, ex); + queue.clear(); + } + } + + assertEquals(null, queue.peekMessage()); + } + else { + try { + sasQueue.peekMessage(); + fail(); + } + catch (StorageException ex) { + assertMessagesMatch(AccountSasTests.INVALID_RESOURCE_TYPE_MESSAGE, ex); + queue.peekMessage(); + } + } + + // Test deleting the queue + if (policy.getResourceTypes().contains(SharedAccessAccountResourceType.CONTAINER) && + policy.getPermissions().contains(SharedAccessAccountPermissions.DELETE)) { + + sasQueue.delete(); + } + else { + try { + sasQueue.delete(); + fail(); + } + catch (StorageException ex) { + if (policy.getResourceTypes().contains(SharedAccessAccountResourceType.CONTAINER)) { + assertMessagesMatch(AccountSasTests.INVALID_PERMISSION_MESSAGE, ex); + } + else { + assertMessagesMatch(AccountSasTests.INVALID_RESOURCE_TYPE_MESSAGE, ex); + } + queue.delete(); + } + } + } + + private void testTableAccountSas(final boolean useHttps, final int bits) + throws InvalidKeyException, StorageException, URISyntaxException, IOException { + SharedAccessAccountPolicy policy = generatePolicy(bits, SharedAccessAccountService.TABLE, null, null); + this.tableTable = this.tableClient.getTableReference("tabletest" + bits); + + try { + testTableAccountSas(this.tableTable, useHttps, policy); + } + catch (StorageException ex) { + if (bits < AccountSasTests.OBJECT_CODE || + bits % AccountSasTests.OBJECT_CODE == AccountSasTests.EMPTY_PERMS_CODE) { + // Expected failure if permissions or resource type is empty. + assertEquals(AccountSasTests.QUERY_PARAM_MISSING_MESSAGE, ex.getMessage()); + } + else { + throw ex; + } + } + finally { + this.fileShare.deleteIfExists(); + } + } + + private void testTableAccountSas(CloudTable table, boolean useHttps, SharedAccessAccountPolicy policy) + throws InvalidKeyException, StorageException, URISyntaxException, IOException { + + assertNotEquals(null, policy); + assertNotEquals(null, table); + assertFalse(table.exists()); + + final CloudTableClient sasClient = TestHelper.createCloudTableClient(policy, useHttps); + URI sasUri = sasClient.getTableReference(table.getName()).getUri(); + sasUri = sasClient.getCredentials().transformUri(sasUri); + final CloudTable sasTable = new CloudTable(sasUri); + + final String key = "testkey"; + final String value = "testvalue"; + final String value2 = "testvalue2"; + final String partition1 = "testpartition1"; + final String partition2 = "testpartition2"; + final String row1 = "testrow1"; + final String row2 = "testrow2"; + + // Test creating the table + if (policy.getResourceTypes().contains(SharedAccessAccountResourceType.CONTAINER) && + (policy.getPermissions().contains(SharedAccessAccountPermissions.CREATE) || + policy.getPermissions().contains(SharedAccessAccountPermissions.WRITE))) { + + sasTable.create(); + } + else { + try { + sasTable.create(); + fail(); + } + catch (StorageException ex) { + if (AccountSasTests.QUERY_PARAM_MISSING_MESSAGE.equals(ex.getMessage())) { + throw ex; + } + + if (policy.getResourceTypes().contains(SharedAccessAccountResourceType.CONTAINER)) { + assertMessagesMatch(AccountSasTests.INVALID_PERMISSION_MESSAGE, ex); + } + else { + assertMessagesMatch(AccountSasTests.INVALID_RESOURCE_TYPE_MESSAGE, ex); + } + table.create(); + } + } + + assertTrue(table.exists()); + + // Test listing the tables on the client + if (policy.getResourceTypes().contains(SharedAccessAccountResourceType.CONTAINER) && + policy.getPermissions().contains(SharedAccessAccountPermissions.LIST)) { + + assertEquals(sasTable.getName(), sasClient.listTables(sasTable.getName()).iterator().next()); + } + else { + try { + sasClient.listTables(sasTable.getName()).iterator().next(); + fail(); + } + catch (NoSuchElementException ex) { + assertEquals(AccountSasTests.ENUMERATION_ERROR_MESSAGE, ex.getMessage()); + assertEquals(sasTable.getName(), + this.tableClient.listTables(sasTable.getName()).iterator().next()); + } + } + + if (policy.getResourceTypes().contains(SharedAccessAccountResourceType.OBJECT)) { + // Test inserting an entity into a table + final HashMap columns = new HashMap(); + columns.put(key, new EntityProperty(value)); + final DynamicTableEntity entity = new DynamicTableEntity(partition1, row1, columns); + TableOperation op = TableOperation.insert(entity); + + if (policy.getPermissions().contains(SharedAccessAccountPermissions.ADD)) { + sasTable.execute(op); + } + else { + try { + sasTable.execute(op); + fail(); + } + catch (StorageException ex) { + assertMessagesMatch(AccountSasTests.INVALID_PERMISSION_MESSAGE, ex); + table.execute(op); + } + } + + op = TableOperation.retrieve(partition1, row1, DynamicTableEntity.class); + DynamicTableEntity result = (DynamicTableEntity) table.execute(op).getResultAsType(); + assertEquals(value, result.getProperties().get(key).getValueAsString()); + + // Test merging an entity in a table + entity.getProperties().put(key, new EntityProperty(value2)); + op = TableOperation.merge(entity); + if (policy.getPermissions().contains(SharedAccessAccountPermissions.UPDATE)) { + sasTable.execute(op); + } + else { + try { + sasTable.execute(op); + fail(); + } + catch (StorageException ex) { + assertMessagesMatch(AccountSasTests.INVALID_PERMISSION_MESSAGE, ex); + table.execute(op); + } + } + + // Test retrieving an entity from a table + op = TableOperation.retrieve(partition1, row1, DynamicTableEntity.class); + result = null; + if (policy.getPermissions().contains(SharedAccessAccountPermissions.READ)) { + result = (DynamicTableEntity) sasTable.execute(op).getResultAsType(); + } + else { + try { + sasTable.execute(op); + fail(); + } + catch (StorageException ex) { + assertMessagesMatch(AccountSasTests.INVALID_PERMISSION_MESSAGE, ex); + result = (DynamicTableEntity) table.execute(op).getResultAsType(); + } + } + + assertEquals(value2, result.getProperties().get(key).getValueAsString()); + + // Test insert or merge on two entities + entity.getProperties().put(key, new EntityProperty(value)); + op = TableOperation.insertOrMerge(entity); + + final HashMap columns2 = new HashMap(); + columns2.put(key, new EntityProperty(value2)); + final DynamicTableEntity entity2 = new DynamicTableEntity(partition2, row2, columns2); + TableOperation op2 = TableOperation.insertOrMerge(entity2); + + if (policy.getPermissions().contains(SharedAccessAccountPermissions.ADD) && + policy.getPermissions().contains(SharedAccessAccountPermissions.UPDATE)) { + sasTable.execute(op); + sasTable.execute(op2); + } + else { + try { + sasTable.execute(op); + fail(); + } + catch (StorageException ex) { + assertMessagesMatch(AccountSasTests.INVALID_PERMISSION_MESSAGE, ex); + try { + sasTable.execute(op2); + fail(); + } + catch (StorageException exeption) { + assertMessagesMatch(AccountSasTests.INVALID_PERMISSION_MESSAGE, exeption); + table.execute(op); + table.execute(op2); + } + } + } + + TableOperation retrieveOp = TableOperation.retrieve(partition2, row2, DynamicTableEntity.class); + result = (DynamicTableEntity) table.execute(retrieveOp).getResultAsType(); + assertEquals(value2, result.getProperties().get(key).getValueAsString()); + + retrieveOp = TableOperation.retrieve(partition1, row1, DynamicTableEntity.class); + result = (DynamicTableEntity) table.execute(retrieveOp).getResultAsType(); + assertEquals(value, result.getProperties().get(key).getValueAsString()); + + // Test deleting an entity from a table. + op = TableOperation.delete(entity); + if (policy.getPermissions().contains(SharedAccessAccountPermissions.DELETE)) { + sasTable.execute(op); + } + else { + try { + sasTable.execute(op); + fail(); + } + catch (StorageException ex) { + assertMessagesMatch(AccountSasTests.INVALID_PERMISSION_MESSAGE, ex); + table.execute(op); + } + } + + try { + table.execute(op); + } + catch (StorageException ex) { + assertMessagesMatch(AccountSasTests.DOES_NOT_EXIST_ERROR_MESSAGE, ex); + } + } + else { + TableOperation op = TableOperation.retrieve(partition1, row1, DynamicTableEntity.class); + try { + sasTable.execute(op); + fail(); + } + catch (StorageException ex) { + assertMessagesMatch(AccountSasTests.INVALID_RESOURCE_TYPE_MESSAGE, ex); + table.execute(op); + } + } + + // Test deleting the share + if (policy.getResourceTypes().contains(SharedAccessAccountResourceType.CONTAINER) && + policy.getPermissions().contains(SharedAccessAccountPermissions.DELETE)) { + + sasTable.delete(); + } + else { + try { + sasTable.delete(); + fail(); + } + catch (StorageException ex) { + if (policy.getResourceTypes().contains(SharedAccessAccountResourceType.CONTAINER)) { + assertMessagesMatch(AccountSasTests.INVALID_PERMISSION_MESSAGE, ex); + } + else { + assertMessagesMatch(AccountSasTests.INVALID_RESOURCE_TYPE_MESSAGE, ex); + } + table.delete(); + } + } + } + + private static void assertMessagesMatch(String expectedMessage, StorageException ex) { + final String message = ex.getExtendedErrorInformation().getErrorMessage().split("\n")[0]; + assertEquals(expectedMessage, message); + } + + private static SharedAccessAccountPolicy generatePolicy( + final int bits, final SharedAccessAccountService service, IPRange ipRange, SharedAccessProtocols protocols) { + + EnumSet services = EnumSet.noneOf(SharedAccessAccountService.class); + EnumSet permissions = EnumSet.noneOf(SharedAccessAccountPermissions.class); + EnumSet resourceTypes = EnumSet.noneOf(SharedAccessAccountResourceType.class); + + services.add(service); + + if ((bits & AccountSasTests.ADD_CODE) == AccountSasTests.ADD_CODE) { + permissions.add(SharedAccessAccountPermissions.ADD); + } + + if ((bits & AccountSasTests.CREATE_CODE) == AccountSasTests.CREATE_CODE) { + permissions.add(SharedAccessAccountPermissions.CREATE); + } + + if ((bits & AccountSasTests.DELETE_CODE) == AccountSasTests.DELETE_CODE) { + permissions.add(SharedAccessAccountPermissions.DELETE); + } + + if ((bits & AccountSasTests.LIST_CODE) == AccountSasTests.LIST_CODE) { + permissions.add(SharedAccessAccountPermissions.LIST); + } + + if ((bits & AccountSasTests.PROCESS_CODE) == AccountSasTests.PROCESS_CODE) { + permissions.add(SharedAccessAccountPermissions.PROCESS_MESSAGES); + } + + if ((bits & AccountSasTests.READ_CODE) == AccountSasTests.READ_CODE) { + permissions.add(SharedAccessAccountPermissions.READ); + } + + if ((bits & AccountSasTests.UPDATE_CODE) == AccountSasTests.UPDATE_CODE) { + permissions.add(SharedAccessAccountPermissions.UPDATE); + } + + if ((bits & AccountSasTests.WRITE_CODE) == AccountSasTests.WRITE_CODE) { + permissions.add(SharedAccessAccountPermissions.WRITE); + } + + if ((bits & AccountSasTests.OBJECT_CODE) == AccountSasTests.OBJECT_CODE) { + resourceTypes.add(SharedAccessAccountResourceType.OBJECT); + } + + if ((bits & AccountSasTests.CONTAINER_CODE) == AccountSasTests.CONTAINER_CODE) { + resourceTypes.add(SharedAccessAccountResourceType.CONTAINER); + } + + if ((bits & AccountSasTests.SERVICE_CODE) == AccountSasTests.SERVICE_CODE) { + resourceTypes.add(SharedAccessAccountResourceType.SERVICE); + } + + Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC")); + cal.setTime(new Date()); + cal.add(Calendar.SECOND, 300); + + SharedAccessAccountPolicy policy = new SharedAccessAccountPolicy(); + policy.setServices(services); + policy.setPermissions(permissions); + policy.setResourceTypes(resourceTypes); + policy.setRange(ipRange); + policy.setProtocols(protocols); + policy.setSharedAccessExpiryTime(cal.getTime()); + + return policy; + } +} \ No newline at end of file diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/LoggerTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/LoggerTests.java index e6559ee11..994e3ffbd 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/LoggerTests.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/LoggerTests.java @@ -26,7 +26,6 @@ import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; -import org.junit.Ignore; import org.junit.Test; import org.junit.experimental.categories.Category; import org.slf4j.LoggerFactory; @@ -42,27 +41,6 @@ import com.microsoft.azure.storage.core.Logger; /* - * To run pass these tests you must: - * - * 1. Have the simple sl4j binding on your classpath. Add the following to the dependency section of your pom: - * - * org.slf4j - * slf4j-simple - * 1.7.5 - * test - * - * - * 2. Create a "resources" directory under src/test. In src/test/resources create a "simplelogger.properties" file with - * the following lines: - * org.slf4j.simpleLogger.defaultLogLevel = trace - * org.slf4j.simpleLogger.log.limited = error - * org.slf4j.simpleLogger.showThreadName = false - * - * 3. Remove or comment out the @Ignore annotation on the class - * - * See http://www.slf4j.org/apidocs/org/slf4j/impl/SimpleLogger.html for more information. - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * If you'd like to use a different slf4j binding and/or not use Maven, you will need to add a different class path * dependency and set the properties for it accordingly. The dependency and the properties file will need to be put in * the appropriate locations for the logger implementation chosen. @@ -75,7 +53,6 @@ * Then, you will need to modify the readAndCompareOutput method to parse the logs entries accordingly. */ @Category({ DevFabricTests.class, DevStoreTests.class, CloudTests.class }) -@Ignore public class LoggerTests { private final static String TRACE = "TRACE"; diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/ServicePropertiesTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/ServicePropertiesTests.java index fe99ac30b..808f15715 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/ServicePropertiesTests.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/ServicePropertiesTests.java @@ -76,7 +76,7 @@ private void testAnalyticsDisable(ServiceClient client, ServiceProperties props) props.getCors().getCorsRules().clear(); - callUploadServiceProps(client, props); + callUploadServiceProps(client, props, null); assertServicePropertiesAreEqual(props, callDownloadServiceProperties(client)); } @@ -107,29 +107,29 @@ private void testAnalyticsDefaultServiceVersion(ServiceClient client, ServicePro throws StorageException, InterruptedException { if (client.getClass().equals(CloudBlobClient.class)) { props.setDefaultServiceVersion("2009-09-19"); - callUploadServiceProps(client, props); + callUploadServiceProps(client, props, null); assertServicePropertiesAreEqual(props, callDownloadServiceProperties(client)); props.setDefaultServiceVersion("2011-08-18"); - callUploadServiceProps(client, props); + callUploadServiceProps(client, props, null); assertServicePropertiesAreEqual(props, callDownloadServiceProperties(client)); props.setDefaultServiceVersion("2012-02-12"); - callUploadServiceProps(client, props); + callUploadServiceProps(client, props, null); assertServicePropertiesAreEqual(props, callDownloadServiceProperties(client)); props.setDefaultServiceVersion(Constants.HeaderConstants.TARGET_STORAGE_VERSION); - callUploadServiceProps(client, props); + callUploadServiceProps(client, props, null); assertServicePropertiesAreEqual(props, callDownloadServiceProperties(client)); } else { try { props.setDefaultServiceVersion("2009-09-19"); - callUploadServiceProps(client, props); + callUploadServiceProps(client, props, null); fail("Should not be able to set default Service Version for non Blob Client"); } catch (IllegalArgumentException e) { @@ -170,13 +170,13 @@ private void testAnalyticsLoggingOperations(ServiceClient client, ServicePropert props.getLogging().setRetentionIntervalInDays(null); props.getLogging().setVersion("1.0"); - callUploadServiceProps(client, props); + callUploadServiceProps(client, props, null); assertServicePropertiesAreEqual(props, callDownloadServiceProperties(client)); // None props.getLogging().setLogOperationTypes(EnumSet.allOf(LoggingOperations.class)); - callUploadServiceProps(client, props); + callUploadServiceProps(client, props, null); assertServicePropertiesAreEqual(props, callDownloadServiceProperties(client)); } @@ -188,42 +188,65 @@ private void testAnalyticsLoggingOperations(ServiceClient client, ServicePropert * @throws InterruptedException */ @Test - public void testAnalyticsMetricsLevel() throws StorageException, InterruptedException { + public void testAnalyticsHourMetricsLevel() throws StorageException, InterruptedException { ServiceClient client = TestHelper.createCloudBlobClient(); ServiceProperties props = new ServiceProperties(); props.setDefaultServiceVersion(Constants.HeaderConstants.TARGET_STORAGE_VERSION); - testAnalyticsMetricsLevel(client, props); + testAnalyticsHourMetricsLevel(client, props, null); client = TestHelper.createCloudQueueClient(); props = new ServiceProperties(); - testAnalyticsMetricsLevel(client, props); + testAnalyticsHourMetricsLevel(client, props, null); client = TestHelper.createCloudTableClient(); props = new ServiceProperties(); - testAnalyticsMetricsLevel(client, props); + testAnalyticsHourMetricsLevel(client, props, null); + + client = TestHelper.createCloudFileClient(); + FileServiceProperties fileProps = new FileServiceProperties(); + testAnalyticsHourMetricsLevel(client, null, fileProps); } - private void testAnalyticsMetricsLevel(ServiceClient client, ServiceProperties props) throws StorageException, - InterruptedException { + private void testAnalyticsHourMetricsLevel( + ServiceClient client, ServiceProperties props, FileServiceProperties fileProps) + throws StorageException, InterruptedException { + + final MetricsProperties hours = (props == null) ? fileProps.getHourMetrics() : props.getHourMetrics(); + // None - props.getHourMetrics().setMetricsLevel(MetricsLevel.DISABLED); - props.getHourMetrics().setRetentionIntervalInDays(null); - props.getHourMetrics().setVersion("1.0"); - callUploadServiceProps(client, props); + hours.setMetricsLevel(MetricsLevel.DISABLED); + hours.setRetentionIntervalInDays(null); + hours.setVersion("1.0"); + callUploadServiceProps(client, props, fileProps); - assertServicePropertiesAreEqual(props, callDownloadServiceProperties(client)); + if (props == null) { + assertFileServicePropertiesAreEqual(fileProps, ((CloudFileClient) client).downloadServiceProperties()); + } + else { + assertServicePropertiesAreEqual(props, callDownloadServiceProperties(client)); + } // Service - props.getHourMetrics().setMetricsLevel(MetricsLevel.SERVICE); - callUploadServiceProps(client, props); + hours.setMetricsLevel(MetricsLevel.SERVICE); + callUploadServiceProps(client, props, fileProps); - assertServicePropertiesAreEqual(props, callDownloadServiceProperties(client)); + if (props == null) { + assertFileServicePropertiesAreEqual(fileProps, ((CloudFileClient) client).downloadServiceProperties()); + } + else { + assertServicePropertiesAreEqual(props, callDownloadServiceProperties(client)); + } // ServiceAndAPI - props.getHourMetrics().setMetricsLevel(MetricsLevel.SERVICE_AND_API); - callUploadServiceProps(client, props); + hours.setMetricsLevel(MetricsLevel.SERVICE_AND_API); + callUploadServiceProps(client, props, fileProps); - assertServicePropertiesAreEqual(props, callDownloadServiceProperties(client)); + if (props == null) { + assertFileServicePropertiesAreEqual(fileProps, ((CloudFileClient) client).downloadServiceProperties()); + } + else { + assertServicePropertiesAreEqual(props, callDownloadServiceProperties(client)); + } } /** @@ -237,38 +260,61 @@ public void testAnalyticsMinuteMetricsLevel() throws StorageException, Interrupt ServiceClient client = TestHelper.createCloudBlobClient(); ServiceProperties props = new ServiceProperties(); props.setDefaultServiceVersion(Constants.HeaderConstants.TARGET_STORAGE_VERSION); - testAnalyticsMinuteMetricsLevel(client, props); + testAnalyticsMinuteMetricsLevel(client, props, null); client = TestHelper.createCloudQueueClient(); props = new ServiceProperties(); - testAnalyticsMinuteMetricsLevel(client, props); + testAnalyticsMinuteMetricsLevel(client, props, null); client = TestHelper.createCloudTableClient(); props = new ServiceProperties(); - testAnalyticsMinuteMetricsLevel(client, props); + testAnalyticsMinuteMetricsLevel(client, props, null); + + client = TestHelper.createCloudFileClient(); + FileServiceProperties fileProps = new FileServiceProperties(); + testAnalyticsMinuteMetricsLevel(client, null, fileProps); } - private void testAnalyticsMinuteMetricsLevel(ServiceClient client, ServiceProperties props) + private void testAnalyticsMinuteMetricsLevel( + final ServiceClient client, final ServiceProperties props, final FileServiceProperties fileProps) throws StorageException, InterruptedException { + + final MetricsProperties minutes = (props == null) ? fileProps.getMinuteMetrics() : props.getMinuteMetrics(); + // None - props.getMinuteMetrics().setMetricsLevel(MetricsLevel.DISABLED); - props.getMinuteMetrics().setRetentionIntervalInDays(null); - props.getMinuteMetrics().setVersion("1.0"); - callUploadServiceProps(client, props); + minutes.setMetricsLevel(MetricsLevel.DISABLED); + minutes.setRetentionIntervalInDays(null); + minutes.setVersion("1.0"); + callUploadServiceProps(client, props, fileProps); - assertServicePropertiesAreEqual(props, callDownloadServiceProperties(client)); + if (props == null) { + assertFileServicePropertiesAreEqual(fileProps, ((CloudFileClient) client).downloadServiceProperties()); + } + else { + assertServicePropertiesAreEqual(props, callDownloadServiceProperties(client)); + } // Service - props.getMinuteMetrics().setMetricsLevel(MetricsLevel.SERVICE); - callUploadServiceProps(client, props); + minutes.setMetricsLevel(MetricsLevel.SERVICE); + callUploadServiceProps(client, props, fileProps); - assertServicePropertiesAreEqual(props, callDownloadServiceProperties(client)); + if (props == null) { + assertFileServicePropertiesAreEqual(fileProps, ((CloudFileClient) client).downloadServiceProperties()); + } + else { + assertServicePropertiesAreEqual(props, callDownloadServiceProperties(client)); + } // ServiceAndAPI - props.getMinuteMetrics().setMetricsLevel(MetricsLevel.SERVICE_AND_API); - callUploadServiceProps(client, props); + minutes.setMetricsLevel(MetricsLevel.SERVICE_AND_API); + callUploadServiceProps(client, props, fileProps); - assertServicePropertiesAreEqual(props, callDownloadServiceProperties(client)); + if (props == null) { + assertFileServicePropertiesAreEqual(fileProps, ((CloudFileClient) client).downloadServiceProperties()); + } + else { + assertServicePropertiesAreEqual(props, callDownloadServiceProperties(client)); + } } /** @@ -300,7 +346,7 @@ private void testAnalyticsRetentionPolicies(ServiceClient client, ServicePropert props.getHourMetrics().setRetentionIntervalInDays(null); props.getMinuteMetrics().setMetricsLevel(MetricsLevel.DISABLED); props.getMinuteMetrics().setRetentionIntervalInDays(null); - callUploadServiceProps(client, props); + callUploadServiceProps(client, props, null); assertServicePropertiesAreEqual(props, callDownloadServiceProperties(client)); @@ -309,7 +355,7 @@ private void testAnalyticsRetentionPolicies(ServiceClient client, ServicePropert props.getHourMetrics().setMetricsLevel(MetricsLevel.SERVICE); props.getMinuteMetrics().setRetentionIntervalInDays(1); props.getMinuteMetrics().setMetricsLevel(MetricsLevel.SERVICE); - callUploadServiceProps(client, props); + callUploadServiceProps(client, props, null); assertServicePropertiesAreEqual(props, callDownloadServiceProperties(client)); @@ -318,35 +364,35 @@ private void testAnalyticsRetentionPolicies(ServiceClient client, ServicePropert props.getHourMetrics().setMetricsLevel(MetricsLevel.SERVICE_AND_API); props.getMinuteMetrics().setRetentionIntervalInDays(2); props.getMinuteMetrics().setMetricsLevel(MetricsLevel.SERVICE_AND_API); - callUploadServiceProps(client, props); + callUploadServiceProps(client, props, null); assertServicePropertiesAreEqual(props, callDownloadServiceProperties(client)); // Set retention policy null with logging disabled. props.getLogging().setRetentionIntervalInDays(null); props.getLogging().setLogOperationTypes(EnumSet.noneOf(LoggingOperations.class)); - callUploadServiceProps(client, props); + callUploadServiceProps(client, props, null); assertServicePropertiesAreEqual(props, callDownloadServiceProperties(client)); // Set retention policy not null with logging disabled. props.getLogging().setRetentionIntervalInDays(3); props.getLogging().setLogOperationTypes(EnumSet.noneOf(LoggingOperations.class)); - callUploadServiceProps(client, props); + callUploadServiceProps(client, props, null); assertServicePropertiesAreEqual(props, callDownloadServiceProperties(client)); // Set retention policy null with logging enabled. props.getLogging().setRetentionIntervalInDays(null); props.getLogging().setLogOperationTypes(EnumSet.allOf(LoggingOperations.class)); - callUploadServiceProps(client, props); + callUploadServiceProps(client, props, null); assertServicePropertiesAreEqual(props, callDownloadServiceProperties(client)); // Set retention policy not null with logging enabled. props.getLogging().setRetentionIntervalInDays(4); props.getLogging().setLogOperationTypes(EnumSet.allOf(LoggingOperations.class)); - callUploadServiceProps(client, props); + callUploadServiceProps(client, props, null); assertServicePropertiesAreEqual(props, callDownloadServiceProperties(client)); } @@ -732,7 +778,7 @@ private void testOptionalServiceProperties(ServiceClient client, ServiceProperti props.getCors().getCorsRules().clear(); - callUploadServiceProps(client, props); + callUploadServiceProps(client, props, null); ServiceProperties newProps = new ServiceProperties(); @@ -750,7 +796,7 @@ private void testOptionalServiceProperties(ServiceClient client, ServiceProperti ruleBasic.setMaxAgeInSeconds(500); newProps.getCors().getCorsRules().add(ruleBasic); - callUploadServiceProps(client, newProps); + callUploadServiceProps(client, newProps, null); props.setCors(newProps.getCors()); assertServicePropertiesAreEqual(props, callDownloadServiceProperties(client)); @@ -759,31 +805,32 @@ private void testOptionalServiceProperties(ServiceClient client, ServiceProperti newProps.setHourMetrics(props.getHourMetrics()); newProps.setMinuteMetrics(props.getMinuteMetrics()); newProps.setCors(null); - callUploadServiceProps(client, newProps); + callUploadServiceProps(client, newProps, null); assertServicePropertiesAreEqual(props, callDownloadServiceProperties(client)); } - private void callUploadServiceProps(ServiceClient client, ServiceProperties props) throws StorageException, - InterruptedException { + private void callUploadServiceProps( + ServiceClient client, ServiceProperties props, FileServiceProperties fileProps) + throws StorageException, InterruptedException { + if (client.getClass().equals(CloudBlobClient.class)) { - CloudBlobClient blobClient = (CloudBlobClient) client; - blobClient.uploadServiceProperties(props); - Thread.sleep(30000); + ((CloudBlobClient) client).uploadServiceProperties(props); } else if (client.getClass().equals(CloudTableClient.class)) { - CloudTableClient tableClient = (CloudTableClient) client; - tableClient.uploadServiceProperties(props); - Thread.sleep(30000); + ((CloudTableClient) client).uploadServiceProperties(props); } else if (client.getClass().equals(CloudQueueClient.class)) { - CloudQueueClient queueClient = (CloudQueueClient) client; - queueClient.uploadServiceProperties(props); - Thread.sleep(30000); + ((CloudQueueClient) client).uploadServiceProperties(props); + } + else if (client.getClass().equals(CloudFileClient.class)) { + ((CloudFileClient) client).uploadServiceProperties(fileProps); } else { fail(); } + + // Thread.sleep(30000); } private ServiceProperties callDownloadServiceProperties(ServiceClient client) throws StorageException { @@ -815,7 +862,7 @@ private void testCorsRules(CorsRule rule, ServiceClient client, ServicePropertie cors.getCorsRules().add(rule); if (fileServiceProperties == null) { - callUploadServiceProps(client, properties); + callUploadServiceProps(client, properties, null); assertServicePropertiesAreEqual(properties, callDownloadServiceProperties(client)); } else { CloudFileClient fileClient = ((CloudFileClient) client); @@ -839,7 +886,7 @@ private void testCorsRules(List corsRules, ServiceClient client, Servi } if (fileServiceProperties == null) { - callUploadServiceProps(client, properties); + callUploadServiceProps(client, properties, null); assertServicePropertiesAreEqual(properties, callDownloadServiceProperties(client)); } else { CloudFileClient fileClient = ((CloudFileClient) client); diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/StorageAccountTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/StorageAccountTests.java index 3deb09214..c7f0f17e5 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/StorageAccountTests.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/StorageAccountTests.java @@ -47,7 +47,7 @@ public class StorageAccountTests { @Test public void testStorageCredentialsAnonymous() throws URISyntaxException, StorageException { - StorageCredentialsAnonymous cred = new StorageCredentialsAnonymous(); + StorageCredentials cred = StorageCredentialsAnonymous.ANONYMOUS; assertNull(cred.getAccountName()); @@ -150,7 +150,7 @@ public void testStorageCredentialsNullKeyValue() { fail("Did not hit expected exception"); } catch (IllegalArgumentException ex) { - assertEquals(SR.INVALID_KEY, ex.getMessage()); + assertEquals(SR.STRING_NOT_VALID, ex.getMessage()); } StorageCredentialsAccountAndKey credentials2 = new StorageCredentialsAccountAndKey(ACCOUNT_NAME, ACCOUNT_KEY); diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/Tenant.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/Tenant.java index 677930654..2c111b132 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/Tenant.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/Tenant.java @@ -28,6 +28,10 @@ public class Tenant { private URI fileServiceSecondaryEndpoint; private URI queueServiceSecondaryEndpoint; private URI tableServiceSecondaryEndpoint; + private Integer blobHttpsPortOverride = null; + private Integer fileHttpsPortOverride = null; + private Integer queueHttpsPortOverride = null; + private Integer tableHttpsPortOverride = null; public String getTenantName() { return this.tenantName; @@ -73,6 +77,22 @@ public URI getTableServiceSecondaryEndpoint() { return this.tableServiceSecondaryEndpoint; } + public Integer getBlobHttpsPortOverride() { + return this.blobHttpsPortOverride; + } + + public Integer getFileHttpsPortOverride() { + return this.fileHttpsPortOverride; + } + + public Integer getQueueHttpsPortOverride() { + return this.queueHttpsPortOverride; + } + + public Integer getTableHttpsPortOverride() { + return this.tableHttpsPortOverride; + } + void setTenantName(String tenantName) { this.tenantName = tenantName; } @@ -117,6 +137,22 @@ void setTableServiceSecondaryEndpoint(URI tableServiceSecondaryEndpoint) { this.tableServiceSecondaryEndpoint = tableServiceSecondaryEndpoint; } + void setBlobHttpsPortOverride(Integer blobHttpsPortOverride) { + this.blobHttpsPortOverride = blobHttpsPortOverride; + } + + void setFileHttpsPortOverride(Integer fileHttpsPortOverride) { + this.fileHttpsPortOverride = fileHttpsPortOverride; + } + + void setQueueHttpsPortOverride(Integer queueHttpsPortOverride) { + this.queueHttpsPortOverride = queueHttpsPortOverride; + } + + void setTableHttpsPortOverride(Integer tableHttpsPortOverride) { + this.tableHttpsPortOverride = tableHttpsPortOverride; + } + public void assertSecondaryEndpoint() { if ((this.getBlobServiceSecondaryEndpoint() == null) || (this.getQueueServiceSecondaryEndpoint() == null) || (this.getTableServiceSecondaryEndpoint() == null)) { diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/TestHelper.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/TestHelper.java index 3e75ec214..c3614bbb7 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/TestHelper.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/TestHelper.java @@ -22,6 +22,7 @@ import java.net.URI; import java.net.URISyntaxException; import java.net.URL; +import java.security.InvalidKeyException; import java.util.ArrayList; import java.util.Arrays; import java.util.Random; @@ -56,21 +57,80 @@ public static CloudBlobClient createCloudBlobClient() throws StorageException { return client; } + public static CloudBlobClient createCloudBlobClient(SharedAccessAccountPolicy policy, boolean useHttps) + throws StorageException, InvalidKeyException, URISyntaxException { + + CloudStorageAccount sasAccount = getAccount(); + final String token = sasAccount.generateSharedAccessSignature(policy); + final StorageCredentials creds = + new StorageCredentialsSharedAccessSignature(token); + + sasAccount = new CloudStorageAccount( + creds, TestHelper.securePortUri(sasAccount.getBlobEndpoint(), useHttps, 'b'), + sasAccount.getQueueEndpoint(), sasAccount.getTableEndpoint(), sasAccount.getFileEndpoint()); + return sasAccount.createCloudBlobClient(); + } + public static CloudFileClient createCloudFileClient() throws StorageException { CloudFileClient client = getAccount().createCloudFileClient(); return client; } + public static CloudFileClient createCloudFileClient(SharedAccessAccountPolicy policy, boolean useHttps) + throws StorageException, InvalidKeyException, URISyntaxException { + + CloudStorageAccount sasAccount = getAccount(); + final String token = sasAccount.generateSharedAccessSignature(policy); + final StorageCredentials creds = + new StorageCredentialsSharedAccessSignature(token); + + sasAccount = new CloudStorageAccount( + creds, sasAccount.getBlobEndpoint(), sasAccount.getQueueEndpoint(), sasAccount.getTableEndpoint(), + TestHelper.securePortUri(sasAccount.getFileEndpoint(), useHttps, 'f')); + return sasAccount.createCloudFileClient(); + } + + public static CloudQueueClient createCloudQueueClient() throws StorageException { CloudQueueClient client = getAccount().createCloudQueueClient(); return client; } + public static CloudQueueClient createCloudQueueClient(SharedAccessAccountPolicy policy, boolean useHttps) + throws StorageException, InvalidKeyException, URISyntaxException { + + CloudStorageAccount sasAccount = getAccount(); + final String token = sasAccount.generateSharedAccessSignature(policy); + final StorageCredentials creds = + new StorageCredentialsSharedAccessSignature(token); + + sasAccount = new CloudStorageAccount( + creds, sasAccount.getBlobEndpoint(), TestHelper.securePortUri(sasAccount.getQueueEndpoint(), useHttps, 'q'), + sasAccount.getTableEndpoint(), sasAccount.getFileEndpoint()); + return sasAccount.createCloudQueueClient(); + } + + public static CloudTableClient createCloudTableClient() throws StorageException { CloudTableClient client = getAccount().createCloudTableClient(); return client; } + public static CloudTableClient createCloudTableClient(SharedAccessAccountPolicy policy, boolean useHttps) + throws StorageException, InvalidKeyException, URISyntaxException { + + CloudStorageAccount sasAccount = getAccount(); + final String token = sasAccount.generateSharedAccessSignature(policy); + final StorageCredentials creds = + new StorageCredentialsSharedAccessSignature(token); + + sasAccount = new CloudStorageAccount( + creds, sasAccount.getBlobEndpoint(), sasAccount.getQueueEndpoint(), + TestHelper.securePortUri(sasAccount.getTableEndpoint(), useHttps, 't'), sasAccount.getFileEndpoint()); + return sasAccount.createCloudTableClient(); + } + + public static CloudAnalyticsClient createCloudAnalyticsClient() throws StorageException { CloudAnalyticsClient client = getAccount().createCloudAnalyticsClient(); return client; @@ -91,6 +151,47 @@ public static byte[] getRandomBuffer(int length) { public static ByteArrayInputStream getRandomDataStream(int length) { return new ByteArrayInputStream(getRandomBuffer(length)); } + + public static URI securePortUri(URI uri, boolean useHttps, char service) throws URISyntaxException { + Integer port = null; + String scheme; + + if (useHttps) { + if (TestHelper.tenant != null) { + switch(service) { + case 'b' : + port = TestHelper.tenant.getBlobHttpsPortOverride(); + break; + + case 'f' : + port = TestHelper.tenant.getFileHttpsPortOverride(); + break; + + case 't' : + port = TestHelper.tenant.getTableHttpsPortOverride(); + break; + + case 'q' : + port = TestHelper.tenant.getQueueHttpsPortOverride(); + break; + + default : + fail(); + } + } + + scheme = Constants.HTTPS; + if (port == null) { + port = 443; + } + } + else { + scheme = Constants.HTTP; + port = uri.getPort(); + } + + return new URI(scheme, uri.getUserInfo(), uri.getHost(), port, uri.getPath(), uri.getQuery(), uri.getFragment()); + } public static void assertStreamsAreEqual(ByteArrayInputStream src, ByteArrayInputStream dst) { dst.reset(); @@ -251,12 +352,14 @@ private static Tenant readTestConfigsFromXml(File testConfigurations) throws Par Node parent = tenantNodes.item(i).getParentNode(); final NodeList childNodes = parent.getChildNodes(); for (int j = 0; j < childNodes.getLength(); j++) { - Node node = childNodes.item(j); + final Node node = childNodes.item(j); + if (node.getNodeType() != Node.ELEMENT_NODE) { // do nothing } else { final String name = node.getNodeName(); + if (name.equals("TenantName")) { tenant.setTenantName(node.getTextContent()); } @@ -273,25 +376,37 @@ else if (name.equals("BlobServiceEndpoint")) { tenant.setBlobServiceEndpoint(new URI(node.getTextContent())); } else if (name.equals("QueueServiceEndpoint")) { - tenant.setQueueServiceEndpoint(new URI(node.getTextContent()));; + tenant.setQueueServiceEndpoint(new URI(node.getTextContent())); } else if (name.equals("TableServiceEndpoint")) { - tenant.setTableServiceEndpoint(new URI(node.getTextContent()));; + tenant.setTableServiceEndpoint(new URI(node.getTextContent())); } else if (name.equals("FileServiceEndpoint")) { - tenant.setFileServiceEndpoint(new URI(node.getTextContent()));; + tenant.setFileServiceEndpoint(new URI(node.getTextContent())); } else if (name.equals("BlobServiceSecondaryEndpoint")) { - tenant.setBlobServiceSecondaryEndpoint(new URI(node.getTextContent()));; + tenant.setBlobServiceSecondaryEndpoint(new URI(node.getTextContent())); } else if (name.equals("QueueServiceSecondaryEndpoint")) { - tenant.setQueueServiceSecondaryEndpoint(new URI(node.getTextContent()));; + tenant.setQueueServiceSecondaryEndpoint(new URI(node.getTextContent())); } else if (name.equals("TableServiceSecondaryEndpoint")) { - tenant.setTableServiceSecondaryEndpoint(new URI(node.getTextContent()));; + tenant.setTableServiceSecondaryEndpoint(new URI(node.getTextContent())); } else if (name.equals("FileServiceSecondaryEndpoint")) { - tenant.setFileServiceSecondaryEndpoint(new URI(node.getTextContent()));; + tenant.setFileServiceSecondaryEndpoint(new URI(node.getTextContent())); + } + else if (name.equals("BlobHttpsPortOverride")) { + tenant.setBlobHttpsPortOverride(Integer.parseInt(node.getTextContent())); + } + else if (name.equals("QueueHttpsPortOverride")) { + tenant.setQueueHttpsPortOverride(Integer.parseInt(node.getTextContent())); + } + else if (name.equals("TableHttpsPortOverride")) { + tenant.setTableHttpsPortOverride(Integer.parseInt(node.getTextContent())); + } + else if (name.equals("FileHttpsPortOverride")) { + tenant.setFileHttpsPortOverride(Integer.parseInt(node.getTextContent())); } else { throw new IllegalArgumentException(String.format( diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/TestRunners.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/TestRunners.java index 3fbab8cd2..bc640ddda 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/TestRunners.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/TestRunners.java @@ -89,8 +89,9 @@ public interface DevFabricTests { // Test suites @RunWith(Suite.class) - @SuiteClasses({ EventFiringTests.class, GenericTests.class, LoggerTests.class, MaximumExecutionTimeTests.class, - SecondaryTests.class, ServicePropertiesTests.class, StorageAccountTests.class, StorageUriTests.class }) + @SuiteClasses({ AccountSasTests.class, EventFiringTests.class, GenericTests.class, LoggerTests.class, + MaximumExecutionTimeTests.class, SecondaryTests.class, ServicePropertiesTests.class, StorageAccountTests.class, + StorageUriTests.class }) public static class CoreTestSuite { } diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/analytics/CloudAnalyticsClientTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/analytics/CloudAnalyticsClientTests.java index 64d9ca601..06e5b8132 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/analytics/CloudAnalyticsClientTests.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/analytics/CloudAnalyticsClientTests.java @@ -74,47 +74,41 @@ public void analyticsTestMethodTearDown() throws StorageException { public void testCloudAnalyticsClientGetTables() throws URISyntaxException, StorageException { CloudTable blobHourPrimary = this.client.getHourMetricsTable(StorageService.BLOB); CloudTable blobHourSecondary = this.client.getHourMetricsTable(StorageService.BLOB, StorageLocation.SECONDARY); + CloudTable fileHourPrimary = this.client.getHourMetricsTable(StorageService.FILE); + CloudTable fileHourSecondary = this.client.getHourMetricsTable(StorageService.FILE, StorageLocation.SECONDARY); CloudTable queueHourPrimary = this.client.getHourMetricsTable(StorageService.QUEUE, StorageLocation.PRIMARY); - CloudTable queueHourSecondary = this.client - .getHourMetricsTable(StorageService.QUEUE, StorageLocation.SECONDARY); + CloudTable queueHourSecondary = this.client.getHourMetricsTable(StorageService.QUEUE, StorageLocation.SECONDARY); CloudTable tableHourPrimary = this.client.getHourMetricsTable(StorageService.TABLE, StorageLocation.PRIMARY); - CloudTable tableHourSecondary = this.client - .getHourMetricsTable(StorageService.TABLE, StorageLocation.SECONDARY); + CloudTable tableHourSecondary = this.client.getHourMetricsTable(StorageService.TABLE, StorageLocation.SECONDARY); CloudTable blobMinutePrimary = this.client.getMinuteMetricsTable(StorageService.BLOB); - CloudTable blobMinuteSecondary = this.client.getMinuteMetricsTable(StorageService.BLOB, - StorageLocation.SECONDARY); - CloudTable queueMinutePrimary = this.client - .getMinuteMetricsTable(StorageService.QUEUE, StorageLocation.PRIMARY); - CloudTable queueMinuteSecondary = this.client.getMinuteMetricsTable(StorageService.QUEUE, - StorageLocation.SECONDARY); - CloudTable tableMinutePrimary = this.client - .getMinuteMetricsTable(StorageService.TABLE, StorageLocation.PRIMARY); - CloudTable tableMinuteSecondary = this.client.getMinuteMetricsTable(StorageService.TABLE, - StorageLocation.SECONDARY); + CloudTable blobMinuteSecondary = this.client.getMinuteMetricsTable(StorageService.BLOB, StorageLocation.SECONDARY); + CloudTable fileMinutePrimary = this.client.getMinuteMetricsTable(StorageService.FILE); + CloudTable fileMinuteSecondary = this.client.getMinuteMetricsTable(StorageService.FILE, StorageLocation.SECONDARY); + CloudTable queueMinutePrimary = this.client.getMinuteMetricsTable(StorageService.QUEUE, StorageLocation.PRIMARY); + CloudTable queueMinuteSecondary = this.client.getMinuteMetricsTable(StorageService.QUEUE, StorageLocation.SECONDARY); + CloudTable tableMinutePrimary = this.client.getMinuteMetricsTable(StorageService.TABLE, StorageLocation.PRIMARY); + CloudTable tableMinuteSecondary = this.client.getMinuteMetricsTable(StorageService.TABLE, StorageLocation.SECONDARY); CloudTable capacity = this.client.getCapacityTable(); assertEquals(Constants.AnalyticsConstants.METRICS_HOUR_PRIMARY_TRANSACTIONS_BLOB, blobHourPrimary.getName()); assertEquals(Constants.AnalyticsConstants.METRICS_HOUR_SECONDARY_TRANSACTIONS_BLOB, blobHourSecondary.getName()); + assertEquals(Constants.AnalyticsConstants.METRICS_HOUR_PRIMARY_TRANSACTIONS_FILE, fileHourPrimary.getName()); + assertEquals(Constants.AnalyticsConstants.METRICS_HOUR_SECONDARY_TRANSACTIONS_FILE, fileHourSecondary.getName()); assertEquals(Constants.AnalyticsConstants.METRICS_HOUR_PRIMARY_TRANSACTIONS_QUEUE, queueHourPrimary.getName()); - assertEquals(Constants.AnalyticsConstants.METRICS_HOUR_SECONDARY_TRANSACTIONS_QUEUE, - queueHourSecondary.getName()); + assertEquals(Constants.AnalyticsConstants.METRICS_HOUR_SECONDARY_TRANSACTIONS_QUEUE, queueHourSecondary.getName()); assertEquals(Constants.AnalyticsConstants.METRICS_HOUR_PRIMARY_TRANSACTIONS_TABLE, tableHourPrimary.getName()); - assertEquals(Constants.AnalyticsConstants.METRICS_HOUR_SECONDARY_TRANSACTIONS_TABLE, - tableHourSecondary.getName()); + assertEquals(Constants.AnalyticsConstants.METRICS_HOUR_SECONDARY_TRANSACTIONS_TABLE, tableHourSecondary.getName()); assertEquals(Constants.AnalyticsConstants.METRICS_MINUTE_PRIMARY_TRANSACTIONS_BLOB, blobMinutePrimary.getName()); - assertEquals(Constants.AnalyticsConstants.METRICS_MINUTE_SECONDARY_TRANSACTIONS_BLOB, - blobMinuteSecondary.getName()); - assertEquals(Constants.AnalyticsConstants.METRICS_MINUTE_PRIMARY_TRANSACTIONS_QUEUE, - queueMinutePrimary.getName()); - assertEquals(Constants.AnalyticsConstants.METRICS_MINUTE_SECONDARY_TRANSACTIONS_QUEUE, - queueMinuteSecondary.getName()); - assertEquals(Constants.AnalyticsConstants.METRICS_MINUTE_PRIMARY_TRANSACTIONS_TABLE, - tableMinutePrimary.getName()); - assertEquals(Constants.AnalyticsConstants.METRICS_MINUTE_SECONDARY_TRANSACTIONS_TABLE, - tableMinuteSecondary.getName()); + assertEquals(Constants.AnalyticsConstants.METRICS_MINUTE_SECONDARY_TRANSACTIONS_BLOB, blobMinuteSecondary.getName()); + assertEquals(Constants.AnalyticsConstants.METRICS_MINUTE_PRIMARY_TRANSACTIONS_FILE, fileMinutePrimary.getName()); + assertEquals(Constants.AnalyticsConstants.METRICS_MINUTE_SECONDARY_TRANSACTIONS_FILE, fileMinuteSecondary.getName()); + assertEquals(Constants.AnalyticsConstants.METRICS_MINUTE_PRIMARY_TRANSACTIONS_QUEUE, queueMinutePrimary.getName()); + assertEquals(Constants.AnalyticsConstants.METRICS_MINUTE_SECONDARY_TRANSACTIONS_QUEUE, queueMinuteSecondary.getName()); + assertEquals(Constants.AnalyticsConstants.METRICS_MINUTE_PRIMARY_TRANSACTIONS_TABLE, tableMinutePrimary.getName()); + assertEquals(Constants.AnalyticsConstants.METRICS_MINUTE_SECONDARY_TRANSACTIONS_TABLE, tableMinuteSecondary.getName()); assertEquals(Constants.AnalyticsConstants.METRICS_CAPACITY_BLOB, capacity.getName()); } diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/BlobTestHelper.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/BlobTestHelper.java index 56499ff2b..aeb5fd28e 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/BlobTestHelper.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/BlobTestHelper.java @@ -28,6 +28,7 @@ import java.util.UUID; import com.microsoft.azure.storage.OperationContext; +import com.microsoft.azure.storage.StorageCredentials; import com.microsoft.azure.storage.StorageException; import com.microsoft.azure.storage.TestHelper; @@ -56,6 +57,54 @@ public static String generateRandomBlobNameWithPrefix(String prefix) { String blobName = prefix + UUID.randomUUID().toString(); return blobName.replace("-", ""); } + + public static CloudBlob getBlobReference(BlobType type, CloudBlobContainer container, String name) + throws URISyntaxException, StorageException { + CloudBlob blob = null; + + switch (type) { + case APPEND_BLOB: + blob = container.getAppendBlobReference(name); + break; + + case BLOCK_BLOB: + blob = container.getBlockBlobReference(name); + break; + + case PAGE_BLOB: + blob = container.getPageBlobReference(name); + break; + + case UNSPECIFIED: + fail(); + } + + return blob; + } + + public static CloudBlob getBlobReference(BlobType type, StorageCredentials credentials, URI uri) + throws StorageException, URISyntaxException { + CloudBlob blob = null; + + switch (type) { + case APPEND_BLOB: + blob = new CloudAppendBlob(credentials.transformUri(uri)); + break; + + case BLOCK_BLOB: + blob = new CloudBlockBlob(credentials.transformUri(uri)); + break; + + case PAGE_BLOB: + blob = new CloudPageBlob(credentials.transformUri(uri)); + break; + + case UNSPECIFIED: + fail(); + } + + return blob; + } public static List uploadNewBlobs(CloudBlobContainer container, BlobType type, int count, int length, OperationContext context) throws StorageException, IOException, URISyntaxException { @@ -97,18 +146,34 @@ public static CloudBlob uploadNewBlob(CloudBlobContainer container, BlobType typ CloudBlob blob = null; - if (type == BlobType.BLOCK_BLOB) { - blob = container.getBlockBlobReference(name); - blob.upload(getRandomDataStream(length), length, null, null, context); - } - else if (type == BlobType.PAGE_BLOB) { - blob = container.getPageBlobReference(name); - blob.upload(getRandomDataStream(length), length, null, null, context); - } - else if (type == BlobType.APPEND_BLOB) { - blob = container.getAppendBlobReference(name); - blob.upload(getRandomDataStream(length), length, null, null, context); + switch (type) { + case BLOCK_BLOB: + blob = container.getBlockBlobReference(name); + blob.upload(getRandomDataStream(length), length, null, null, context); + break; + + case PAGE_BLOB: + blob = container.getPageBlobReference(name); + if (length == 0) { + ((CloudPageBlob) blob).create(length, null, null, context); + } else { + blob.upload(getRandomDataStream(length), length, null, null, context); + } + break; + + case APPEND_BLOB: + blob = container.getAppendBlobReference(name); + if (length == 0) { + ((CloudAppendBlob) blob).createOrReplace(null, null, context); + } else { + blob.upload(getRandomDataStream(length), length, null, null, context); + } + break; + + default: + fail(); } + return blob; } diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/CloudBlobContainerTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/CloudBlobContainerTests.java index 897d85fa8..da5316648 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/CloudBlobContainerTests.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/CloudBlobContainerTests.java @@ -258,7 +258,7 @@ public void testCloudBlobContainerSetPermissions() permissions.setPublicAccess(BlobContainerPublicAccessType.CONTAINER); SharedAccessBlobPolicy policy = new SharedAccessBlobPolicy(); - policy.setPermissions(EnumSet.of(SharedAccessBlobPermissions.LIST)); + policy.setPermissions(EnumSet.of(SharedAccessBlobPermissions.LIST, SharedAccessBlobPermissions.CREATE)); policy.setSharedAccessStartTime(start); policy.setSharedAccessExpiryTime(expiry); permissions.getSharedAccessPolicies().put("key1", policy); @@ -288,6 +288,17 @@ public void testCloudBlobContainerSetPermissions() public void testCloudBlobContainerPermissionsFromString() { SharedAccessBlobPolicy policy = new SharedAccessBlobPolicy(); + policy.setPermissionsFromString("racwdl"); + assertEquals(EnumSet.of( + SharedAccessBlobPermissions.READ, SharedAccessBlobPermissions.ADD, SharedAccessBlobPermissions.CREATE, + SharedAccessBlobPermissions.WRITE, SharedAccessBlobPermissions.DELETE, SharedAccessBlobPermissions.LIST), + policy.getPermissions()); + + policy.setPermissionsFromString("rawdl"); + assertEquals(EnumSet.of(SharedAccessBlobPermissions.READ, SharedAccessBlobPermissions.ADD, + SharedAccessBlobPermissions.WRITE, SharedAccessBlobPermissions.DELETE, SharedAccessBlobPermissions.LIST), + policy.getPermissions()); + policy.setPermissionsFromString("rwdl"); assertEquals(EnumSet.of(SharedAccessBlobPermissions.READ, SharedAccessBlobPermissions.WRITE, SharedAccessBlobPermissions.DELETE, SharedAccessBlobPermissions.LIST), policy.getPermissions()); @@ -312,6 +323,15 @@ public void testCloudBlobContainerPermissionsFromString() { public void testCloudBlobContainerPermissionsToString() { SharedAccessBlobPolicy policy = new SharedAccessBlobPolicy(); + policy.setPermissions(EnumSet.of( + SharedAccessBlobPermissions.READ, SharedAccessBlobPermissions.ADD, SharedAccessBlobPermissions.CREATE, + SharedAccessBlobPermissions.WRITE, SharedAccessBlobPermissions.DELETE, SharedAccessBlobPermissions.LIST)); + assertEquals("racwdl", policy.permissionsToString()); + + policy.setPermissions(EnumSet.of(SharedAccessBlobPermissions.READ, SharedAccessBlobPermissions.ADD, + SharedAccessBlobPermissions.WRITE, SharedAccessBlobPermissions.DELETE, SharedAccessBlobPermissions.LIST)); + assertEquals("rawdl", policy.permissionsToString()); + policy.setPermissions(EnumSet.of(SharedAccessBlobPermissions.READ, SharedAccessBlobPermissions.WRITE, SharedAccessBlobPermissions.DELETE, SharedAccessBlobPermissions.LIST)); assertEquals("rwdl", policy.permissionsToString()); @@ -619,7 +639,7 @@ else if (blob.getName().equals(copySnapshot.getName())) { */ @Test @Category({ DevFabricTests.class, DevStoreTests.class }) - public void testCloudBlobContainerSharedKeyLite() throws StorageException, InterruptedException { + public void testCloudBlobContainerSharedKey() throws StorageException, InterruptedException { BlobContainerPermissions expectedPermissions; BlobContainerPermissions testPermissions; @@ -642,7 +662,7 @@ public void testCloudBlobContainerSharedKeyLite() throws StorageException, Inter now.add(Calendar.MINUTE, 10); policy1.setSharedAccessExpiryTime(now.getTime()); - policy1.setPermissions(EnumSet.of(SharedAccessBlobPermissions.READ, SharedAccessBlobPermissions.DELETE, + policy1.setPermissions(EnumSet.of(SharedAccessBlobPermissions.READ, SharedAccessBlobPermissions.CREATE, SharedAccessBlobPermissions.LIST, SharedAccessBlobPermissions.DELETE)); expectedPermissions.getSharedAccessPolicies().put(UUID.randomUUID().toString(), policy1); diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/SasTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/SasTests.java index adcce4ede..5ee15bad2 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/SasTests.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/SasTests.java @@ -22,11 +22,14 @@ import java.net.HttpURLConnection; import java.net.URI; import java.net.URISyntaxException; +import java.net.UnknownHostException; 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.NoSuchElementException; import java.util.TimeZone; @@ -36,23 +39,27 @@ import org.junit.experimental.categories.Category; import com.microsoft.azure.storage.Constants; +import com.microsoft.azure.storage.IPRange; import com.microsoft.azure.storage.OperationContext; import com.microsoft.azure.storage.ResponseReceivedEvent; import com.microsoft.azure.storage.SecondaryTests; import com.microsoft.azure.storage.SendingRequestEvent; +import com.microsoft.azure.storage.SharedAccessProtocols; import com.microsoft.azure.storage.StorageCredentials; +import com.microsoft.azure.storage.StorageCredentialsAnonymous; import com.microsoft.azure.storage.StorageCredentialsSharedAccessSignature; import com.microsoft.azure.storage.StorageEvent; import com.microsoft.azure.storage.StorageException; +import com.microsoft.azure.storage.TestHelper; import com.microsoft.azure.storage.TestRunners.CloudTests; import com.microsoft.azure.storage.TestRunners.DevFabricTests; import com.microsoft.azure.storage.TestRunners.DevStoreTests; import com.microsoft.azure.storage.TestRunners.SlowTests; import com.microsoft.azure.storage.core.PathUtility; +import com.microsoft.azure.storage.core.SR; @Category({ DevFabricTests.class, DevStoreTests.class, CloudTests.class }) public class SasTests { - protected CloudBlobContainer container; protected CloudBlockBlob blob; @@ -61,40 +68,155 @@ public void blobSasTestMethodSetup() throws URISyntaxException, StorageException this.container = BlobTestHelper.getRandomContainerReference(); this.container.create(); - this.blob = (CloudBlockBlob) BlobTestHelper.uploadNewBlob(this.container, BlobType.BLOCK_BLOB, "test", 100, - null); + this.blob = (CloudBlockBlob) BlobTestHelper.uploadNewBlob(this.container, BlobType.BLOCK_BLOB, "test", 100, null); } @After public void blobSasTestMethodTearDown() throws StorageException { this.container.deleteIfExists(); } - + @Test public void testApiVersion() throws InvalidKeyException, StorageException, URISyntaxException { SharedAccessBlobPolicy sp1 = createSharedAccessPolicy( EnumSet.of(SharedAccessBlobPermissions.READ, SharedAccessBlobPermissions.WRITE, SharedAccessBlobPermissions.LIST, SharedAccessBlobPermissions.DELETE), 300); - String sas = this.blob.generateSharedAccessSignature(sp1, null); - - // should not be appended before signing - assertEquals(-1, sas.indexOf(Constants.QueryConstants.API_VERSION)); - + String sas = this.blob.generateSharedAccessSignature(sp1, null); + + // should not be appended before signing + assertEquals(-1, sas.indexOf(Constants.QueryConstants.API_VERSION)); + OperationContext ctx = new OperationContext(); ctx.getResponseReceivedEventHandler().addListener(new StorageEvent() { @Override public void eventOccurred(ResponseReceivedEvent eventArg) { - // should be appended after signing - HttpURLConnection conn = (HttpURLConnection) eventArg.getConnectionObject(); - assertTrue(conn.getURL().toString().indexOf(Constants.QueryConstants.API_VERSION) != -1); + // should be appended after signing + HttpURLConnection conn = (HttpURLConnection) eventArg.getConnectionObject(); + assertTrue(conn.getURL().toString().indexOf(Constants.QueryConstants.API_VERSION) != -1); } }); CloudBlockBlob sasBlob = new CloudBlockBlob(new URI(this.blob.getUri().toString() + "?" + sas)); - sasBlob.uploadMetadata(null, null, ctx); + sasBlob.uploadMetadata(null, null, ctx); + } + + @Test + @Category({ SecondaryTests.class }) + public void testIpAcl() + throws StorageException, URISyntaxException, InvalidKeyException, InterruptedException, UnknownHostException { + + // Generate policies + SharedAccessBlobPolicy sp = createSharedAccessPolicy( + EnumSet.of(SharedAccessBlobPermissions.READ, SharedAccessBlobPermissions.LIST), 300); + IPRange range1 = new IPRange("0.0.0.0", "255.255.255.255"); + IPRange range2 = new IPRange("0.0.0.0"); + + // Ensure access attempt from invalid IP fails + IPRange sourceIP = null; + try { + String containerSasNone = this.container.generateSharedAccessSignature(sp, null, range2, null); + CloudBlobContainer noneContainer = + new CloudBlobContainer(PathUtility.addToQuery(this.container.getUri(), containerSasNone)); + + assertEquals(StorageCredentialsSharedAccessSignature.class.toString(), + noneContainer.getServiceClient().getCredentials().getClass().toString()); + + CloudBlockBlob noneBlob = noneContainer.getBlockBlobReference(this.blob.getName()); + noneBlob.download(new ByteArrayOutputStream()); + fail(); + } + catch (StorageException ex) { + assertEquals(HttpURLConnection.HTTP_FORBIDDEN, ex.getHttpStatusCode()); + + final String[] words = ex.getMessage().split(" "); + // final word + String lastWord = words[words.length - 1]; + // strip trailing period + lastWord = lastWord.substring(0, lastWord.length() - 1); + + sourceIP = new IPRange(lastWord); + } + + // Ensure access attempt from the single allowed IP succeeds + String containerSasOne = this.container.generateSharedAccessSignature(sp, null, sourceIP, null); + CloudBlobContainer oneContainer = + new CloudBlobContainer(PathUtility.addToQuery(this.container.getUri(), containerSasOne)); + + assertEquals(StorageCredentialsSharedAccessSignature.class.toString(), + oneContainer.getServiceClient().getCredentials().getClass().toString()); + + CloudBlockBlob oneBlob = oneContainer.getBlockBlobReference(this.blob.getName()); + oneBlob.download(new ByteArrayOutputStream()); + + // Ensure access attempt from one of many valid IPs succeeds + String containerSasAll = this.container.generateSharedAccessSignature(sp, null, range1, null); + CloudBlobContainer allContainer = + new CloudBlobContainer(PathUtility.addToQuery(this.container.getUri(), containerSasAll)); + + assertEquals(StorageCredentialsSharedAccessSignature.class.toString(), + allContainer.getServiceClient().getCredentials().getClass().toString()); + + CloudBlockBlob allBlob = allContainer.getBlockBlobReference(this.blob.getName()); + allBlob.download(new ByteArrayOutputStream()); } + @Test + @Category({ SecondaryTests.class }) + public void testProtocolRestrictions() + throws StorageException, URISyntaxException, InvalidKeyException, InterruptedException { + + // Generate policy + SharedAccessBlobPolicy sp = createSharedAccessPolicy( + EnumSet.of(SharedAccessBlobPermissions.READ, SharedAccessBlobPermissions.LIST), 300); + + // Generate container SAS and URI + String containerSasHttps = this.container.generateSharedAccessSignature( + sp, null, null, SharedAccessProtocols.HTTPS_ONLY); + String containerSasHttp = this.container.generateSharedAccessSignature( + sp, null, null, SharedAccessProtocols.HTTPS_HTTP); + final URI uri = this.container.getUri(); + + // Ensure attempt from http fails against HTTPS_ONLY + try { + CloudBlobContainer httpContainer = new CloudBlobContainer( + PathUtility.addToQuery(TestHelper.securePortUri(uri, false, 'b'), containerSasHttps)); + assertEquals(StorageCredentialsSharedAccessSignature.class.toString(), + httpContainer.getServiceClient().getCredentials().getClass().toString()); + CloudBlockBlob httpBlob = httpContainer.getBlockBlobReference(this.blob.getName()); + httpBlob.download(new ByteArrayOutputStream()); + + fail(); + } + catch (StorageException ex) { + assertEquals(Constants.HeaderConstants.HTTP_UNUSED_306, ex.getHttpStatusCode()); + assertEquals(SR.CANNOT_TRANSFORM_NON_HTTPS_URI_WITH_HTTPS_ONLY_CREDENTIALS, ex.getMessage()); + } + + // Ensure attempt from https succeeds against HTTPS_ONLY + CloudBlobContainer httpsContainer = new CloudBlobContainer( + PathUtility.addToQuery(TestHelper.securePortUri(uri, true, 'b'), containerSasHttps)); + assertEquals(StorageCredentialsSharedAccessSignature.class.toString(), + httpsContainer.getServiceClient().getCredentials().getClass().toString()); + CloudBlockBlob httpsBlob = httpsContainer.getBlockBlobReference(this.blob.getName()); + httpsBlob.download(new ByteArrayOutputStream()); + + //Ensure attempts from both https and http succeed against HTTPS_HTTP + CloudBlobContainer httpContainer = new CloudBlobContainer( + PathUtility.addToQuery(TestHelper.securePortUri(uri, false, 'b'), containerSasHttp)); + assertEquals(StorageCredentialsSharedAccessSignature.class.toString(), + httpContainer.getServiceClient().getCredentials().getClass().toString()); + CloudBlockBlob httpBlob = httpContainer.getBlockBlobReference(this.blob.getName()); + httpBlob.download(new ByteArrayOutputStream()); + + httpsContainer = new CloudBlobContainer( + PathUtility.addToQuery(TestHelper.securePortUri(uri, true, 'b'), containerSasHttp)); + assertEquals(StorageCredentialsSharedAccessSignature.class.toString(), + httpsContainer.getServiceClient().getCredentials().getClass().toString()); + httpsBlob = httpsContainer.getBlockBlobReference(this.blob.getName()); + httpsBlob.download(new ByteArrayOutputStream()); + } + @Test @Category({ SecondaryTests.class, SlowTests.class }) public void testContainerSaS() throws IllegalArgumentException, StorageException, URISyntaxException, @@ -176,51 +298,56 @@ public void testContainerUpdateSAS() throws InvalidKeyException, StorageExceptio fail(); } catch (StorageException ex) { - assertEquals(HttpURLConnection.HTTP_NOT_FOUND, ex.getHttpStatusCode()); + assertEquals(HttpURLConnection.HTTP_FORBIDDEN, ex.getHttpStatusCode()); } } @Test @Category(SlowTests.class) - public void testContainerSASCombinations() throws StorageException, URISyntaxException, IOException, - InvalidKeyException, InterruptedException { - for (int i = 1; i < 16; i++) { - final EnumSet permissions = EnumSet.noneOf(SharedAccessBlobPermissions.class); + public void testContainerSASCombinations() + throws StorageException, URISyntaxException, IOException, InvalidKeyException, InterruptedException { - if ((i & 0x1) == 0x1) { - permissions.add(SharedAccessBlobPermissions.READ); - } - - if ((i & 0x2) == 0x2) { - permissions.add(SharedAccessBlobPermissions.WRITE); - } + EnumSet permissions; + Map containers = new HashMap(); - if ((i & 0x4) == 0x4) { - permissions.add(SharedAccessBlobPermissions.DELETE); - } + try{ + for (int bits = 0x1; bits < 0x40; bits++) { + containers.put(bits, BlobTestHelper.getRandomContainerReference()); + containers.get(bits).createIfNotExists(); - if ((i & 0x8) == 0x8) { - permissions.add(SharedAccessBlobPermissions.LIST); - } + permissions = EnumSet.noneOf(SharedAccessBlobPermissions.class); + addPermissions(permissions, bits); - SharedAccessBlobPolicy policy = createSharedAccessPolicy(permissions, 300); + BlobContainerPermissions perms = new BlobContainerPermissions(); - BlobContainerPermissions perms = new BlobContainerPermissions(); + perms.getSharedAccessPolicies().put("readwrite" + bits, createSharedAccessPolicy(permissions, 300)); + containers.get(bits).uploadPermissions(perms); + } - perms.getSharedAccessPolicies().put("readwrite" + i, policy); - this.container.uploadPermissions(perms); Thread.sleep(30000); - String sasToken = this.container.generateSharedAccessSignature(policy, null); - - CloudBlockBlob testBlockBlob = (CloudBlockBlob) BlobTestHelper.uploadNewBlob(this.container, - BlobType.BLOCK_BLOB, "blockblob", 64, null); - testAccess(sasToken, permissions, null, this.container, testBlockBlob); - - CloudPageBlob testPageBlob = (CloudPageBlob) BlobTestHelper.uploadNewBlob(this.container, - BlobType.PAGE_BLOB, "pageblob", 512, null); - - testAccess(sasToken, permissions, null, this.container, testPageBlob); + for (int bits = 0x1; bits < 0x40; bits++) { + String sasToken = containers.get(bits).generateSharedAccessSignature(null, "readwrite" + bits); + permissions = EnumSet.noneOf(SharedAccessBlobPermissions.class); + addPermissions(permissions, bits); + + CloudAppendBlob testAppendBlob = (CloudAppendBlob) BlobTestHelper.uploadNewBlob( + containers.get(bits), BlobType.APPEND_BLOB, "appendblob", 64, null); + testAccess(sasToken, permissions, null, containers.get(bits), testAppendBlob); + + CloudBlockBlob testBlockBlob = (CloudBlockBlob) BlobTestHelper.uploadNewBlob( + containers.get(bits), BlobType.BLOCK_BLOB, "blockblob", 64, null); + testAccess(sasToken, permissions, null, containers.get(bits), testBlockBlob); + + CloudPageBlob testPageBlob = (CloudPageBlob) BlobTestHelper.uploadNewBlob( + containers.get(bits), BlobType.PAGE_BLOB, "pageblob", 512, null); + testAccess(sasToken, permissions, null, containers.get(bits), testPageBlob); + } + } + finally { + for (int bits = 0x1; bits < containers.size(); bits++) { + containers.get(bits).deleteIfExists(); + } } } @@ -228,10 +355,10 @@ public void testContainerSASCombinations() throws StorageException, URISyntaxExc @Category(SlowTests.class) public void testContainerPublicAccess() throws StorageException, IOException, URISyntaxException, InterruptedException { - CloudBlockBlob testBlockBlob = (CloudBlockBlob) BlobTestHelper.uploadNewBlob(this.container, - BlobType.BLOCK_BLOB, "blockblob", 64, null); - CloudPageBlob testPageBlob = (CloudPageBlob) BlobTestHelper.uploadNewBlob(this.container, BlobType.PAGE_BLOB, - "pageblob", 512, null); + CloudBlockBlob testBlockBlob = (CloudBlockBlob) BlobTestHelper.uploadNewBlob( + this.container, BlobType.BLOCK_BLOB, "blockblob", 64, null); + CloudPageBlob testPageBlob = (CloudPageBlob) BlobTestHelper.uploadNewBlob( + this.container, BlobType.PAGE_BLOB, "pageblob", 512, null); BlobContainerPermissions permissions = new BlobContainerPermissions(); @@ -252,21 +379,31 @@ public void testContainerPublicAccess() throws StorageException, IOException, UR } @Test - public void testBlockBlobSASCombinations() throws URISyntaxException, StorageException, InvalidKeyException, - IOException { - for (int i = 1; i < 8; i++) { + public void testAppendBlobSASCombinations() + throws URISyntaxException, StorageException, InvalidKeyException, IOException { + for (int bits = 0x1; bits < 0x20; bits++) { final EnumSet permissions = EnumSet.noneOf(SharedAccessBlobPermissions.class); - addPermissions(permissions, i); + addPermissions(permissions, bits); + testBlobAccess(this.container, BlobType.APPEND_BLOB, permissions, null); + } + } + + @Test + public void testBlockBlobSASCombinations() + throws URISyntaxException, StorageException, InvalidKeyException, IOException { + for (int bits = 0x1; bits < 0x20; bits++) { + final EnumSet permissions = EnumSet.noneOf(SharedAccessBlobPermissions.class); + addPermissions(permissions, bits); testBlobAccess(this.container, BlobType.BLOCK_BLOB, permissions, null); } } @Test - public void testPageBlobSASCombinations() throws InvalidKeyException, StorageException, IOException, - URISyntaxException { - for (int i = 1; i < 8; i++) { + public void testPageBlobSASCombinations() + throws InvalidKeyException, StorageException, IOException, URISyntaxException { + for (int bits = 0x1; bits < 0x20; bits++) { final EnumSet permissions = EnumSet.noneOf(SharedAccessBlobPermissions.class); - addPermissions(permissions, i); + addPermissions(permissions, bits); testBlobAccess(this.container, BlobType.PAGE_BLOB, permissions, null); } } @@ -361,26 +498,25 @@ private final static SharedAccessBlobPolicy createSharedAccessPolicy(EnumSet permissions, - SharedAccessBlobHeaders headers, CloudBlobContainer container, CloudBlob blob) throws StorageException, - URISyntaxException { - StorageCredentials credentials = new StorageCredentialsSharedAccessSignature(sasToken); - + SharedAccessBlobHeaders headers, CloudBlobContainer container, CloudBlob blob) + throws StorageException, URISyntaxException, IOException { + + final StorageCredentials credentials; + final int permissionsErrorCode; + if (sasToken == null) { + credentials = StorageCredentialsAnonymous.ANONYMOUS; + permissionsErrorCode = HttpURLConnection.HTTP_NOT_FOUND; + } else { + credentials = new StorageCredentialsSharedAccessSignature(sasToken); + permissionsErrorCode = HttpURLConnection.HTTP_FORBIDDEN; + } + if (container != null) { container = new CloudBlobContainer(credentials.transformUri(container.getUri())); - if (blob.getProperties().getBlobType() == BlobType.BLOCK_BLOB) { - blob = container.getBlockBlobReference(blob.getName()); - } - else { - blob = container.getPageBlobReference(blob.getName()); - } + blob = BlobTestHelper.getBlobReference(blob.getProperties().getBlobType(), container, blob.getName()); } else { - if (blob.getProperties().getBlobType() == BlobType.BLOCK_BLOB) { - blob = new CloudBlockBlob(credentials.transformUri(blob.getUri())); - } - else { - blob = new CloudPageBlob(credentials.transformUri(blob.getUri())); - } + blob = BlobTestHelper.getBlobReference(blob.getProperties().getBlobType(), credentials, blob.getUri()); } if (container != null) { @@ -393,8 +529,43 @@ private static void testAccess(String sasToken, EnumSet permissions, SharedAccessBlobHeaders headers) throws StorageException, IOException, URISyntaxException, InvalidKeyException { - CloudBlob blob = BlobTestHelper.uploadNewBlob(container, type, "blob", 512, null); + CloudBlob blob = BlobTestHelper.uploadNewBlob(container, type, SR.BLOB, 512, null); SharedAccessBlobPolicy policy = createSharedAccessPolicy(permissions, 300); @@ -479,11 +650,23 @@ private static void addPermissions(EnumSet permissi } if ((i & 0x2) == 0x2) { - permissions.add(SharedAccessBlobPermissions.WRITE); + permissions.add(SharedAccessBlobPermissions.ADD); } if ((i & 0x4) == 0x4) { + permissions.add(SharedAccessBlobPermissions.CREATE); + } + + if ((i & 0x8) == 0x8) { + permissions.add(SharedAccessBlobPermissions.WRITE); + } + + if ((i & 0x10) == 0x10) { permissions.add(SharedAccessBlobPermissions.DELETE); } + + if ((i & 0x20) == 0x20) { + permissions.add(SharedAccessBlobPermissions.LIST); + } } } diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/file/CloudFileShareTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/file/CloudFileShareTests.java index 0cb7a6079..f151a7e11 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/file/CloudFileShareTests.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/file/CloudFileShareTests.java @@ -202,7 +202,7 @@ public void testCloudFileShareSetPermissions() final Date expiry = cal.getTime(); SharedAccessFilePolicy policy = new SharedAccessFilePolicy(); - policy.setPermissions(EnumSet.of(SharedAccessFilePermissions.LIST)); + policy.setPermissions(EnumSet.of(SharedAccessFilePermissions.LIST, SharedAccessFilePermissions.CREATE)); policy.setSharedAccessStartTime(start); policy.setSharedAccessExpiryTime(expiry); permissions.getSharedAccessPolicies().put("key1", policy); @@ -232,6 +232,11 @@ public void testCloudFileShareSetPermissions() public void testCloudFileSharePermissionsFromString() { SharedAccessFilePolicy policy = new SharedAccessFilePolicy(); + policy.setPermissionsFromString("rcwdl"); + assertEquals(EnumSet.of(SharedAccessFilePermissions.READ, SharedAccessFilePermissions.CREATE, + SharedAccessFilePermissions.WRITE, SharedAccessFilePermissions.DELETE, SharedAccessFilePermissions.LIST), + policy.getPermissions()); + policy.setPermissionsFromString("rwdl"); assertEquals(EnumSet.of(SharedAccessFilePermissions.READ, SharedAccessFilePermissions.WRITE, SharedAccessFilePermissions.DELETE, SharedAccessFilePermissions.LIST), policy.getPermissions()); @@ -256,6 +261,10 @@ public void testCloudFileSharePermissionsFromString() { public void testCloudFileSharePermissionsToString() { SharedAccessFilePolicy policy = new SharedAccessFilePolicy(); + policy.setPermissions(EnumSet.of(SharedAccessFilePermissions.READ, SharedAccessFilePermissions.CREATE, + SharedAccessFilePermissions.WRITE, SharedAccessFilePermissions.DELETE, SharedAccessFilePermissions.LIST)); + assertEquals("rcwdl", policy.permissionsToString()); + policy.setPermissions(EnumSet.of(SharedAccessFilePermissions.READ, SharedAccessFilePermissions.WRITE, SharedAccessFilePermissions.DELETE, SharedAccessFilePermissions.LIST)); assertEquals("rwdl", policy.permissionsToString()); diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/file/FileSasTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/file/FileSasTests.java index 57408592d..98356fa07 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/file/FileSasTests.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/file/FileSasTests.java @@ -22,11 +22,14 @@ import java.net.HttpURLConnection; import java.net.URI; import java.net.URISyntaxException; +import java.net.UnknownHostException; 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.NoSuchElementException; import java.util.TimeZone; @@ -36,24 +39,27 @@ import org.junit.experimental.categories.Category; import com.microsoft.azure.storage.Constants; +import com.microsoft.azure.storage.IPRange; import com.microsoft.azure.storage.OperationContext; import com.microsoft.azure.storage.ResponseReceivedEvent; import com.microsoft.azure.storage.SecondaryTests; import com.microsoft.azure.storage.SendingRequestEvent; +import com.microsoft.azure.storage.SharedAccessProtocols; import com.microsoft.azure.storage.StorageCredentials; import com.microsoft.azure.storage.StorageCredentialsSharedAccessSignature; import com.microsoft.azure.storage.StorageEvent; import com.microsoft.azure.storage.StorageException; +import com.microsoft.azure.storage.StorageUri; import com.microsoft.azure.storage.TestHelper; import com.microsoft.azure.storage.TestRunners.CloudTests; import com.microsoft.azure.storage.TestRunners.DevFabricTests; import com.microsoft.azure.storage.TestRunners.DevStoreTests; import com.microsoft.azure.storage.TestRunners.SlowTests; import com.microsoft.azure.storage.core.PathUtility; +import com.microsoft.azure.storage.core.SR; @Category({ DevFabricTests.class, DevStoreTests.class, CloudTests.class }) public class FileSasTests { - protected static CloudFileClient fileClient = null; protected CloudFileShare share; protected CloudFile file; @@ -128,9 +134,125 @@ public void testDirectorySas() throws InvalidKeyException, IllegalArgumentExcept sasDir.downloadAttributes(); } + @Test + @Category({ SecondaryTests.class }) + public void testIpAcl() + throws StorageException, URISyntaxException, InvalidKeyException, InterruptedException, UnknownHostException { + + // Generate policies + SharedAccessFilePolicy policy = createSharedAccessPolicy( + EnumSet.of(SharedAccessFilePermissions.READ, SharedAccessFilePermissions.LIST), 300); + IPRange range1 = new IPRange("0.0.0.0", "255.255.255.255"); + IPRange range2 = new IPRange("0.0.0.0"); + + // Ensure access attempt from invalid IP fails + IPRange sourceIP = null; + try { + String shareSasNone = this.share.generateSharedAccessSignature(policy, null, range2, null); + CloudFileShare noneShare = + new CloudFileShare(PathUtility.addToQuery(this.share.getUri(), shareSasNone)); + + assertEquals(StorageCredentialsSharedAccessSignature.class.toString(), + noneShare.getServiceClient().getCredentials().getClass().toString()); + + CloudFile noneFile = noneShare.getRootDirectoryReference().getFileReference(this.file.getName()); + noneFile.download(new ByteArrayOutputStream()); + fail(); + } + catch (StorageException ex) { + assertEquals(HttpURLConnection.HTTP_FORBIDDEN, ex.getHttpStatusCode()); + + final String[] words = ex.getMessage().split(" "); + // final word + String lastWord = words[words.length - 1]; + // strip trailing period + lastWord = lastWord.substring(0, lastWord.length() - 1); + + sourceIP = new IPRange(lastWord); + } + + // Ensure access attempt from the single allowed IP succeeds + String shareSasOne = this.share.generateSharedAccessSignature(policy, null, sourceIP, null); + CloudFileShare oneShare = + new CloudFileShare(PathUtility.addToQuery(this.share.getUri(), shareSasOne)); + + assertEquals(StorageCredentialsSharedAccessSignature.class.toString(), + oneShare.getServiceClient().getCredentials().getClass().toString()); + + CloudFile oneFile = oneShare.getRootDirectoryReference().getFileReference(this.file.getName()); + oneFile.download(new ByteArrayOutputStream()); + + // Ensure access attempt from one of many valid IPs succeeds + String shareSasAll = this.share.generateSharedAccessSignature(policy, null, range1, null); + CloudFileShare allShare = + new CloudFileShare(PathUtility.addToQuery(this.share.getUri(), shareSasAll)); + + assertEquals(StorageCredentialsSharedAccessSignature.class.toString(), + allShare.getServiceClient().getCredentials().getClass().toString()); + + CloudFile allFile = allShare.getRootDirectoryReference().getFileReference(this.file.getName()); + allFile.download(new ByteArrayOutputStream()); + } + + @Test + @Category({ SecondaryTests.class }) + public void testProtocolRestrictions() + throws StorageException, URISyntaxException, InvalidKeyException, InterruptedException { + + // Generate policy + SharedAccessFilePolicy policy = createSharedAccessPolicy( + EnumSet.of(SharedAccessFilePermissions.READ, SharedAccessFilePermissions.LIST), 300); + + // Generate share SAS and URI + String shareSasHttps = + this.share.generateSharedAccessSignature(policy, null, null, SharedAccessProtocols.HTTPS_ONLY); + String shareSasHttp = + this.share.generateSharedAccessSignature(policy, null, null, SharedAccessProtocols.HTTPS_HTTP); + final URI uri = this.share.getUri(); + + // Ensure attempt from http fails against HTTPS_ONLY + try { + CloudFileShare httpShare = + new CloudFileShare(PathUtility.addToQuery(TestHelper.securePortUri(uri, false, 'f'), shareSasHttps)); + assertEquals(StorageCredentialsSharedAccessSignature.class.toString(), + httpShare.getServiceClient().getCredentials().getClass().toString()); + CloudFile httpFile = httpShare.getRootDirectoryReference().getFileReference(this.file.getName()); + httpFile.download(new ByteArrayOutputStream()); + + fail(); + } + catch (StorageException ex) { + assertEquals(Constants.HeaderConstants.HTTP_UNUSED_306, ex.getHttpStatusCode()); + assertEquals(SR.CANNOT_TRANSFORM_NON_HTTPS_URI_WITH_HTTPS_ONLY_CREDENTIALS, ex.getMessage()); + } + + // Ensure attempt from https succeeds against HTTPS_ONLY + CloudFileShare httpsShare = + new CloudFileShare(PathUtility.addToQuery(TestHelper.securePortUri(uri, true, 'f'), shareSasHttps)); + assertEquals(StorageCredentialsSharedAccessSignature.class.toString(), + httpsShare.getServiceClient().getCredentials().getClass().toString()); + CloudFile httpsFile = httpsShare.getRootDirectoryReference().getFileReference(this.file.getName()); + httpsFile.download(new ByteArrayOutputStream()); + + // Ensure attempt from both http and https succeed against HTTPS_HTTP + CloudFileShare httpShare = + new CloudFileShare(PathUtility.addToQuery(TestHelper.securePortUri(uri, false, 'f'), shareSasHttp)); + assertEquals(StorageCredentialsSharedAccessSignature.class.toString(), + httpShare.getServiceClient().getCredentials().getClass().toString()); + CloudFile httpFile = httpShare.getRootDirectoryReference().getFileReference(this.file.getName()); + httpFile.download(new ByteArrayOutputStream()); + + httpsShare = + new CloudFileShare(PathUtility.addToQuery(TestHelper.securePortUri(uri, true, 'f'), shareSasHttp)); + assertEquals(StorageCredentialsSharedAccessSignature.class.toString(), + httpsShare.getServiceClient().getCredentials().getClass().toString()); + httpsFile = httpsShare.getRootDirectoryReference().getFileReference(this.file.getName()); + httpsFile.download(new ByteArrayOutputStream()); + } + @Test @Category({ SecondaryTests.class, SlowTests.class }) - public void testShareSAS()throws IllegalArgumentException, StorageException, URISyntaxException, + public void testShareSAS() throws IllegalArgumentException, StorageException, URISyntaxException, InvalidKeyException, InterruptedException { SharedAccessFilePolicy policy1 = createSharedAccessPolicy( EnumSet.of(SharedAccessFilePermissions.READ, SharedAccessFilePermissions.WRITE, @@ -138,19 +260,19 @@ public void testShareSAS()throws IllegalArgumentException, StorageException, URI SharedAccessFilePolicy policy2 = createSharedAccessPolicy( EnumSet.of(SharedAccessFilePermissions.READ, SharedAccessFilePermissions.LIST), 300); FileSharePermissions permissions = new FileSharePermissions(); - + permissions.getSharedAccessPolicies().put("full", policy1); permissions.getSharedAccessPolicies().put("readlist", policy2); this.share.uploadPermissions(permissions); Thread.sleep(30000); - + String shareReadListSas = this.share.generateSharedAccessSignature(policy2, null); CloudFileShare readListShare = new CloudFileShare(PathUtility.addToQuery(this.share.getUri(), shareReadListSas)); - + assertEquals(StorageCredentialsSharedAccessSignature.class.toString(), readListShare.getServiceClient().getCredentials().getClass().toString()); - + CloudFile fileFromSasShare = readListShare.getRootDirectoryReference().getFileReference(this.file.getName()); fileFromSasShare.download(new ByteArrayOutputStream()); @@ -187,8 +309,8 @@ public void testShareUpdateSAS() String sasToken = this.share.generateSharedAccessSignature(policy, null); CloudFile file = FileTestHelper.uploadNewFile(this.share, 64, null); - testAccess(sasToken, EnumSet.of(SharedAccessFilePermissions.READ, SharedAccessFilePermissions.WRITE), - this.share, file); + testAccess(sasToken, + EnumSet.of(SharedAccessFilePermissions.READ, SharedAccessFilePermissions.WRITE), this.share, file); //Change the policy to only read and update SAS. SharedAccessFilePolicy policy2 = createSharedAccessPolicy(EnumSet.of(SharedAccessFilePermissions.READ), 300); @@ -207,7 +329,7 @@ public void testShareUpdateSAS() fail(); } catch (StorageException ex) { - assertEquals(HttpURLConnection.HTTP_NOT_FOUND, ex.getHttpStatusCode()); + assertEquals(HttpURLConnection.HTTP_FORBIDDEN, ex.getHttpStatusCode()); } } @@ -215,65 +337,54 @@ public void testShareUpdateSAS() @Category(SlowTests.class) public void testShareSASCombinations() throws StorageException, URISyntaxException, IOException, InvalidKeyException, InterruptedException { - for (int bits = 1; bits < 16; bits++) { - final EnumSet permissionSet = EnumSet.noneOf(SharedAccessFilePermissions.class); - if ((bits & 0x1) == 0x1) { - permissionSet.add(SharedAccessFilePermissions.READ); - } + EnumSet permissionSet = null; + Map shares = new HashMap(); - if ((bits & 0x2) == 0x2) { - permissionSet.add(SharedAccessFilePermissions.WRITE); - } + try { + for (int bits = 0x1; bits < 0x40; bits++) { + shares.put(bits, FileTestHelper.getRandomShareReference()); + shares.get(bits).createIfNotExists(); - if ((bits & 0x4) == 0x4) { - permissionSet.add(SharedAccessFilePermissions.DELETE); - } + permissionSet = getPermissions(bits); + FileSharePermissions perms = new FileSharePermissions(); - if ((bits & 0x8) == 0x8) { - permissionSet.add(SharedAccessFilePermissions.LIST); + perms.getSharedAccessPolicies().put("readwrite" + bits, createSharedAccessPolicy(permissionSet, 300)); + shares.get(bits).uploadPermissions(perms); } - SharedAccessFilePolicy policy = createSharedAccessPolicy(permissionSet, 300); - - FileSharePermissions permissions = new FileSharePermissions(); - - permissions.getSharedAccessPolicies().put("readwrite" + bits, policy); - this.share.uploadPermissions(permissions); Thread.sleep(30000); - String sasToken = this.share.generateSharedAccessSignature(policy, null); + for (int bits = 0x1; bits < 0x20; bits++) { + permissionSet = getPermissions(bits); + String sasToken = shares.get(bits).generateSharedAccessSignature(null, "readwrite" + bits); - CloudFile testFile = FileTestHelper.uploadNewFile(this.share, 64, null); - testAccess(sasToken, permissionSet, this.share, testFile); + CloudFile testFile = FileTestHelper.uploadNewFile(shares.get(bits), 64, null); + testAccess(sasToken, permissionSet, shares.get(bits), testFile); + } + } + finally { + for (int bits = 0x1; bits < shares.size(); bits++) { + shares.get(bits).deleteIfExists(); + } } } @Test public void testFileSASCombinations() throws URISyntaxException, StorageException, InvalidKeyException, IOException { - for (int bits = 1; bits < 8; bits++) { - EnumSet permissionSet = EnumSet.noneOf(SharedAccessFilePermissions.class); - - if ((bits & 0x1) == 0x1) { - permissionSet.add(SharedAccessFilePermissions.READ); - } - - if ((bits & 0x2) == 0x2) { - permissionSet.add(SharedAccessFilePermissions.WRITE); - } - - if ((bits & 0x4) == 0x4) { - permissionSet.add(SharedAccessFilePermissions.DELETE); - } - - CloudFile testFile = FileTestHelper.uploadNewFile(this.share, 512, null); + EnumSet permissionSet = null; + + for (int bits = 0x1; bits < 0x10; bits++) { + permissionSet = getPermissions(bits); SharedAccessFilePolicy policy = createSharedAccessPolicy(permissionSet, 300); + + CloudFile testFile = FileTestHelper.uploadNewFile(this.share, 512, null); String sasToken = testFile.generateSharedAccessSignature(policy, null, null); testAccess(sasToken, permissionSet, null, testFile); } } - + @Test public void testFileSAS() throws InvalidKeyException, IllegalArgumentException, StorageException, URISyntaxException, InterruptedException { @@ -361,9 +472,9 @@ private final static SharedAccessFilePolicy createSharedAccessPolicy(EnumSet permissions, CloudFileShare share, CloudFile file) - throws StorageException, URISyntaxException { + throws StorageException, URISyntaxException, IOException { + StorageCredentials credentials = new StorageCredentialsSharedAccessSignature(sasToken); - if (share != null) { share = new CloudFileShare(credentials.transformUri(share.getUri())); file = share.getRootDirectoryReference().getFileReference(file.getName()); @@ -383,7 +494,36 @@ private static void testAccess( } catch (NoSuchElementException ex) { assertEquals( - HttpURLConnection.HTTP_NOT_FOUND, ((StorageException) ex.getCause()).getHttpStatusCode()); + HttpURLConnection.HTTP_FORBIDDEN, ((StorageException) ex.getCause()).getHttpStatusCode()); + } + } + } + + if (permissions.contains(SharedAccessFilePermissions.READ)) { + file.downloadAttributes(); + } + else { + try { + file.downloadAttributes(); + fail(); + } + catch (StorageException ex) { + assertEquals(HttpURLConnection.HTTP_FORBIDDEN, ex.getHttpStatusCode()); + } + } + + if (share != null) { + if (permissions.contains(SharedAccessFilePermissions.LIST)) { + for (ListFileItem listedFile : share.getRootDirectoryReference().listFilesAndDirectories()); + } + else { + try { + for (ListFileItem listedFile : share.getRootDirectoryReference().listFilesAndDirectories()); + fail(); + } + catch (NoSuchElementException ex) { + assertEquals( + HttpURLConnection.HTTP_FORBIDDEN, ((StorageException) ex.getCause()).getHttpStatusCode()); } } } @@ -397,20 +537,46 @@ private static void testAccess( fail(); } catch (StorageException ex) { - assertEquals(HttpURLConnection.HTTP_NOT_FOUND, ex.getHttpStatusCode()); + assertEquals(HttpURLConnection.HTTP_FORBIDDEN, ex.getHttpStatusCode()); } } - if (permissions.contains(SharedAccessFilePermissions.WRITE)) { + if (share != null) { + final StorageUri uri = PathUtility.appendPathToUri( + share.getStorageUri(), FileTestHelper.generateRandomFileName()); + final CloudFile upFile = new CloudFile(credentials.transformUri(uri)); + + if(permissions.contains(SharedAccessFilePermissions.WRITE)) { + upFile.upload(FileTestHelper.getRandomDataStream(512), 512, null, null, null); + } + else { + if (permissions.contains(SharedAccessFilePermissions.CREATE)) { + upFile.create(0); + } + + try { + upFile.upload(FileTestHelper.getRandomDataStream(512), 512, null, null, null); + fail(); + } + catch (StorageException ex) { + assertEquals(HttpURLConnection.HTTP_FORBIDDEN, ex.getHttpStatusCode()); + } + } + } + + if (permissions.contains(SharedAccessFilePermissions.WRITE) && + permissions.contains(SharedAccessFilePermissions.READ)) { + file.downloadAttributes(); file.uploadMetadata(); } else { try { + file.downloadAttributes(); file.uploadMetadata(); fail(); } catch (StorageException ex) { - assertEquals(HttpURLConnection.HTTP_NOT_FOUND, ex.getHttpStatusCode()); + assertEquals(HttpURLConnection.HTTP_FORBIDDEN, ex.getHttpStatusCode()); } } @@ -423,8 +589,33 @@ private static void testAccess( fail(); } catch (StorageException ex) { - assertEquals(HttpURLConnection.HTTP_NOT_FOUND, ex.getHttpStatusCode()); + assertEquals(HttpURLConnection.HTTP_FORBIDDEN, ex.getHttpStatusCode()); } } } + + private EnumSet getPermissions(int bits) { + EnumSet permissionSet = EnumSet.noneOf(SharedAccessFilePermissions.class); + if ((bits & 0x1) == 0x1) { + permissionSet.add(SharedAccessFilePermissions.READ); + } + + if ((bits & 0x2) == 0x2) { + permissionSet.add(SharedAccessFilePermissions.CREATE); + } + + if ((bits & 0x4) == 0x4) { + permissionSet.add(SharedAccessFilePermissions.WRITE); + } + + if ((bits & 0x8) == 0x8) { + permissionSet.add(SharedAccessFilePermissions.DELETE); + } + + if ((bits & 0x10) == 0x10) { + permissionSet.add(SharedAccessFilePermissions.LIST); + } + + return permissionSet; + } } \ No newline at end of file diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/file/FileTestHelper.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/file/FileTestHelper.java index 6525b3b8a..adea6fdc8 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/file/FileTestHelper.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/file/FileTestHelper.java @@ -18,6 +18,7 @@ import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import java.net.URI; import java.net.URISyntaxException; import java.util.Random; @@ -38,19 +39,26 @@ protected static String generateRandomShareName() { return shareName.replace("-", ""); } - protected static String generateRandomFileName() { + public static String generateRandomFileName() { String shareName = "file" + UUID.randomUUID().toString(); return shareName.replace("-", ""); } public static CloudFile uploadNewFile(CloudFileShare share, int length, OperationContext context) throws StorageException, IOException, URISyntaxException { + + return uploadNewFile(share, getRandomDataStream(length), length, context); + } + + public static CloudFile uploadNewFile( + CloudFileShare share, InputStream stream, int length, OperationContext context) + throws StorageException, IOException, URISyntaxException { String name = generateRandomFileName(); CloudFile file = null; file = share.getRootDirectoryReference().getFileReference(name); - file.upload(getRandomDataStream(length), length, null, null, context); + file.upload(stream, length, null, null, context); return file; } diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableBatchOperationTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableBatchOperationTests.java index 0c71a43c9..95a4a5602 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableBatchOperationTests.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableBatchOperationTests.java @@ -410,19 +410,11 @@ public void testBatchSecondaryNoWrite() throws StorageException { this.table.execute(batch, options, null); } - @SuppressWarnings("deprecation") @Test public void testBatchOver100Entities() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testBatchOver100Entities(options); - options.setTablePayloadFormat(TablePayloadFormat.Json); - testBatchOver100Entities(options); - } - - private void testBatchOver100Entities(TableRequestOptions options) throws StorageException { + TableBatchOperation batch = new TableBatchOperation(); try { for (int m = 0; m < 101; m++) { @@ -441,19 +433,11 @@ private void testBatchOver100Entities(TableRequestOptions options) throws Storag } } - @SuppressWarnings("deprecation") @Test public void testBatchInsertEntityOver1MB() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testBatchInsertEntityOver1MB(options); - options.setTablePayloadFormat(TablePayloadFormat.Json); - testBatchInsertEntityOver1MB(options); - } - private void testBatchInsertEntityOver1MB(TableRequestOptions options) throws StorageException { TableBatchOperation batch = new TableBatchOperation(); Class1 bigEnt = new Class1(); @@ -490,19 +474,11 @@ private void testBatchInsertEntityOver1MB(TableRequestOptions options) throws St } } - @SuppressWarnings("deprecation") @Test public void testBatchInsertEntityWithPropertyMoreThan255chars() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testBatchInsertEntityWithPropertyMoreThan255chars(options); - options.setTablePayloadFormat(TablePayloadFormat.Json); - testBatchInsertEntityWithPropertyMoreThan255chars(options); - } - private void testBatchInsertEntityWithPropertyMoreThan255chars(TableRequestOptions options) throws StorageException { TableBatchOperation batch = new TableBatchOperation(); DynamicTableEntity bigEnt = new DynamicTableEntity(); @@ -540,19 +516,11 @@ private void testBatchInsertEntityWithPropertyMoreThan255chars(TableRequestOptio } } - @SuppressWarnings("deprecation") @Test public void testBatchSizeOver4mb() { TableRequestOptions options = new TableRequestOptions(); - - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testBatchSizeOver4mb(options); - options.setTablePayloadFormat(TablePayloadFormat.Json); - testBatchSizeOver4mb(options); - } - private void testBatchSizeOver4mb(TableRequestOptions options) { TableBatchOperation batch = new TableBatchOperation(); byte[] datArr = new byte[1024 * 128]; Random rand = new Random(); @@ -583,19 +551,11 @@ private void testBatchSizeOver4mb(TableRequestOptions options) { } } - @SuppressWarnings("deprecation") @Test public void testBatchDeleteFail() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testBatchDeleteFail(options); - options.setTablePayloadFormat(TablePayloadFormat.Json); - testBatchDeleteFail(options); - } - private void testBatchDeleteFail(TableRequestOptions options) throws StorageException { TableBatchOperation batch = new TableBatchOperation(); // Insert entity to delete @@ -623,19 +583,11 @@ private void testBatchDeleteFail(TableRequestOptions options) throws StorageExce } } - @SuppressWarnings("deprecation") @Test public void testBatchInsertFail() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testBatchInsertFail(options); - options.setTablePayloadFormat(TablePayloadFormat.Json); - testBatchInsertFail(options); - } - - private void testBatchInsertFail(TableRequestOptions options) throws StorageException { + // insert entity Class1 ref = TableTestHelper.generateRandomEntity("jxscl_odata"); this.table.execute(TableOperation.insert(ref), options, null); @@ -653,19 +605,11 @@ private void testBatchInsertFail(TableRequestOptions options) throws StorageExce } } - @SuppressWarnings("deprecation") @Test public void testBatchReplaceFail() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testBatchReplaceFail(options); - options.setTablePayloadFormat(TablePayloadFormat.Json); - testBatchReplaceFail(options); - } - - private void testBatchReplaceFail(TableRequestOptions options) throws StorageException { + TableBatchOperation batch = new TableBatchOperation(); // Insert entity to merge @@ -693,19 +637,11 @@ private void testBatchReplaceFail(TableRequestOptions options) throws StorageExc } } - @SuppressWarnings("deprecation") @Test public void testBatchMergeFail() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testBatchMergeFail(options); - options.setTablePayloadFormat(TablePayloadFormat.Json); - testBatchMergeFail(options); - } - private void testBatchMergeFail(TableRequestOptions options) throws StorageException { TableBatchOperation batch = new TableBatchOperation(); addInsertBatch(batch); @@ -736,19 +672,11 @@ private void testBatchMergeFail(TableRequestOptions options) throws StorageExcep } } - @SuppressWarnings("deprecation") @Test public void testBatchEmptyQuery() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testBatchEmptyQuery(options); - options.setTablePayloadFormat(TablePayloadFormat.Json); - testBatchEmptyQuery(options); - } - private void testBatchEmptyQuery(TableRequestOptions options) throws StorageException { // insert entity Class1 ref = TableTestHelper.generateRandomEntity("jxscl_odata"); @@ -762,14 +690,10 @@ private void testBatchEmptyQuery(TableRequestOptions options) throws StorageExce assertEquals(results.get(0).getHttpStatusCode(), HttpURLConnection.HTTP_NOT_FOUND); } - @SuppressWarnings("deprecation") @Test public void testBatchWithAllOperations() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testBatchWithAllOperations(options); - options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); testBatchWithAllOperations(options); @@ -845,14 +769,10 @@ private void testBatchWithAllOperations(TableRequestOptions options) throws Stor } - @SuppressWarnings("deprecation") @Test public void testBatchDelete() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testBatchDelete(options); - options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); testBatchDelete(options); @@ -886,14 +806,10 @@ private void testBatchDelete(TableRequestOptions options) throws StorageExceptio } } - @SuppressWarnings("deprecation") @Test public void testBatchRetrieve() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testBatchRetrieve(options); - options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); testBatchRetrieve(options); @@ -930,14 +846,10 @@ private void testBatchRetrieve(TableRequestOptions options) throws StorageExcept this.table.execute(TableOperation.delete(ref), options, null); } - @SuppressWarnings("deprecation") @Test public void tableBatchRetrieveWithEntityResolver() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - tableBatchRetrieveWithEntityResolver(options); - options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); tableBatchRetrieveWithEntityResolver(options); @@ -984,14 +896,10 @@ public Class1 resolve(String partitionKey, String rowKey, Date timeStamp, assertTrue(Arrays.equals(ent.getD(), randEnt.getD())); } - @SuppressWarnings("deprecation") @Test public void testBatchInsert() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testBatchInsert(options); - options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); testBatchInsert(options); @@ -1033,14 +941,10 @@ private void testBatchInsert(TableRequestOptions options) throws StorageExceptio assertEquals(iter.next().getHttpStatusCode(), HttpURLConnection.HTTP_NO_CONTENT); } - @SuppressWarnings("deprecation") @Test public void testBatchMerge() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testBatchMerge(options); - options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); testBatchMerge(options); @@ -1080,14 +984,10 @@ private void testBatchMerge(TableRequestOptions options) throws StorageException assertEquals(iter.next().getHttpStatusCode(), HttpURLConnection.HTTP_NO_CONTENT); } - @SuppressWarnings("deprecation") @Test public void testBatchReplace() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testBatchReplace(options); - options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); testBatchReplace(options); @@ -1127,14 +1027,10 @@ private void testBatchReplace(TableRequestOptions options) throws StorageExcepti assertEquals(iter.next().getHttpStatusCode(), HttpURLConnection.HTTP_NO_CONTENT); } - @SuppressWarnings("deprecation") @Test public void testBatchInsertOrMerge() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testBatchInsertOrMerge(options); - options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); testBatchInsertOrMerge(options); @@ -1174,14 +1070,10 @@ private void testBatchInsertOrMerge(TableRequestOptions options) throws StorageE assertEquals(iter.next().getHttpStatusCode(), HttpURLConnection.HTTP_NO_CONTENT); } - @SuppressWarnings("deprecation") @Test public void testBatchInsertOrReplace() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testBatchInsertOrReplace(options); - options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); testBatchInsertOrReplace(options); @@ -1221,14 +1113,10 @@ private void testBatchInsertOrReplace(TableRequestOptions options) throws Storag assertEquals(iter.next().getHttpStatusCode(), HttpURLConnection.HTTP_NO_CONTENT); } - @SuppressWarnings("deprecation") @Test public void testInsertBatch1() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - insertAndDeleteBatchWithX(1, options); - options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); insertAndDeleteBatchWithX(1, options); @@ -1239,14 +1127,10 @@ public void testInsertBatch1() throws StorageException { insertAndDeleteBatchWithX(1, options); } - @SuppressWarnings("deprecation") @Test public void testInsertBatch10() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - insertAndDeleteBatchWithX(10, options); - options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); insertAndDeleteBatchWithX(10, options); @@ -1257,14 +1141,10 @@ public void testInsertBatch10() throws StorageException { insertAndDeleteBatchWithX(10, options); } - @SuppressWarnings("deprecation") @Test public void testInsertBatch100() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - insertAndDeleteBatchWithX(100, options); - options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); insertAndDeleteBatchWithX(100, options); @@ -1275,14 +1155,10 @@ public void testInsertBatch100() throws StorageException { insertAndDeleteBatchWithX(100, options); } - @SuppressWarnings("deprecation") @Test public void testUpsertBatch1() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - upsertAndDeleteBatchWithX(1, options); - options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); upsertAndDeleteBatchWithX(1, options); @@ -1293,14 +1169,10 @@ public void testUpsertBatch1() throws StorageException { upsertAndDeleteBatchWithX(1, options); } - @SuppressWarnings("deprecation") @Test public void testUpsertBatch10() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - upsertAndDeleteBatchWithX(10, options); - options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); upsertAndDeleteBatchWithX(10, options); @@ -1311,14 +1183,10 @@ public void testUpsertBatch10() throws StorageException { upsertAndDeleteBatchWithX(10, options); } - @SuppressWarnings("deprecation") @Test public void testUpsertBatch100() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - upsertAndDeleteBatchWithX(100, options); - options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); upsertAndDeleteBatchWithX(100, options); diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableClientTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableClientTests.java index 706f1577f..27729c251 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableClientTests.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableClientTests.java @@ -55,7 +55,6 @@ /** * Table Client Tests */ -@SuppressWarnings("deprecation") public class TableClientTests { @Category({ DevFabricTests.class, DevStoreTests.class, CloudTests.class }) @@ -63,8 +62,7 @@ public class TableClientTests { public void testListTablesSegmented() throws URISyntaxException, StorageException { TableRequestOptions options = new TableRequestOptions(); TablePayloadFormat[] formats = - {TablePayloadFormat.AtomPub, - TablePayloadFormat.JsonFullMetadata, + {TablePayloadFormat.JsonFullMetadata, TablePayloadFormat.Json, TablePayloadFormat.JsonNoMetadata}; @@ -143,16 +141,8 @@ public void testListTablesSegmentedMaxResultsValidation() @Test public void testListTablesSegmentedNoPrefix() throws URISyntaxException, StorageException { TableRequestOptions options = new TableRequestOptions(); - - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testListTablesSegmentedNoPrefix(options); - options.setTablePayloadFormat(TablePayloadFormat.Json); - testListTablesSegmentedNoPrefix(options); - } - private void testListTablesSegmentedNoPrefix(TableRequestOptions options) throws URISyntaxException, - StorageException { final CloudTableClient tClient = TableTestHelper.createCloudTableClient(); String tableBaseName = TableTestHelper.generateRandomTableName(); @@ -208,15 +198,8 @@ private void testListTablesSegmentedNoPrefix(TableRequestOptions options) throws @Test public void testListTablesWithIterator() throws URISyntaxException, StorageException { TableRequestOptions options = new TableRequestOptions(); - - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testListTablesWithIterator(options); - options.setTablePayloadFormat(TablePayloadFormat.Json); - testListTablesWithIterator(options); - } - private void testListTablesWithIterator(TableRequestOptions options) throws URISyntaxException, StorageException { final CloudTableClient tClient = TableTestHelper.createCloudTableClient(); String tableBaseName = TableTestHelper.generateRandomTableName(); @@ -580,8 +563,8 @@ public void testTableSASPkRk() throws StorageException, URISyntaxException, Inva transformedTable.execute(TableOperation.insert(ent)); } catch (StorageException e) { - assertEquals(404, e.getHttpStatusCode()); - assertEquals("ResourceNotFound", e.getExtendedErrorInformation().getErrorCode()); + assertEquals(HttpURLConnection.HTTP_FORBIDDEN, e.getHttpStatusCode()); + assertEquals("AuthorizationFailure", e.getExtendedErrorInformation().getErrorCode()); } ent = new Class1("javatables_batch_1", "05"); @@ -590,8 +573,8 @@ public void testTableSASPkRk() throws StorageException, URISyntaxException, Inva transformedTable.execute(TableOperation.insert(ent)); } catch (StorageException e) { - assertEquals(404, e.getHttpStatusCode()); - assertEquals("ResourceNotFound", e.getExtendedErrorInformation().getErrorCode()); + assertEquals(HttpURLConnection.HTTP_FORBIDDEN, e.getHttpStatusCode()); + assertEquals("AuthorizationFailure", e.getExtendedErrorInformation().getErrorCode()); } } diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableDateTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableDateTests.java index e22829ff2..08c68542b 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableDateTests.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableDateTests.java @@ -59,7 +59,6 @@ public void testTableQueryRoundTripDate() throws URISyntaxException, StorageExce } @Test - @SuppressWarnings("deprecation") public void testRoundTripDateJsonAtom() throws URISyntaxException, StorageException { // JSON // 2014-12-07T09:15:12.123Z from Java @@ -95,27 +94,9 @@ public void testRoundTripDateJsonAtom() throws URISyntaxException, StorageExcept // 2015-02-14T03:11:13.0000229Z from .Net testTableQueryRoundTripDate( "2015-02-14T03:11:13.0000229Z", 1423883473000L, 229, false, false, TablePayloadFormat.JsonNoMetadata); - - // ATOM PUB - // 2014-12-07T09:15:12.123Z from Java - testTableQueryRoundTripDate( - "2014-12-07T09:15:12.123Z", 1417943712123L, 0, false, false, TablePayloadFormat.AtomPub); - - // 2015-01-14T14:53:32.800Z from Java - testTableQueryRoundTripDate( - "2015-01-14T14:53:32.800Z", 1421247212800L, 0, false, false, TablePayloadFormat.AtomPub); - - // 2014-11-29T22:55:21.9876543Z from .Net - testTableQueryRoundTripDate( - "2014-11-29T22:55:21.9876543Z", 1417301721987L, 6543, false, false, TablePayloadFormat.AtomPub); - - // 2015-02-14T03:11:13.0000229Z from .Net - testTableQueryRoundTripDate( - "2015-02-14T03:11:13.0000229Z", 1423883473000L, 229, false, false, TablePayloadFormat.AtomPub); } @Test - @SuppressWarnings("deprecation") public void testRoundTripDateJsonAtomCrossVersion() throws URISyntaxException, StorageException { // JSON @@ -151,27 +132,9 @@ public void testRoundTripDateJsonAtomCrossVersion() // 2015-02-14T03:11:13.0000229Z from .Net testTableQueryRoundTripDate( "2015-02-14T03:11:13.0000229Z", 1423883473000L, 229, true, false, TablePayloadFormat.JsonNoMetadata); - - // ATOM PUB - // 2014-12-07T09:15:12.123Z from Java - testTableQueryRoundTripDate( - "2014-12-07T09:15:12.0000123Z", 1417943712123L, 0, true, false, TablePayloadFormat.AtomPub); - - // 2015-01-14T14:53:32.800Z from Java - testTableQueryRoundTripDate( - "2015-01-14T14:53:32.0000800Z", 1421247212800L, 0, true, false, TablePayloadFormat.AtomPub); - - // 2014-11-29T22:55:21.9876543Z from .Net - testTableQueryRoundTripDate( - "2014-11-29T22:55:21.9876543Z", 1417301721987L, 6543, true, false, TablePayloadFormat.AtomPub); - - // 2015-02-14T03:11:13.0000229Z from .Net - testTableQueryRoundTripDate( - "2015-02-14T03:11:13.0000229Z", 1423883473000L, 229, true, false, TablePayloadFormat.AtomPub); } @Test - @SuppressWarnings("deprecation") public void testRoundTripDateJsonAtomWithBackwardCompatibility() throws URISyntaxException, StorageException { // JSON @@ -207,27 +170,9 @@ public void testRoundTripDateJsonAtomWithBackwardCompatibility() // 2015-02-14T03:11:13.0000229Z from .Net testTableQueryRoundTripDate( "2015-02-14T03:11:13.0000229Z", 1423883473000L, 229, false, true, TablePayloadFormat.JsonNoMetadata); - - // ATOM PUB - // 2014-12-07T09:15:12.123Z from Java - testTableQueryRoundTripDate( - "2014-12-07T09:15:12.123Z", 1417943712123L, 0, false, true, TablePayloadFormat.AtomPub); - - // 2015-01-14T14:53:32.800Z from Java - testTableQueryRoundTripDate( - "2015-01-14T14:53:32.800Z", 1421247212800L, 0, false, true, TablePayloadFormat.AtomPub); - - // 2014-11-29T22:55:21.9876543Z from .Net - testTableQueryRoundTripDate( - "2014-11-29T22:55:21.9876543Z", 1417301721987L, 6543, false, true, TablePayloadFormat.AtomPub); - - // 2015-02-14T03:11:13.0000229Z from .Net - testTableQueryRoundTripDate( - "2015-02-14T03:11:13.0000229Z", 1423883473000L, 229, false, true, TablePayloadFormat.AtomPub); } @Test - @SuppressWarnings("deprecation") public void testRoundTripDateJsonAtomCrossVersionWithBackwardCompatibility() throws URISyntaxException, StorageException { // JSON @@ -263,23 +208,6 @@ public void testRoundTripDateJsonAtomCrossVersionWithBackwardCompatibility() // 2015-02-14T03:11:13.0000229Z from .Net testTableQueryRoundTripDate( "2015-02-14T03:11:13.0000229Z", 1423883473000L, 229, true, true, TablePayloadFormat.JsonNoMetadata); - - // ATOM PUB - // 2014-12-07T09:15:12.123Z from Java - testTableQueryRoundTripDate( - "2014-12-07T09:15:12.0000123Z", 1417943712123L, 0, true, true, TablePayloadFormat.AtomPub); - - // 2015-01-14T14:53:32.800Z from Java - testTableQueryRoundTripDate( - "2015-01-14T14:53:32.0000800Z", 1421247212800L, 0, true, true, TablePayloadFormat.AtomPub); - - // 2014-11-29T22:55:21.9876543Z from .Net - testTableQueryRoundTripDate( - "2014-11-29T22:55:21.9876543Z", 1417301721987L, 6543, true, true, TablePayloadFormat.AtomPub); - - // 2015-02-14T03:11:13.0000229Z from .Net - testTableQueryRoundTripDate( - "2015-02-14T03:11:13.0000229Z", 1423883473000L, 229, true, true, TablePayloadFormat.AtomPub); } private void testRoundTripDate(final Date date) throws URISyntaxException, StorageException { diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableEscapingTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableEscapingTests.java index 4720492cb..96a9be166 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableEscapingTests.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableEscapingTests.java @@ -155,13 +155,9 @@ private void doEscapeTest(String data, boolean useBatch) throws StorageException doEscapeTest(data, useBatch, false); } - @SuppressWarnings("deprecation") private void doEscapeTest(String data, boolean useBatch, boolean includeInKey) throws StorageException { TableRequestOptions options = new TableRequestOptions(); - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - doEscapeTestHelper(data, useBatch, includeInKey, options); - options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); doEscapeTestHelper(data, useBatch, includeInKey, options); @@ -273,13 +269,9 @@ private void doEscapeTestHelper(String data, boolean useBatch, boolean includeIn } } - @SuppressWarnings("deprecation") private void doQueryEscapeTest(String data) throws StorageException { TableRequestOptions options = new TableRequestOptions(); - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - doQueryEscapeTestHelper(data, options); - options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); doQueryEscapeTestHelper(data, options); diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableOperationTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableOperationTests.java index d0ee3aad8..e17fdffb8 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableOperationTests.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableOperationTests.java @@ -88,19 +88,11 @@ public void testRetrieveWithNullResolver() { } } - @SuppressWarnings("deprecation") @Test public void testEntityWithSingleQuote() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testEntityWithSingleQuote(options); - options.setTablePayloadFormat(TablePayloadFormat.Json); - testEntityWithSingleQuote(options); - } - private void testEntityWithSingleQuote(TableRequestOptions options) throws StorageException { EmptyClass ref = new EmptyClass(); ref.setPartitionKey("partition'key"); ref.setRowKey("row'key"); @@ -114,19 +106,11 @@ private void testEntityWithSingleQuote(TableRequestOptions options) throws Stora this.table.execute(TableOperation.delete(ref), options, null); } - @SuppressWarnings("deprecation") @Test public void testInsertEntityWithoutPartitionKeyRowKey() { TableRequestOptions options = new TableRequestOptions(); - - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testInsertEntityWithoutPartitionKeyRowKey(options); - options.setTablePayloadFormat(TablePayloadFormat.Json); - testInsertEntityWithoutPartitionKeyRowKey(options); - } - private void testInsertEntityWithoutPartitionKeyRowKey(TableRequestOptions options) { EmptyClass ref = new EmptyClass(); ref.setPartitionKey("jxscl_odata"); @@ -155,19 +139,11 @@ private void testInsertEntityWithoutPartitionKeyRowKey(TableRequestOptions optio } } - @SuppressWarnings("deprecation") @Test public void testInsertEntityWithPropertyMoreThan255chars() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testInsertEntityWithPropertyMoreThan255chars(options); - options.setTablePayloadFormat(TablePayloadFormat.Json); - testInsertEntityWithPropertyMoreThan255chars(options); - } - private void testInsertEntityWithPropertyMoreThan255chars(TableRequestOptions options) throws StorageException { DynamicTableEntity ref = new DynamicTableEntity(); String propName = ""; @@ -191,19 +167,11 @@ private void testInsertEntityWithPropertyMoreThan255chars(TableRequestOptions op } } - @SuppressWarnings("deprecation") @Test public void testInsertEntityOver1MB() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testInsertEntityOver1MB(options); - options.setTablePayloadFormat(TablePayloadFormat.Json); - testInsertEntityOver1MB(options); - } - private void testInsertEntityOver1MB(TableRequestOptions options) throws StorageException { Class1 ref = new Class1(); ref.setA("foo_A"); ref.setB("foo_B"); @@ -225,20 +193,11 @@ private void testInsertEntityOver1MB(TableRequestOptions options) throws Storage } } - @SuppressWarnings("deprecation") @Test public void testInsertEntityWithNumericProperty() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testInsertEntityWithNumericProperty(options); - options.setTablePayloadFormat(TablePayloadFormat.Json); - testInsertEntityWithNumericProperty(options); - } - @SuppressWarnings("deprecation") - private void testInsertEntityWithNumericProperty(TableRequestOptions options) throws StorageException { DynamicTableEntity ref = new DynamicTableEntity(); String propName = ""; @@ -255,36 +214,17 @@ private void testInsertEntityWithNumericProperty(TableRequestOptions options) th fail(); } catch (TableServiceException ex) { - // OData handles AtomPub and Json differently when properties start with a number. - // Hence, a different error code is returned. This may be fixed later. - if (options.getTablePayloadFormat() == TablePayloadFormat.AtomPub) { - assertEquals(ex.getMessage(), "Bad Request"); - assertTrue(ex.getExtendedErrorInformation().getErrorMessage() - .startsWith("One of the request inputs is not valid.")); - assertEquals(ex.getExtendedErrorInformation().getErrorCode(), "InvalidInput"); - } - else { - assertEquals(ex.getMessage(), "Bad Request"); - assertTrue(ex.getExtendedErrorInformation().getErrorMessage() - .startsWith("The property name is invalid.")); - assertEquals(ex.getExtendedErrorInformation().getErrorCode(), "PropertyNameInvalid"); - } + assertEquals(ex.getMessage(), "Bad Request"); + assertTrue(ex.getExtendedErrorInformation().getErrorMessage().startsWith("The property name is invalid.")); + assertEquals(ex.getExtendedErrorInformation().getErrorCode(), "PropertyNameInvalid"); } } - @SuppressWarnings("deprecation") @Test public void testDeleteFail() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testDeleteFail(options); - options.setTablePayloadFormat(TablePayloadFormat.Json); - testDeleteFail(options); - } - private void testDeleteFail(TableRequestOptions options) throws StorageException { Class1 ref = new Class1(); ref.setA("foo_A"); ref.setB("foo_B"); @@ -333,19 +273,11 @@ private void testDeleteFail(TableRequestOptions options) throws StorageException } } - @SuppressWarnings("deprecation") @Test public void testMergeFail() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testMergeFail(options); - options.setTablePayloadFormat(TablePayloadFormat.Json); - testMergeFail(options); - } - private void testMergeFail(TableRequestOptions options) throws StorageException { // Insert base entity Class1 baseEntity = new Class1(); baseEntity.setA("foo_A"); @@ -402,19 +334,11 @@ private void testMergeFail(TableRequestOptions options) throws StorageException } } - @SuppressWarnings("deprecation") @Test public void testInsertFail() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testInsertFail(options); - options.setTablePayloadFormat(TablePayloadFormat.Json); - testInsertFail(options); - } - private void testInsertFail(TableRequestOptions options) throws StorageException { Class1 ref = new Class1(); ref.setA("foo_A"); ref.setB("foo_B"); @@ -438,19 +362,11 @@ private void testInsertFail(TableRequestOptions options) throws StorageException } } - @SuppressWarnings("deprecation") @Test public void testReplaceFail() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testReplaceFail(options); - options.setTablePayloadFormat(TablePayloadFormat.Json); - testReplaceFail(options); - } - private void testReplaceFail(TableRequestOptions options) throws StorageException { Class1 baseEntity = new Class1(); baseEntity.setA("foo_A"); baseEntity.setB("foo_B"); @@ -510,19 +426,11 @@ private void testReplaceFail(TableRequestOptions options) throws StorageExceptio } } - @SuppressWarnings("deprecation") @Test public void testEmptyRetrieve() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testEmptyRetrieve(options); - options.setTablePayloadFormat(TablePayloadFormat.Json); - testEmptyRetrieve(options); - } - - private void testEmptyRetrieve(TableRequestOptions options) throws StorageException { + Class1 ref = new Class1(); ref.setA("foo_A"); ref.setB("foo_B"); @@ -538,19 +446,11 @@ private void testEmptyRetrieve(TableRequestOptions options) throws StorageExcept assertEquals(res.getHttpStatusCode(), HttpURLConnection.HTTP_NOT_FOUND); } - @SuppressWarnings("deprecation") @Test public void testInsertEmptyEntity() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testInsertEmptyEntity(options); - options.setTablePayloadFormat(TablePayloadFormat.Json); - testInsertEmptyEntity(options); - } - private void testInsertEmptyEntity(TableRequestOptions options) throws StorageException { EmptyClass ref = new EmptyClass(); ref.setPartitionKey("jxscl_odata"); ref.setRowKey("echo_default" + UUID.randomUUID().toString()); @@ -566,14 +466,10 @@ private void testInsertEmptyEntity(TableRequestOptions options) throws StorageEx assertEquals(HttpURLConnection.HTTP_NO_CONTENT, res.getHttpStatusCode()); } - @SuppressWarnings("deprecation") @Test public void testDelete() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testDelete(options); - options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); testDelete(options); @@ -604,14 +500,10 @@ private void testDelete(TableRequestOptions options) throws StorageException { assertTrue(res2.getResult() == null); } - @SuppressWarnings("deprecation") @Test public void testInsertOrMerge() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testInsertOrMerge(options, false); - + options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); testInsertOrMerge(options, false); @@ -690,14 +582,10 @@ private void testInsertOrMerge(TableRequestOptions options, boolean usePropertyR assertEquals(secondEntity.getO(), retrievedEntity.getProperties().get("O").getValueAsString()); } - @SuppressWarnings("deprecation") @Test public void testInsertOrReplace() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testInsertOrReplace(options); - options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); testInsertOrReplace(options); @@ -762,14 +650,10 @@ private void testInsertOrReplace(TableRequestOptions options) throws StorageExce assertEquals(secondEntity.getO(), retrievedEntity.getProperties().get("O").getValueAsString()); } - @SuppressWarnings("deprecation") @Test public void testMerge() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testMerge(options, false); - options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); testMerge(options, false); @@ -844,14 +728,10 @@ private void testMerge(TableRequestOptions options, boolean usePropertyResolver) assertEquals(secondEntity.getO(), mergedEntity.getProperties().get("O").getValueAsString()); } - @SuppressWarnings("deprecation") @Test public void testReplace() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testReplace(options); - options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); testReplace(options); @@ -910,14 +790,10 @@ private void testReplace(TableRequestOptions options) throws StorageException { assertTrue(retrievedEntity.getProperties().get("D") == null); } - @SuppressWarnings("deprecation") @Test public void testInsert() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testInsert(options); - options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); testInsert(options); @@ -950,14 +826,10 @@ private void testInsert(TableRequestOptions options) throws StorageException { assertEquals(HttpURLConnection.HTTP_NO_CONTENT, res.getHttpStatusCode()); } - @SuppressWarnings("deprecation") @Test public void testRetrieveWithoutEntityResolver() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testRetrieveWithoutEntityResolver(options, false); - options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); testRetrieveWithoutEntityResolver(options, false); @@ -1012,14 +884,10 @@ private void testRetrieveWithoutEntityResolver(TableRequestOptions options, bool } } - @SuppressWarnings("deprecation") @Test public void testRetrieveWithEntityResolver() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testRetrieveWithEntityResolver(options, false); - options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); testRetrieveWithEntityResolver(options, false); diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableQueryTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableQueryTests.java index 21a29a595..37a0a1ec6 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableQueryTests.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableQueryTests.java @@ -103,14 +103,10 @@ public void testQueryWithInvalidTakeCount() { } } - @SuppressWarnings("deprecation") @Test public void testTableWithSelectOnMissingFields() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testTableWithSelectOnMissingFields(options); - options.setTablePayloadFormat(TablePayloadFormat.Json); testTableWithSelectOnMissingFields(options); @@ -134,7 +130,6 @@ private void testTableWithSelectOnMissingFields(TableRequestOptions options) thr assertEquals(EdmType.STRING, ent.getProperties().get("F").getEdmType()); } - @SuppressWarnings("deprecation") @Test public void testTableQueryProjectionWithNull() throws URISyntaxException, StorageException { CloudTable table = TableTestHelper.getRandomTableReference(); @@ -147,15 +142,13 @@ public void testTableQueryProjectionWithNull() throws URISyntaxException, Storag .toString()); table.execute(TableOperation.insert(entity)); - testTableQueryProjectionWithSpecialCases(table, TablePayloadFormat.Json); - testTableQueryProjectionWithSpecialCases(table, TablePayloadFormat.AtomPub); + testTableQueryProjectionWithSpecialCases(table); } finally { table.deleteIfExists(); } } - @SuppressWarnings("deprecation") @Test public void testTableQueryProjectionWithIncorrectTypes() throws URISyntaxException, StorageException { CloudTable table = TableTestHelper.getRandomTableReference(); @@ -170,16 +163,15 @@ public void testTableQueryProjectionWithIncorrectTypes() throws URISyntaxExcepti entity.getProperties().put("IntegerPrimitive", new EntityProperty(true)); table.execute(TableOperation.insert(entity)); - testTableQueryProjectionWithSpecialCases(table, TablePayloadFormat.Json); - testTableQueryProjectionWithSpecialCases(table, TablePayloadFormat.AtomPub); + testTableQueryProjectionWithSpecialCases(table); } finally { table.deleteIfExists(); } } - private void testTableQueryProjectionWithSpecialCases(CloudTable table, TablePayloadFormat format) { - table.getServiceClient().getDefaultRequestOptions().setTablePayloadFormat(format); + private void testTableQueryProjectionWithSpecialCases(CloudTable table) { + table.getServiceClient().getDefaultRequestOptions().setTablePayloadFormat(TablePayloadFormat.Json); // Query on String and IntegerPrimitive TableQuery query = TableQuery.from(ComplexEntity.class).select( @@ -233,19 +225,10 @@ private void testTableQueryWithSpecialChars(char charToTest, CloudTable table) assertEquals(partitionKey, seg.getResults().get(0).getPartitionKey()); } - @SuppressWarnings("deprecation") @Test public void testTableInvalidQuery() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testTableInvalidQuery(options); - options.setTablePayloadFormat(TablePayloadFormat.Json); - testTableInvalidQuery(options); - } - - private void testTableInvalidQuery(TableRequestOptions options) throws StorageException { TableQuery query = TableQuery.from(Class1.class).where( String.format("(PartitionKey ) and (RowKey ge '%s')", "000050")); @@ -261,14 +244,10 @@ private void testTableInvalidQuery(TableRequestOptions options) throws StorageEx } } - @SuppressWarnings("deprecation") @Test public void testQueryOnSupportedTypes() throws StorageException, InterruptedException { TableRequestOptions options = new TableRequestOptions(); - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testQueryOnSupportedTypes(options, false); - options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); testQueryOnSupportedTypes(options, false); @@ -436,14 +415,10 @@ private void executeQueryAndAssertResults(String filter, int expectedResults, Ta assertEquals(expectedResults, count); } - @SuppressWarnings("deprecation") @Test public void testTableQueryIterateTwice() { TableRequestOptions options = new TableRequestOptions(); - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testTableQueryIterateTwice(options); - options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); testTableQueryIterateTwice(options); @@ -500,14 +475,10 @@ private void testTableQueryIterateTwice(TableRequestOptions options) { } } - @SuppressWarnings("deprecation") @Test public void testTableQueryWithDynamicEntity() { TableRequestOptions options = new TableRequestOptions(); - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testTableQueryWithDynamicEntity(options); - options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); testTableQueryWithDynamicEntity(options); @@ -535,14 +506,10 @@ private void testTableQueryWithDynamicEntity(TableRequestOptions options) { } } - @SuppressWarnings("deprecation") @Test public void testTableQueryWithProjection() { TableRequestOptions options = new TableRequestOptions(); - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testTableQueryWithProjection(options, false); - options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); testTableQueryWithProjection(options, false); @@ -582,14 +549,10 @@ private void testTableQueryWithProjection(TableRequestOptions options, boolean u } } - @SuppressWarnings("deprecation") @Test public void testSelectOnlySendsReservedColumnsOnce() { TableRequestOptions options = new TableRequestOptions(); - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testSelectOnlySendsReservedColumnsOnce(options, false); - options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); testSelectOnlySendsReservedColumnsOnce(options, false); @@ -639,14 +602,10 @@ public void eventOccurred(ResponseReceivedEvent eventArg) { } } - @SuppressWarnings("deprecation") @Test public void testTableQueryWithReflection() { TableRequestOptions options = new TableRequestOptions(); - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testTableQueryWithReflection(options, false); - options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); testTableQueryWithReflection(options, false); @@ -679,14 +638,10 @@ private void testTableQueryWithReflection(TableRequestOptions options, boolean u } } - @SuppressWarnings("deprecation") @Test public void testTableQueryWithEntityResolver() { TableRequestOptions options = new TableRequestOptions(); - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testTableQueryWithEntityResolver(options, false); - options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); testTableQueryWithEntityResolver(options, false); @@ -732,14 +687,10 @@ public Class1 resolve(String partitionKey, String rowKey, Date timeStamp, } } - @SuppressWarnings("deprecation") @Test public void testTableQueryWithTake() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testTableQueryWithTake(options, false); - options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); testTableQueryWithTake(options, false); @@ -778,14 +729,10 @@ private void testTableQueryWithTake(TableRequestOptions options, boolean useProp assertEquals(count, 25); } - @SuppressWarnings("deprecation") @Test public void testTableQueryWithFilter() { TableRequestOptions options = new TableRequestOptions(); - - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testTableQueryWithFilter(options, false); - + options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); testTableQueryWithFilter(options, false); @@ -823,14 +770,10 @@ private void testTableQueryWithFilter(TableRequestOptions options, boolean usePr assertEquals(count, 50); } - @SuppressWarnings("deprecation") @Test public void testTableQueryWithContinuation() { TableRequestOptions options = new TableRequestOptions(); - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testTableQueryWithContinuation(options, false); - options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); testTableQueryWithContinuation(options, false); diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableSerializerTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableSerializerTests.java index 74aed0ec2..4dc19f8f2 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableSerializerTests.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableSerializerTests.java @@ -16,13 +16,9 @@ import static org.junit.Assert.*; -import java.io.ByteArrayInputStream; -import java.io.StringReader; import java.net.URISyntaxException; import java.util.UUID; -import javax.xml.stream.XMLStreamException; - import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -60,33 +56,6 @@ public void tableTestMethodTearDown() throws StorageException { this.table.deleteIfExists(); } - /** - * In some versions of Java, when more than 64000 XmlReaders are created with the same factory, bizarre - * error messages are thrown on every subsequent attempt to make an XmlReader: “Message: JAXP00010001: The parser - * has encountered more than "64000" entity expansions in this document; this is the limit imposed by the JDK.” This - * is an issue with our library as high concurrency, or even simply long running processes, will have every service - * request which must read a result fail after 64000 readers have been made. This tests that our workaround is - * functioning correctly. The JDK bug applies to, at least, versions 6u65 and 7u45 and will be fixed in future - * versions. - * - * @see Oracle JDK Bug - * @throws XMLStreamException - */ - @Test - public void testStaxEntityExpansionExceptionHandling() throws XMLStreamException { - String testXML = "javasdk"; - - for (int i = 0; i < 64001; i++) { - ByteArrayInputStream stream = new ByteArrayInputStream(testXML.getBytes()); - DeserializationHelper.createXMLStreamReaderFromStream(stream); - } - - for (int i = 0; i < 64001; i++) { - StringReader reader = new StringReader(testXML); - DeserializationHelper.createXMLStreamReaderFromReader(reader); - } - } - @Test public void testIgnoreAnnotation() throws StorageException { // Ignore On Getter @@ -174,14 +143,10 @@ public void testInvalidStoreAsAnnotation() throws StorageException { assertEquals(retrievedStoreAsRef.getStoreAsString(), null); } - @SuppressWarnings("deprecation") @Test public void testComplexEntityInsert() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testComplexEntityInsert(options, false); - options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); testComplexEntityInsert(options, false); @@ -215,14 +180,10 @@ private void testComplexEntityInsert(TableRequestOptions options, boolean usePro ref.assertEquality(retrievedComplexRef); } - @SuppressWarnings("deprecation") @Test public void testDoubles() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testDoubles(options, false); - options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); testDoubles(options, false); @@ -256,14 +217,10 @@ private void testDoubles(TableRequestOptions options, boolean usePropertyResolve ref.assertEquality(retrievedComplexRef); } - @SuppressWarnings("deprecation") @Test public void testWhitespaceTest() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testWhitespaceTest(options, false); - options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); testWhitespaceTest(options, false); @@ -299,14 +256,10 @@ private void testWhitespaceTest(TableRequestOptions options, boolean useProperty assertEquals(((Class1) res.getResult()).getA(), ref.getA()); } - @SuppressWarnings("deprecation") @Test public void testWhitespaceOnEmptyKeysTest() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testWhitespaceOnEmptyKeysTest(options, false); - options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); testWhitespaceOnEmptyKeysTest(options, false); @@ -345,14 +298,10 @@ private void testWhitespaceOnEmptyKeysTest(TableRequestOptions options, boolean this.table.execute(TableOperation.delete(ref), options, null); } - @SuppressWarnings("deprecation") @Test public void testNewLineTest() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testNewLineTest(options, false); - options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); testNewLineTest(options, false); @@ -388,14 +337,10 @@ private void testNewLineTest(TableRequestOptions options, boolean usePropertyRes assertEquals(((Class1) res.getResult()).getA(), ref.getA()); } - @SuppressWarnings("deprecation") @Test public void testNulls() throws StorageException { TableRequestOptions options = new TableRequestOptions(); - options.setTablePayloadFormat(TablePayloadFormat.AtomPub); - testNulls(options, false); - options.setTablePayloadFormat(TablePayloadFormat.JsonFullMetadata); testNulls(options, false); diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableTests.java index a1b2e1db1..08fa48e8e 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableTests.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableTests.java @@ -171,21 +171,12 @@ public void testTablePermissionsFromString() { assertEquals(EnumSet.of(SharedAccessTablePermissions.UPDATE), policy.getPermissions()); } - @SuppressWarnings("deprecation") @Test public void testTableCreateAndAttemptCreateOnceExists() throws StorageException, URISyntaxException { CloudTableClient tClient = TableTestHelper.createCloudTableClient(); - - tClient.getDefaultRequestOptions().setTablePayloadFormat(TablePayloadFormat.AtomPub); - testTableCreateAndAttemptCreateOnceExists(tClient); - tClient.getDefaultRequestOptions().setTablePayloadFormat(TablePayloadFormat.Json); - testTableCreateAndAttemptCreateOnceExists(tClient); - } - private void testTableCreateAndAttemptCreateOnceExists(CloudTableClient tClient) throws StorageException, - URISyntaxException { String tableName = TableTestHelper.generateRandomTableName(); CloudTable table = tClient.getTableReference(tableName); @@ -209,20 +200,12 @@ private void testTableCreateAndAttemptCreateOnceExists(CloudTableClient tClient) } } - @SuppressWarnings("deprecation") @Test public void testTableCreateExistsAndDelete() throws StorageException, URISyntaxException { CloudTableClient tClient = TableTestHelper.createCloudTableClient(); - - tClient.getDefaultRequestOptions().setTablePayloadFormat(TablePayloadFormat.AtomPub); - testTableCreateExistsAndDelete(tClient); - tClient.getDefaultRequestOptions().setTablePayloadFormat(TablePayloadFormat.Json); - testTableCreateExistsAndDelete(tClient); - } - private void testTableCreateExistsAndDelete(CloudTableClient tClient) throws StorageException, URISyntaxException { String tableName = TableTestHelper.generateRandomTableName(); CloudTable table = tClient.getTableReference(tableName); @@ -237,20 +220,12 @@ private void testTableCreateExistsAndDelete(CloudTableClient tClient) throws Sto } } - @SuppressWarnings("deprecation") @Test public void testTableCreateIfNotExists() throws StorageException, URISyntaxException { CloudTableClient tClient = TableTestHelper.createCloudTableClient(); - - tClient.getDefaultRequestOptions().setTablePayloadFormat(TablePayloadFormat.AtomPub); - testTableCreateIfNotExists(tClient); - tClient.getDefaultRequestOptions().setTablePayloadFormat(TablePayloadFormat.Json); - testTableCreateIfNotExists(tClient); - } - private void testTableCreateIfNotExists(CloudTableClient tClient) throws StorageException, URISyntaxException { String tableName = TableTestHelper.generateRandomTableName(); CloudTable table = tClient.getTableReference(tableName); @@ -265,20 +240,12 @@ private void testTableCreateIfNotExists(CloudTableClient tClient) throws Storage } } - @SuppressWarnings("deprecation") @Test public void testTableDeleteIfExists() throws StorageException, URISyntaxException { CloudTableClient tClient = TableTestHelper.createCloudTableClient(); - - tClient.getDefaultRequestOptions().setTablePayloadFormat(TablePayloadFormat.AtomPub); - testTableDeleteIfExists(tClient); - tClient.getDefaultRequestOptions().setTablePayloadFormat(TablePayloadFormat.Json); - testTableDeleteIfExists(tClient); - } - private void testTableDeleteIfExists(CloudTableClient tClient) throws StorageException, URISyntaxException { String tableName = TableTestHelper.generateRandomTableName(); CloudTable table = tClient.getTableReference(tableName); @@ -324,21 +291,12 @@ public void eventOccurred(SendingRequestEvent eventArg) { } } - @SuppressWarnings("deprecation") @Test public void testTableDeleteWhenExistAndNotExists() throws StorageException, URISyntaxException { CloudTableClient tClient = TableTestHelper.createCloudTableClient(); - - tClient.getDefaultRequestOptions().setTablePayloadFormat(TablePayloadFormat.AtomPub); - testTableDeleteWhenExistAndNotExists(tClient); - tClient.getDefaultRequestOptions().setTablePayloadFormat(TablePayloadFormat.Json); - testTableDeleteWhenExistAndNotExists(tClient); - } - private void testTableDeleteWhenExistAndNotExists(CloudTableClient tClient) throws StorageException, - URISyntaxException { String tableName = TableTestHelper.generateRandomTableName(); CloudTable table = tClient.getTableReference(tableName); @@ -363,20 +321,12 @@ private void testTableDeleteWhenExistAndNotExists(CloudTableClient tClient) thro } } - @SuppressWarnings("deprecation") @Test public void testTableDoesTableExist() throws StorageException, URISyntaxException { CloudTableClient tClient = TableTestHelper.createCloudTableClient(); - - tClient.getDefaultRequestOptions().setTablePayloadFormat(TablePayloadFormat.AtomPub); - testTableDoesTableExist(tClient); - tClient.getDefaultRequestOptions().setTablePayloadFormat(TablePayloadFormat.Json); - testTableDoesTableExist(tClient); - } - private void testTableDoesTableExist(CloudTableClient tClient) throws StorageException, URISyntaxException { String tableName = TableTestHelper.generateRandomTableName(); CloudTable table = tClient.getTableReference(tableName); @@ -391,22 +341,13 @@ private void testTableDoesTableExist(CloudTableClient tClient) throws StorageExc } } - @SuppressWarnings("deprecation") @Category(SlowTests.class) @Test public void testTableGetSetPermissionTest() throws StorageException, URISyntaxException, InterruptedException { CloudTableClient tClient = TableTestHelper.createCloudTableClient(); - - tClient.getDefaultRequestOptions().setTablePayloadFormat(TablePayloadFormat.AtomPub); - testTableGetSetPermissionTest(tClient); - tClient.getDefaultRequestOptions().setTablePayloadFormat(TablePayloadFormat.Json); - testTableGetSetPermissionTest(tClient); - } - private void testTableGetSetPermissionTest(CloudTableClient tClient) throws StorageException, URISyntaxException, - InterruptedException { String tableName = TableTestHelper.generateRandomTableName(); CloudTable table = tClient.getTableReference(tableName); @@ -450,22 +391,13 @@ private void testTableGetSetPermissionTest(CloudTableClient tClient) throws Stor } } - @SuppressWarnings("deprecation") @Category(SlowTests.class) @Test public void testTableSas() throws StorageException, URISyntaxException, InvalidKeyException, InterruptedException { CloudTableClient tClient = TableTestHelper.createCloudTableClient(); - - tClient.getDefaultRequestOptions().setTablePayloadFormat(TablePayloadFormat.AtomPub); - testTableSas(tClient); - tClient.getDefaultRequestOptions().setTablePayloadFormat(TablePayloadFormat.Json); - testTableSas(tClient); - } - private void testTableSas(CloudTableClient tClient) throws InvalidKeyException, URISyntaxException, - StorageException, InterruptedException { // use capital letters to make sure SAS signature converts name to lower case correctly String name = "CAPS" + TableTestHelper.generateRandomTableName(); CloudTable table = tClient.getTableReference(name); @@ -507,7 +439,6 @@ private void testTableSas(CloudTableClient tClient) throws InvalidKeyException, new StorageCredentialsSharedAccessSignature(sasString)); CloudTable policySasTable = tableClientFromPermission.getTableReference(name); - policySasTable.exists(); // do not give the client and check that the new table's client has the correct perms CloudTable tableFromUri = new CloudTable(PathUtility.addToQuery(table.getStorageUri(), table diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/CloudStorageAccount.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/CloudStorageAccount.java index df3b347ea..1d62a34ab 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/CloudStorageAccount.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/CloudStorageAccount.java @@ -26,7 +26,9 @@ import com.microsoft.azure.storage.blob.CloudBlobClient; import com.microsoft.azure.storage.blob.CloudBlobContainer; import com.microsoft.azure.storage.core.SR; +import com.microsoft.azure.storage.core.SharedAccessSignatureHelper; import com.microsoft.azure.storage.core.StorageCredentialsHelper; +import com.microsoft.azure.storage.core.UriQueryBuilder; import com.microsoft.azure.storage.core.Utility; import com.microsoft.azure.storage.file.CloudFileClient; import com.microsoft.azure.storage.queue.CloudQueue; @@ -275,9 +277,9 @@ public static CloudStorageAccount parse(final String connectionString) throws UR * @return The {@link StorageUri}. * @throws URISyntaxException */ - private static StorageUri getStorageUri(final Map settings, - final String service, final String serviceEndpoint) throws URISyntaxException { - + private static StorageUri getStorageUri( + final Map settings, final String service, final String serviceEndpoint) + throws URISyntaxException { // Explicit Endpoint Case if (settings.containsKey(serviceEndpoint)) { return new StorageUri(new URI(settings.get(serviceEndpoint))); @@ -308,8 +310,8 @@ else if (settings.containsKey(DEFAULT_ENDPOINTS_PROTOCOL_NAME) && * A String that represents the service's base DNS name. * @return The default {@link StorageUri}. */ - private static StorageUri getDefaultStorageUri(final String scheme, final String accountName, - final String service) throws URISyntaxException { + private static StorageUri getDefaultStorageUri(final String scheme, final String accountName, final String service) + throws URISyntaxException { if (Utility.isNullOrEmpty(scheme)) { throw new IllegalArgumentException(SR.SCHEME_NULL_OR_EMPTY); } @@ -318,11 +320,9 @@ private static StorageUri getDefaultStorageUri(final String scheme, final String throw new IllegalArgumentException(SR.ACCOUNT_NAME_NULL_OR_EMPTY); } - URI primaryUri = new URI(String.format( - PRIMARY_ENDPOINT_FORMAT, scheme, accountName, service)); - URI secondaryUri = new URI(String.format( - SECONDARY_ENDPOINT_FORMAT,scheme, accountName, - SECONDARY_LOCATION_ACCOUNT_SUFFIX, service)); + URI primaryUri = new URI(String.format(PRIMARY_ENDPOINT_FORMAT, scheme, accountName, service)); + URI secondaryUri = new URI( + String.format(SECONDARY_ENDPOINT_FORMAT, scheme, accountName, SECONDARY_LOCATION_ACCOUNT_SUFFIX, service)); return new StorageUri(primaryUri, secondaryUri); } @@ -519,8 +519,7 @@ private static CloudStorageAccount tryConfigureServiceAccount(final MapstorageCredentials specify an invalid account name. */ - public CloudStorageAccount(final StorageCredentials storageCredentials) - throws URISyntaxException { + public CloudStorageAccount(final StorageCredentials storageCredentials) throws URISyntaxException { // Protocol defaults to HTTP unless otherwise specified this(storageCredentials, false, null); } @@ -553,8 +552,8 @@ public CloudStorageAccount(final StorageCredentials storageCredentials) * @throws URISyntaxException * If storageCredentials specify an invalid account name. */ - public CloudStorageAccount(final StorageCredentials storageCredentials, - final boolean useHttps) throws URISyntaxException { + public CloudStorageAccount(final StorageCredentials storageCredentials, final boolean useHttps) + throws URISyntaxException { this (storageCredentials, useHttps, null); } @@ -582,20 +581,60 @@ public CloudStorageAccount(final StorageCredentials storageCredentials, * @throws URISyntaxException * If storageCredentials specify an invalid account name. */ - public CloudStorageAccount(final StorageCredentials storageCredentials, - final boolean useHttps, final String endpointSuffix) throws URISyntaxException { + public CloudStorageAccount( + final StorageCredentials storageCredentials, final boolean useHttps, final String endpointSuffix) + throws URISyntaxException { + this(storageCredentials, useHttps, endpointSuffix, null); + } + + /** + * Creates an instance of the CloudStorageAccount class using the specified + * account credentials. + *

+ * With this constructor, the CloudStorageAccount object is constructed using the + * given HTTP storage service endpoint suffix (if any, otherwise the default is used). + * + * The credentials provided when constructing the CloudStorageAccount object + * are used to authenticate all further requests against resources that are accessed via + * the CloudStorageAccount object or a client object created from it. + * A client object may be a {@link CloudBlobClient} object. + * + * @param storageCredentials + * A {@link StorageCredentials} object that represents the storage credentials + * to use to authenticate this account. + * @param useHttps + * true to use HTTPS to connect to the storage service endpoints; + * otherwise, false. + * @param endpointSuffix + * A String that represents the endpointSuffix to use, if any. + * @param accountName + * A String that contains the account name. This will be used in place of a + * null {@link StorageCredentials#getAccountName()}, but the two must match if + * both are not null. + * + * @throws URISyntaxException + * If storageCredentials specify an invalid account name. + */ + public CloudStorageAccount( + final StorageCredentials storageCredentials, final boolean useHttps, final String endpointSuffix, String accountName) + throws URISyntaxException { Utility.assertNotNull("storageCredentials", storageCredentials); + if (Utility.isNullOrEmpty(accountName)) { + accountName = storageCredentials.getAccountName(); + } + else if (!Utility.isNullOrEmpty(storageCredentials.getAccountName()) && + !accountName.equals(storageCredentials.getAccountName())) { + + throw new IllegalArgumentException(SR.ACCOUNT_NAME_MISMATCH); + } + String protocol = useHttps ? Constants.HTTPS : Constants.HTTP; this.credentials = storageCredentials; - this.blobStorageUri = getDefaultStorageUri(protocol, storageCredentials.getAccountName(), - getDNS(SR.BLOB, endpointSuffix)); - this.fileStorageUri = getDefaultStorageUri(protocol, storageCredentials.getAccountName(), - getDNS(SR.FILE, endpointSuffix)); - this.queueStorageUri = getDefaultStorageUri(protocol, storageCredentials.getAccountName(), - getDNS(SR.QUEUE, endpointSuffix)); - this.tableStorageUri = getDefaultStorageUri(protocol, storageCredentials.getAccountName(), - getDNS(SR.TABLE, endpointSuffix)); + this.blobStorageUri = getDefaultStorageUri(protocol, accountName, getDNS(SR.BLOB, endpointSuffix)); + this.fileStorageUri = getDefaultStorageUri(protocol, accountName, getDNS(SR.FILE, endpointSuffix)); + this.queueStorageUri = getDefaultStorageUri(protocol, accountName, getDNS(SR.QUEUE, endpointSuffix)); + this.tableStorageUri = getDefaultStorageUri(protocol, accountName, getDNS(SR.TABLE, endpointSuffix)); this.endpointSuffix = endpointSuffix; this.isBlobEndpointDefault = true; @@ -726,7 +765,7 @@ public CloudStorageAccount( this.tableStorageUri = tableStorageUri; this.endpointSuffix = null; } - + /** * Creates a new Analytics service client. * @@ -763,9 +802,6 @@ public CloudBlobClient createCloudBlobClient() { throw new IllegalArgumentException(SR.MISSING_CREDENTIALS); } - if (!StorageCredentialsHelper.canCredentialsSignRequest(this.credentials)) { - throw new IllegalArgumentException(SR.CREDENTIALS_CANNOT_SIGN_REQUEST); - } return new CloudBlobClient(this.getBlobStorageUri(), this.getCredentials()); } @@ -784,9 +820,11 @@ public CloudFileClient createCloudFileClient() { throw new IllegalArgumentException(SR.MISSING_CREDENTIALS); } - if (!StorageCredentialsHelper.canCredentialsSignRequest(this.credentials)) { + if (!StorageCredentialsHelper.canCredentialsGenerateClient(this.credentials)) { + throw new IllegalArgumentException(SR.CREDENTIALS_CANNOT_SIGN_REQUEST); } + return new CloudFileClient(this.getFileStorageUri(), this.getCredentials()); } @@ -804,9 +842,11 @@ public CloudQueueClient createCloudQueueClient() { throw new IllegalArgumentException(SR.MISSING_CREDENTIALS); } - if (!StorageCredentialsHelper.canCredentialsSignRequest(this.credentials)) { + if (!StorageCredentialsHelper.canCredentialsGenerateClient(this.credentials)) { + throw new IllegalArgumentException(SR.CREDENTIALS_CANNOT_SIGN_REQUEST); } + return new CloudQueueClient(this.getQueueStorageUri(), this.getCredentials()); } @@ -824,9 +864,11 @@ public CloudTableClient createCloudTableClient() { throw new IllegalArgumentException(SR.MISSING_CREDENTIALS); } - if (!StorageCredentialsHelper.canCredentialsSignRequest(this.credentials)) { + if (!StorageCredentialsHelper.canCredentialsGenerateClient(this.credentials)) { + throw new IllegalArgumentException(SR.CREDENTIALS_CANNOT_SIGN_REQUEST); } + return new CloudTableClient(this.getTableStorageUri(), this.getCredentials()); } @@ -837,10 +879,6 @@ public CloudTableClient createCloudTableClient() { * @return A java.net.URI object that represents the Blob endpoint associated with this account. */ public URI getBlobEndpoint() { - if (this.getCredentials() instanceof StorageCredentialsSharedAccessSignature) { - throw new IllegalArgumentException(SR.ENDPOINT_INFORMATION_UNAVAILABLE); - } - if (this.blobStorageUri == null) { return null; } @@ -855,10 +893,6 @@ public URI getBlobEndpoint() { * @return A {@link StorageUri} object that represents the Blob endpoint associated with this account. */ public StorageUri getBlobStorageUri() { - if (this.getCredentials() instanceof StorageCredentialsSharedAccessSignature) { - throw new IllegalArgumentException(SR.ENDPOINT_INFORMATION_UNAVAILABLE); - } - return this.blobStorageUri; } @@ -886,10 +920,6 @@ public String getEndpointSuffix() { * @return A java.net.URI object that represents the File endpoint associated with this account. */ public URI getFileEndpoint() { - if (this.getCredentials() instanceof StorageCredentialsSharedAccessSignature) { - throw new IllegalArgumentException(SR.ENDPOINT_INFORMATION_UNAVAILABLE); - } - if (this.fileStorageUri == null) { return null; } @@ -904,10 +934,6 @@ public URI getFileEndpoint() { * @return A {@link StorageUri} object that represents the File endpoint associated with this account. */ public StorageUri getFileStorageUri() { - if (this.getCredentials() instanceof StorageCredentialsSharedAccessSignature) { - throw new IllegalArgumentException(SR.ENDPOINT_INFORMATION_UNAVAILABLE); - } - return this.fileStorageUri; } @@ -917,10 +943,6 @@ public StorageUri getFileStorageUri() { * @return A java.net.URI object that represents the queue endpoint associated with this account. */ public URI getQueueEndpoint() { - if (this.getCredentials() instanceof StorageCredentialsSharedAccessSignature) { - throw new IllegalArgumentException(SR.ENDPOINT_INFORMATION_UNAVAILABLE); - } - if (this.queueStorageUri == null) { return null; } @@ -934,10 +956,6 @@ public URI getQueueEndpoint() { * @return A {@link StorageUri} object that represents the Queue endpoint associated with this account. */ public StorageUri getQueueStorageUri() { - if (this.getCredentials() instanceof StorageCredentialsSharedAccessSignature) { - throw new IllegalArgumentException(SR.ENDPOINT_INFORMATION_UNAVAILABLE); - } - return this.queueStorageUri; } @@ -947,10 +965,6 @@ public StorageUri getQueueStorageUri() { * @return A {@link StorageUri} object that represents the Table endpoint associated with this account. */ public URI getTableEndpoint() { - if (this.getCredentials() instanceof StorageCredentialsSharedAccessSignature) { - throw new IllegalArgumentException(SR.ENDPOINT_INFORMATION_UNAVAILABLE); - } - if (this.tableStorageUri == null) { return null; } @@ -964,12 +978,33 @@ public URI getTableEndpoint() { * @return A java.net.URI object that represents the Table endpoint associated with this account. */ public StorageUri getTableStorageUri() { - if (this.getCredentials() instanceof StorageCredentialsSharedAccessSignature) { - throw new IllegalArgumentException(SR.ENDPOINT_INFORMATION_UNAVAILABLE); - } - return this.tableStorageUri; } + + /** + * Returns a shared access signature for the account. + * + * @param policy + * A {@link SharedAccessAccountPolicy} specifying the access policy for the shared access signature. + * + * @return The query string returned includes the leading question mark. + * @throws StorageException + * If a storage service error occurred. + * @throws InvalidKeyException + * If the key is invalid. + */ + public String generateSharedAccessSignature(SharedAccessAccountPolicy policy) + throws InvalidKeyException, StorageException { + if (!StorageCredentialsHelper.canCredentialsSignRequest(this.getCredentials())) { + throw new IllegalArgumentException(SR.CANNOT_CREATE_SAS_WITHOUT_ACCOUNT_KEY); + } + + final String sig = SharedAccessSignatureHelper.generateSharedAccessSignatureHashForAccount( + this.credentials.getAccountName(), policy, this.getCredentials()); + final UriQueryBuilder sasBuilder = + SharedAccessSignatureHelper.generateSharedAccessSignatureForAccount(policy, sig); + return sasBuilder.toString(); + } /** * Returns a connection string for this storage account, without sensitive data. diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/Constants.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/Constants.java index e992042de..2a0208cff 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/Constants.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/Constants.java @@ -113,6 +113,11 @@ public static class AnalyticsConstants { */ public static final String METRICS_HOUR_PRIMARY_TRANSACTIONS_BLOB = "$MetricsHourPrimaryTransactionsBlob"; + /** + * Constant for the file service primary location hourly metrics table. + */ + public static final String METRICS_HOUR_PRIMARY_TRANSACTIONS_FILE = "$MetricsHourPrimaryTransactionsFile"; + /** * Constant for the table service primary location hourly metrics table. */ @@ -128,6 +133,11 @@ public static class AnalyticsConstants { */ public static final String METRICS_MINUTE_PRIMARY_TRANSACTIONS_BLOB = "$MetricsMinutePrimaryTransactionsBlob"; + /** + * Constant for the file service primary location minute metrics table. + */ + public static final String METRICS_MINUTE_PRIMARY_TRANSACTIONS_FILE = "$MetricsMinutePrimaryTransactionsFile"; + /** * Constant for the table service primary location minute metrics table. */ @@ -143,6 +153,11 @@ public static class AnalyticsConstants { */ public static final String METRICS_HOUR_SECONDARY_TRANSACTIONS_BLOB = "$MetricsHourSecondaryTransactionsBlob"; + /** + * Constant for the file service secondary location hourly metrics table. + */ + public static final String METRICS_HOUR_SECONDARY_TRANSACTIONS_FILE = "$MetricsHourSecondaryTransactionsFile"; + /** * Constant for the table service secondary location hourly metrics table. */ @@ -158,6 +173,11 @@ public static class AnalyticsConstants { */ public static final String METRICS_MINUTE_SECONDARY_TRANSACTIONS_BLOB = "$MetricsMinuteSecondaryTransactionsBlob"; + /** + * Constant for the file service secondary location minute metrics table. + */ + public static final String METRICS_MINUTE_SECONDARY_TRANSACTIONS_FILE = "$MetricsMinuteSecondaryTransactionsFile"; + /** * Constant for the table service secondary location minute metrics table. */ @@ -530,7 +550,7 @@ public static class HeaderConstants { /** * The current storage version header value. */ - public static final String TARGET_STORAGE_VERSION = "2015-02-21"; + public static final String TARGET_STORAGE_VERSION = "2015-04-05"; /** * The header that specifies the next visible time for a queue message. @@ -550,7 +570,7 @@ public static class HeaderConstants { /** * Specifies the value to use for UserAgent header. */ - public static final String USER_AGENT_VERSION = "3.1.0"; + public static final String USER_AGENT_VERSION = "4.0.0"; /** * The default type for content-type and accept @@ -562,6 +582,10 @@ public static class HeaderConstants { * Defines constants for use with query strings. */ public static class QueryConstants { + /** + * The query component for the api version. + */ + public static final String API_VERSION = "api-version"; /** * Query component for SAS cache control. @@ -627,11 +651,6 @@ public static class QueryConstants { * Query component for resource type. */ public static final String RESOURCETYPE = "restype"; - - /** - * The query component for the api version. - */ - public static final String API_VERSION = "api-version"; /** * The query component for the SAS table name. @@ -653,6 +672,11 @@ public static class QueryConstants { */ public static final String SIGNED_IDENTIFIER = "si"; + /** + * The query component for the signed SAS IP address. + */ + public static final String SIGNED_IP = "sip"; + /** * The query component for the signing SAS key. */ @@ -663,11 +687,26 @@ public static class QueryConstants { */ public static final String SIGNED_PERMISSIONS = "sp"; + /** + * The query component for the signed SAS Internet protocols. + */ + public static final String SIGNED_PROTOCOLS = "spr"; + /** * The query component for the signed SAS resource. */ public static final String SIGNED_RESOURCE = "sr"; + /** + * The query component for the signed SAS resource type. + */ + public static final String SIGNED_RESOURCE_TYPE = "srt"; + + /** + * The query component for the signed SAS service. + */ + public static final String SIGNED_SERVICE = "ss"; + /** * The query component for the signed SAS start time. */ @@ -909,6 +948,11 @@ public static class QueryConstants { */ public static final String HTTPS = "https"; + /** + * Specifies both HTTPS and HTTP. + */ + public static final String HTTPS_HTTP = "https,http"; + /** * XML attribute for IDs. */ diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/Credentials.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/Credentials.java deleted file mode 100644 index de9a849f3..000000000 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/Credentials.java +++ /dev/null @@ -1,157 +0,0 @@ -/** - * Copyright Microsoft Corporation - * - * Licensed 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 com.microsoft.azure.storage; - -import com.microsoft.azure.storage.core.Base64; -import com.microsoft.azure.storage.core.SR; -import com.microsoft.azure.storage.core.Utility; - -/** - * Represents the credentials used to sign a request against the storage services. - * - * @deprecated as of 3.0.0. Please use the equivalent methods on {@link StorageCredentialsAccountAndKey}. - */ -@Deprecated -public final class Credentials { - /** - * Stores the Account name for the credentials. - */ - private String accountName; - - /** - * Stores the StorageKey for the credentials. - */ - private StorageKey key; - - /** - * Stores the name of the access key to be used when signing the request. - */ - private String keyName; - - /** - * Creates an instance of the Credentials class, using the specified storage account name and access - * key; the specified access key is in the form of a byte array. - * - * @param accountName - * A String that represents the name of the storage account. - * - * @param key - * An array of bytes that represent the account access key. - * - */ - public Credentials(final String accountName, final byte[] key) { - if (Utility.isNullOrEmptyOrWhitespace(accountName)) { - throw new IllegalArgumentException(SR.INVALID_ACCOUNT_NAME); - } - - if (key == null || key.length == 0) { - throw new IllegalArgumentException(SR.INVALID_KEY); - } - - this.accountName = accountName; - this.key = new StorageKey(key); - } - - /** - * Creates an instance of the Credentials class, using the specified storage account name and access - * key; the specified access key is stored as a Base64-encoded String. - * - * @param accountName - * A String that represents the name of the storage account. - * - * @param key - * A String that represents the Base64-encoded account access key. - * - */ - public Credentials(final String accountName, final String key) { - if (Utility.isNullOrEmptyOrWhitespace(accountName)) { - throw new IllegalArgumentException(SR.INVALID_ACCOUNT_NAME); - } - - if (Utility.isNullOrEmptyOrWhitespace(key) || !Base64.validateIsBase64String(key)) { - throw new IllegalArgumentException(SR.INVALID_KEY); - } - - this.accountName = accountName; - this.key = new StorageKey(Base64.decode(key)); - } - - /** - * Exports the value of the access key to a Base64-encoded string. - * - * @return A String that represents the Base64-encoded access key. - */ - public String exportBase64EncodedKey() { - return this.getKey().getBase64EncodedKey(); - } - - /** - * Exports the value of the access key to an array of bytes. - * - * @return A byte array that represents the access key. - */ - public byte[] exportKey() { - return this.getKey().getKey(); - } - - /** - * Gets the account name to be used when signing the request. - * - * @return A String that represents the account name to be used when signing the request. - */ - public String getAccountName() { - return this.accountName; - } - - /** - * Gets the name of the access key to be used when signing the request. - * Internal use only. - */ - public String getKeyName() { - return this.keyName; - } - - /** - * Returns the access key to be used when signing the request. - * - * @return A String that represents the access key to be used when signing the request. - */ - public StorageKey getKey() { - return this.key; - } - - /** - * Sets the account name to be used when signing the request. - * - * @param accountName - * A String that represents the account name being set. - */ - protected void setAccountName(final String accountName) { - this.accountName = accountName; - } - - /** - * Sets the access key to be used when signing the request. - * - * @param keyName - * A String that represents the name of the access key to be used when signing the request. - */ - protected void setKey(final StorageKey key) { - this.key = key; - } -} diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/IPRange.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/IPRange.java new file mode 100644 index 000000000..2ee6194db --- /dev/null +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/IPRange.java @@ -0,0 +1,113 @@ +/** + * Copyright Microsoft Corporation + * + * Licensed 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 com.microsoft.azure.storage; + +import java.net.Inet4Address; + +import com.microsoft.azure.storage.core.SR; +import com.microsoft.azure.storage.core.Utility; + +/** + * A continuous range of IP addresses. + */ +public final class IPRange { + private String ipMin; + private String ipMax; + + /** + * Creates an IP Range using the specified single IP address. The IP address must be IPv4. + * + * @param ip + * the single IP address + */ + public IPRange(String ip) { + Utility.assertNotNull("ip", ip); + IPRange.validateIPAddress(ip); + + this.ipMin = ip; + this.ipMax = ip; + } + + /** + * Creates an IP Range using the specified minimum and maximum IP addresses. The IP addresses must be IPv4. + * + * @param mininimumIP + * the minimum IP address of the range + * @param maximumIP + * the maximum IP address of the range + */ + public IPRange(String mininimumIP, String maximumIP) { + Utility.assertNotNull("mininimumIP", mininimumIP); + Utility.assertNotNull("maximumIP", maximumIP); + + IPRange.validateIPAddress(mininimumIP); + IPRange.validateIPAddress(maximumIP); + + this.ipMin = mininimumIP; + this.ipMax = maximumIP; + } + + /** + * The minimum IP address for the range, inclusive. + * Will match {@link #getIpMax()} if this IPRange represents a single IP address. + * + * @return The minimum IP address + */ + public String getIpMin() { + return this.ipMin; + } + + /** + * The maximum IP address for the range, inclusive. + * Will match {@link #getIpMin()} if this IPRange represents a single IP address. + * + * @return The maximum IP address + */ + public String getIpMax() { + return this.ipMax; + } + + /** + * Output the single IP address or range of IP addresses. + * + * @return the single IP address or range of IP addresses formated as a String + */ + @Override + public String toString() { + StringBuilder str = new StringBuilder(this.ipMin); + if (!this.ipMin.equals(this.ipMax)) { + str.append("-"); + str.append(this.ipMax); + } + + return str.toString(); + } + + /** + * Validate that the IP address is IPv4. + * + * @param ipAddress + * the IP address to validate + */ + private static void validateIPAddress(String ipAddress) { + try { + @SuppressWarnings("unused") + Inet4Address address = (Inet4Address) Inet4Address.getByName(ipAddress); + } + catch (Exception ex) { + throw new IllegalArgumentException(String.format(SR.INVALID_IP_ADDRESS, ipAddress), ex); + } + } +} diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/ServiceClient.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/ServiceClient.java index f5338be3f..341aa8348 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/ServiceClient.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/ServiceClient.java @@ -76,7 +76,8 @@ protected StorageRequest downloadService @Override public HttpURLConnection buildRequest(ServiceClient client, Void parentObject, OperationContext context) throws Exception { - return BaseRequest.getServiceProperties(client.getEndpoint(), options, null, context); + return BaseRequest.getServiceProperties( + credentials.transformUri(client.getEndpoint()), options, null, context); } @Override @@ -124,8 +125,9 @@ public void setRequestLocationMode() { @Override public HttpURLConnection buildRequest(ServiceClient client, Void parentObject, OperationContext context) throws Exception { - return BaseRequest.getServiceStats(client.getStorageUri().getUri(this.getCurrentLocation()), options, - null, context); + return BaseRequest.getServiceStats( + credentials.transformUri(client.getStorageUri().getUri(this.getCurrentLocation())), + options, null, context); } @Override @@ -234,7 +236,8 @@ public HttpURLConnection buildRequest(ServiceClient client, Void parentObject, O throws Exception { this.setSendStream(sendStream); this.setLength(descriptor.getLength()); - return BaseRequest.setServiceProperties(client.getEndpoint(), options, null, context); + return BaseRequest.setServiceProperties( + credentials.transformUri(client.getEndpoint()), options, null, context); } @Override diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/SharedAccessAccountPermissions.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/SharedAccessAccountPermissions.java new file mode 100644 index 000000000..d89b999db --- /dev/null +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/SharedAccessAccountPermissions.java @@ -0,0 +1,129 @@ +/** + * Copyright Microsoft Corporation + * + * Licensed 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 com.microsoft.azure.storage; + +import java.util.EnumSet; + +import com.microsoft.azure.storage.core.SR; + +/** + * Specifies the set of possible permissions for a shared access account policy. + */ +public enum SharedAccessAccountPermissions { + /** + * Permission to read resources and list queues and tables granted. + */ + READ('r'), + + /** + * Permission to add messages, table entities, and append to blobs granted. + */ + ADD('a'), + + /** + * Permission to create blobs and files granted. + */ + CREATE('c'), + + /** + * Permission to write resources granted. + */ + WRITE('w'), + + /** + * Permission to delete resources granted. + */ + DELETE('d'), + + /** + * Permission to list blob containers, blobs, shares, directories, and files granted. + */ + LIST('l'), + + /** + * Permissions to update messages and table entities granted. + */ + UPDATE('u'), + + /** + * Permission to get and delete messages granted. + */ + PROCESS_MESSAGES('p'); + + final private char value; + + /** + * Create a SharedAccessAccountPermissions. + * + * @param c + * The char which represents this permission. + */ + private SharedAccessAccountPermissions(char c) { + this.value = c; + } + + /** + * Converts the given permissions to a String. + * + * @param permissions + * The permissions to convert to a String. + * + * @return A String which represents the SharedAccessAccountPermissions. + */ + static String permissionsToString(EnumSet permissions) { + if (permissions == null) { + return Constants.EMPTY_STRING; + } + + StringBuilder value = new StringBuilder(); + + for (SharedAccessAccountPermissions perm : permissions) { + value.append(perm.value); + } + + return value.toString(); + } + + /** + * Creates an {@link EnumSet} from the specified permissions string. + * + * @param permString + * A String which represents the SharedAccessAccountPermissions. + * @return A {@link EnumSet} generated from the given String. + */ + static EnumSet permissionsFromString(String permString) { + EnumSet permissions = EnumSet.noneOf(SharedAccessAccountPermissions.class); + + for (final char c : permString.toLowerCase().toCharArray()) { + boolean invalidCharacter = true; + + for (SharedAccessAccountPermissions perm : SharedAccessAccountPermissions.values()) { + if (c == perm.value) { + permissions.add(perm); + invalidCharacter = false; + break; + } + + } + + if (invalidCharacter) { + throw new IllegalArgumentException( + String.format(SR.ENUM_COULD_NOT_BE_PARSED, "Permissions", permString)); + } + } + + return permissions; + } +} \ No newline at end of file diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/SharedAccessAccountPolicy.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/SharedAccessAccountPolicy.java new file mode 100644 index 000000000..95bf00418 --- /dev/null +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/SharedAccessAccountPolicy.java @@ -0,0 +1,202 @@ +/** + * Copyright Microsoft Corporation + * + * Licensed 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 com.microsoft.azure.storage; + +import java.util.EnumSet; + +/** + * Represents a shared access policy, which specifies the start time, expiry time, + * and permissions for a shared access signature. + */ +public final class SharedAccessAccountPolicy extends SharedAccessPolicy { + /** + * The permissions for a shared access signature associated with this shared access policy. + */ + private EnumSet permissions; + + /** + * The services (blob, file, queue, table) for a shared access signature associated with this shared access policy. + */ + private EnumSet services; + + /** + * The resource type for a shared access signature associated with this shared access policy. + */ + private EnumSet resourceTypes; + + /** + * The allowed IP addresses for a shared access signature associated with this shared access policy. + */ + private IPRange range; + + /** + * The allowed protocols for a shared access signature associated with this shared access policy. + */ + private SharedAccessProtocols protocols; + + /** + * Gets the permissions for a shared access signature associated with this shared access policy. + * + * @return the permissions + */ + public EnumSet getPermissions() { + return this.permissions; + } + + /** + * Gets the services (blob, file, queue, table) for a shared access signature associated with + * this shared access policy. + * + * @return the services + */ + public EnumSet getServices() { + return this.services; + } + + /** + * Gets the resource type for a shared access signature associated with this shared access policy. + * + * @return the resourceTypes + */ + public EnumSet getResourceTypes() { + return this.resourceTypes; + } + + /** + * Gets the allowed IP addresses for a shared access signature associated with this shared access policy. + * + * @return A {@link IPRange} object containing the range of IP addresses. + */ + public IPRange getRange() { + return range; + } + + /** + * Gets the allowed protocols for a shared access signature associated with this shared access policy. + * + * @return A {@link SharedAccessProtocols} representing the chosen Internet protocols. + */ + public SharedAccessProtocols getProtocols() { + return protocols; + } + + /** + * Sets the permissions for a shared access signature associated with this shared access policy. + * + * @param permissions + * the permissions to set + */ + public void setPermissions(EnumSet permissions) { + this.permissions = permissions; + } + + /** + * Sets the services (blob, file, queue, table) for a shared access signature associated with + * this shared access policy. + * + * @param services + * the services to set + */ + public void setServices(EnumSet services) { + this.services = services; + } + + /** + * Sets the resource type for a shared access signature associated with this shared access policy. + * + * @param resourceTypes + * the resourceTypes to set + */ + public void setResourceTypes(EnumSet resourceTypes) { + this.resourceTypes = resourceTypes; + } + + /** + * Sets the allowed IP addresses for a shared access signature associated with this shared access policy. + * + * @param range + * A {@link IPRange} object containing the range of IP addresses. + */ + public void setRange(IPRange range) { + this.range = range; + } + + /** + * Sets the allowed protocols for a shared access signature associated with this shared access policy. + * + * @param protocols + * A {@link SharedAccessProtocols} representing the chosen Internet protocols. + */ + public void setProtocols(SharedAccessProtocols protocols) { + this.protocols = protocols; + } + + /** + * Converts this shared access policy's permissions to a String. + * + * @return A String which represents the shared access permissions. + */ + public String permissionsToString() { + return SharedAccessAccountPermissions.permissionsToString(this.getPermissions()); + } + + /** + * Converts this shared access policy's resource types to a String. + * + * @return A String which represents the shared access permissions. + */ + public String resourceTypesToString() { + return SharedAccessAccountResourceType.resourceTypesToString(this.getResourceTypes()); + } + + /** + * Converts this shared access policy's services to a String. + * + * @return A String which represents the shared access permissions. + */ + public String servicesToString() { + return SharedAccessAccountService.servicesToString(this.getServices()); + } + + /** + * Sets shared access permissions using the specified permissions String. + * + * @param value + * A String which represents the shared access permissions. + **/ + public void setPermissionsFromString(final String value) { + this.setPermissions(SharedAccessAccountPermissions.permissionsFromString(value)); + } + + /** + * Sets shared access resource types using the specified resource types String. + * + * @param value + * A String which represents the shared access resource types. + **/ + public void setResourceTypeFromString(final String value) { + this.setResourceTypes(SharedAccessAccountResourceType.resourceTypesFromString(value)); + } + + /** + * Sets shared access services using the specified services String. + * + * @param value + * A String which represents the shared access services. + **/ + public void setServiceFromString(final String value) { + this.setServices(SharedAccessAccountService.servicesFromString(value)); + } +} \ No newline at end of file diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/SharedAccessAccountResourceType.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/SharedAccessAccountResourceType.java new file mode 100644 index 000000000..8195b18bf --- /dev/null +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/SharedAccessAccountResourceType.java @@ -0,0 +1,103 @@ +/** + * Copyright Microsoft Corporation + * + * Licensed 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 com.microsoft.azure.storage; + +import java.util.EnumSet; + +import com.microsoft.azure.storage.core.SR; + +/** + * Specifies the set of possible resource types for a shared access account policy. + */ +public enum SharedAccessAccountResourceType { + /** + * Permission to access service level APIs granted. + */ + SERVICE('s'), + + /** + * Permission to access container level APIs (Blob Containers, Tables, Queues, File Shares) granted. + */ + CONTAINER('c'), + + /** + * Permission to access object level APIs (Blobs, Table Entities, Queue Messages, Files) granted. + */ + OBJECT('o'); + + char value; + + /** + * Create a SharedAccessAccountResourceType. + * + * @param c + * The char which represents this resource type. + */ + private SharedAccessAccountResourceType(char c) { + this.value = c; + } + + /** + * Converts the given resource types to a String. + * + * @param types + * The resource types to convert to a String. + * + * @return A String which represents the SharedAccessAccountResourceTypes. + */ + static String resourceTypesToString(EnumSet types) { + if (types == null) { + return Constants.EMPTY_STRING; + } + + StringBuilder value = new StringBuilder(); + + for (SharedAccessAccountResourceType type : types) { + value.append(type.value); + } + + return value.toString(); + } + + /** + * Creates an {@link EnumSet} from the specified resource types string. + * + * @param rsrcString + * A String which represents the SharedAccessAccountResourceTypes. + * @return A {@link EnumSet} generated from the given String. + */ + static EnumSet resourceTypesFromString(String rsrcString) { + EnumSet resources = EnumSet.noneOf(SharedAccessAccountResourceType.class); + + for (final char c : rsrcString.toLowerCase().toCharArray()) { + boolean invalidCharacter = true; + + for (SharedAccessAccountResourceType rsrc : SharedAccessAccountResourceType.values()) { + if (c == rsrc.value) { + resources.add(rsrc); + invalidCharacter = false; + break; + } + } + + if (invalidCharacter) { + throw new IllegalArgumentException( + String.format(SR.ENUM_COULD_NOT_BE_PARSED, "Resource Types", rsrcString)); + } + } + + return resources; + } +} \ No newline at end of file diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/SharedAccessAccountService.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/SharedAccessAccountService.java new file mode 100644 index 000000000..46e2f4f63 --- /dev/null +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/SharedAccessAccountService.java @@ -0,0 +1,108 @@ +/** + * Copyright Microsoft Corporation + * + * Licensed 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 com.microsoft.azure.storage; + +import java.util.EnumSet; + +import com.microsoft.azure.storage.core.SR; + +/** + * Specifies the set of possible services for a shared access account policy. + */ +public enum SharedAccessAccountService { + /** + * Permission to access blob resources granted. + */ + BLOB('b'), + + /** + * Permission to access file resources granted. + */ + FILE('f'), + + /** + * Permission to access queue resources granted. + */ + QUEUE('q'), + + /** + * Permission to access table resources granted. + */ + TABLE('t'); + + char value; + + /** + * Create a SharedAccessAccountService. + * + * @param c + * The char which represents this service. + */ + private SharedAccessAccountService(char c) { + this.value = c; + } + + /** + * Converts the given services to a String. + * + * @param services + * The services to convert to a String. + * + * @return A String which represents the SharedAccessAccountServices. + */ + static String servicesToString(EnumSet services) { + if (services == null) { + return Constants.EMPTY_STRING; + } + + StringBuilder value = new StringBuilder(); + + for (SharedAccessAccountService service : services) { + value.append(service.value); + } + + return value.toString(); + } + + /** + * Creates an {@link EnumSet} from the specified services string. + * + * @param servicesString + * A String which represents the SharedAccessAccountServices. + * @return A {@link EnumSet} generated from the given String. + */ + static EnumSet servicesFromString(String servicesString) { + EnumSet resources = EnumSet.noneOf(SharedAccessAccountService.class); + + for (final char c : servicesString.toLowerCase().toCharArray()) { + boolean invalidCharacter = true; + + for (SharedAccessAccountService service : SharedAccessAccountService.values()) { + if (c == service.value) { + resources.add(service); + invalidCharacter = false; + break; + } + } + + if (invalidCharacter) { + throw new IllegalArgumentException( + String.format(SR.ENUM_COULD_NOT_BE_PARSED, "Services", servicesString)); + } + } + + return resources; + } +} \ No newline at end of file diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/SharedAccessPolicy.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/SharedAccessPolicy.java index fa288f671..bebc190fd 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/SharedAccessPolicy.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/SharedAccessPolicy.java @@ -31,14 +31,14 @@ public abstract class SharedAccessPolicy { * The start time for a shared access signature associated with this shared access policy. */ private Date sharedAccessStartTime; - + /** * Creates an instance of the SharedAccessPolicy class. - * */ + */ public SharedAccessPolicy() { // Empty Default Constructor. } - + /** * Gets the expiry time for a shared access signature associated with this shared access policy. * diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/SharedAccessProtocols.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/SharedAccessProtocols.java new file mode 100644 index 000000000..b18bfae62 --- /dev/null +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/SharedAccessProtocols.java @@ -0,0 +1,41 @@ +/** + * Copyright Microsoft Corporation + * + * Licensed 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 com.microsoft.azure.storage; + +/** + * Specifies the set of possible permissions for a shared access protocol. + */ +public enum SharedAccessProtocols { + /** + * Permission to use SAS only through https granted. + */ + HTTPS_ONLY(Constants.HTTPS), + + /** + * Permission to use SAS only through https or http granted. + */ + HTTPS_HTTP(Constants.HTTPS_HTTP); + + private final String protocols; + + private SharedAccessProtocols(String p) { + this.protocols = p; + } + + @Override + public String toString() { + return this.protocols; + } +} \ No newline at end of file diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/StorageCredentials.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/StorageCredentials.java index 59459e2aa..3b65558da 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/StorageCredentials.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/StorageCredentials.java @@ -51,14 +51,14 @@ public abstract class StorageCredentials { */ protected static StorageCredentials tryParseCredentials(final Map settings) throws InvalidKeyException { - final String accountName = settings.get(CloudStorageAccount.ACCOUNT_NAME_NAME) != null ? settings - .get(CloudStorageAccount.ACCOUNT_NAME_NAME) : null; + final String accountName = settings.get(CloudStorageAccount.ACCOUNT_NAME_NAME) != null ? + settings.get(CloudStorageAccount.ACCOUNT_NAME_NAME) : null; - final String accountKey = settings.get(CloudStorageAccount.ACCOUNT_KEY_NAME) != null ? settings - .get(CloudStorageAccount.ACCOUNT_KEY_NAME) : null; + final String accountKey = settings.get(CloudStorageAccount.ACCOUNT_KEY_NAME) != null ? + settings.get(CloudStorageAccount.ACCOUNT_KEY_NAME) : null; - final String sasSignature = settings.get(CloudStorageAccount.SHARED_ACCESS_SIGNATURE_NAME) != null ? settings - .get(CloudStorageAccount.SHARED_ACCESS_SIGNATURE_NAME) : null; + final String sasSignature = settings.get(CloudStorageAccount.SHARED_ACCESS_SIGNATURE_NAME) != null ? + settings.get(CloudStorageAccount.SHARED_ACCESS_SIGNATURE_NAME) : null; if (accountName != null && accountKey != null && sasSignature == null) { if (Base64.validateIsBase64String(accountKey)) { @@ -94,10 +94,28 @@ protected static StorageCredentials tryParseCredentials(final MapconnectionString is not valid. + * @throws StorageException */ - public static StorageCredentials tryParseCredentials(final String connectionString) throws InvalidKeyException { + public static StorageCredentials tryParseCredentials(final String connectionString) + throws InvalidKeyException, StorageException { + return tryParseCredentials(Utility.parseAccountString(connectionString)); } + + /** + * A boolean representing whether this StorageCredentials object only allows access via HTTPS. + */ + private boolean httpsOnly = false; + + /** + * Gets whether this StorageCredentials object only allows access via HTTPS. + * + * @return A boolean representing whether this StorageCredentials + * object only allows access via HTTPS. + */ + public boolean isHttpsOnly() { + return this.httpsOnly; + } /** * Returns the associated account name for the credentials. This is null for anonymous and shared access signature @@ -108,6 +126,16 @@ public static StorageCredentials tryParseCredentials(final String connectionStri public String getAccountName() { return null; } + + /** + * Sets whether this StorageCredentials object only allows access via HTTPS. + * @param httpsOnly + * A boolean representing whether this StorageCredentials + * object only allows access via HTTPS. + */ + protected void setHttpsOnly(boolean httpsOnly) { + this.httpsOnly = httpsOnly; + } /** * Returns a String that represents this instance. diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/StorageCredentialsAccountAndKey.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/StorageCredentialsAccountAndKey.java index 3ac243376..709c8e259 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/StorageCredentialsAccountAndKey.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/StorageCredentialsAccountAndKey.java @@ -15,6 +15,11 @@ package com.microsoft.azure.storage; import java.net.URI; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; import com.microsoft.azure.storage.core.Base64; import com.microsoft.azure.storage.core.SR; @@ -27,10 +32,19 @@ public final class StorageCredentialsAccountAndKey extends StorageCredentials { /** - * The internal Credentials associated with the StorageCredentials. + * Stores the Account name for the credentials. + */ + private String accountName; + + /** + * Stores a reference to the hmacsha256 Mac. + */ + private Mac hmacSha256; + + /** + * Stores the key. */ - @SuppressWarnings("deprecation") - private Credentials credentials; + private byte[] key; /** * Creates an instance of the StorageCredentialsAccountAndKey class, using the specified storage @@ -41,9 +55,17 @@ public final class StorageCredentialsAccountAndKey extends StorageCredentials { * @param key * An array of bytes that represent the account access key. */ - @SuppressWarnings("deprecation") public StorageCredentialsAccountAndKey(final String accountName, final byte[] key) { - this.credentials = new Credentials(accountName, key); + if (Utility.isNullOrEmptyOrWhitespace(accountName)) { + throw new IllegalArgumentException(SR.INVALID_ACCOUNT_NAME); + } + + if (key == null || key.length == 0) { + throw new IllegalArgumentException(SR.INVALID_KEY); + } + + this.accountName = accountName; + this.key = key; } /** @@ -55,9 +77,8 @@ public StorageCredentialsAccountAndKey(final String accountName, final byte[] ke * @param key * A String that represents the Base-64-encoded account access key. */ - @SuppressWarnings("deprecation") public StorageCredentialsAccountAndKey(final String accountName, final String key) { - this.credentials = new Credentials(accountName, key); + this(accountName, Base64.decode(key)); } /** @@ -65,10 +86,9 @@ public StorageCredentialsAccountAndKey(final String accountName, final String ke * * @return A String that contains the account name. */ - @SuppressWarnings("deprecation") @Override public String getAccountName() { - return this.credentials.getAccountName(); + return this.accountName; } /** @@ -76,9 +96,8 @@ public String getAccountName() { * * @return A String that represents the Base64-encoded access key. */ - @SuppressWarnings("deprecation") public String exportBase64EncodedKey() { - return this.credentials.getKey().getBase64EncodedKey(); + return Base64.encode(this.key); } /** @@ -86,9 +105,9 @@ public String exportBase64EncodedKey() { * * @return A byte array that represents the access key. */ - @SuppressWarnings("deprecation") public byte[] exportKey() { - return this.credentials.getKey().getKey(); + final byte[] copy = this.key.clone(); + return copy; } /** @@ -97,9 +116,8 @@ public byte[] exportKey() { * @param accountName * A String that contains the account name. */ - @SuppressWarnings("deprecation") public void setAccountName(String accountName) { - this.credentials.setAccountName(accountName); + this.accountName = accountName; } /** @@ -108,13 +126,8 @@ public void setAccountName(String accountName) { * @param key * A String that represents the name of the access key to be used when signing the request. */ - @SuppressWarnings("deprecation") - public void updateKey(final String key) { - if (Utility.isNullOrEmptyOrWhitespace(key) || !Base64.validateIsBase64String(key)) { - throw new IllegalArgumentException(SR.INVALID_KEY); - } - - this.credentials.setKey(new StorageKey(Base64.decode(key))); + public synchronized void updateKey(final String key) { + this.updateKey(Base64.decode(key)); } /** @@ -123,49 +136,13 @@ public void updateKey(final String key) { * @param key * A String that represents the name of the access key to be used when signing the request. */ - @SuppressWarnings("deprecation") - public void updateKey(final byte[] key) { + public synchronized void updateKey(final byte[] key) { if (key == null || key.length == 0) { throw new IllegalArgumentException(SR.INVALID_KEY); } - this.credentials.setKey(new StorageKey(key)); - } - - /** - * Gets the name of the key used by these credentials. - * @deprecated as of 3.0.0. The key name property is only useful internally. - */ - @Deprecated - public String getAccountKeyName() { - return this.credentials.getKeyName(); - } - - /** - * Returns the internal credentials associated with the storage credentials. - * - * @return A Credentials object that contains the internal credentials associated with this instance of - * the StorageCredentialsAccountAndKey class. - * @deprecated as of 3.0.0. Please use {@link #getAccountName()}, {@link #exportKey()}, or - * {@link #exportBase64EncodedKey()} - */ - @Deprecated - public Credentials getCredentials() { - return this.credentials; - } - - /** - * Sets the credentials. - * - * @param credentials - * A Credentials object that represents the credentials to set for this instance of the - * StorageCredentialsAccountAndKey class. - * @deprecated as of 3.0.0. Please use {@link #setAccountName(String)}, {@link #updateKey(String)}, or - * {@link #updateKey(byte[])} - */ - @Deprecated - public void setCredentials(final Credentials credentials) { - this.credentials = credentials; + this.key = key; + this.hmacSha256 = null; } /** @@ -176,11 +153,10 @@ public void setCredentials(final Credentials credentials) { * * @return A String that represents this object, optionally including sensitive data. */ - @SuppressWarnings("deprecation") @Override public String toString(final boolean exportSecrets) { return String.format("%s=%s;%s=%s", CloudStorageAccount.ACCOUNT_NAME_NAME, this.getAccountName(), - CloudStorageAccount.ACCOUNT_KEY_NAME, exportSecrets ? this.credentials.getKey().getBase64EncodedKey() + CloudStorageAccount.ACCOUNT_KEY_NAME, exportSecrets ? this.exportBase64EncodedKey() : "[key hidden]"); } @@ -193,4 +169,26 @@ public URI transformUri(URI resourceUri, OperationContext opContext) { public StorageUri transformUri(StorageUri resourceUri, OperationContext opContext) { return resourceUri; } + + /** + * Gets the HmacSha256 associated with the account key. + * + * @return A MAC created with the account key. + * + * @throws InvalidKeyException + * If the key is not a valid storage key. + */ + public synchronized Mac getHmac256() throws InvalidKeyException { + if (this.hmacSha256 == null) { + // Initializes the HMAC-SHA256 Mac and SecretKey. + try { + this.hmacSha256 = Mac.getInstance("HmacSHA256"); + } + catch (final NoSuchAlgorithmException e) { + throw new IllegalArgumentException(); + } + this.hmacSha256.init(new SecretKeySpec(this.key, "HmacSHA256")); + } + return this.hmacSha256; + } } diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/StorageCredentialsAnonymous.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/StorageCredentialsAnonymous.java index 010ead369..be8961084 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/StorageCredentialsAnonymous.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/StorageCredentialsAnonymous.java @@ -20,7 +20,6 @@ * Represents credentials for anonymous access. */ public final class StorageCredentialsAnonymous extends StorageCredentials { - /** * Stores the singleton instance of this class. */ @@ -38,8 +37,8 @@ protected static StorageCredentials getInstance() { /** * Enforces the singleton pattern via a private constructor. */ - protected StorageCredentialsAnonymous() { - // Empty Default Ctor + private StorageCredentialsAnonymous() { + // Empty Default Ctor } /** diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/StorageCredentialsSharedAccessSignature.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/StorageCredentialsSharedAccessSignature.java index 0ec3aaebf..0e79d565e 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/StorageCredentialsSharedAccessSignature.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/StorageCredentialsSharedAccessSignature.java @@ -16,8 +16,10 @@ import java.net.URI; import java.net.URISyntaxException; +import java.util.Map; import com.microsoft.azure.storage.core.PathUtility; +import com.microsoft.azure.storage.core.SR; /** * Represents storage credentials for delegated access to Blob service resources via a shared access signature. @@ -38,6 +40,20 @@ public final class StorageCredentialsSharedAccessSignature extends StorageCreden */ public StorageCredentialsSharedAccessSignature(final String token) { this.token = token; + + if (token == null) { + this.setHttpsOnly(false); + } + else { + try { + Map queryParams = PathUtility.parseQueryString(token); + final String[] protocols = queryParams.get(Constants.QueryConstants.SIGNED_PROTOCOLS); + this.setHttpsOnly((protocols != null) && Constants.HTTPS.equals(protocols[0])); + } + catch (StorageException e) { + this.setHttpsOnly(false); + } + } } /** @@ -82,12 +98,16 @@ public String toString(final boolean exportSecrets) { * If the resource URI is not properly formatted. */ @Override - public URI transformUri(final URI resourceUri, final OperationContext opContext) throws URISyntaxException, - StorageException { + public URI transformUri(final URI resourceUri, final OperationContext opContext) + throws URISyntaxException, StorageException { if (resourceUri == null) { return null; } - + + if(this.isHttpsOnly() && !resourceUri.getScheme().equals(Constants.HTTPS)) { + throw new IllegalArgumentException(SR.CANNOT_TRANSFORM_NON_HTTPS_URI_WITH_HTTPS_ONLY_CREDENTIALS); + } + // append the sas token to the resource uri URI sasUri = PathUtility.addToQuery(resourceUri, this.token); diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/StorageKey.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/StorageKey.java deleted file mode 100644 index 7188a8194..000000000 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/StorageKey.java +++ /dev/null @@ -1,212 +0,0 @@ -/** - * Copyright Microsoft Corporation - * - * Licensed 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 com.microsoft.azure.storage; - -import java.io.UnsupportedEncodingException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; - -import javax.crypto.Mac; -import javax.crypto.SecretKey; -import javax.crypto.spec.SecretKeySpec; - -import com.microsoft.azure.storage.core.Base64; - -/** - * Represents a container for a storage key. - * @deprecated as of 3.0.0. Please use the methods on {@link StorageCredentialsAccountAndKey}. - */ -@Deprecated -public final class StorageKey { - /** - * Computes a signature for the specified string using the HMAC-SHA256 algorithm. - * - * @param storageKey - * A StorageKey object that represents the storage key to use. - * @param stringToSign - * The UTF-8-encoded string to sign. - * - * @return A String that contains the HMAC-SHA256-encoded signature. - * - * @throws IllegalArgumentException - * If the string to sign is not a valid Base64-encoded string. - * @throws InvalidKeyException - * If the key is not a valid storage key. - */ - public static synchronized String computeMacSha256(final StorageKey storageKey, final String stringToSign) - throws InvalidKeyException { - if (storageKey.hmacSha256 == null) { - storageKey.initHmacSha256(); - } - - byte[] utf8Bytes = null; - try { - utf8Bytes = stringToSign.getBytes(Constants.UTF8_CHARSET); - } - catch (final UnsupportedEncodingException e) { - throw new IllegalArgumentException(e); - } - - return Base64.encode(storageKey.hmacSha256.doFinal(utf8Bytes)); - } - - /** - * Computes a signature for the specified string using the HMAC-SHA512 algorithm. - * - * @param storageKey - * A StorageKey object that represents the storage key to use. - * @param stringToSign - * The UTF-8-encoded string to sign. - * - * @return A String that contains the HMAC-SHA512-encoded signature. - * - * @throws IllegalArgumentException - * If the string to sign is not a valid Base64-encoded string. - * @throws InvalidKeyException - * If the key is not a valid storage key. - */ - public static synchronized String computeMacSha512(final StorageKey storageKey, final String stringToSign) - throws InvalidKeyException { - if (storageKey.hmacSha512 == null) { - storageKey.initHmacSha512(); - } - - byte[] utf8Bytes = null; - try { - utf8Bytes = stringToSign.getBytes(Constants.UTF8_CHARSET); - } - catch (final UnsupportedEncodingException e) { - throw new IllegalArgumentException(e); - } - - return Base64.encode(storageKey.hmacSha512.doFinal(utf8Bytes)); - } - - /** - * Stores a reference to the hmacsha256 Mac. - */ - private Mac hmacSha256; - - /** - * Stores a reference to the hmacsha512 Mac. - */ - private Mac hmacSha512; - - /** - * Stores a reference to the hmacsha256 SecretKey. - */ - private SecretKey key256; - - /** - * Stores a reference to the hmacsha512 SecretKey. - */ - private SecretKey key512; - - /** - * Stores the key. - */ - private byte[] key; - - /** - * Creates an instance of the StorageKey class. - * - * @param key - * An array of bytes that represent the storage key. - */ - public StorageKey(final byte[] key) { - this.setKey(key); - } - - /** - * Returns the Base64-encoded key. - * - * @return A String that represents the Base64-encoded key. - */ - public String getBase64EncodedKey() { - return Base64.encode(this.key); - } - - /** - * Returns the key. - * - * @return A byte array that represents the key. - */ - public byte[] getKey() { - final byte[] copy = this.key.clone(); - return copy; - } - - /** - * Initializes the HMAC-SHA256 Mac and SecretKey. - * - * @throws InvalidKeyException - * If the key is not a valid SecretKey according to specification. - */ - private void initHmacSha256() throws InvalidKeyException { - this.key256 = new SecretKeySpec(this.key, "HmacSHA256"); - try { - this.hmacSha256 = Mac.getInstance("HmacSHA256"); - } - catch (final NoSuchAlgorithmException e) { - throw new IllegalArgumentException(); - } - this.hmacSha256.init(this.key256); - } - - /** - * Initializes the HMAC-SHA256 Mac and SecretKey. - * - * @throws InvalidKeyException - * If the key is not a valid SecretKey according to specification. - */ - private void initHmacSha512() throws InvalidKeyException { - this.key512 = new SecretKeySpec(this.key, "HmacSHA512"); - try { - this.hmacSha512 = Mac.getInstance("HmacSHA512"); - } - catch (final NoSuchAlgorithmException e) { - throw new IllegalArgumentException(); - } - this.hmacSha512.init(this.key512); - } - - /** - * Sets the key to be used, using the specified byte array as the key. - *

- * This method is provided to support key rotation. This method is not thread-safe. - * - * @param key - * A byte array that represents the key being assigned. - */ - public void setKey(final byte[] key) { - this.key = key; - this.hmacSha256 = null; - this.hmacSha512 = null; - this.key256 = null; - this.key512 = null; - } - - /** - * Sets the key to be used, using the specified String as the key. - *

- * This method is provided to support key rotation. This method is not thread-safe. - * - * @param key - * A String that represents the key being assigned. - */ - public void setKey(final String key) { - this.key = Base64.decode(key); - } -} diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/analytics/CloudAnalyticsClient.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/analytics/CloudAnalyticsClient.java index 6033e786f..db7efd5d0 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/analytics/CloudAnalyticsClient.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/analytics/CloudAnalyticsClient.java @@ -133,31 +133,44 @@ public CloudTable getHourMetricsTable(StorageService service, StorageLocation lo switch (service) { case BLOB: if (location == StorageLocation.PRIMARY) { - return this.tableClient - .getTableReference(Constants.AnalyticsConstants.METRICS_HOUR_PRIMARY_TRANSACTIONS_BLOB); + return this.tableClient.getTableReference( + Constants.AnalyticsConstants.METRICS_HOUR_PRIMARY_TRANSACTIONS_BLOB); } else { - return this.tableClient - .getTableReference(Constants.AnalyticsConstants.METRICS_HOUR_SECONDARY_TRANSACTIONS_BLOB); + return this.tableClient.getTableReference( + Constants.AnalyticsConstants.METRICS_HOUR_SECONDARY_TRANSACTIONS_BLOB); } + + case FILE: + if (location == StorageLocation.PRIMARY) { + return this.tableClient.getTableReference( + Constants.AnalyticsConstants.METRICS_HOUR_PRIMARY_TRANSACTIONS_FILE); + } + else { + return this.tableClient.getTableReference( + Constants.AnalyticsConstants.METRICS_HOUR_SECONDARY_TRANSACTIONS_FILE); + } + case QUEUE: if (location == StorageLocation.PRIMARY) { - return this.tableClient - .getTableReference(Constants.AnalyticsConstants.METRICS_HOUR_PRIMARY_TRANSACTIONS_QUEUE); + return this.tableClient.getTableReference( + Constants.AnalyticsConstants.METRICS_HOUR_PRIMARY_TRANSACTIONS_QUEUE); } else { - return this.tableClient - .getTableReference(Constants.AnalyticsConstants.METRICS_HOUR_SECONDARY_TRANSACTIONS_QUEUE); + return this.tableClient.getTableReference( + Constants.AnalyticsConstants.METRICS_HOUR_SECONDARY_TRANSACTIONS_QUEUE); } + case TABLE: if (location == StorageLocation.PRIMARY) { - return this.tableClient - .getTableReference(Constants.AnalyticsConstants.METRICS_HOUR_PRIMARY_TRANSACTIONS_TABLE); + return this.tableClient.getTableReference( + Constants.AnalyticsConstants.METRICS_HOUR_PRIMARY_TRANSACTIONS_TABLE); } else { - return this.tableClient - .getTableReference(Constants.AnalyticsConstants.METRICS_HOUR_SECONDARY_TRANSACTIONS_TABLE); + return this.tableClient.getTableReference( + Constants.AnalyticsConstants.METRICS_HOUR_SECONDARY_TRANSACTIONS_TABLE); } + default: throw new IllegalArgumentException(SR.INVALID_STORAGE_SERVICE); } @@ -199,30 +212,39 @@ public CloudTable getMinuteMetricsTable(StorageService service, StorageLocation switch (service) { case BLOB: if (location == StorageLocation.PRIMARY) { - return this.tableClient - .getTableReference(Constants.AnalyticsConstants.METRICS_MINUTE_PRIMARY_TRANSACTIONS_BLOB); + return this.tableClient.getTableReference( + Constants.AnalyticsConstants.METRICS_MINUTE_PRIMARY_TRANSACTIONS_BLOB); + } + else { + return this.tableClient.getTableReference( + Constants.AnalyticsConstants.METRICS_MINUTE_SECONDARY_TRANSACTIONS_BLOB); + } + case FILE: + if (location == StorageLocation.PRIMARY) { + return this.tableClient.getTableReference( + Constants.AnalyticsConstants.METRICS_MINUTE_PRIMARY_TRANSACTIONS_FILE); } else { - return this.tableClient - .getTableReference(Constants.AnalyticsConstants.METRICS_MINUTE_SECONDARY_TRANSACTIONS_BLOB); + return this.tableClient.getTableReference( + Constants.AnalyticsConstants.METRICS_MINUTE_SECONDARY_TRANSACTIONS_FILE); } case QUEUE: if (location == StorageLocation.PRIMARY) { - return this.tableClient - .getTableReference(Constants.AnalyticsConstants.METRICS_MINUTE_PRIMARY_TRANSACTIONS_QUEUE); + return this.tableClient.getTableReference( + Constants.AnalyticsConstants.METRICS_MINUTE_PRIMARY_TRANSACTIONS_QUEUE); } else { - return this.tableClient - .getTableReference(Constants.AnalyticsConstants.METRICS_MINUTE_SECONDARY_TRANSACTIONS_QUEUE); + return this.tableClient.getTableReference( + Constants.AnalyticsConstants.METRICS_MINUTE_SECONDARY_TRANSACTIONS_QUEUE); } case TABLE: if (location == StorageLocation.PRIMARY) { - return this.tableClient - .getTableReference(Constants.AnalyticsConstants.METRICS_MINUTE_PRIMARY_TRANSACTIONS_TABLE); + return this.tableClient.getTableReference( + Constants.AnalyticsConstants.METRICS_MINUTE_PRIMARY_TRANSACTIONS_TABLE); } else { - return this.tableClient - .getTableReference(Constants.AnalyticsConstants.METRICS_MINUTE_SECONDARY_TRANSACTIONS_TABLE); + return this.tableClient.getTableReference( + Constants.AnalyticsConstants.METRICS_MINUTE_SECONDARY_TRANSACTIONS_TABLE); } default: throw new IllegalArgumentException(SR.INVALID_STORAGE_SERVICE); diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/analytics/StorageService.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/analytics/StorageService.java index 794bdff5d..abffcb0f5 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/analytics/StorageService.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/analytics/StorageService.java @@ -22,6 +22,11 @@ public enum StorageService { * Blob service. */ BLOB, + + /** + * File service. + */ + FILE, /** * Queue Service. diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudAppendBlob.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudAppendBlob.java index 95d372632..16af8d792 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudAppendBlob.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudAppendBlob.java @@ -146,24 +146,23 @@ public CloudAppendBlob(final StorageUri blobAbsoluteUri, final String snapshotID throws StorageException { super(BlobType.APPEND_BLOB, blobAbsoluteUri, snapshotID, credentials); } - + /** - * Creates an instance of the CloudAppendBlob class using the specified URI, snapshot ID, and cloud blob - * client. - * - * @param blobAbsoluteUri - * A {@link StorageUri} object which represents the absolute URI to the blob. + * Creates an instance of the CloudAppendBlob class using the specified type, name, snapshot ID, and + * container. + * + * @param blobName + * Name of the blob. * @param snapshotID - * A String which represents the snapshot version, if applicable. - * @param client - * A {@link CloudBlobContainer} object which represents the container to use for the blob. - * - * @throws StorageException - * If a storage service error occurred. + * A String that represents the snapshot version, if applicable. + * @param container + * The reference to the parent container. + * @throws URISyntaxException + * If the resource URI is invalid. */ - protected CloudAppendBlob(final StorageUri blobAbsoluteUri, final String snapshotID, final CloudBlobClient client) - throws StorageException { - super(BlobType.APPEND_BLOB, blobAbsoluteUri, snapshotID, client); + protected CloudAppendBlob(String blobName, String snapshotID, CloudBlobContainer container) + throws URISyntaxException { + super(BlobType.APPEND_BLOB, blobName, snapshotID, container); } /** diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudBlob.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudBlob.java index 43e7abc51..3a0433d34 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudBlob.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudBlob.java @@ -34,8 +34,10 @@ import com.microsoft.azure.storage.AccessCondition; import com.microsoft.azure.storage.Constants; import com.microsoft.azure.storage.DoesServiceRequest; +import com.microsoft.azure.storage.IPRange; import com.microsoft.azure.storage.OperationContext; import com.microsoft.azure.storage.SharedAccessPolicy; +import com.microsoft.azure.storage.SharedAccessProtocols; import com.microsoft.azure.storage.StorageCredentials; import com.microsoft.azure.storage.StorageCredentialsSharedAccessSignature; import com.microsoft.azure.storage.StorageErrorCodeStrings; @@ -110,108 +112,33 @@ public abstract class CloudBlob implements ListBlobItem { * Represents the blob client. */ protected CloudBlobClient blobServiceClient; - - /** - * Creates an instance of the CloudBlob class. - * - * @param type - * A {@link BlobType} value which represents the type of the blob. - */ - protected CloudBlob(final BlobType type) { - this.properties = new BlobProperties(type); - } - - /** - * Creates an instance of the CloudBlob class using the specified URI and cloud blob client. - * - * @param type - * A {@link BlobType} value which represents the type of the blob. - * @param uri - * A {@link StorageUri} object that represents the URI to the blob, beginning with the container name. - * @param client - * A {@link CloudBlobClient} object that specifies the endpoint for the Blob service. - * - * @throws StorageException - * If a storage service error occurred. - */ - protected CloudBlob(final BlobType type, final StorageUri uri, final CloudBlobClient client) - throws StorageException { - this(type); - this.parseQueryAndVerify(uri, client == null ? null : client.getCredentials()); - - // Override the client set in parseQueryAndVerify to make sure request options are propagated. - if (client != null) { - this.blobServiceClient = client; - } - } - - /** - * Creates an instance of the CloudBlob class using the specified URI, cloud blob client, and cloud - * blob container. - * - * @param type - * A {@link BlobType} value which represents the type of the blob. - * @param uri - * A {@link StorageUri} object that represents the URI to the blob, beginning with the container name. - * @param client - * A {@link CloudBlobClient} object that specifies the endpoint for the Blob service. - * @param container - * A {@link CloudBlobContainer} object that represents the container to use for the blob. - * - * @throws StorageException - * If a storage service error occurred. - */ - protected CloudBlob(final BlobType type, final StorageUri uri, final CloudBlobClient client, - final CloudBlobContainer container) throws StorageException { - this(type, uri, client); - this.container = container; - } /** - * Creates an instance of the CloudBlob class using the specified URI, snapshot ID, and cloud blob - * client. + * Creates an instance of the CloudBlob class using the specified type, name, snapshot ID, and + * container. * * @param type * A {@link BlobType} value which represents the type of the blob. - * @param uri - * A {@link StorageUri} object that represents the URI to the blob, beginning with the container name. + * @param blobName + * Name of the blob. * @param snapshotID * A String that represents the snapshot version, if applicable. - * @param client - * A {@link CloudBlobClient} object that specifies the endpoint for the Blob service. - * - * @throws StorageException - * If a storage service error occurred. - */ - protected CloudBlob(final BlobType type, final StorageUri uri, final String snapshotID, final CloudBlobClient client) - throws StorageException { - this(type, uri, client); - if (snapshotID != null) { - if (this.snapshotID != null) { - throw new IllegalArgumentException(SR.SNAPSHOT_QUERY_OPTION_ALREADY_DEFINED); - } - else { - this.snapshotID = snapshotID; - } - } - } - - /** - * Creates an instance of the CloudBlob class using the specified URI and cloud blob client. - * - * @param type - * A {@link BlobType} value which represents the type of the blob. - * @param uri - * A {@link StorageUri} object that represents the URI to the blob, beginning with the container name. - * @param credentials - * A {@link StorageCredentials} object used to authenticate access. - * - * @throws StorageException - * If a storage service error occurred. + * @param container + * The reference to the parent container. + * @throws URISyntaxException + * If the resource URI is invalid. */ - protected CloudBlob(final BlobType type, final StorageUri uri, final StorageCredentials credentials) - throws StorageException { - this(type, uri, null /* snapshotID */, credentials); + protected CloudBlob(final BlobType type, String blobName, String snapshotID, CloudBlobContainer container) + throws URISyntaxException { + Utility.assertNotNullOrEmpty("blobName", blobName); + Utility.assertNotNull("container", container); + + this.storageUri = PathUtility.appendPathToUri(container.getStorageUri(), blobName); + this.name = blobName; + this.blobServiceClient = container.getServiceClient(); + this.container = container; + this.snapshotID = snapshotID; + this.properties = new BlobProperties(type); } /** @@ -231,7 +158,7 @@ protected CloudBlob(final BlobType type, final StorageUri uri, final StorageCred */ protected CloudBlob(final BlobType type, final StorageUri uri, final String snapshotID, final StorageCredentials credentials) throws StorageException { - this(type); + this.properties = new BlobProperties(type); this.parseQueryAndVerify(uri, credentials); if (snapshotID != null) { @@ -708,123 +635,6 @@ public String preProcessResponse(CloudBlob blob, CloudBlobClient client, Operati return putRequest; } - /** - * Requests the service to start copying a blob's contents, properties, and metadata to a new blob. - * - * @param sourceBlob - * A CloudBlob object that represents the source blob to copy. - * - * @return A String which represents the copy ID associated with the copy operation. - * - * @throws StorageException - * If a storage service error occurred. - * @throws URISyntaxException - * - * @deprecated as of 3.0.0. Use {@link CloudBlob#startCopy(URI)} instead. - */ - @Deprecated - @DoesServiceRequest - public final String startCopyFromBlob(final CloudBlob sourceBlob) throws StorageException, URISyntaxException { - return this.startCopyFromBlob(sourceBlob, null /* sourceAccessCondition */, - null /* destinationAccessCondition */, null /* options */, null /* opContext */); - } - - /** - * Requests the service to start copying a blob's contents, properties, and metadata to a new blob, using the - * specified access conditions, lease ID, request options, and operation context. - * - * @param sourceBlob - * A CloudBlob object that represents the source blob to copy. - * @param sourceAccessCondition - * An {@link AccessCondition} object that represents the access conditions for the source blob. - * @param destinationAccessCondition - * An {@link AccessCondition} object that represents the access conditions for the destination blob. - * @param options - * A {@link BlobRequestOptions} object that specifies any additional options for the request. Specifying - * null will use the default request options from the associated service client ( - * {@link CloudBlobClient}). - * @param opContext - * An {@link OperationContext} object that represents the context for the current operation. This object - * is used to track requests to the storage service, and to provide additional runtime information about - * the operation. - * - * @return A String which represents the copy ID associated with the copy operation. - * - * @throws StorageException - * If a storage service error occurred. - * @throws URISyntaxException - * - * @deprecated as of 3.0.0. Use {@link CloudBlob#startCopy( - * URI, AccessCondition, AccessCondition, BlobRequestOptions, OperationContext)} instead. - */ - @Deprecated - @DoesServiceRequest - public final String startCopyFromBlob(final CloudBlob sourceBlob, final AccessCondition sourceAccessCondition, - final AccessCondition destinationAccessCondition, BlobRequestOptions options, OperationContext opContext) - throws StorageException, URISyntaxException { - Utility.assertNotNull("sourceBlob", sourceBlob); - return this.startCopy( - sourceBlob.getServiceClient().getCredentials().transformUri(sourceBlob.getQualifiedUri()), - sourceAccessCondition, destinationAccessCondition, options, opContext); - } - - /** - * Requests the service to start copying a URI's contents, properties, and metadata to a new blob. - * - * @param source - * A java.net.URI The source URI. URIs for resources outside of Azure - * may only be copied into block blobs. - * - * @return A String which represents the copy ID associated with the copy operation. - * - * @throws StorageException - * If a storage service error occurred. - * - * @deprecated as of 3.0.0. Use {@link CloudBlob#startCopy(URI)} instead. - */ - @Deprecated - @DoesServiceRequest - public final String startCopyFromBlob(final URI source) throws StorageException { - return this.startCopy(source, null /* sourceAccessCondition */, null /* destinationAccessCondition */, - null /* options */, null /* opContext */); - } - - /** - * Requests the service to start copying a URI's contents, properties, and metadata to a new blob, using the - * specified access conditions, lease ID, request options, and operation context. - * - * @param source - * A java.net.URI The source URI. URIs for resources outside of Azure - * may only be copied into block blobs. - * @param sourceAccessCondition - * An {@link AccessCondition} object that represents the access conditions for the source. - * @param destinationAccessCondition - * An {@link AccessCondition} object that represents the access conditions for the destination. - * @param options - * A {@link BlobRequestOptions} object that specifies any additional options for the request. - * Specifying null will use the default request options from the associated - * service client ({@link CloudBlobClient}). - * @param opContext - * An {@link OperationContext} object that represents the context for the current operation. - * This object is used to track requests to the storage service, and to provide additional - * runtime information about the operation. - * - * @return A String which represents the copy ID associated with the copy operation. - * - * @throws StorageException - * If a storage service error occurred. - * - * @deprecated as of 3.0.0. Use {@link CloudBlob#startCopy( - * URI, AccessCondition, AccessCondition, BlobRequestOptions, OperationContext)} instead. - */ - @Deprecated - @DoesServiceRequest - public final String startCopyFromBlob(final URI source, final AccessCondition sourceAccessCondition, - final AccessCondition destinationAccessCondition, BlobRequestOptions options, OperationContext opContext) - throws StorageException { - return this.startCopy(source, sourceAccessCondition, destinationAccessCondition, options, opContext); - } - /** * Requests the service to start copying a URI's contents, properties, and metadata to a new blob. * @@ -1035,7 +845,6 @@ public void signRequest(HttpURLConnection connection, CloudBlobClient client, Op StorageRequest.signBlobQueueAndFileRequest(connection, client, 0L, context); } - @SuppressWarnings("deprecation") @Override public CloudBlob preProcessResponse(CloudBlob blob, CloudBlobClient client, OperationContext context) throws Exception { @@ -1046,13 +855,13 @@ public CloudBlob preProcessResponse(CloudBlob blob, CloudBlobClient client, Oper CloudBlob snapshot = null; final String snapshotTime = BlobResponse.getSnapshotTime(this.getConnection()); if (blob instanceof CloudBlockBlob) { - snapshot = new CloudBlockBlob(blob.getStorageUri(), snapshotTime, client); + snapshot = new CloudBlockBlob(blob.getName(), snapshotTime, CloudBlob.this.getContainer()); } else if (blob instanceof CloudPageBlob) { - snapshot = new CloudPageBlob(blob.getStorageUri(), snapshotTime, client); + snapshot = new CloudPageBlob(blob.getName(), snapshotTime, CloudBlob.this.getContainer()); } else if (blob instanceof CloudAppendBlob) { - snapshot = new CloudAppendBlob(blob.getStorageUri(), snapshotTime, client); + snapshot = new CloudAppendBlob(blob.getName(), snapshotTime, CloudBlob.this.getContainer()); } snapshot.setProperties(blob.properties); @@ -2060,20 +1869,53 @@ public String generateSharedAccessSignature(final SharedAccessBlobPolicy policy, return this.generateSharedAccessSignature(policy, null /* headers */, groupPolicyIdentifier); } + + /** + * Returns a shared access signature for the blob using the specified group policy identifier and operation context. + * Note this does not contain the leading "?". + * + * @param policy + * A SharedAccessPolicy object that represents the access policy for the shared access + * signature. + * @param headers + * A {@link SharedAccessBlobHeaders} object that represents the optional header values to + * set for a blob accessed with this shared access signature. + * @param groupPolicyIdentifier + * A String that represents the container-level access policy. + * + * @return A String that represents the shared access signature. + * + * @throws IllegalArgumentException + * If the credentials are invalid or the blob is a snapshot. + * @throws InvalidKeyException + * If the credentials are invalid. + * @throws StorageException + * If a storage service error occurred. + */ + public String generateSharedAccessSignature(final SharedAccessBlobPolicy policy, final SharedAccessBlobHeaders headers, + final String groupPolicyIdentifier) + throws InvalidKeyException, StorageException { + return this.generateSharedAccessSignature(policy, headers, groupPolicyIdentifier, + null /* IP range */, null /* protocols */); + } + /** * Returns a shared access signature for the blob using the specified group policy identifier and operation context. * Note this does not contain the leading "?". * * @param policy * A {@link SharedAccessPolicy} object that represents the access policy for the shared - * access - * signature. + * access signature. * @param headers * A {@link SharedAccessBlobHeaders} object that represents the optional header values to * set for a blob accessed with this shared access signature. * @param groupPolicyIdentifier * A String that represents the container-level access policy. + * @param ipRange + * A {@link IPRange} object containing the range of allowed IP addresses. + * @param protocols + * A {@link SharedAccessProtocols} representing the allowed Internet protocols. * * @return A String that represents the shared access signature. * @@ -2084,25 +1926,26 @@ public String generateSharedAccessSignature(final SharedAccessBlobPolicy policy, * @throws StorageException * If a storage service error occurred. */ - public String generateSharedAccessSignature(final SharedAccessBlobPolicy policy, - final SharedAccessBlobHeaders headers, final String groupPolicyIdentifier) throws InvalidKeyException, - StorageException { + public String generateSharedAccessSignature( + final SharedAccessBlobPolicy policy, final SharedAccessBlobHeaders headers, + final String groupPolicyIdentifier, final IPRange ipRange, final SharedAccessProtocols protocols) + throws InvalidKeyException, StorageException { if (!StorageCredentialsHelper.canCredentialsSignRequest(this.blobServiceClient.getCredentials())) { throw new IllegalArgumentException(SR.CANNOT_CREATE_SAS_WITHOUT_ACCOUNT_KEY); } - + if (this.isSnapshot()) { throw new IllegalArgumentException(SR.CANNOT_CREATE_SAS_FOR_SNAPSHOTS); } final String resourceName = this.getCanonicalName(true); - final String signature = SharedAccessSignatureHelper.generateSharedAccessSignatureHashForBlobAndFile(policy, headers, - groupPolicyIdentifier, resourceName, this.blobServiceClient); + final String signature = SharedAccessSignatureHelper.generateSharedAccessSignatureHashForBlobAndFile( + policy, headers, groupPolicyIdentifier, resourceName, ipRange, protocols, this.blobServiceClient); - final UriQueryBuilder builder = SharedAccessSignatureHelper.generateSharedAccessSignatureForBlobAndFile(policy, - headers, groupPolicyIdentifier, "b", signature); + final UriQueryBuilder builder = SharedAccessSignatureHelper.generateSharedAccessSignatureForBlobAndFile( + policy, headers, groupPolicyIdentifier, "b", ipRange, protocols, signature); return builder.toString(); } @@ -2148,13 +1991,12 @@ String getCanonicalName(final boolean ignoreSnapshotTime) { * @throws URISyntaxException * If the resource URI is invalid. */ - @SuppressWarnings("deprecation") @Override public final CloudBlobContainer getContainer() throws StorageException, URISyntaxException { if (this.container == null) { final StorageUri containerURI = PathUtility.getContainerURI(this.getStorageUri(), this.blobServiceClient.isUsePathStyleUris()); - this.container = new CloudBlobContainer(containerURI, this.blobServiceClient); + this.container = new CloudBlobContainer(containerURI, this.blobServiceClient.getCredentials()); } return this.container; diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudBlobClient.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudBlobClient.java index 05841ca61..a01ddd021 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudBlobClient.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudBlobClient.java @@ -128,7 +128,6 @@ public CloudBlobClient(final StorageUri storageUri, StorageCredentials credentia * @see Naming and Referencing Containers, Blobs, * and Metadata */ - @SuppressWarnings("deprecation") public CloudBlobContainer getContainerReference(final String containerName) throws URISyntaxException, StorageException { return new CloudBlobContainer(containerName, this); diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudBlobContainer.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudBlobContainer.java index 1d654d6b2..6ec136a0a 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudBlobContainer.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudBlobContainer.java @@ -31,12 +31,14 @@ import com.microsoft.azure.storage.AccessCondition; import com.microsoft.azure.storage.Constants; import com.microsoft.azure.storage.DoesServiceRequest; +import com.microsoft.azure.storage.IPRange; import com.microsoft.azure.storage.OperationContext; import com.microsoft.azure.storage.ResultContinuation; import com.microsoft.azure.storage.ResultContinuationType; import com.microsoft.azure.storage.ResultSegment; import com.microsoft.azure.storage.SharedAccessPolicyHandler; import com.microsoft.azure.storage.SharedAccessPolicySerializer; +import com.microsoft.azure.storage.SharedAccessProtocols; import com.microsoft.azure.storage.StorageCredentials; import com.microsoft.azure.storage.StorageCredentialsSharedAccessSignature; import com.microsoft.azure.storage.StorageErrorCodeStrings; @@ -75,10 +77,10 @@ static BlobContainerPermissions getContainerAcl(final String aclString) { if (!Utility.isNullOrEmpty(aclString)) { final String lowerAclString = aclString.toLowerCase(); - if ("container".equals(lowerAclString)) { + if (SR.CONTAINER.equals(lowerAclString)) { accessType = BlobContainerPublicAccessType.CONTAINER; } - else if ("blob".equals(lowerAclString)) { + else if (SR.BLOB.equals(lowerAclString)) { accessType = BlobContainerPublicAccessType.BLOB; } else { @@ -196,10 +198,8 @@ public CloudBlobContainer(final StorageUri storageUri, final StorageCredentials * * @see Naming and Referencing Containers, Blobs, * and Metadata - * @deprecated as of 3.0.0. Please use {@link CloudBlobClient#getContainerReference(String)} */ - @Deprecated - public CloudBlobContainer(final String containerName, final CloudBlobClient client) throws URISyntaxException, + protected CloudBlobContainer(final String containerName, final CloudBlobClient client) throws URISyntaxException, StorageException { Utility.assertNotNull("client", client); Utility.assertNotNull("containerName", containerName); @@ -208,52 +208,6 @@ public CloudBlobContainer(final String containerName, final CloudBlobClient clie this.name = containerName; this.blobServiceClient = client; } - - /** - * Creates an instance of the CloudBlobContainer class using the specified URI and client. - * - * @param uri - * A java.net.URI object that represents the absolute URI of the container. - * @param client - * A {@link CloudBlobClient} object that represents the associated service client, and that specifies the - * endpoint for the Blob service. - * - * @throws StorageException - * If a storage service error occurred. - * @throws URISyntaxException - * If the resource URI is invalid. - * @deprecated as of 3.0.0. Please use {@link CloudBlobContainer#CloudBlobContainer(URI, StorageCredentials)} - */ - @Deprecated - public CloudBlobContainer(final URI uri, final CloudBlobClient client) throws URISyntaxException, StorageException { - this(new StorageUri(uri), client); - } - - /** - * Creates an instance of the CloudBlobContainer class using the specified URI and client. - * - * @param storageUri - * A {@link StorageUri} object which represents the absolute URI of the container. - * @param client - * A {@link CloudBlobClient} object that represents the associated service client, and that specifies the - * endpoint for the Blob service. - * - * @throws StorageException - * If a storage service error occurred. - * @throws URISyntaxException - * If the resource URI is invalid. - * @deprecated as of 3.0.0. Please use {@link CloudBlobContainer#CloudBlobContainer(StorageUri, StorageCredentials)} - */ - @Deprecated - public CloudBlobContainer(final StorageUri storageUri, final CloudBlobClient client) throws URISyntaxException, - StorageException { - this.parseQueryAndVerify(storageUri, client == null ? null : client.getCredentials()); - - // Override the client set in parseQueryAndVerify to make sure request options are propagated. - if (client != null) { - this.blobServiceClient = client; - } - } /** * Creates the container. @@ -434,15 +388,15 @@ public void delete(AccessCondition accessCondition, BlobRequestOptions options, options.getRetryPolicyFactory(), opContext); } - private StorageRequest deleteImpl(final AccessCondition accessCondition, - final BlobRequestOptions options) { - final StorageRequest putRequest = new StorageRequest( - options, this.getStorageUri()) { + private StorageRequest deleteImpl( + final AccessCondition accessCondition, final BlobRequestOptions options) { + final StorageRequest putRequest = + new StorageRequest(options, this.getStorageUri()) { @Override - public HttpURLConnection buildRequest(CloudBlobClient client, CloudBlobContainer container, - OperationContext context) throws Exception { - return BlobRequest.deleteContainer(container.getStorageUri().getPrimaryUri(), options, context, - accessCondition); + public HttpURLConnection buildRequest( + CloudBlobClient client, CloudBlobContainer container, OperationContext context) throws Exception { + return BlobRequest.deleteContainer( + container.getTransformedAddress().getPrimaryUri(), options, context, accessCondition); } @Override @@ -452,8 +406,8 @@ public void signRequest(HttpURLConnection connection, CloudBlobClient client, Op } @Override - public Void preProcessResponse(CloudBlobContainer container, CloudBlobClient client, - OperationContext context) throws Exception { + public Void preProcessResponse(CloudBlobContainer container, CloudBlobClient client, OperationContext context) + throws Exception { if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_ACCEPTED) { this.setNonExceptionedRetryableFailure(true); } @@ -829,18 +783,46 @@ else if (this.getResult().getStatusCode() == HttpURLConnection.HTTP_NOT_FOUND) { public String generateSharedAccessSignature(final SharedAccessBlobPolicy policy, final String groupPolicyIdentifier) throws InvalidKeyException, StorageException { + return this.generateSharedAccessSignature( + policy, groupPolicyIdentifier, null /* IP range */, null /* protocols */); + } + + /** + * Returns a shared access signature for the container. Note this does not contain the leading "?". + * + * @param policy + * An {@link SharedAccessBlobPolicy} object that represents the access policy for the shared access + * signature. + * @param groupPolicyIdentifier + * A String which represents the container-level access policy. + * @param ipRange + * A {@link IPRange} object containing the range of allowed IP addresses. + * @param protocols + * A {@link SharedAccessProtocols} representing the allowed Internet protocols. + * + * @return A String which represents a shared access signature for the container. + * + * @throws StorageException + * If a storage service error occurred. + * @throws InvalidKeyException + * If the key is invalid. + */ + public String generateSharedAccessSignature(final SharedAccessBlobPolicy policy, + final String groupPolicyIdentifier, final IPRange ipRange, final SharedAccessProtocols protocols) + throws InvalidKeyException, StorageException { if (!StorageCredentialsHelper.canCredentialsSignRequest(this.blobServiceClient.getCredentials())) { final String errorMessage = SR.CANNOT_CREATE_SAS_WITHOUT_ACCOUNT_KEY; throw new IllegalArgumentException(errorMessage); } - + final String resourceName = this.getSharedAccessCanonicalName(); - final String signature = SharedAccessSignatureHelper.generateSharedAccessSignatureHashForBlobAndFile(policy, - null /* SharedAccessBlobHeaders */, groupPolicyIdentifier, resourceName, this.blobServiceClient); + final String signature = SharedAccessSignatureHelper.generateSharedAccessSignatureHashForBlobAndFile( + policy, null /* SharedAccessBlobHeaders */, groupPolicyIdentifier, resourceName, + ipRange, protocols, this.blobServiceClient); final UriQueryBuilder builder = SharedAccessSignatureHelper.generateSharedAccessSignatureForBlobAndFile(policy, - null /* SharedAccessBlobHeaders */, groupPolicyIdentifier, "c", signature); + null /* SharedAccessBlobHeaders */, groupPolicyIdentifier, "c", ipRange, protocols, signature); return builder.toString(); } @@ -880,13 +862,7 @@ public CloudAppendBlob getAppendBlobReference(final String blobName) throws URIS */ public CloudAppendBlob getAppendBlobReference(final String blobName, final String snapshotID) throws URISyntaxException, StorageException { - Utility.assertNotNullOrEmpty("blobName", blobName); - - final StorageUri address = PathUtility.appendPathToUri(this.storageUri, blobName); - - final CloudAppendBlob retBlob = new CloudAppendBlob(address, snapshotID, this.blobServiceClient); - retBlob.setContainer(this); - return retBlob; + return new CloudAppendBlob(blobName, snapshotID, this); } /** @@ -922,16 +898,9 @@ public CloudBlockBlob getBlockBlobReference(final String blobName) throws URISyn * @throws URISyntaxException * If the resource URI is invalid. */ - @SuppressWarnings("deprecation") public CloudBlockBlob getBlockBlobReference(final String blobName, final String snapshotID) throws URISyntaxException, StorageException { - Utility.assertNotNullOrEmpty("blobName", blobName); - - final StorageUri address = PathUtility.appendPathToUri(this.storageUri, blobName); - - final CloudBlockBlob retBlob = new CloudBlockBlob(address, snapshotID, this.blobServiceClient); - retBlob.setContainer(this); - return retBlob; + return new CloudBlockBlob(blobName, snapshotID, this); } /** @@ -1019,16 +988,9 @@ public CloudPageBlob getPageBlobReference(final String blobName) throws URISynta * @throws URISyntaxException * If the resource URI is invalid. */ - @SuppressWarnings("deprecation") public CloudPageBlob getPageBlobReference(final String blobName, final String snapshotID) throws URISyntaxException, StorageException { - Utility.assertNotNullOrEmpty("blobName", blobName); - - final StorageUri address = PathUtility.appendPathToUri(this.storageUri, blobName); - - final CloudPageBlob retBlob = new CloudPageBlob(address, snapshotID, this.blobServiceClient); - retBlob.setContainer(this); - return retBlob; + return new CloudPageBlob(blobName, snapshotID, this); } /** @@ -1611,8 +1573,8 @@ private StorageRequest uploadMetadata public HttpURLConnection buildRequest(CloudBlobClient client, CloudBlobContainer container, OperationContext context) throws Exception { return BlobRequest.setContainerMetadata( - container.getTransformedAddress().getUri(this.getCurrentLocation()), options, context, - accessCondition); + container.getTransformedAddress().getUri(this.getCurrentLocation()), + options, context, accessCondition); } @Override diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudBlobDirectory.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudBlobDirectory.java index c147bbbc1..636126da9 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudBlobDirectory.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudBlobDirectory.java @@ -74,14 +74,7 @@ public final class CloudBlobDirectory implements ListBlobItem { */ protected CloudBlobDirectory(final StorageUri uri, final String prefix, final CloudBlobClient client, final CloudBlobContainer container) { - Utility.assertNotNull("uri", uri); - Utility.assertNotNull("client", client); - Utility.assertNotNull("container", container); - - this.blobServiceClient = client; - this.container = container; - this.prefix = prefix; - this.storageUri = uri; + this(uri, prefix, client, container, null); } /** @@ -146,14 +139,7 @@ public CloudAppendBlob getAppendBlobReference(final String blobName) throws URIS public CloudAppendBlob getAppendBlobReference(final String blobName, final String snapshotID) throws URISyntaxException, StorageException { Utility.assertNotNullOrEmpty("blobName", blobName); - - final StorageUri address = PathUtility.appendPathToUri(this.storageUri, blobName, - this.blobServiceClient.getDirectoryDelimiter()); - - final CloudAppendBlob retBlob = new CloudAppendBlob(address, snapshotID, this.blobServiceClient); - retBlob.setContainer(this.container); - - return retBlob; + return new CloudAppendBlob(this.getPrefix().concat(blobName), snapshotID, this.getContainer()); } /** @@ -189,17 +175,10 @@ public CloudBlockBlob getBlockBlobReference(final String blobName) throws URISyn * @throws URISyntaxException * If the resource URI is invalid. */ - @SuppressWarnings("deprecation") public CloudBlockBlob getBlockBlobReference(final String blobName, final String snapshotID) throws URISyntaxException, StorageException { Utility.assertNotNullOrEmpty("blobName", blobName); - - final StorageUri address = PathUtility.appendPathToUri(this.storageUri, blobName, - this.blobServiceClient.getDirectoryDelimiter()); - - final CloudBlockBlob retBlob = new CloudBlockBlob(address, snapshotID, this.blobServiceClient); - retBlob.setContainer(this.container); - return retBlob; + return new CloudBlockBlob(this.getPrefix().concat(blobName), snapshotID, this.getContainer()); } /** @@ -250,18 +229,10 @@ public CloudPageBlob getPageBlobReference(final String blobName) throws URISynta * @throws URISyntaxException * If the resource URI is invalid. */ - @SuppressWarnings("deprecation") public CloudPageBlob getPageBlobReference(final String blobName, final String snapshotID) throws URISyntaxException, StorageException { Utility.assertNotNullOrEmpty("blobName", blobName); - - final StorageUri address = PathUtility.appendPathToUri(this.storageUri, blobName, - this.blobServiceClient.getDirectoryDelimiter()); - - final CloudPageBlob retBlob = new CloudPageBlob(address, snapshotID, this.blobServiceClient); - retBlob.setContainer(this.container); - - return retBlob; + return new CloudPageBlob(this.getPrefix().concat(blobName), snapshotID, this.getContainer()); } /** diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudBlockBlob.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudBlockBlob.java index bcbb868d1..e53886cd5 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudBlockBlob.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudBlockBlob.java @@ -148,125 +148,23 @@ public CloudBlockBlob(final StorageUri blobAbsoluteUri, final String snapshotID, throws StorageException { super(BlobType.BLOCK_BLOB, blobAbsoluteUri, snapshotID, credentials); } - - /** - * Creates an instance of the CloudBlockBlob class using the specified absolute URI and storage service - * client. - * - * @param blobAbsoluteUri - * A java.net.URI object that represents the absolute URI to the blob. - * @param client - * A {@link CloudBlobClient} object that specifies the endpoint for the Blob service. - * - * @throws StorageException - * If a storage service error occurred. - * @deprecated as of 3.0.0. Please use {@link CloudBlockBlob#CloudBlockBlob(URI, StorageCredentials)} - */ - @Deprecated - public CloudBlockBlob(final URI blobAbsoluteUri, final CloudBlobClient client) throws StorageException { - this(new StorageUri(blobAbsoluteUri), client); - } - - /** - * Creates an instance of the CloudBlockBlob class using the specified absolute StorageUri and storage - * service client. - * - * @param blobAbsoluteUri - * A {@link StorageUri} object that represents the absolute URI to the blob. - * @param client - * A {@link CloudBlobClient} object that specifies the endpoint for the Blob service. - * - * @throws StorageException - * If a storage service error occurred. - * @deprecated as of 3.0.0. Please use {@link CloudBlockBlob#CloudBlockBlob(StorageUri, StorageCredentials)} - */ - @Deprecated - public CloudBlockBlob(final StorageUri blobAbsoluteUri, final CloudBlobClient client) throws StorageException { - super(BlobType.BLOCK_BLOB, blobAbsoluteUri, client); - } - - /** - * Creates an instance of the CloudBlockBlob class using the specified absolute URI, storage service - * client and container. - * - * @param blobAbsoluteUri - * A java.net.URI object that represents the absolute URI to the blob. - * @param client - * A {@link CloudBlobClient} object that specifies the endpoint for the Blob service. - * @param container - * A {@link CloudBlobContainer} object that represents the container to use for the blob. - * - * @throws StorageException - * If a storage service error occurred. - * @deprecated as of 3.0.0. Please use {@link CloudBlockBlob#CloudBlockBlob(URI, StorageCredentials)} - */ - @Deprecated - public CloudBlockBlob(final URI blobAbsoluteUri, final CloudBlobClient client, final CloudBlobContainer container) - throws StorageException { - this(new StorageUri(blobAbsoluteUri), client, container); - } - - /** - * Creates an instance of the CloudBlockBlob class using the specified absolute StorageUri, storage - * service client and container. - * - * @param blobAbsoluteUri - * A {@link StorageUri} object that represents the absolute URI to the blob. - * @param client - * A {@link CloudBlobClient} object that specifies the endpoint for the Blob service. - * @param container - * A {@link CloudBlobContainer} object that represents the container to use for the blob. - * - * @throws StorageException - * If a storage service error occurred. - * @deprecated as of 3.0.0. Please use {@link CloudBlockBlob#CloudBlockBlob(StorageUri, StorageCredentials)} - */ - @Deprecated - public CloudBlockBlob(final StorageUri blobAbsoluteUri, final CloudBlobClient client, - final CloudBlobContainer container) throws StorageException { - super(BlobType.BLOCK_BLOB, blobAbsoluteUri, client, container); - } - - /** - * Creates an instance of the CloudBlockBlob class using the specified absolute URI, snapshot ID, and - * storage service client. - * - * @param blobAbsoluteUri - * A java.net.URI object that represents the absolute URI to the blob. - * @param snapshotID - * A String that represents the snapshot version, if applicable. - * @param client - * A {@link CloudBlobClient} object that specifies the endpoint for the Blob service. - * - * @throws StorageException - * If a storage service error occurred. - * @deprecated as of 3.0.0. Please use {@link CloudBlockBlob#CloudBlockBlob(URI, String, StorageCredentials)} - */ - @Deprecated - public CloudBlockBlob(final URI blobAbsoluteUri, final String snapshotID, final CloudBlobClient client) - throws StorageException { - this(new StorageUri(blobAbsoluteUri), snapshotID, client); - } - + /** - * Creates an instance of the CloudBlockBlob class using the specified absolute StorageUri, snapshot - * ID, and storage service client. - * - * @param blobAbsoluteUri - * A {@link StorageUri} object that represents the absolute URI to the blob. + * Creates an instance of the CloudBlockBlob class using the specified type, name, snapshot ID, and + * container. + * + * @param blobName + * Name of the blob. * @param snapshotID * A String that represents the snapshot version, if applicable. - * @param client - * A {@link CloudBlobClient} object that specifies the endpoint for the Blob service. - * - * @throws StorageException - * If a storage service error occurred. - * @deprecated as of 3.0.0. Please use {@link CloudBlockBlob#CloudBlockBlob(StorageUri, String, StorageCredentials)} + * @param container + * The reference to the parent container. + * @throws URISyntaxException + * If the resource URI is invalid. */ - @Deprecated - public CloudBlockBlob(final StorageUri blobAbsoluteUri, final String snapshotID, final CloudBlobClient client) - throws StorageException { - super(BlobType.BLOCK_BLOB, blobAbsoluteUri, snapshotID, client); + protected CloudBlockBlob(String blobName, String snapshotID, CloudBlobContainer container) + throws URISyntaxException { + super(BlobType.BLOCK_BLOB, blobName, snapshotID, container); } /** diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudPageBlob.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudPageBlob.java index ddf859671..b435f650c 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudPageBlob.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudPageBlob.java @@ -145,123 +145,23 @@ public CloudPageBlob(final StorageUri blobAbsoluteUri, final String snapshotID, throws StorageException { super(BlobType.PAGE_BLOB, blobAbsoluteUri, snapshotID, credentials); } - - /** - * Creates an instance of the CloudPageBlob class using the specified URI and cloud blob client. - * - * @param blobAbsoluteUri - * A java.net.URI object which represents the absolute URI to the blob. - * @param client - * A {@link CloudBlobClient} object that specifies the endpoint for the Blob service. - * - * @throws StorageException - * If a storage service error occurred. - * @deprecated as of 3.0.0. Please use {@link CloudPageBlob#CloudPageBlob(URI, StorageCredentials)} - */ - @Deprecated - public CloudPageBlob(final URI blobAbsoluteUri, final CloudBlobClient client) throws StorageException { - this(new StorageUri(blobAbsoluteUri), client); - } - - /** - * Creates an instance of the CloudPageBlob class using the specified URI and cloud blob client. - * - * @param blobAbsoluteUri - * A {@link StorageUri} object which represents the absolute URI to the blob. - * @param client - * A {@link CloudBlobClient} object that specifies the endpoint for the Blob service. - * - * @throws StorageException - * If a storage service error occurred. - * @deprecated as of 3.0.0. Please use {@link CloudPageBlob#CloudPageBlob(StorageUri, StorageCredentials)} - */ - @Deprecated - public CloudPageBlob(final StorageUri blobAbsoluteUri, final CloudBlobClient client) throws StorageException { - super(BlobType.PAGE_BLOB, blobAbsoluteUri, client); - } - - /** - * Creates an instance of the CloudPageBlob class using the specified URI, cloud blob client, and cloud - * blob container. - * - * @param blobAbsoluteUri - * A java.net.URI object which represents the absolute URI to the blob. - * @param client - * A {@link CloudBlobClient} object that specifies the endpoint for the Blob service. - * @param container - * A {@link CloudBlobContainer} object which represents the container to use for the blob. - * - * @throws StorageException - * If a storage service error occurred. - * @deprecated as of 3.0.0. Please use {@link CloudPageBlob#CloudPageBlob(URI, StorageCredentials)} - */ - @Deprecated - public CloudPageBlob(final URI blobAbsoluteUri, final CloudBlobClient client, final CloudBlobContainer container) - throws StorageException { - this(new StorageUri(blobAbsoluteUri), client, container); - } - - /** - * Creates an instance of the CloudPageBlob class using the specified URI, cloud blob client, and cloud - * blob container. - * - * @param blobAbsoluteUri - * A {@link StorageUri} object which represents the absolute URI to the blob. - * @param client - * A {@link CloudBlobClient} object that specifies the endpoint for the Blob service. - * @param container - * A {@link CloudBlobContainer} object which represents the container to use for the blob. - * - * @throws StorageException - * If a storage service error occurred. - * @deprecated as of 3.0.0. Please use {@link CloudPageBlob#CloudPageBlob(StorageUri, StorageCredentials)} - */ - @Deprecated - public CloudPageBlob(final StorageUri blobAbsoluteUri, final CloudBlobClient client, - final CloudBlobContainer container) throws StorageException { - super(BlobType.PAGE_BLOB, blobAbsoluteUri, client, container); - } - - /** - * Creates an instance of the CloudPageBlob class using the specified URI, snapshot ID, and cloud blob - * client. - * - * @param blobAbsoluteUri - * A java.net.URI object which represents the absolute URI to the blob. - * @param snapshotID - * A String which represents the snapshot version, if applicable. - * @param client - * A {@link CloudBlobContainer} object which represents the container to use for the blob. - * - * @throws StorageException - * If a storage service error occurred. - * @deprecated as of 3.0.0. Please use {@link CloudPageBlob#CloudPageBlob(URI, String, StorageCredentials)} - */ - @Deprecated - public CloudPageBlob(final URI blobAbsoluteUri, final String snapshotID, final CloudBlobClient client) - throws StorageException { - this(new StorageUri(blobAbsoluteUri), snapshotID, client); - } - + /** - * Creates an instance of the CloudPageBlob class using the specified URI, snapshot ID, and cloud blob - * client. - * - * @param blobAbsoluteUri - * A {@link StorageUri} object which represents the absolute URI to the blob. + * Creates an instance of the CloudPageBlob class using the specified type, name, snapshot ID, and + * container. + * + * @param blobName + * Name of the blob. * @param snapshotID - * A String which represents the snapshot version, if applicable. - * @param client - * A {@link CloudBlobContainer} object which represents the container to use for the blob. - * - * @throws StorageException - * If a storage service error occurred. - * @deprecated as of 3.0.0. Please use {@link CloudPageBlob#CloudPageBlob(StorageUri, String, StorageCredentials)} + * A String that represents the snapshot version, if applicable. + * @param container + * The reference to the parent container. + * @throws URISyntaxException + * If the resource URI is invalid. */ - @Deprecated - public CloudPageBlob(final StorageUri blobAbsoluteUri, final String snapshotID, final CloudBlobClient client) - throws StorageException { - super(BlobType.PAGE_BLOB, blobAbsoluteUri, snapshotID, client); + protected CloudPageBlob(String blobName, String snapshotID, CloudBlobContainer container) + throws URISyntaxException { + super(BlobType.PAGE_BLOB, blobName, snapshotID, container); } /** diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/SharedAccessBlobPermissions.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/SharedAccessBlobPermissions.java index d9d46a05e..3b0088822 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/SharedAccessBlobPermissions.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/SharedAccessBlobPermissions.java @@ -23,13 +23,23 @@ public enum SharedAccessBlobPermissions { */ READ, + /** + * Specifies Add access granted. + */ + ADD, + + /** + * Specifies Create access granted. + */ + CREATE, + /** * Specifies Write access granted. */ WRITE, /** - * Specifies Delete access granted for blobs. + * Specifies Delete access granted. */ DELETE, diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/SharedAccessBlobPolicy.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/SharedAccessBlobPolicy.java index d1ecfa0c8..631daf43a 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/SharedAccessBlobPolicy.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/SharedAccessBlobPolicy.java @@ -18,7 +18,6 @@ import com.microsoft.azure.storage.Constants; import com.microsoft.azure.storage.SharedAccessPolicy; -import com.microsoft.azure.storage.blob.SharedAccessBlobPermissions; /** * Represents a shared access policy, which specifies the start time, expiry time, and permissions for a shared access @@ -29,7 +28,7 @@ public final class SharedAccessBlobPolicy extends SharedAccessPolicy { * The permissions for a shared access signature associated with this shared access policy. */ private EnumSet permissions; - + /** * Gets the permissions for a shared access signature associated with this shared access policy. * @@ -50,11 +49,11 @@ public EnumSet getPermissions() { public void setPermissions(final EnumSet permissions) { this.permissions = permissions; } - + /** * Converts this policy's permissions to a string. * - * @return A String that represents the shared access permissions in the "rwdl" format, + * @return A String that represents the shared access permissions in the "racwdl" format, * which is described at {@link #setPermissionsFromString(String)}. */ @Override @@ -63,13 +62,21 @@ public String permissionsToString() { return Constants.EMPTY_STRING; } - // The service supports a fixed order => rwdl + // The service supports a fixed order => racwdl final StringBuilder builder = new StringBuilder(); if (this.permissions.contains(SharedAccessBlobPermissions.READ)) { builder.append("r"); } + if (this.permissions.contains(SharedAccessBlobPermissions.ADD)) { + builder.append("a"); + } + + if (this.permissions.contains(SharedAccessBlobPermissions.CREATE)) { + builder.append("c"); + } + if (this.permissions.contains(SharedAccessBlobPermissions.WRITE)) { builder.append("w"); } @@ -90,36 +97,50 @@ public String permissionsToString() { * * @param value * A String that represents the shared access permissions. The string must contain one or - * more of the following values. Note they must be lowercase, and the order that they are specified must - * be in the order of "rwdl". + * more of the following values. Note they must all be lowercase. *

    - *
  • d: Delete access.
  • - *
  • l: List access.
  • *
  • r: Read access.
  • + *
  • a: Add access.
  • + *
  • c: Create access.
  • *
  • w: Write access.
  • + *
  • d: Delete access.
  • + *
  • l: List access.
  • *
*/ + @Override public void setPermissionsFromString(final String value) { - EnumSet initial = EnumSet.noneOf(SharedAccessBlobPermissions.class); + final EnumSet initial = EnumSet.noneOf(SharedAccessBlobPermissions.class); for (final char c : value.toCharArray()) { switch (c) { case 'r': initial.add(SharedAccessBlobPermissions.READ); break; + + case 'a': + initial.add(SharedAccessBlobPermissions.ADD); + break; + + case 'c': + initial.add(SharedAccessBlobPermissions.CREATE); + break; + case 'w': initial.add(SharedAccessBlobPermissions.WRITE); break; + case 'd': initial.add(SharedAccessBlobPermissions.DELETE); break; + case 'l': initial.add(SharedAccessBlobPermissions.LIST); break; + default: throw new IllegalArgumentException("value"); } } - + this.permissions = initial; } } \ No newline at end of file diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/core/BaseRequest.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/core/BaseRequest.java index db10a4bf3..32d15b55a 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/core/BaseRequest.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/core/BaseRequest.java @@ -19,21 +19,17 @@ import java.net.URI; import java.net.URISyntaxException; import java.net.URL; -import java.security.InvalidKeyException; import java.util.Map; import java.util.Map.Entry; import com.microsoft.azure.storage.Constants; import com.microsoft.azure.storage.OperationContext; import com.microsoft.azure.storage.RequestOptions; -import com.microsoft.azure.storage.StorageCredentialsAccountAndKey; import com.microsoft.azure.storage.StorageException; -import com.microsoft.azure.storage.StorageKey; /** * RESERVED FOR INTERNAL USE. The Base Request class for the protocol layer. */ -@SuppressWarnings("deprecation") public final class BaseRequest { private static final String METADATA = "metadata"; @@ -448,72 +444,6 @@ public static HttpURLConnection setServiceProperties(final URI uri, final Reques return retConnection; } - /** - * Signs the request appropriately to make it an authenticated request for Blob and Queue. - * - * @param request - * a HttpURLConnection for the operation. - * @param credentials - * the credentials to use for signing. - * @param contentLength - * the length of the content written to the output stream, -1 if unknown. - * @param opContext - * an object used to track the execution of the operation - * @throws InvalidKeyException - * if the credentials key is invalid. - * @throws StorageException - */ - public static void signRequestForBlobAndQueue(final HttpURLConnection request, - final StorageCredentialsAccountAndKey credentials, final Long contentLength, - final OperationContext opContext) throws InvalidKeyException, StorageException { - request.setRequestProperty(Constants.HeaderConstants.DATE, Utility.getGMTTime()); - final Canonicalizer canonicalizer = CanonicalizerFactory.getBlobQueueFileCanonicalizer(request); - - final String stringToSign = canonicalizer.canonicalize(request, credentials.getAccountName(), contentLength); - - final String computedBase64Signature = StorageKey.computeMacSha256(credentials.getCredentials().getKey(), - stringToSign); - - Logger.trace(opContext, LogConstants.SIGNING, stringToSign); - - request.setRequestProperty(Constants.HeaderConstants.AUTHORIZATION, - String.format("%s %s:%s", "SharedKey", credentials.getAccountName(), computedBase64Signature)); - } - - /** - * - * Signs the request appropriately to make it an authenticated request for Table. - * - * @param request - * a HttpURLConnection for the operation. - * @param credentials - * the credentials to use for signing. - * @param contentLength - * the length of the content written to the output stream, -1 if unknown. - * @param opContext - * an object used to track the execution of the operation - * @throws InvalidKeyException - * if the credentials key is invalid. - * @throws StorageException - */ - public static void signRequestForTableSharedKey(final HttpURLConnection request, - final StorageCredentialsAccountAndKey credentials, final Long contentLength, - final OperationContext opContext) throws InvalidKeyException, StorageException { - request.setRequestProperty(Constants.HeaderConstants.DATE, Utility.getGMTTime()); - - final Canonicalizer canonicalizer = CanonicalizerFactory.getTableCanonicalizer(request); - - final String stringToSign = canonicalizer.canonicalize(request, credentials.getAccountName(), contentLength); - - final String computedBase64Signature = StorageKey.computeMacSha256( - credentials.getCredentials().getKey(), stringToSign); - - Logger.trace(opContext, LogConstants.SIGNING, stringToSign); - - request.setRequestProperty(Constants.HeaderConstants.AUTHORIZATION, - String.format("%s %s:%s", "SharedKey", credentials.getAccountName(), computedBase64Signature)); - } - /** * A private default constructor. All methods of this class are static so no instances of it should ever be created. */ diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/core/Canonicalizer.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/core/Canonicalizer.java index f8f713d00..ed5ebbc61 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/core/Canonicalizer.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/core/Canonicalizer.java @@ -40,11 +40,6 @@ abstract class Canonicalizer { */ private static final int ExpectedBlobQueueCanonicalizedStringLength = 300; - /** - * The expected length for the canonicalized string when SharedKeyLite is used to sign requests. - */ - private static final int ExpectedBlobQueueLiteCanonicalizedStringLength = 250; - /** * The expected length for the canonicalized string when SharedKeyFull is used to sign table requests. */ @@ -184,60 +179,6 @@ protected static String canonicalizeHttpRequest(final java.net.URL address, fina return canonicalizedString.toString(); } - /** - * Constructs a canonicalized string from the request's headers that will be used to construct the signature string - * for signing a Blob or Queue service request under the Shared Key Lite authentication scheme. - * - * @param address - * the request URI - * @param accountName - * the account name associated with the request - * @param method - * the verb to be used for the HTTP request. - * @param contentType - * the content type of the HTTP request. - * @param contentLength - * the length of the content written to the outputstream in bytes, -1 if unknown - * @param date - * the date/time specification for the HTTP request - * @param conn - * the HttpURLConnection for the operation. - * @return A canonicalized string. - * @throws StorageException - */ - protected static String canonicalizeHttpRequestLite(final java.net.URL address, final String accountName, - final String method, final String contentType, final long contentLength, final String date, - final HttpURLConnection conn) throws StorageException { - // The first element should be the Method of the request. - // I.e. GET, POST, PUT, or HEAD. - // - final StringBuilder canonicalizedString = new StringBuilder(ExpectedBlobQueueLiteCanonicalizedStringLength); - canonicalizedString.append(conn.getRequestMethod()); - - // The second element should be the MD5 value. - // This is optional and may be empty. - final String httpContentMD5Value = Utility.getStandardHeaderValue(conn, Constants.HeaderConstants.CONTENT_MD5); - appendCanonicalizedElement(canonicalizedString, httpContentMD5Value); - - // The third element should be the content type. - appendCanonicalizedElement(canonicalizedString, contentType); - - // The fourth element should be the request date. - // See if there's an storage date header. - // If there's one, then don't use the date header. - - final String dateString = Utility.getStandardHeaderValue(conn, Constants.HeaderConstants.DATE); - // If x-ms-date header exists, Date should be empty string - appendCanonicalizedElement(canonicalizedString, dateString.equals(Constants.EMPTY_STRING) ? date - : Constants.EMPTY_STRING); - - addCanonicalizedHeaders(conn, canonicalizedString); - - appendCanonicalizedElement(canonicalizedString, getCanonicalizedResourceLite(address, accountName)); - - return canonicalizedString.toString(); - } - /** * Constructs a canonicalized string that will be used to construct the signature string * for signing a Table service request under the Shared Key authentication scheme. diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/table/ODataUtilities.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/core/JsonUtilities.java similarity index 84% rename from microsoft-azure-storage/src/com/microsoft/azure/storage/table/ODataUtilities.java rename to microsoft-azure-storage/src/com/microsoft/azure/storage/core/JsonUtilities.java index e5a3e3605..736a49097 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/table/ODataUtilities.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/core/JsonUtilities.java @@ -13,19 +13,18 @@ * limitations under the License. */ -package com.microsoft.azure.storage.table; +package com.microsoft.azure.storage.core; import java.io.IOException; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; -import com.microsoft.azure.storage.core.SR; /*** * RESERVED FOR INTERNAL USE. A class to hold utility methods for parsing OData payloads */ -final class ODataUtilities { +public final class JsonUtilities { /*** * Reserved for internal use. Asserts that the current name of the parser equals the expected value * @@ -34,7 +33,7 @@ final class ODataUtilities { * @param expectedValue * The expected current name of the parser's current token. */ - protected static void assertIsExpectedFieldName(final JsonParser parser, final String expectedValue) + public static void assertIsExpectedFieldName(final JsonParser parser, final String expectedValue) throws JsonParseException, IOException { final String actualValue = parser.getCurrentName(); if (expectedValue == null) { @@ -57,7 +56,7 @@ protected static void assertIsExpectedFieldName(final JsonParser parser, final S * @param parser * The {@link JsonParser} whose current token to check. */ - protected static void assertIsFieldNameJsonToken(final JsonParser parser) throws JsonParseException { + public static void assertIsFieldNameJsonToken(final JsonParser parser) throws JsonParseException { if (!(parser.getCurrentToken() == JsonToken.FIELD_NAME)) { throw new JsonParseException(SR.EXPECTED_A_FIELD_NAME, parser.getCurrentLocation()); } @@ -69,7 +68,7 @@ protected static void assertIsFieldNameJsonToken(final JsonParser parser) throws * @param parser * The {@link JsonParser} whose current token to check. */ - protected static void assertIsStartObjectJsonToken(final JsonParser parser) throws JsonParseException { + public static void assertIsStartObjectJsonToken(final JsonParser parser) throws JsonParseException { if (!(parser.getCurrentToken() == JsonToken.START_OBJECT)) { throw new JsonParseException(SR.EXPECTED_START_OBJECT, parser.getCurrentLocation()); } @@ -81,7 +80,7 @@ protected static void assertIsStartObjectJsonToken(final JsonParser parser) thro * @param parser * The {@link JsonParser} whose current token to check. */ - protected static void assertIsEndObjectJsonToken(final JsonParser parser) throws JsonParseException { + public static void assertIsEndObjectJsonToken(final JsonParser parser) throws JsonParseException { if (!(parser.getCurrentToken() == JsonToken.END_OBJECT)) { throw new JsonParseException(SR.EXPECTED_END_OBJECT, parser.getCurrentLocation()); } @@ -93,7 +92,7 @@ protected static void assertIsEndObjectJsonToken(final JsonParser parser) throws * @param parser * The {@link JsonParser} whose current token to check. */ - protected static void assertIsStartArrayJsonToken(final JsonParser parser) throws JsonParseException { + public static void assertIsStartArrayJsonToken(final JsonParser parser) throws JsonParseException { if (!(parser.getCurrentToken() == JsonToken.START_ARRAY)) { throw new JsonParseException(SR.EXPECTED_START_ARRAY, parser.getCurrentLocation()); } @@ -105,7 +104,7 @@ protected static void assertIsStartArrayJsonToken(final JsonParser parser) throw * @param parser * The {@link JsonParser} whose current token to check. */ - protected static void assertIsEndArrayJsonToken(final JsonParser parser) throws JsonParseException { + public static void assertIsEndArrayJsonToken(final JsonParser parser) throws JsonParseException { if (!(parser.getCurrentToken() == JsonToken.END_ARRAY)) { throw new JsonParseException(SR.EXPECTED_END_ARRAY, parser.getCurrentLocation()); } diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/core/PathUtility.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/core/PathUtility.java index 034492760..5b3113595 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/core/PathUtility.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/core/PathUtility.java @@ -382,21 +382,6 @@ public static StorageUri getShareURI(final StorageUri fileAddress, final boolean return shareUri; } - /** - * Get the queue name from address from the URI. - * - * @param resourceAddress - * The queue Uri. - * @param usePathStyleUris - * a value indicating if the address is a path style uri. - * @return container name from address from the URI. - * @throws IllegalArgumentException - */ - public static String getQueueNameFromUri(final URI resourceAddress, final boolean usePathStyleUris) { - return getResourceNameFromUri(resourceAddress, usePathStyleUris, - String.format("Invalid queue URI '%s'.", resourceAddress)); - } - /** * Get the service client address from a complete Uri. * @@ -433,25 +418,6 @@ public static String getServiceClientBaseAddress(final URI address, final boolea } } - /** - * Get the service client address from a complete Uri. - * - * @param address - * Complete address of the resource. - * @param usePathStyleUris - * a value indicating if the address is a path style uri. - * @return the service client address from a complete Uri. - * @throws StorageException - */ - public static StorageUri getServiceClientBaseAddress(final StorageUri addressUri) throws StorageException { - boolean usePathStyleUris = Utility.determinePathStyleFromUri(addressUri.getPrimaryUri()); - try { - return getServiceClientBaseAddress(addressUri, usePathStyleUris); - } catch (final URISyntaxException e) { - throw Utility.generateNewUnexpectedStorageException(e); - } - } - /** * Get the service client address from a complete Uri. * diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/core/SR.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/core/SR.java index 070e6b0e9..df78aa108 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/core/SR.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/core/SR.java @@ -20,6 +20,7 @@ */ public class SR { public static final String ACCOUNT_NAME_NULL_OR_EMPTY = "The account name is null or empty."; + public static final String ACCOUNT_NAME_MISMATCH = "The account name does not match the existing account name on the credentials."; public static final String APPEND_BLOB_MD5_NOT_POSSIBLE = "MD5 cannot be calculated for an existing append blob because it would require reading the existing data. Please disable StoreFileContentMD5."; public static final String ARGUMENT_NULL_OR_EMPTY = "The argument must not be null or an empty string. Argument name: %s."; public static final String ARGUMENT_OUT_OF_RANGE_ERROR = "The argument is out of range. Argument name: %s, Value passed: %s."; @@ -33,6 +34,7 @@ public class SR { public static final String CANNOT_CREATE_SAS_FOR_GIVEN_CREDENTIALS = "Cannot create Shared Access Signature as the credentials does not have account name information. Please check that the credentials provided support creating Shared Access Signature."; public static final String CANNOT_CREATE_SAS_FOR_SNAPSHOTS = "Cannot create Shared Access Signature via references to blob snapshots. Please perform the given operation on the root blob instead."; public static final String CANNOT_CREATE_SAS_WITHOUT_ACCOUNT_KEY = "Cannot create Shared Access Signature unless the Account Key credentials are used by the ServiceClient."; + public static final String CANNOT_TRANSFORM_NON_HTTPS_URI_WITH_HTTPS_ONLY_CREDENTIALS = "Cannot use HTTP with credentials that only support HTTPS."; public static final String CONTAINER = "container"; public static final String CONTENT_LENGTH_MISMATCH = "An incorrect number of bytes was read from the connection. The connection may have been closed."; public static final String CREATING_NETWORK_STREAM = "Creating a NetworkInputStream and expecting to read %s bytes."; @@ -49,6 +51,7 @@ public class SR { public static final String ETAG_INVALID_FOR_DELETE = "Delete requires a valid ETag (which may be the '*' wildcard)."; public static final String ETAG_INVALID_FOR_MERGE = "Merge requires a valid ETag (which may be the '*' wildcard)."; public static final String ETAG_INVALID_FOR_UPDATE = "Replace requires a valid ETag (which may be the '*' wildcard)."; + public static final String ENUM_COULD_NOT_BE_PARSED = "%s could not be parsed from '%s'."; public static final String EXCEPTION_THROWN_DURING_DESERIALIZATION = "The entity threw an exception during deserialization."; public static final String EXCEPTION_THROWN_DURING_SERIALIZATION = "The entity threw an exception during serialization."; public static final String EXPECTED_A_FIELD_NAME = "Expected a field name."; @@ -79,6 +82,7 @@ public class SR { public static final String INVALID_EDMTYPE_VALUE = "Invalid value '%s' for EdmType."; public static final String INVALID_FILE_LENGTH = "File length must be greater than 0 bytes."; public static final String INVALID_GEO_REPLICATION_STATUS = "Null or Invalid geo-replication status in response: %s."; + public static final String INVALID_IP_ADDRESS = "Error when parsing IPv4 address: IP address '%s' is invalid."; public static final String INVALID_KEY = "Storage Key is not a valid base64 encoded string."; public static final String INVALID_LISTING_DETAILS = "Invalid blob listing details specified."; public static final String INVALID_LOGGING_LEVEL = "Invalid logging operations specified."; @@ -122,7 +126,6 @@ public class SR { public static final String PARTITIONKEY_MISSING_FOR_UPDATE = "Replace requires a partition key."; public static final String PARTITIONKEY_MISSING_FOR_INSERT = "Insert requires a partition key."; public static final String PATH_STYLE_URI_MISSING_ACCOUNT_INFORMATION = "Missing account name information inside path style URI. Path style URIs should be of the form http:///"; - public static final String PERMISSIONS_COULD_NOT_BE_PARSED = "Permissions could not be parsed from '%s'."; public static final String PRIMARY_ONLY_COMMAND = "This operation can only be executed against the primary storage location."; public static final String PROPERTY_CANNOT_BE_SERIALIZED_AS_GIVEN_EDMTYPE = "Property %s with Edm Type %s cannot be de-serialized."; public static final String PRECONDITION_FAILURE_IGNORED = "Pre-condition failure on a retry is being ignored since the request should have succeeded in the first attempt."; diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/core/SharedAccessSignatureHelper.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/core/SharedAccessSignatureHelper.java index aac0eea20..af91d35a0 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/core/SharedAccessSignatureHelper.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/core/SharedAccessSignatureHelper.java @@ -21,10 +21,15 @@ import java.util.List; import java.util.Map.Entry; +import com.microsoft.azure.storage.CloudStorageAccount; import com.microsoft.azure.storage.Constants; +import com.microsoft.azure.storage.IPRange; import com.microsoft.azure.storage.ServiceClient; +import com.microsoft.azure.storage.SharedAccessAccountPolicy; import com.microsoft.azure.storage.SharedAccessHeaders; import com.microsoft.azure.storage.SharedAccessPolicy; +import com.microsoft.azure.storage.SharedAccessProtocols; +import com.microsoft.azure.storage.StorageCredentials; import com.microsoft.azure.storage.StorageCredentialsSharedAccessSignature; import com.microsoft.azure.storage.StorageException; import com.microsoft.azure.storage.StorageUri; @@ -36,61 +41,223 @@ */ public class SharedAccessSignatureHelper { /** - * Get the complete query builder for creating the Shared Access Signature query. + * Get the signature hash embedded inside the Shared Access Signature for a {@link CloudStorageAccount}. * * @param policy * The shared access policy to hash. + * @param ipRange + * An optional range of IP addresses. + * @param protocols + * An optional restriction of allowed protocols. + * @param signature + * The signature to use. + * + * @return The finished query builder + * @throws InvalidKeyException + * @throws StorageException + */ + public static UriQueryBuilder generateSharedAccessSignatureForAccount( + final SharedAccessAccountPolicy policy, final String signature) + throws StorageException { + + Utility.assertNotNull("policy", policy); + + Utility.assertNotNull("signature", signature); + + String permissions = null; + Date startTime = null; + Date expiryTime = null; + IPRange ipRange = null; + SharedAccessProtocols protocols = null; + String services = null; + String resourceTypes = null; + if (policy != null) { + permissions = policy.permissionsToString(); + startTime = policy.getSharedAccessStartTime(); + expiryTime = policy.getSharedAccessExpiryTime(); + ipRange = policy.getRange(); + protocols = policy.getProtocols(); + services = policy.servicesToString(); + resourceTypes = policy.resourceTypesToString(); + } + + final UriQueryBuilder builder = new UriQueryBuilder(); + + builder.add(Constants.QueryConstants.SIGNED_VERSION, Constants.HeaderConstants.TARGET_STORAGE_VERSION); + + addIfNotNullOrEmpty(builder, Constants.QueryConstants.SIGNED_SERVICE, services); + addIfNotNullOrEmpty(builder, Constants.QueryConstants.SIGNED_RESOURCE_TYPE, resourceTypes); + addIfNotNullOrEmpty(builder, Constants.QueryConstants.SIGNED_PERMISSIONS, permissions); + + final String startString = Utility.getUTCTimeOrEmpty(startTime); + addIfNotNullOrEmpty(builder, Constants.QueryConstants.SIGNED_START, startString); + + final String stopString = Utility.getUTCTimeOrEmpty(expiryTime); + addIfNotNullOrEmpty(builder, Constants.QueryConstants.SIGNED_EXPIRY, stopString); + + addIfNotNullOrEmpty(builder, Constants.QueryConstants.SIGNED_RESOURCE, resourceTypes); + addIfNotNullOrEmpty(builder, Constants.QueryConstants.SIGNED_IP, ipRange != null ? ipRange.toString() : null); + addIfNotNullOrEmpty( + builder, Constants.QueryConstants.SIGNED_PROTOCOLS, protocols != null ? protocols.toString() : null); + + addIfNotNullOrEmpty(builder, Constants.QueryConstants.SIGNATURE, signature); + + return builder; + } + + /** + * Get the complete query builder for creating the Shared Access Signature query. + * + * @param policy + * The shared access policy for the shared access signature. + * @param headers + * The optional header values to set for a blob or file accessed with this shared access signature. * @param groupPolicyIdentifier * An optional identifier for the policy. * @param resourceType * Either "b" for blobs, "c" for containers, "f" for files, or "s" for shares. + * @param ipRange + * The range of IP addresses for the shared access signature. + * @param protocols + * The Internet protocols for the shared access signature. * @param signature * The signature to use. + * * @return The finished query builder * @throws IllegalArgumentException * @throws StorageException */ public static UriQueryBuilder generateSharedAccessSignatureForBlobAndFile( - final SharedAccessPolicy policy, final SharedAccessHeaders headers, - final String groupPolicyIdentifier, final String resourceType, final String signature) + final SharedAccessPolicy policy, final SharedAccessHeaders headers, final String groupPolicyIdentifier, + final String resourceType, final IPRange ipRange, final SharedAccessProtocols protocols, final String signature) throws StorageException { + Utility.assertNotNullOrEmpty("resourceType", resourceType); return generateSharedAccessSignatureHelper(policy, null /* startPartitionKey */, null /* startRowKey */, - null /* endPartitionKey */, null /* endRowKey */, groupPolicyIdentifier, resourceType, - null /* tableName */, signature, headers); + null /* endPartitionKey */, null /* endRowKey */, groupPolicyIdentifier, resourceType, ipRange, + protocols, null /* tableName */, signature, headers); } /** * Get the complete query builder for creating the Shared Access Signature query. * * @param policy - * The shared access policy to hash. + * The shared access policy for the shared access signature. * @param groupPolicyIdentifier * An optional identifier for the policy. + * @param ipRange + * The range of IP addresses for the shared access signature. + * @param protocols + * The Internet protocols for the shared access signature. * @param signature * The signature to use. + * * @return The finished query builder * @throws IllegalArgumentException * @throws StorageException */ - public static UriQueryBuilder generateSharedAccessSignatureForQueue(final SharedAccessQueuePolicy policy, - final String groupPolicyIdentifier, final String signature) throws StorageException { + public static UriQueryBuilder generateSharedAccessSignatureForQueue( + final SharedAccessQueuePolicy policy, final String groupPolicyIdentifier, final IPRange ipRange, + final SharedAccessProtocols protocols, final String signature) + throws StorageException { + return generateSharedAccessSignatureHelper(policy, null /* startPartitionKey */, null /* startRowKey */, null /* endPartitionKey */, null /* endRowKey */, groupPolicyIdentifier, null /* resourceType */, - null /* tableName */, signature, null /* headers */); + ipRange, protocols, null /* tableName */, signature, null /* headers */); } /** * Get the complete query builder for creating the Shared Access Signature query. + * + * @param policy + * The shared access policy for the shared access signature. + * @param startPartitionKey + * An optional restriction of the beginning of the range of partition keys to include. + * @param startRowKey + * An optional restriction of the beginning of the range of row keys to include. + * @param endPartitionKey + * An optional restriction of the end of the range of partition keys to include. + * @param endRowKey + * An optional restriction of the end of the range of row keys to include. + * @param accessPolicyIdentifier + * An optional identifier for the policy. + * @param ipRange + * The range of IP addresses for the shared access signature. + * @param protocols + * The Internet protocols for the shared access signature. + * @param tableName + * The table name. + * @param signature + * The signature to use. + * + * @return The finished query builder + * @throws IllegalArgumentException + * @throws StorageException */ - public static UriQueryBuilder generateSharedAccessSignatureForTable(final SharedAccessTablePolicy policy, - final String startPartitionKey, final String startRowKey, final String endPartitionKey, - final String endRowKey, final String accessPolicyIdentifier, final String tableName, - final String signature) throws StorageException { + public static UriQueryBuilder generateSharedAccessSignatureForTable( + final SharedAccessTablePolicy policy, final String startPartitionKey, final String startRowKey, + final String endPartitionKey, final String endRowKey, final String accessPolicyIdentifier, + final IPRange ipRange, final SharedAccessProtocols protocols, final String tableName, final String signature) + throws StorageException { + Utility.assertNotNull("tableName", tableName); - return generateSharedAccessSignatureHelper(policy, startPartitionKey, startRowKey, endPartitionKey, endRowKey, - accessPolicyIdentifier, null /* resourceType */, tableName, signature, null /* headers */); + return generateSharedAccessSignatureHelper( + policy, startPartitionKey, startRowKey, endPartitionKey, endRowKey, accessPolicyIdentifier, + null /* resourceType */, ipRange, protocols, tableName, signature, null /* headers */); + } + + /** + * Get the signature hash embedded inside the Shared Access Signature for a {@link CloudStorageAccount}. + * + * @param accountName + * The name of the account to use for the SAS. + * @param policy + * The shared access policy to hash. + * @param ipRange + * An optional range of IP addresses. + * @param protocols + * An optional restriction of allowed protocols. + * @param creds + * The {@link StorageCredentials} associated with the object. + * + * @return The signature hash embedded inside the Shared Access Signature. + * @throws InvalidKeyException + * @throws StorageException + */ + public static String generateSharedAccessSignatureHashForAccount( + final String accountName, final SharedAccessAccountPolicy policy, final StorageCredentials creds) + throws InvalidKeyException, StorageException { + Utility.assertNotNullOrEmpty("resource", accountName); + Utility.assertNotNull("credentials", creds); + + String permissions = null; + Date startTime = null; + Date expiryTime = null; + IPRange ipRange = null; + SharedAccessProtocols protocols = null; + String services = null; + String resourceTypes = null; + + if (policy != null) { + permissions = policy.permissionsToString(); + startTime = policy.getSharedAccessStartTime(); + expiryTime = policy.getSharedAccessExpiryTime(); + ipRange = policy.getRange(); + protocols = policy.getProtocols(); + services = policy.servicesToString(); + resourceTypes = policy.resourceTypesToString(); + } + + + String stringToSign = String.format("%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n", + accountName, permissions == null ? Constants.EMPTY_STRING : permissions, services, resourceTypes, + Utility.getUTCTimeOrEmpty(startTime), Utility.getUTCTimeOrEmpty(expiryTime), + ipRange == null ? Constants.EMPTY_STRING : ipRange.toString(), + protocols == null ? Constants.EMPTY_STRING : protocols.toString(), + Constants.HeaderConstants.TARGET_STORAGE_VERSION); + + return generateSharedAccessSignatureHashHelper(stringToSign, creds); } /** @@ -104,18 +271,46 @@ public static UriQueryBuilder generateSharedAccessSignatureForTable(final Shared * An optional identifier for the policy. * @param resourceName * The resource name. + * @param ipRange + * The range of IP addresses to hash. + * @param protocols + * The Internet protocols to hash. * @param client * The ServiceClient associated with the object. - * + * * @return The signature hash embedded inside the Shared Access Signature. * @throws InvalidKeyException * @throws StorageException */ public static String generateSharedAccessSignatureHashForBlobAndFile(final SharedAccessPolicy policy, - final SharedAccessHeaders headers, final String accessPolicyIdentifier, final String resourceName, - final ServiceClient client) throws InvalidKeyException, StorageException { - return generateSharedAccessSignatureHashForBlobAndFile( - policy, resourceName, accessPolicyIdentifier, client, headers); + SharedAccessHeaders headers, final String accessPolicyIdentifier, final String resourceName, + final IPRange ipRange, final SharedAccessProtocols protocols, final ServiceClient client) + throws InvalidKeyException, StorageException { + + String stringToSign = generateSharedAccessSignatureStringToSign( + policy, resourceName, ipRange, protocols, accessPolicyIdentifier); + + String cacheControl = null; + String contentDisposition = null; + String contentEncoding = null; + String contentLanguage = null; + String contentType = null; + if (headers != null) { + cacheControl = headers.getCacheControl(); + contentDisposition = headers.getContentDisposition(); + contentEncoding = headers.getContentEncoding(); + contentLanguage = headers.getContentLanguage(); + contentType = headers.getContentType(); + } + + stringToSign = String.format("%s\n%s\n%s\n%s\n%s\n%s", stringToSign, + cacheControl == null ? Constants.EMPTY_STRING : cacheControl, + contentDisposition == null ? Constants.EMPTY_STRING : contentDisposition, + contentEncoding == null ? Constants.EMPTY_STRING : contentEncoding, + contentLanguage == null ? Constants.EMPTY_STRING : contentLanguage, + contentType == null ? Constants.EMPTY_STRING : contentType); + + return generateSharedAccessSignatureHashHelper(stringToSign, client.getCredentials()); } /** @@ -127,19 +322,27 @@ public static String generateSharedAccessSignatureHashForBlobAndFile(final Share * An optional identifier for the policy. * @param resourceName * The resource name. + * @param ipRange + * The range of IP addresses to hash. + * @param protocols + * The Internet protocols to hash. * @param client * The ServiceClient associated with the object. - * + * * @return The signature hash embedded inside the Shared Access Signature. * @throws InvalidKeyException * @throws StorageException */ - public static String generateSharedAccessSignatureHashForQueue(final SharedAccessQueuePolicy policy, - final String accessPolicyIdentifier, final String resourceName, final ServiceClient client) + public static String generateSharedAccessSignatureHashForQueue( + final SharedAccessQueuePolicy policy, final String accessPolicyIdentifier, final String resourceName, + final IPRange ipRange, final SharedAccessProtocols protocols, final ServiceClient client) throws InvalidKeyException, StorageException { - return generateSharedAccessSignatureHashForQueueAndTable( - policy, resourceName, accessPolicyIdentifier, false, null, null, null, null, client); + + final String stringToSign = generateSharedAccessSignatureStringToSign( + policy, resourceName, ipRange, protocols, accessPolicyIdentifier); + + return generateSharedAccessSignatureHashHelper(stringToSign, client.getCredentials()); } /** @@ -151,34 +354,54 @@ public static String generateSharedAccessSignatureHashForQueue(final SharedAcces * An optional identifier for the policy. * @param resourceName * The resource name. + * @param ipRange + * The range of IP addresses to hash. + * @param protocols + * The Internet protocols to hash. + * @param startPartitionKey + * An optional restriction of the beginning of the range of partition keys to hash. + * @param startRowKey + * An optional restriction of the beginning of the range of row keys to hash. + * @param endPartitionKey + * An optional restriction of the end of the range of partition keys to hash. + * @param endRowKey + * An optional restriction of the end of the range of row keys to hash. * @param client * The ServiceClient associated with the object. - * @param opContext - * An object used to track the execution of the operation + * * @return The signature hash embedded inside the Shared Access Signature. * @throws InvalidKeyException * @throws StorageException */ - public static String generateSharedAccessSignatureHashForTable(final SharedAccessTablePolicy policy, - final String accessPolicyIdentifier, final String resourceName, final String startPartitionKey, + public static String generateSharedAccessSignatureHashForTable( + final SharedAccessTablePolicy policy, final String accessPolicyIdentifier, final String resourceName, + final IPRange ipRange, final SharedAccessProtocols protocols, final String startPartitionKey, final String startRowKey, final String endPartitionKey, final String endRowKey, final ServiceClient client) throws InvalidKeyException, StorageException { - return generateSharedAccessSignatureHashForQueueAndTable(policy, resourceName, accessPolicyIdentifier, true, - startPartitionKey, startRowKey, endPartitionKey, endRowKey, client); + + String stringToSign = generateSharedAccessSignatureStringToSign( + policy, resourceName, ipRange, protocols, accessPolicyIdentifier); + + stringToSign = String.format("%s\n%s\n%s\n%s\n%s", stringToSign, + startPartitionKey == null ? Constants.EMPTY_STRING : startPartitionKey, + startRowKey == null ? Constants.EMPTY_STRING : startRowKey, + endPartitionKey == null ? Constants.EMPTY_STRING : endPartitionKey, + endRowKey == null ? Constants.EMPTY_STRING : endRowKey); + + return generateSharedAccessSignatureHashHelper(stringToSign, client.getCredentials()); } - + /** * Parses the query parameters and populates a StorageCredentialsSharedAccessSignature object if one is present. * * @param completeUri * A {@link StorageUri} object which represents the complete Uri. - * @return The StorageCredentialsSharedAccessSignature if one is present, otherwise null - * @throws IllegalArgumentException + * + * @return The StorageCredentialsSharedAccessSignature if one is present, otherwise null. * @throws StorageException * An exception representing any error which occurred during the operation. */ - public static StorageCredentialsSharedAccessSignature parseQuery(final StorageUri completeUri) - throws StorageException { + public static StorageCredentialsSharedAccessSignature parseQuery(final StorageUri completeUri) throws StorageException { final HashMap queryParameters = PathUtility.parseQueryString(completeUri.getQuery()); return parseQuery(queryParameters); } @@ -187,14 +410,15 @@ public static StorageCredentialsSharedAccessSignature parseQuery(final StorageUr * Parses the query parameters and populates a StorageCredentialsSharedAccessSignature object if one is present. * * @param queryParams - * The parameters to parse - * @return The StorageCredentialsSharedAccessSignature if one is present, otherwise null - * @throws IllegalArgumentException + * The parameters to parse. + * + * @return The StorageCredentialsSharedAccessSignature if one is present, otherwise null. * @throws StorageException * An exception representing any error which occurred during the operation. */ public static StorageCredentialsSharedAccessSignature parseQuery(final HashMap queryParams) throws StorageException { + boolean sasParameterFound = false; List removeList = new ArrayList(); for (final Entry entry : queryParams.entrySet()) { @@ -247,7 +471,9 @@ public static StorageCredentialsSharedAccessSignature parseQuery(final HashMapfalse */ public static boolean canCredentialsSignRequest(final StorageCredentials creds) { - if (creds.getClass().equals(StorageCredentialsAccountAndKey.class)) { - return true; - } - else { - return false; - } + return creds.getClass().equals(StorageCredentialsAccountAndKey.class); + } + + /** + * RESERVED, for internal use only. Gets a value indicating whether a + * client can be generated under the Shared Key or Shared Access Signature + * authentication schemes using the specified credentials. + * @return true if a client can be generated with these + * credentials; otherwise, false + */ + public static boolean canCredentialsGenerateClient(final StorageCredentials creds) { + return canCredentialsSignRequest(creds) || creds.getClass().equals(StorageCredentialsSharedAccessSignature.class); } /** @@ -56,35 +63,22 @@ public static boolean canCredentialsSignRequest(final StorageCredentials creds) * @throws InvalidKeyException * If the key is not a valid Base64-encoded string. */ - public static String computeHmac256(final StorageCredentials creds, final String value) throws InvalidKeyException { + public static synchronized String computeHmac256(final StorageCredentials creds, final String value) throws InvalidKeyException { if (creds.getClass().equals(StorageCredentialsAccountAndKey.class)) { - return StorageKey.computeMacSha256(((StorageCredentialsAccountAndKey) creds).getCredentials().getKey(), - value); + byte[] utf8Bytes = null; + try { + utf8Bytes = value.getBytes(Constants.UTF8_CHARSET); + } + catch (final UnsupportedEncodingException e) { + throw new IllegalArgumentException(e); + } + return Base64.encode(((StorageCredentialsAccountAndKey) creds).getHmac256().doFinal(utf8Bytes)); } else { return null; } } - /** - * Signs a request under the Shared Key authentication scheme. - * - * @param request - * An HttpURLConnection object that represents the request to sign. - * @param contentLength - * The length of the content written to the output stream. If unknown, specify -1. - * - * @throws InvalidKeyException - * If the given key is invalid. - * @throws StorageException - * If a storage service error occurred. - */ - public static void signBlobAndQueueRequest(final StorageCredentials creds, - final java.net.HttpURLConnection request, final long contentLength) throws InvalidKeyException, - StorageException { - signBlobQueueAndFileRequest(creds, request, contentLength, null); - } - /** * Signs a request using the specified operation context under the Shared Key authentication scheme. * @@ -108,29 +102,20 @@ public static void signBlobQueueAndFileRequest(final StorageCredentials creds, if (creds.getClass().equals(StorageCredentialsAccountAndKey.class)) { opContext = opContext == null ? new OperationContext() : opContext; - BaseRequest.signRequestForBlobAndQueue( - request, (StorageCredentialsAccountAndKey) creds, contentLength, opContext); - } - } + request.setRequestProperty(Constants.HeaderConstants.DATE, Utility.getGMTTime()); + final Canonicalizer canonicalizer = CanonicalizerFactory.getBlobQueueFileCanonicalizer(request); - /** - * Signs a request under the Shared Key authentication scheme. - * - * @param request - * An HttpURLConnection object that represents the request to sign. - * @param contentLength - * The length of the content written to the output stream. If unknown, specify -1. - * - * @throws InvalidKeyException - * If the given key is invalid. - * @throws StorageException - * If a storage service error occurred. - */ - public static void signTableRequest(final StorageCredentials creds, final java.net.HttpURLConnection request, - final long contentLength) throws InvalidKeyException, StorageException { - signTableRequest(creds, request, contentLength, null); - } + final String stringToSign = canonicalizer.canonicalize(request, creds.getAccountName(), contentLength); + + final String computedBase64Signature = StorageCredentialsHelper.computeHmac256(creds, stringToSign); + Logger.trace(opContext, LogConstants.SIGNING, stringToSign); + + request.setRequestProperty(Constants.HeaderConstants.AUTHORIZATION, + String.format("%s %s:%s", "SharedKey", creds.getAccountName(), computedBase64Signature)); + } + } + /** * Signs a request using the specified operation context under the Shared Key authentication scheme. * @@ -152,8 +137,18 @@ public static void signTableRequest(final StorageCredentials creds, final java.n final long contentLength, OperationContext opContext) throws InvalidKeyException, StorageException { if (creds.getClass().equals(StorageCredentialsAccountAndKey.class)) { opContext = opContext == null ? new OperationContext() : opContext; - BaseRequest.signRequestForTableSharedKey(request, - (StorageCredentialsAccountAndKey) creds, contentLength, opContext); + request.setRequestProperty(Constants.HeaderConstants.DATE, Utility.getGMTTime()); + + final Canonicalizer canonicalizer = CanonicalizerFactory.getTableCanonicalizer(request); + + final String stringToSign = canonicalizer.canonicalize(request, creds.getAccountName(), contentLength); + + final String computedBase64Signature = StorageCredentialsHelper.computeHmac256(creds, stringToSign); + + Logger.trace(opContext, LogConstants.SIGNING, stringToSign); + + request.setRequestProperty(Constants.HeaderConstants.AUTHORIZATION, + String.format("%s %s:%s", "SharedKey", creds.getAccountName(), computedBase64Signature)); } } diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/core/Utility.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/core/Utility.java index 5d8a28f27..857020da4 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/core/Utility.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/core/Utility.java @@ -54,10 +54,6 @@ import com.microsoft.azure.storage.RequestOptions; import com.microsoft.azure.storage.ResultContinuation; import com.microsoft.azure.storage.ResultContinuationType; -import com.microsoft.azure.storage.StorageCredentials; -import com.microsoft.azure.storage.StorageCredentialsAccountAndKey; -import com.microsoft.azure.storage.StorageCredentialsAnonymous; -import com.microsoft.azure.storage.StorageCredentialsSharedAccessSignature; import com.microsoft.azure.storage.StorageErrorCode; import com.microsoft.azure.storage.StorageErrorCodeStrings; import com.microsoft.azure.storage.StorageException; @@ -222,42 +218,6 @@ public static StreamMd5AndLength analyzeStream(final InputStream sourceStream, l return retVal; } - /** - * Returns a value that indicates whether the specified credentials are equal. - * - * @param thisCred - * An object derived from {@link StorageCredentials} that represents the first set of credentials being - * compared for equality. - * @param thatCred - * An object derived from StorageCredentials that represents the second set of credentials - * being compared for equality. - * - * @return true if the credentials are equal; otherwise, false. - */ - public static boolean areCredentialsEqual(final StorageCredentials thisCred, final StorageCredentials thatCred) { - if (thisCred == thatCred) { - return true; - } - - if (thisCred == null || thatCred == null || thisCred.getClass() != thatCred.getClass()) { - return false; - } - - if (thisCred instanceof StorageCredentialsAccountAndKey) { - return ((StorageCredentialsAccountAndKey) thisCred).toString(true).equals( - ((StorageCredentialsAccountAndKey) thatCred).toString(true)); - } - else if (thisCred instanceof StorageCredentialsSharedAccessSignature) { - return ((StorageCredentialsSharedAccessSignature) thisCred).getToken().equals( - ((StorageCredentialsSharedAccessSignature) thatCred).getToken()); - } - else if (thisCred instanceof StorageCredentialsAnonymous) { - return true; - } - - return thisCred.equals(thatCred); - } - /** * Asserts a continuation token is of the specified type. * @@ -488,24 +448,6 @@ public static StorageException generateNewUnexpectedStorageException(final Excep return exceptionRef; } - /** - * Returns a byte array that represents the data of a long value. - * - * @param value - * The value from which the byte array will be returned. - * - * @return A byte array that represents the data of the specified long value. - */ - public static byte[] getBytesFromLong(final long value) { - final byte[] tempArray = new byte[8]; - - for (int m = 0; m < 8; m++) { - tempArray[7 - m] = (byte) ((value >> (8 * m)) & 0xFF); - } - - return tempArray; - } - /** * Returns the current GMT date/time String using the RFC1123 pattern. * @@ -615,22 +557,6 @@ public static XMLStreamWriter createXMLStreamWriter(StringWriter outWriter) thro return xmlOutputFactory.createXMLStreamWriter(outWriter); } - /** - * Returns a XMLStreamWriter with the specified OutputStream and charset. - * - * @param outStream - * The OutputStream to use to create the XMLStreamWriter instance. - * @param charset - * The charset to use to create the XMLStreamWriter instance. - * @return A XMLStreamWriter instance - * - * @throws XMLStreamException - */ - public static XMLStreamWriter createXMLStreamWriter(OutputStream outStream, String charset) - throws XMLStreamException { - return xmlOutputFactory.createXMLStreamWriter(outStream, charset); - } - /** * Creates an instance of the IOException class using the specified exception. * diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFile.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFile.java index 19093a93b..c8b894c83 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFile.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFile.java @@ -36,7 +36,9 @@ import com.microsoft.azure.storage.AccessCondition; import com.microsoft.azure.storage.Constants; import com.microsoft.azure.storage.DoesServiceRequest; +import com.microsoft.azure.storage.IPRange; import com.microsoft.azure.storage.OperationContext; +import com.microsoft.azure.storage.SharedAccessProtocols; import com.microsoft.azure.storage.StorageCredentials; import com.microsoft.azure.storage.StorageCredentialsSharedAccessSignature; import com.microsoft.azure.storage.StorageErrorCode; @@ -65,7 +67,7 @@ /** * Represents a Microsoft Azure File. */ -public class CloudFile implements ListFileItem { +public final class CloudFile implements ListFileItem { /** * Holds the number of bytes to buffer when writing to a {@link FileOutputStream}. */ @@ -119,8 +121,10 @@ public class CloudFile implements ListFileItem { * * @throws StorageException * If a storage service error occurred. + * @throws URISyntaxException + * If the resource URI is invalid. */ - public CloudFile(final URI fileAbsoluteUri) throws StorageException { + public CloudFile(final URI fileAbsoluteUri) throws StorageException, URISyntaxException { this(new StorageUri(fileAbsoluteUri)); } @@ -132,9 +136,11 @@ public CloudFile(final URI fileAbsoluteUri) throws StorageException { * * @throws StorageException * If a storage service error occurred. + * @throws URISyntaxException + * If the resource URI is invalid. */ - public CloudFile(final StorageUri fileAbsoluteUri) throws StorageException { - this(fileAbsoluteUri, (StorageCredentials)null); + public CloudFile(final StorageUri fileAbsoluteUri) throws StorageException, URISyntaxException { + this(fileAbsoluteUri, null); } /** @@ -168,95 +174,6 @@ public CloudFile(final URI fileAbsoluteUri, final StorageCredentials credentials public CloudFile(final StorageUri fileAbsoluteUri, final StorageCredentials credentials) throws StorageException { this.parseQueryAndVerify(fileAbsoluteUri, credentials); } - - /** - * Creates an instance of the CloudFile class using the specified absolute URI and storage service - * client. - * - * @param fileAbsoluteUri - * A java.net.URI object that represents the absolute URI to the file. - * @param client - * A {@link CloudFileClient} object that specifies the endpoint for the file service. - * - * @throws StorageException - * If a storage service error occurred. - * @throws URISyntaxException - * @deprecated as of 3.0.0. Please use {@link CloudFile#CloudFile(URI, StorageCredentials)} - */ - @Deprecated - public CloudFile(final URI fileAbsoluteUri, final CloudFileClient client) throws StorageException, - URISyntaxException { - this(new StorageUri(fileAbsoluteUri), client); - } - - /** - * Creates an instance of the CloudFile class using the specified URI and storage service client. - * - * @param fileAbsoluteUri - * A {@link StorageUri} object that represents the absolute URI to the file. - * @param client - * A {@link CloudFileClient} object that specifies the endpoint for the file service. - * - * @throws StorageException - * If a storage service error occurred. - * @throws URISyntaxException - * @deprecated as of 3.0.0. Please use {@link CloudFile#CloudFile(StorageUri, StorageCredentials)} - */ - @Deprecated - public CloudFile(final StorageUri fileAbsoluteUri, final CloudFileClient client) throws StorageException, - URISyntaxException { - this.parseQueryAndVerify(fileAbsoluteUri, client == null ? null : client.getCredentials()); - - // Override the client set in parseQueryAndVerify to make sure request options are propagated. - if (client != null) { - this.fileServiceClient = client; - } - } - - /** - * Creates an instance of the CloudFile class using the specified absolute URI, storage service - * client and share. - * - * @param fileAbsoluteUri - * A java.net.URI object that represents the absolute URI to the file. - * @param client - * A {@link CloudFileClient} object that specifies the endpoint for the file service. - * @param share - * A {@link CloudFileShare} object that represents the share to use for the file. - * - * @throws StorageException - * If a storage service error occurred. - * @throws URISyntaxException - * @deprecated as of 3.0.0. Please use {@link CloudFile#CloudFile(URI, StorageCredentials)} - */ - @Deprecated - public CloudFile(final URI fileAbsoluteUri, final CloudFileClient client, final CloudFileShare share) - throws StorageException, URISyntaxException { - this(new StorageUri(fileAbsoluteUri), client, share); - } - - /** - * Creates an instance of the CloudFile class using the specified absolute StorageUri, storage - * service client and share. - * - * @param fileAbsoluteUri - * A {@link StorageUri} object that represents the absolute URI to the file. - * @param client - * A {@link CloudFileClient} object that specifies the endpoint for the file service. - * @param share - * A {@link CloudFileShare} object that represents the share to use for the file. - * - * @throws StorageException - * If a storage service error occurred. - * @throws URISyntaxException - * @deprecated as of 3.0.0. Please use {@link CloudFile#CloudFile(StorageUri, StorageCredentials)} - */ - @Deprecated - public CloudFile(final StorageUri fileAbsoluteUri, final CloudFileClient client, final CloudFileShare share) - throws StorageException, URISyntaxException { - this(fileAbsoluteUri, client); - this.share = share; - } /** * Creates an instance of the CloudFile class by copying values from another cloud file. @@ -281,6 +198,28 @@ public CloudFile(final CloudFile otherFile) { this.setStreamMinimumReadSizeInBytes(otherFile.getStreamMinimumReadSizeInBytes()); this.setStreamWriteSizeInBytes(otherFile.getStreamWriteSizeInBytes()); } + + /** + * Creates an instance of the CloudFile class using the specified address, share, + * and client. + * + * @param uri + * A {@link StorageUri} that represents the file directory's address. + * @param fileName + * A String that represents the name of the file. + * @param share + * A {@link CloudFileShare} object that represents the associated file share. + */ + protected CloudFile(final StorageUri uri, final String fileName, final CloudFileShare share) { + Utility.assertNotNull("uri", uri); + Utility.assertNotNull("fileName", fileName); + Utility.assertNotNull("share", share); + + this.name = fileName; + this.fileServiceClient = share.getServiceClient(); + this.share = share; + this.storageUri = uri; + } /** * Aborts an ongoing Azure File copy operation. @@ -1744,18 +1683,55 @@ public String generateSharedAccessSignature(final SharedAccessFilePolicy policy, * @throws StorageException * If a storage service error occurred. */ - public String generateSharedAccessSignature(final SharedAccessFilePolicy policy, - final SharedAccessFileHeaders headers, final String groupPolicyIdentifier) + public String generateSharedAccessSignature( + final SharedAccessFilePolicy policy, final SharedAccessFileHeaders headers, final String groupPolicyIdentifier) throws InvalidKeyException, StorageException { + + return this.generateSharedAccessSignature(policy, headers, groupPolicyIdentifier, + null /* IP range */, null /* protocols */); + } + + /** + * Returns a shared access signature for the file using the specified group policy identifier and + * shared access file headers. Note this does not contain the leading "?". + * + * @param policy + * A {@link SharedAccessFilePolicy} object that represents the access policy for the shared + * access signature. + * @param headers + * A {@link SharedAccessFileHeaders} object that represents the optional header values to + * set for a file accessed with this shared access signature. + * @param groupPolicyIdentifier + * A String that represents the share-level access policy. + * @param ipRange + * A {@link IPRange} object containing the range of allowed IP addresses. + * @param protocols + * A {@link SharedAccessProtocols} representing the allowed Internet protocols. + * + * @return A String that represents the shared access signature. + * + * @throws IllegalArgumentException + * If the credentials are invalid. + * @throws InvalidKeyException + * If the credentials are invalid. + * @throws StorageException + * If a storage service error occurred. + */ + public String generateSharedAccessSignature( + final SharedAccessFilePolicy policy, final SharedAccessFileHeaders headers, + final String groupPolicyIdentifier, final IPRange ipRange, final SharedAccessProtocols protocols) + throws InvalidKeyException, StorageException { + if (!StorageCredentialsHelper.canCredentialsSignRequest(this.fileServiceClient.getCredentials())) { throw new IllegalArgumentException(SR.CANNOT_CREATE_SAS_WITHOUT_ACCOUNT_KEY); } final String signature = SharedAccessSignatureHelper.generateSharedAccessSignatureHashForBlobAndFile( - policy, headers, groupPolicyIdentifier, this.getCanonicalName(), this.fileServiceClient); + policy, headers, groupPolicyIdentifier, this.getCanonicalName(), + ipRange, protocols, this.fileServiceClient); final UriQueryBuilder builder = SharedAccessSignatureHelper.generateSharedAccessSignatureForBlobAndFile( - policy, headers, groupPolicyIdentifier, "f", signature); + policy, headers, groupPolicyIdentifier, "f", ipRange, protocols, signature); return builder.toString(); } @@ -2753,13 +2729,12 @@ protected void updateLengthFromResponse(HttpURLConnection request) { * @throws URISyntaxException * If the resource URI is invalid. */ - @SuppressWarnings("deprecation") @Override public final CloudFileShare getShare() throws StorageException, URISyntaxException { if (this.share == null) { final StorageUri shareUri = PathUtility.getShareURI(this.getStorageUri(), this.fileServiceClient.isUsePathStyleUris()); - this.share = new CloudFileShare(shareUri, this.fileServiceClient); + this.share = new CloudFileShare(shareUri, this.fileServiceClient.getCredentials()); } return this.share; @@ -2793,7 +2768,6 @@ public final String getName() { * @throws URISyntaxException * If the resource URI is invalid. */ - @SuppressWarnings("deprecation") @Override public final CloudFileDirectory getParent() throws URISyntaxException, StorageException { if (this.parent == null) { @@ -2801,7 +2775,7 @@ public final CloudFileDirectory getParent() throws URISyntaxException, StorageEx if (parentName != null) { StorageUri parentURI = PathUtility.appendPathToUri(this.share.getStorageUri(), parentName); - this.parent = new CloudFileDirectory(parentURI, this.getServiceClient()); + this.parent = new CloudFileDirectory(parentURI, this.getServiceClient().getCredentials()); } } return this.parent; diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileClient.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileClient.java index 60ebcc78c..f5bac5295 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileClient.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileClient.java @@ -98,7 +98,6 @@ public CloudFileClient(StorageUri storageUri, StorageCredentials credentials) { * @see Naming and Referencing Shares, * Directories, Files, and Metadata */ - @SuppressWarnings("deprecation") public CloudFileShare getShareReference(final String shareName) throws URISyntaxException, StorageException { Utility.assertNotNullOrEmpty("shareName", shareName); return new CloudFileShare(shareName, this); diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileDirectory.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileDirectory.java index 16937dfde..9c10fe995 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileDirectory.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileDirectory.java @@ -140,45 +140,6 @@ public CloudFileDirectory(final StorageUri directoryAbsoluteUri, final StorageCr this.parseQueryAndVerify(directoryAbsoluteUri, credentials); } - /** - * Creates an instance of the CloudFileDirectory class using an absolute URI to the directory. - * - * @param directoryAbsoluteUri - * A {@link URI} that represents the file directory's address. - * @param client - * A {@link CloudFileClient} object that represents the associated service client. - * @throws StorageException - * @throws URISyntaxException - * @deprecated as of 3.0.0. Please use {@link CloudFileDirectory#CloudFileDirectory(URI, StorageCredentials)} - */ - @Deprecated - public CloudFileDirectory(final URI directoryAbsoluteUri, final CloudFileClient client) throws StorageException, - URISyntaxException { - this(new StorageUri(directoryAbsoluteUri), client); - } - - /** - * Creates an instance of the CloudFileDirectory class using an absolute URI to the directory. - * - * @param directoryAbsoluteUri - * A {@link StorageUri} that represents the file directory's address. - * @param client - * A {@link CloudFileClient} object that represents the associated service client. - * @throws StorageException - * @throws URISyntaxException - * @deprecated as of 3.0.0. Please use {@link CloudFileDirectory#CloudFileDirectory(StorageUri, StorageCredentials)} - */ - @Deprecated - public CloudFileDirectory(final StorageUri directoryAbsoluteUri, final CloudFileClient client) - throws StorageException, URISyntaxException { - this.parseQueryAndVerify(directoryAbsoluteUri, client == null ? null : client.getCredentials()); - - // Override the client set in parseQueryAndVerify to make sure request options are propagated. - if (client != null) { - this.fileServiceClient = client; - } - } - /** * Creates an instance of the CloudFileDirectory class using the specified address, share, * and client. @@ -935,13 +896,11 @@ public ResultSegment postProcessResponse(HttpURLConnection connect * @throws URISyntaxException * If the resource URI is invalid. */ - @SuppressWarnings("deprecation") public CloudFile getFileReference(final String fileName) throws URISyntaxException, StorageException { Utility.assertNotNullOrEmpty("fileName", fileName); StorageUri subdirectoryUri = PathUtility.appendPathToUri(this.storageUri, fileName); - - return new CloudFile(subdirectoryUri, this.fileServiceClient, this.getShare()); + return new CloudFile(subdirectoryUri, fileName, this.getShare()); } /** @@ -1036,7 +995,7 @@ public CloudFileDirectory getParent() throws URISyntaxException, StorageExceptio if (parentName != null) { StorageUri parentURI = PathUtility.appendPathToUri(this.getShare().getStorageUri(), parentName); - this.parent = new CloudFileDirectory(parentURI, this.getServiceClient()); + this.parent = new CloudFileDirectory(parentURI, this.getServiceClient().getCredentials()); } } return this.parent; diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileShare.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileShare.java index 18abe6bad..ac17d9186 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileShare.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileShare.java @@ -31,9 +31,11 @@ import com.microsoft.azure.storage.AccessCondition; import com.microsoft.azure.storage.Constants; import com.microsoft.azure.storage.DoesServiceRequest; +import com.microsoft.azure.storage.IPRange; import com.microsoft.azure.storage.OperationContext; import com.microsoft.azure.storage.SharedAccessPolicyHandler; import com.microsoft.azure.storage.SharedAccessPolicySerializer; +import com.microsoft.azure.storage.SharedAccessProtocols; import com.microsoft.azure.storage.StorageCredentials; import com.microsoft.azure.storage.StorageCredentialsSharedAccessSignature; import com.microsoft.azure.storage.StorageErrorCodeStrings; @@ -101,10 +103,8 @@ public final class CloudFileShare { * * @see Naming and Referencing Shares, * Directories, Files, and Metadata - * @deprecated as of 3.0.0. Please use {@link CloudFileClient#getShareReference(String)} */ - @Deprecated - public CloudFileShare(final String shareName, final CloudFileClient client) throws URISyntaxException, + protected CloudFileShare(final String shareName, final CloudFileClient client) throws URISyntaxException, StorageException { Utility.assertNotNull("client", client); Utility.assertNotNull("shareName", shareName); @@ -170,50 +170,6 @@ public CloudFileShare(final StorageUri storageUri, final StorageCredentials cred this.parseQueryAndVerify(storageUri, credentials); } - /** - * Creates an instance of the CloudFileShare class using the specified URI and client. - * - * @param uri - * A java.net.URI object that represents the absolute URI of the share. - * @param client - * A {@link CloudFileClient} object that represents the associated service client, and that specifies the - * endpoint for the File service. - * - * @throws StorageException - * If a storage service error occurred. - * @throws URISyntaxException - * @deprecated as of 3.0.0. Please use {@link CloudFileShare#CloudFileShare(URI, StorageCredentials)} - */ - @Deprecated - public CloudFileShare(final URI uri, final CloudFileClient client) throws StorageException, URISyntaxException { - this(new StorageUri(uri), client); - } - - /** - * Creates an instance of the CloudFileShare class using the specified URI and client. - * - * @param storageUri - * A {@link StorageUri} object which represents the absolute URI of the share. - * @param client - * A {@link CloudFileClient} object that represents the associated service client, and that specifies the - * endpoint for the File service. - * - * @throws StorageException - * If a storage service error occurred. - * @throws URISyntaxException - * @deprecated as of 3.0.0. Please use {@link CloudFileShare#CloudFileShare(StorageUri, StorageCredentials)} - */ - @Deprecated - public CloudFileShare(final StorageUri storageUri, final CloudFileClient client) throws StorageException, - URISyntaxException { - this.parseQueryAndVerify(storageUri, client == null ? null : client.getCredentials()); - - // Override the client set in parseQueryAndVerify to make sure request options are propagated. - if (client != null) { - this.fileServiceClient = client; - } - } - /** * Creates the share. * @@ -393,15 +349,17 @@ public void delete(AccessCondition accessCondition, FileRequestOptions options, options.getRetryPolicyFactory(), opContext); } - private StorageRequest deleteImpl(final AccessCondition accessCondition, - final FileRequestOptions options) { - final StorageRequest putRequest = new StorageRequest( - options, this.getStorageUri()) { + private StorageRequest deleteImpl( + final AccessCondition accessCondition, final FileRequestOptions options) { + + final StorageRequest putRequest = + new StorageRequest(options, this.getStorageUri()) { + @Override - public HttpURLConnection buildRequest(CloudFileClient client, CloudFileShare share, OperationContext context) - throws Exception { - return FileRequest - .deleteShare(share.getStorageUri().getPrimaryUri(), options, context, accessCondition); + public HttpURLConnection buildRequest( + CloudFileClient client, CloudFileShare share, OperationContext context) throws Exception { + return FileRequest.deleteShare( + share.getTransformedAddress().getPrimaryUri(), options, context, accessCondition); } @Override @@ -883,6 +841,35 @@ private void updatePropertiesFromResponse(HttpURLConnection request) { */ public String generateSharedAccessSignature(final SharedAccessFilePolicy policy, final String groupPolicyIdentifier) throws InvalidKeyException, StorageException { + + return this.generateSharedAccessSignature(policy, groupPolicyIdentifier, null /* IP range */, null /* protocols */); + } + + /** + * Returns a shared access signature for the share. Note this does not contain the leading "?". + * + * @param policy + * An {@link SharedAccessFilePolicy} object that represents the access policy for the + * shared access signature. + * @param groupPolicyIdentifier + * A String which represents the share-level access policy. + * @param ipRange + * A {@link IPRange} object containing the range of allowed IP addresses. + * @param protocols + * A {@link SharedAccessProtocols} representing the allowed Internet protocols. + * + * @return A String which represents a shared access signature for the share. + * + * @throws StorageException + * If a storage service error occurred. + * @throws InvalidKeyException + * If the key is invalid. + */ + public String generateSharedAccessSignature( + final SharedAccessFilePolicy policy, final String groupPolicyIdentifier, final IPRange ipRange, + final SharedAccessProtocols protocols) + throws InvalidKeyException, StorageException { + if (!StorageCredentialsHelper.canCredentialsSignRequest(this.fileServiceClient.getCredentials())) { final String errorMessage = SR.CANNOT_CREATE_SAS_WITHOUT_ACCOUNT_KEY; throw new IllegalArgumentException(errorMessage); @@ -890,11 +877,12 @@ public String generateSharedAccessSignature(final SharedAccessFilePolicy policy, final String resourceName = this.getSharedAccessCanonicalName(); - final String signature = SharedAccessSignatureHelper.generateSharedAccessSignatureHashForBlobAndFile(policy, - null /* SharedAccessHeaders */, groupPolicyIdentifier, resourceName, this.fileServiceClient); + final String signature = SharedAccessSignatureHelper.generateSharedAccessSignatureHashForBlobAndFile( + policy, null /* SharedAccessHeaders */, groupPolicyIdentifier, resourceName, + ipRange, protocols, this.fileServiceClient); - final UriQueryBuilder builder = SharedAccessSignatureHelper.generateSharedAccessSignatureForBlobAndFile(policy, - null /* SharedAccessHeaders */, groupPolicyIdentifier, "s", signature); + final UriQueryBuilder builder = SharedAccessSignatureHelper.generateSharedAccessSignatureForBlobAndFile( + policy, null /* SharedAccessHeaders */, groupPolicyIdentifier, "s", ipRange, protocols, signature); return builder.toString(); } @@ -1187,9 +1175,8 @@ public Void preProcessResponse(CloudFileShare share, CloudFileClient client, * @throws StorageException * @throws URISyntaxException */ - @SuppressWarnings("deprecation") public CloudFileDirectory getRootDirectoryReference() throws StorageException, URISyntaxException { - return new CloudFileDirectory(this.storageUri, this.fileServiceClient); + return new CloudFileDirectory(this.storageUri, "", this); } /** diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/FileServiceProperties.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/FileServiceProperties.java index 1bd712670..0e0fe1fe9 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/FileServiceProperties.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/FileServiceProperties.java @@ -15,6 +15,7 @@ package com.microsoft.azure.storage.file; import com.microsoft.azure.storage.CorsProperties; +import com.microsoft.azure.storage.MetricsProperties; import com.microsoft.azure.storage.ServiceProperties; /** @@ -37,11 +38,9 @@ public FileServiceProperties() { * The ServiceProperties to use */ FileServiceProperties(ServiceProperties properties) { - serviceProperties = properties; - serviceProperties.setHourMetrics(null); - serviceProperties.setMinuteMetrics(null); - serviceProperties.setLogging(null); - serviceProperties.setDefaultServiceVersion(null); + this.serviceProperties = properties; + this.serviceProperties.setLogging(null); + this.serviceProperties.setDefaultServiceVersion(null); } /** @@ -50,7 +49,7 @@ public FileServiceProperties() { * @return A {@link CorsProperties} object which represents the CORS properties. */ public CorsProperties getCors() { - return serviceProperties.getCors(); + return this.serviceProperties.getCors(); } /** @@ -60,15 +59,53 @@ public CorsProperties getCors() { * A {@link CorsProperties} object which represents the CORS properties. */ public void setCors(CorsProperties cors) { - serviceProperties.setCors(cors); + this.serviceProperties.setCors(cors); } + /** + * Gets the hour metrics properties. + * + * @return A {@link MetricsProperties} object which represents the hour metrics properties. + */ + public MetricsProperties getHourMetrics() { + return this.serviceProperties.getHourMetrics(); + } + + /** + * Sets the hour metrics properties. + * + * @param metrics + * A {@link MetricsProperties} object which represents the hour metrics properties. + */ + public void setHourMetrics(final MetricsProperties metrics) { + this.serviceProperties.setHourMetrics(metrics); + } + + /** + * Gets the minute metrics properties. + * + * @return A {@link MetricsProperties} object which represents the minute metrics properties. + */ + public MetricsProperties getMinuteMetrics() { + return this.serviceProperties.getMinuteMetrics(); + } + + /** + * Sets the minute metrics properties. + * + * @param metrics + * A {@link MetricsProperties} object which represents the minute metrics properties. + */ + public void setMinuteMetrics(final MetricsProperties metrics) { + this.serviceProperties.setMinuteMetrics(metrics); + } + /** * Gets the ServiceProperties for use by the service. * * @return The ServiceProperties */ ServiceProperties getServiceProperties() { - return serviceProperties; + return this.serviceProperties; } -} +} \ No newline at end of file diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/SharedAccessFilePermissions.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/SharedAccessFilePermissions.java index 414349b38..3c8d14707 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/SharedAccessFilePermissions.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/SharedAccessFilePermissions.java @@ -23,13 +23,18 @@ public enum SharedAccessFilePermissions { */ READ, + /** + * Specifies Create access granted. + */ + CREATE, + /** * Specifies Write access granted. */ WRITE, /** - * Specifies Delete access granted for files. + * Specifies Delete access granted. */ DELETE, diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/SharedAccessFilePolicy.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/SharedAccessFilePolicy.java index 0e0468da9..e1995f3f4 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/SharedAccessFilePolicy.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/SharedAccessFilePolicy.java @@ -53,7 +53,7 @@ public void setPermissions(final EnumSet permission /** * Converts this policy's permissions to a string. * - * @return A String that represents the shared access permissions in the "rwdl" format, + * @return A String that represents the shared access permissions in the "rcwdl" format, * which is described at {@link #setPermissionsFromString(String)}. */ @Override @@ -62,13 +62,17 @@ public String permissionsToString() { return Constants.EMPTY_STRING; } - // The service supports a fixed order => rwdl + // The service supports a fixed order => rcwdl final StringBuilder builder = new StringBuilder(); if (this.permissions.contains(SharedAccessFilePermissions.READ)) { builder.append("r"); } + if (this.permissions.contains(SharedAccessFilePermissions.CREATE)) { + builder.append("c"); + } + if (this.permissions.contains(SharedAccessFilePermissions.WRITE)) { builder.append("w"); } @@ -89,13 +93,13 @@ public String permissionsToString() { * * @param value * A String that represents the shared access permissions. The string must contain one or - * more of the following values. Note they must be lowercase, and the order that they are specified must - * be in the order of "rwdl". + * more of the following values. Note they must all be lowercase. *
    - *
  • d: Delete access.
  • - *
  • l: List access.
  • *
  • r: Read access.
  • + *
  • c: Create access.
  • *
  • w: Write access.
  • + *
  • d: Delete access.
  • + *
  • l: List access.
  • *
*/ public void setPermissionsFromString(final String value) { @@ -105,6 +109,9 @@ public void setPermissionsFromString(final String value) { case 'r': initial.add(SharedAccessFilePermissions.READ); break; + case 'c': + initial.add(SharedAccessFilePermissions.CREATE); + break; case 'w': initial.add(SharedAccessFilePermissions.WRITE); break; diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/queue/CloudQueue.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/queue/CloudQueue.java index 561615413..75c65d5a7 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/queue/CloudQueue.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/queue/CloudQueue.java @@ -30,9 +30,11 @@ import com.microsoft.azure.storage.Constants; import com.microsoft.azure.storage.DoesServiceRequest; +import com.microsoft.azure.storage.IPRange; import com.microsoft.azure.storage.OperationContext; import com.microsoft.azure.storage.SharedAccessPolicyHandler; import com.microsoft.azure.storage.SharedAccessPolicySerializer; +import com.microsoft.azure.storage.SharedAccessProtocols; import com.microsoft.azure.storage.StorageCredentials; import com.microsoft.azure.storage.StorageCredentialsSharedAccessSignature; import com.microsoft.azure.storage.StorageErrorCodeStrings; @@ -184,10 +186,8 @@ public CloudQueue(final StorageUri uri, final StorageCredentials credentials) th * @throws StorageException * If a storage service error occurred. * @see Naming Queues and Metadata - * @deprecated as of 3.0.0. Please use {@link CloudQueueClient#getQueueReference(String)} */ - @Deprecated - public CloudQueue(final String queueName, final CloudQueueClient client) throws URISyntaxException, + protected CloudQueue(final String queueName, final CloudQueueClient client) throws URISyntaxException, StorageException { Utility.assertNotNull("client", client); Utility.assertNotNull("queueName", queueName); @@ -198,53 +198,6 @@ public CloudQueue(final String queueName, final CloudQueueClient client) throws this.shouldEncodeMessage = true; } - /** - * Creates an instance of the CloudQueue class using the specified queue URI and client. If the - * URI contains a SAS token, the service client must be null. - * - * @param uri - * A java.net.URI object that represents the absolute URI of the queue. - * @param client - * A {@link CloudQueueClient} object that represents the associated service client, and that specifies - * the endpoint for the Queue service. - * - * @throws StorageException - * If a storage service error occurred. - * @throws URISyntaxException - * If the resource URI is invalid. - * @deprecated as of 3.0.0. Please use {@link CloudQueue#CloudQueue(URI, StorageCredentials)} - */ - @Deprecated - public CloudQueue(final URI uri, final CloudQueueClient client) throws URISyntaxException, StorageException { - this(new StorageUri(uri, null), client); - } - - /** - * Creates an instance of the CloudQueue class using the specified queue URI and client. - * - * @param uri - * A StorageUri object that represents the absolute URI of the queue. - * @param client - * A {@link CloudQueueClient} object that represents the associated service client, and that specifies - * the endpoint for the Queue service. - * - * @throws StorageException - * If a storage service error occurred. - * @throws URISyntaxException - * If the resource URI is invalid. - * @deprecated as of 3.0.0. Please use {@link CloudQueue#CloudQueue(StorageUri, StorageCredentials)} - */ - @Deprecated - public CloudQueue(final StorageUri uri, final CloudQueueClient client) throws URISyntaxException, StorageException { - this.shouldEncodeMessage = true; - this.parseQueryAndVerify(uri, client == null ? null : client.getCredentials()); - - // Override the client set in parseQueryAndVerify to make sure request options are propagated. - if (client != null) { - this.queueServiceClient = client; - } - } - /** * Adds a message to the back of the queue. * @@ -1762,7 +1715,9 @@ public QueuePermissions postProcessResponse(HttpURLConnection connection, CloudQ * The access policy for the shared access signature. * @param groupPolicyIdentifier * A queue-level access policy. + * * @return A shared access signature for the queue. + * * @throws InvalidKeyException * If an invalid key was passed. * @throws StorageException @@ -1773,6 +1728,35 @@ public QueuePermissions postProcessResponse(HttpURLConnection connection, CloudQ public String generateSharedAccessSignature(final SharedAccessQueuePolicy policy, final String groupPolicyIdentifier) throws InvalidKeyException, StorageException { + return this.generateSharedAccessSignature(policy, groupPolicyIdentifier, null /* IP range */, null /* protocols */); + } + + /** + * Returns a shared access signature for the queue. + * + * @param policy + * The access policy for the shared access signature. + * @param groupPolicyIdentifier + * A queue-level access policy. + * @param ipRange + * A {@link IPRange} object containing the range of allowed IP addresses. + * @param protocols + * A {@link SharedAccessProtocols} representing the allowed Internet protocols. + * + * @return A shared access signature for the queue. + * + * @throws InvalidKeyException + * If an invalid key was passed. + * @throws StorageException + * If a storage service error occurred. + * @throws IllegalArgumentException + * If an unexpected value is passed. + */ + public String generateSharedAccessSignature( + final SharedAccessQueuePolicy policy, final String groupPolicyIdentifier, final IPRange ipRange, + final SharedAccessProtocols protocols) + throws InvalidKeyException, StorageException { + if (!StorageCredentialsHelper.canCredentialsSignRequest(this.queueServiceClient.getCredentials())) { final String errorMessage = SR.CANNOT_CREATE_SAS_WITHOUT_ACCOUNT_KEY; throw new IllegalArgumentException(errorMessage); @@ -1780,11 +1764,11 @@ public String generateSharedAccessSignature(final SharedAccessQueuePolicy policy final String resourceName = this.getSharedAccessCanonicalName(); - final String signature = SharedAccessSignatureHelper.generateSharedAccessSignatureHashForQueue(policy, - groupPolicyIdentifier, resourceName, this.queueServiceClient); + final String signature = SharedAccessSignatureHelper.generateSharedAccessSignatureHashForQueue( + policy, groupPolicyIdentifier, resourceName, ipRange, protocols, this.queueServiceClient); - final UriQueryBuilder builder = SharedAccessSignatureHelper.generateSharedAccessSignatureForQueue(policy, - groupPolicyIdentifier, signature); + final UriQueryBuilder builder = SharedAccessSignatureHelper.generateSharedAccessSignatureForQueue( + policy, groupPolicyIdentifier, ipRange, protocols, signature); return builder.toString(); } diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/queue/CloudQueueClient.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/queue/CloudQueueClient.java index 893fe4b18..7b6212788 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/queue/CloudQueueClient.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/queue/CloudQueueClient.java @@ -99,7 +99,6 @@ public CloudQueueClient(final StorageUri baseUri, final StorageCredentials crede * If a storage service error occurred. * @see Naming Queues and Metadata */ - @SuppressWarnings("deprecation") public CloudQueue getQueueReference(final String queueName) throws URISyntaxException, StorageException { return new CloudQueue(queueName, this); } @@ -277,8 +276,9 @@ public HttpURLConnection buildRequest(CloudQueueClient client, Void parentObject throws Exception { listingContext.setMarker(segmentedRequest.getToken() != null ? segmentedRequest.getToken() .getNextMarker() : null); - return QueueRequest.list(client.getStorageUri().getUri(this.getCurrentLocation()), options, context, - listingContext, detailsIncluded); + return QueueRequest.list( + credentials.transformUri(client.getStorageUri().getUri(this.getCurrentLocation())), + options, context, listingContext, detailsIncluded); } @Override diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/table/CloudTable.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/table/CloudTable.java index ca9b493fc..3611960e7 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/table/CloudTable.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/table/CloudTable.java @@ -29,11 +29,13 @@ import com.microsoft.azure.storage.Constants; import com.microsoft.azure.storage.DoesServiceRequest; +import com.microsoft.azure.storage.IPRange; import com.microsoft.azure.storage.OperationContext; import com.microsoft.azure.storage.ResultContinuation; import com.microsoft.azure.storage.ResultSegment; import com.microsoft.azure.storage.SharedAccessPolicyHandler; import com.microsoft.azure.storage.SharedAccessPolicySerializer; +import com.microsoft.azure.storage.SharedAccessProtocols; import com.microsoft.azure.storage.StorageCredentials; import com.microsoft.azure.storage.StorageCredentialsSharedAccessSignature; import com.microsoft.azure.storage.StorageErrorCodeStrings; @@ -182,10 +184,8 @@ public CloudTable(final StorageUri uri, final StorageCredentials credentials) th * If a storage service error occurred. * @see Understanding the Table Service Data * Model - * @deprecated as of 3.0.0. Please use {@link CloudTableClient#getTableReference(String)} */ - @Deprecated - public CloudTable(final String tableName, final CloudTableClient client) throws URISyntaxException, + protected CloudTable(final String tableName, final CloudTableClient client) throws URISyntaxException, StorageException { Utility.assertNotNull("client", client); Utility.assertNotNull("tableName", tableName); @@ -195,51 +195,6 @@ public CloudTable(final String tableName, final CloudTableClient client) throws this.tableServiceClient = client; } - /** - * Creates an instance of the CloudTable class using the specified table URI and client. - * - * @param uri - * A java.net.URI object that represents the absolute URI of the table. - * @param client - * A {@link CloudTableClient} object that represents the associated service client, and that specifies - * the endpoint for the Table service. - * - * @throws StorageException - * If a storage service error occurred. - * @throws URISyntaxException - * If the resource URI is invalid. - * @deprecated as of 3.0.0. Please use {@link CloudTable#CloudTable(URI, StorageCredentials)} - */ - @Deprecated - public CloudTable(final URI uri, final CloudTableClient client) throws URISyntaxException, StorageException { - this(new StorageUri(uri, null), client); - } - - /** - * Creates an instance of the CloudTable class using the specified table StorageUri and client. - * - * @param uri - * A {@link StorageUri} object that represents the absolute StorageUri of the table. - * @param client - * A {@link CloudTableClient} object that represents the associated service client, and that specifies - * the endpoint for the Table service. - * - * @throws StorageException - * If a storage service error occurred. - * @throws URISyntaxException - * If the resource URI is invalid. - * @deprecated as of 3.0.0. Please use {@link CloudTable#CloudTable(StorageUri, StorageCredentials)} - */ - @Deprecated - public CloudTable(final StorageUri uri, final CloudTableClient client) throws URISyntaxException, StorageException { - this.parseQueryAndVerify(uri, client == null ? null : client.getCredentials()); - - // Override the client set in parseQueryAndVerify to make sure request options are propagated. - if (client != null) { - this.tableServiceClient = client; - } - } - /** * Creates the table in the storage service with default request options. *

@@ -1175,6 +1130,46 @@ public StorageExtendedErrorInformation parseErrorDetails() { public String generateSharedAccessSignature(final SharedAccessTablePolicy policy, final String accessPolicyIdentifier, final String startPartitionKey, final String startRowKey, final String endPartitionKey, final String endRowKey) throws InvalidKeyException, StorageException { + + return generateSharedAccessSignature(policy, accessPolicyIdentifier, + startPartitionKey, startRowKey, endPartitionKey, endRowKey, null /* IP Range */, null /* Protocols */); + } + + /** + * Creates a shared access signature for the table. + * + * @param policy + * A {@link SharedAccessTablePolicy} object which represents the access policy for the shared access + * signature. + * @param accessPolicyIdentifier + * A String which represents a table-level access policy. + * @param startPartitionKey + * A String which represents the starting partition key. + * @param startRowKey + * A String which represents the starting row key. + * @param endPartitionKey + * A String which represents the ending partition key. + * @param endRowKey + * A String which represents the ending end key. + * @param ipRange + * A {@link IPRange} object containing the range of allowed IP addresses. + * @param protocols + * A {@link SharedAccessProtocols} representing the allowed Internet protocols. + * + * @return A String containing the shared access signature for the table. + * + * @throws InvalidKeyException + * If an invalid key was passed. + * @throws StorageException + * If a storage service error occurred. + * @throws IllegalArgumentException + * If an unexpected value is passed. + */ + public String generateSharedAccessSignature( + final SharedAccessTablePolicy policy, final String accessPolicyIdentifier, final String startPartitionKey, + final String startRowKey, final String endPartitionKey, final String endRowKey, final IPRange ipRange, + final SharedAccessProtocols protocols) + throws InvalidKeyException, StorageException { if (!StorageCredentialsHelper.canCredentialsSignRequest(this.tableServiceClient.getCredentials())) { throw new IllegalArgumentException(SR.CANNOT_CREATE_SAS_WITHOUT_ACCOUNT_KEY); @@ -1182,13 +1177,13 @@ public String generateSharedAccessSignature(final SharedAccessTablePolicy policy final String resourceName = this.getSharedAccessCanonicalName(); - final String signature = SharedAccessSignatureHelper.generateSharedAccessSignatureHashForTable(policy, - accessPolicyIdentifier, resourceName, startPartitionKey, startRowKey, endPartitionKey, endRowKey, - this.tableServiceClient); + final String signature = SharedAccessSignatureHelper.generateSharedAccessSignatureHashForTable( + policy, accessPolicyIdentifier, resourceName, ipRange, protocols, + startPartitionKey, startRowKey, endPartitionKey, endRowKey, this.tableServiceClient); - final UriQueryBuilder builder = SharedAccessSignatureHelper.generateSharedAccessSignatureForTable(policy, - startPartitionKey, startRowKey, endPartitionKey, endRowKey, accessPolicyIdentifier, this.name, - signature); + final UriQueryBuilder builder = SharedAccessSignatureHelper.generateSharedAccessSignatureForTable( + policy, startPartitionKey, startRowKey, endPartitionKey, endRowKey, accessPolicyIdentifier, + ipRange, protocols, this.name, signature); return builder.toString(); } diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/table/CloudTableClient.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/table/CloudTableClient.java index 20d1ed9a4..d2db48afb 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/table/CloudTableClient.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/table/CloudTableClient.java @@ -35,8 +35,6 @@ import com.microsoft.azure.storage.StorageException; import com.microsoft.azure.storage.StorageExtendedErrorInformation; import com.microsoft.azure.storage.StorageUri; -import com.microsoft.azure.storage.blob.BlobRequestOptions; -import com.microsoft.azure.storage.blob.CloudBlobClient; import com.microsoft.azure.storage.core.ExecutionEngine; import com.microsoft.azure.storage.core.LazySegmentedIterable; import com.microsoft.azure.storage.core.SR; @@ -132,9 +130,7 @@ public CloudTableClient(final StorageUri baseUri, StorageCredentials credentials * @see Understanding the Table Service Data * Model */ - @SuppressWarnings("deprecation") public CloudTable getTableReference(final String tableName) throws URISyntaxException, StorageException { - Utility.assertNotNullOrEmpty("tableName", tableName); return new CloudTable(tableName, this); } diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/table/DeserializationHelper.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/table/DeserializationHelper.java deleted file mode 100644 index 74630f8b5..000000000 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/table/DeserializationHelper.java +++ /dev/null @@ -1,216 +0,0 @@ -/** - * Copyright Microsoft Corporation - * - * Licensed 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 com.microsoft.azure.storage.table; - -import java.io.InputStream; -import java.io.Reader; - -import javax.xml.stream.XMLInputFactory; -import javax.xml.stream.XMLStreamConstants; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.XMLStreamReader; - -import com.microsoft.azure.storage.Constants; - -/** - * RESERVED FOR INTERNAL USE. An internal helper class used to parse objects from responses. - */ -final class DeserializationHelper { - // see use below for more explanation - private final static String ENTITY_EXPANSION_EXCEPTION_MESSAGE = "(.|\n)*Message: JAXP00010001: The parser has encountered more than \"\\d+\" entity expansions in this document; this is the limit imposed by the JDK\\."; - - private static XMLInputFactory xmlif; - - static { - setupXMLInputFactory(); - } - - /** - * Sets this class's XMLInputFactory instance to a new factory instance and sets the appropriate - * properties on it. This is not synchronized so multiple threads executing this will cause multiple factories to be - * created, but this is okay as it does not matter for the reader which factory creates it as they are functionally - * identical. - */ - public static void setupXMLInputFactory() { - final XMLInputFactory xmlif = XMLInputFactory.newInstance(); - xmlif.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, Boolean.TRUE); - xmlif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE); - // set the IS_COALESCING property to true , if application desires to - // get whole text data as one event. - xmlif.setProperty(XMLInputFactory.IS_COALESCING, Boolean.TRUE); - DeserializationHelper.xmlif = xmlif; - } - - /** - * Tests whether the provided exception is an entity expansion exception. - * - * Some versions of Java cause an exception to be incorrectly thrown when more XMLStreamReaders have been - * created by a single factory than the entity expansion limit property (default 64000). The default form of this - * exception is: "ParseError at [row,col]:[1,1]\n - * Message: JAXP00010001: The parser has encountered more than - * \"64000\" entity expansions in this document; this is the limit imposed by the JDK." - * - * @param e - * the XMLStreamException to test - * @return - * true if entity expansion exception, false otherwise - */ - public static boolean isEntityExpansionLimitException(XMLStreamException e) { - return e.getMessage() != null && e.getMessage().matches(ENTITY_EXPANSION_EXCEPTION_MESSAGE); - } - - /** - * Creates an XML stream reader from the specified input stream. - * - * Some versions of Java cause an exception to be incorrectly thrown when more XMLStreamReaders have been - * created by a single factory than the entity expansion limit property (default 64000). This model catches - * that exception, creates a new factory with the appropriate settings, and then retries the reader creation. - * - * @param reader - * An InputStreamReader object that represents the input reader to use as the source. - * - * @return A java.xml.stream.XMLStreamReader object that represents the XML stream reader created from - * the specified input stream. - * - * @throws XMLStreamException - * If the XML stream reader could not be created. - * @see https://bugs.openjdk.java.net/browse/JDK-8028111 - */ - public static XMLStreamReader createXMLStreamReaderFromReader(final Reader reader) throws XMLStreamException { - while (true) { - try { - return xmlif.createXMLStreamReader(reader); - } - catch (XMLStreamException e) { - if (isEntityExpansionLimitException(e)) { - setupXMLInputFactory(); - } - else { - throw e; - } - } - } - } - - /** - * Creates an XML stream reader from the specified input stream. - * - * Some versions of Java cause an exception to be incorrectly thrown when more XMLStreamReaders have been - * created by a single factory than the entity expansion limit property (default 64000). This model catches - * that exception, creates a new factory with the appropriate settings, and then retries the reader creation. - * - * @param streamRef - * An InputStream object that represents the input stream to use as the source. - * - * @return A java.xml.stream.XMLStreamReader object that represents the XML stream reader created from - * the specified input stream. - * - * @throws XMLStreamException - * If the XML stream reader could not be created. - * - * @see https://bugs.openjdk.java.net/browse/JDK-8028111 - */ - public static XMLStreamReader createXMLStreamReaderFromStream(final InputStream streamRef) - throws XMLStreamException { - while (true) { - try { - return xmlif.createXMLStreamReader(streamRef, Constants.UTF8_CHARSET); - } - catch (XMLStreamException e) { - if (isEntityExpansionLimitException(e)) { - setupXMLInputFactory(); - } - else { - throw e; - } - } - } - } - - /** - * Reads character data for the specified XML element from an XML stream reader. This method will read start events, - * characters, and end events from a stream. - * - * @param xmlr - * An XMLStreamReader object that represents the source XML stream reader. - * - * @param elementName - * A String that represents XML element name. - * - * @return A String that represents the character data for the specified element. - * - * @throws XMLStreamException - * If an XML stream failure occurs. - */ - public static String readElementFromXMLReader(final XMLStreamReader xmlr, final String elementName) - throws XMLStreamException { - return readElementFromXMLReader(xmlr, elementName, true); - } - - /** - * Reads character data for the specified XML element from an XML stream reader. This method will read start events, - * characters, and end events from a stream. - * - * @param xmlr - * An XMLStreamReader object that represents the source XML stream reader. - * - * @param elementName - * A String that represents XML element name. - * @param returnNullOnEmpty - * If true, returns null when a empty string is read, otherwise EmptyString ("") is returned. - * - * @return A String that represents the character data for the specified element. - * - * @throws XMLStreamException - * If an XML stream failure occurs. - */ - public static String readElementFromXMLReader(final XMLStreamReader xmlr, final String elementName, - boolean returnNullOnEmpty) throws XMLStreamException { - xmlr.require(XMLStreamConstants.START_ELEMENT, null, elementName); - int eventType = xmlr.next(); - final StringBuilder retVal = new StringBuilder(); - - if (eventType == XMLStreamConstants.CHARACTERS) { - // This do while is in case the XMLStreamReader does not have - // the IS_COALESCING property set - // to true which may result in text being read in multiple events - // If we ensure all xmlreaders have this property we can optimize - // the StringBuilder and while loop - // away - do { - retVal.append(xmlr.getText()); - eventType = xmlr.next(); - - } while (eventType == XMLStreamConstants.CHARACTERS); - } - - xmlr.require(XMLStreamConstants.END_ELEMENT, null, elementName); - if (retVal.length() == 0) { - return returnNullOnEmpty ? null : Constants.EMPTY_STRING; - } - else { - return retVal.toString(); - } - } - - /** - * Private Default Ctor - */ - private DeserializationHelper() { - // No op - } -} diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/table/DynamicTableEntity.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/table/DynamicTableEntity.java index 6af9ddb75..451d78ec7 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/table/DynamicTableEntity.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/table/DynamicTableEntity.java @@ -15,7 +15,6 @@ package com.microsoft.azure.storage.table; -import java.util.Date; import java.util.HashMap; import com.microsoft.azure.storage.OperationContext; @@ -91,36 +90,8 @@ public DynamicTableEntity(String partitionKey, String rowKey, final HashMap properties) { - this(partitionKey, rowKey, null /* timestamp */, etag, properties); - } - - /** - * Initializes a new instance of the {@link DynamicTableEntity} class with the specified partition key and row key. - * - * @param partitionKey - * The partition key of the {@link DynamicTableEntity} to be initialized. - * @param rowKey - * A String which represents the row key of the {@link DynamicTableEntity} to be initialized. - * @param etag - * A String which represents the ETag of the {@link DynamicTableEntity} to be initialized. - * @param timestamp - * A java.util.Date object which represents the timestamp of the {@link DynamicTableEntity} to be initialized. - * @param properties - * A java.util.HashMap containing a map of String property names to - * {@link EntityProperty} data typed values to store in the new {@link DynamicTableEntity}. - * @deprecated as of 3.0.0. The timestamp property is read-only, set by the service only. Please use - * {@link DynamicTableEntity#DynamicTableEntity(String, String, String, HashMap)} instead. - */ - @Deprecated - public DynamicTableEntity(String partitionKey, String rowKey, Date timestamp, String etag, - final HashMap properties) { super(partitionKey, rowKey); - // only set if timestamp is not null; otherwise default to new Date() - if (timestamp != null) { - this.timeStamp = timestamp; - } - this.etag = etag; this.setProperties(properties); } diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/table/MimeHelper.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/table/MimeHelper.java index 4b609a86f..014398982 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/table/MimeHelper.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/table/MimeHelper.java @@ -26,8 +26,6 @@ import java.net.URISyntaxException; import java.util.ArrayList; -import javax.xml.stream.XMLStreamException; - import com.microsoft.azure.storage.Constants; import com.microsoft.azure.storage.OperationContext; import com.microsoft.azure.storage.StorageErrorCodeStrings; @@ -125,13 +123,11 @@ static ArrayList readBatchResponseStream(final InputStream inStream, f * if an invalid URI is used. * @throws StorageException * if an error occurs accessing the Storage service. - * @throws XMLStreamException - * if an error occurs accessing the stream. */ static void writeBatchToStream(final OutputStream outStream, final TableRequestOptions options, final String tableName, final URI baseUri, final TableBatchOperation batch, final String batchID, final String changeSet, final OperationContext opContext) throws IOException, URISyntaxException, - StorageException, XMLStreamException { + StorageException { final OutputStreamWriter outWriter = new OutputStreamWriter(outStream, Constants.UTF8_CHARSET); MimePart mimePart; @@ -207,7 +203,7 @@ static void writeBatchToStream(final OutputStream outStream, final TableRequestO if (op.getOperationType() != TableOperationType.DELETE) { mimePart.headers.put(Constants.HeaderConstants.CONTENT_TYPE, - generateContentTypeHeaderValue(options.getTablePayloadFormat())); + TableConstants.HeaderConstants.JSON_CONTENT_TYPE); mimePart.payload = writeStringForOperation(op, options.getTablePayloadFormat(), opContext); mimePart.headers.put(Constants.HeaderConstants.CONTENT_LENGTH, Integer.toString(mimePart.payload.getBytes(Constants.UTF8_CHARSET).length)); @@ -440,7 +436,7 @@ private static void writeMIMEContentType(final OutputStreamWriter outWriter, fin /** * Reserved for internal use. Generates a String containing the entity associated with an operation in - * AtomPub format. + * Json format. * * @param operation * A {@link TableOperation} containing the entity to write to the returned String. @@ -448,15 +444,13 @@ private static void writeMIMEContentType(final OutputStreamWriter outWriter, fin * An {@link OperationContext} object for tracking the current operation. Specify null to * safely ignore operation context. * @return - * A String containing the entity associated with the operation in AtomPub format + * A String containing the entity associated with the operation in Json format * @throws StorageException * if a Storage error occurs. - * @throws XMLStreamException - * if an error occurs creating or writing to the output string. * @throws IOException */ private static String writeStringForOperation(final TableOperation operation, TablePayloadFormat format, - final OperationContext opContext) throws StorageException, XMLStreamException, IOException { + final OperationContext opContext) throws StorageException, IOException { Utility.assertNotNull("entity", operation.getEntity()); final StringWriter outWriter = new StringWriter(); @@ -466,12 +460,8 @@ private static String writeStringForOperation(final TableOperation operation, Ta return outWriter.toString(); } - @SuppressWarnings("deprecation") private static String generateAcceptHeaderValue(TablePayloadFormat payloadFormat) { - if (payloadFormat == TablePayloadFormat.AtomPub) { - return TableConstants.HeaderConstants.ATOM_ACCEPT_TYPE; - } - else if (payloadFormat == TablePayloadFormat.JsonFullMetadata) { + if (payloadFormat == TablePayloadFormat.JsonFullMetadata) { return TableConstants.HeaderConstants.JSON_FULL_METADATA_ACCEPT_TYPE; } else if (payloadFormat == TablePayloadFormat.Json) { @@ -482,16 +472,6 @@ else if (payloadFormat == TablePayloadFormat.Json) { } } - @SuppressWarnings("deprecation") - private static String generateContentTypeHeaderValue(TablePayloadFormat payloadFormat) { - if (payloadFormat == TablePayloadFormat.AtomPub) { - return TableConstants.HeaderConstants.ATOM_CONTENT_TYPE; - } - else { - return TableConstants.HeaderConstants.JSON_CONTENT_TYPE; - } - } - /** * Reserved for internal use. A static factory method that generates a {@link StorageException} for invalid MIME * responses. diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/table/ODataConstants.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/table/ODataConstants.java index 17e710bae..28e3ba89a 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/table/ODataConstants.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/table/ODataConstants.java @@ -20,131 +20,11 @@ */ final class ODataConstants { - /** - * The String representation of the Atom namespace. - */ - public static final String ATOM_NS = "http://www.w3.org/2005/Atom"; - - /** - * The String representation of the OData Data namespace. - */ - public static final String DATA_SERVICES_NS = "http://schemas.microsoft.com/ado/2007/08/dataservices"; - - /** - * The String representation of the OData Metadata namespace. - */ - public static final String DATA_SERVICES_METADATA_NS = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"; - - /** - * The String representation of the Atom namespace in brackets. - */ - public static final String BRACKETED_ATOM_NS = "{" + ATOM_NS + "}"; // default - - /** - * The String representation of the OData Data namespace in brackets. - */ - public static final String BRACKETED_DATA_SERVICES_NS = "{" + DATA_SERVICES_NS + "}"; // d: - - /** - * The String representation of the OData Metadata namespace in brackets. - */ - public static final String BRACKETED_DATA_SERVICES_METADATA_NS = "{" + DATA_SERVICES_METADATA_NS + "}"; // m: - - /** - * The String representation of the Atom Entry feed element name. - */ - public static final String FEED = "feed"; - - /** - * The String representation of the Atom Entry title element name. - */ - public static final String TITLE = "title"; - - /** - * The String representation of the Atom Entry id element name. - */ - public static final String ID = "id"; - - /** - * The String representation of the Atom Entry updated element name. - */ - public static final String UPDATED = "updated"; - - /** - * The String representation of the Atom Entry link element name. - */ - public static final String LINK = "link"; - - /** - * The String representation of the Atom Entry author element name. - */ - public static final String AUTHOR = "author"; - - /** - * The String representation of the Atom Entry name element name. - */ - public static final String NAME = "name"; - - /** - * The String representation of the Atom Entry entry element name. - */ - public static final String ENTRY = "entry"; - - /** - * The String representation of the Atom Entry category element name. - */ - public static final String CATEGORY = "category"; - - /** - * The String representation of the Atom Entry content element name. - */ - public static final String CONTENT = "content"; - - /** - * The String representation of the OData Metadata properties element name. - */ - public static final String PROPERTIES = "properties"; - /** * The String representation of the Atom Entry etag element name. */ public static final String ETAG = "etag"; - /** - * The String representation of the type attribute name. - */ - public static final String TYPE = "type"; - - /** - * The String representation of the term element name. - */ - public static final String TERM = "term"; - - /** - * The String representation of scheme. - */ - public static final String SCHEME = "scheme"; - - /** - * The String representation of href. - */ - public static final String HREF = "href"; - - /** - * The String representation of rel. - */ - public static final String REL = "rel"; - - /** - * The String representation of the null attribute name. - */ - public static final String NULL = "null"; - - /** - * The String representation of the content type attribute value to send. - */ - public static final String ODATA_CONTENT_TYPE = "application/xml"; - /** * The String representation of the JSON annotation prefix */ diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/table/QueryTableOperation.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/table/QueryTableOperation.java index 691ebff80..279ff2737 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/table/QueryTableOperation.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/table/QueryTableOperation.java @@ -19,8 +19,6 @@ import java.io.InputStream; import java.net.HttpURLConnection; -import javax.xml.stream.XMLStreamException; - import com.fasterxml.jackson.core.JsonParseException; import com.microsoft.azure.storage.OperationContext; import com.microsoft.azure.storage.StorageException; @@ -127,8 +125,6 @@ protected Class getClazzType() { * @return * The {@link TableResult} representing the result of the query operation. * - * @throws XMLStreamException - * if an error occurs accessing the {@link InputStream} with AtomPub. * @throws InstantiationException * if an error occurs in object construction. * @throws IllegalAccessException @@ -142,8 +138,8 @@ protected Class getClazzType() { */ @Override protected TableResult parseResponse(final InputStream inStream, final int httpStatusCode, String etagFromHeader, - final OperationContext opContext, final TableRequestOptions options) throws XMLStreamException, - InstantiationException, IllegalAccessException, StorageException, JsonParseException, IOException { + final OperationContext opContext, final TableRequestOptions options) throws InstantiationException, + IllegalAccessException, StorageException, JsonParseException, IOException { TableResult res = TableDeserializer.parseSingleOpResponse(inStream, options, httpStatusCode, this.getClazzType(), this.getResolver(), opContext); res.setEtag(etagFromHeader); diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/table/SharedAccessTablePolicy.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/table/SharedAccessTablePolicy.java index 5525ade6e..c32b537f7 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/table/SharedAccessTablePolicy.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/table/SharedAccessTablePolicy.java @@ -119,7 +119,7 @@ public void setPermissionsFromString(final String value) { retSet.add(SharedAccessTablePermissions.DELETE); break; default: - throw new IllegalArgumentException(String.format(SR.PERMISSIONS_COULD_NOT_BE_PARSED, value)); + throw new IllegalArgumentException(String.format(SR.ENUM_COULD_NOT_BE_PARSED, "Permissions", value)); } } diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/table/TableBatchOperation.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/table/TableBatchOperation.java index fef130177..2148fbb43 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/table/TableBatchOperation.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/table/TableBatchOperation.java @@ -25,8 +25,6 @@ import java.util.ArrayList; import java.util.UUID; -import javax.xml.stream.XMLStreamException; - import com.microsoft.azure.storage.Constants; import com.microsoft.azure.storage.OperationContext; import com.microsoft.azure.storage.StorageErrorCodeStrings; @@ -561,11 +559,6 @@ public StorageExtendedErrorInformation parseErrorDetails() { StorageException translatedException = StorageException.translateClientException(e); throw translatedException; } - catch (XMLStreamException e) { - // The request was not even made. There was an error while trying to read the batch contents. Just throw. - StorageException translatedException = StorageException.translateClientException(e); - throw translatedException; - } } /** diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/table/TableConstants.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/table/TableConstants.java index ae4613357..502df5140 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/table/TableConstants.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/table/TableConstants.java @@ -55,32 +55,6 @@ public static class ErrorConstants { * The constants used in HTML header fields for Table service requests. */ public static class HeaderConstants { - /** - * The Accept header value to send. - * - * @deprecated use {@link AtomAcceptType} - */ - @Deprecated - public static final String ACCEPT_TYPE = "application/atom+xml,application/xml"; - - /** - * The Accept header value to send for AtomPub. - */ - public static final String ATOM_ACCEPT_TYPE = "application/atom+xml,application/atomsvc+xml,application/xml"; - - /** - * The Content-Type header value to send for AtomPub. - */ - public static final String ATOM_CONTENT_TYPE = "application/atom+xml"; - - /** - * The Content-Type header value to send for single operations. - * - * @deprecated use {@link AtomContentType} - */ - @Deprecated - public static final String ATOMPUB_TYPE = "application/atom+xml"; - /** * The Content-ID header value to send for batch operations. */ diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/table/TableDeserializer.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/table/TableDeserializer.java index 8d4912860..312e1535d 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/table/TableDeserializer.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/table/TableDeserializer.java @@ -23,10 +23,6 @@ import java.util.HashMap; import java.util.Map.Entry; -import javax.xml.stream.XMLStreamConstants; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.XMLStreamReader; - import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.JsonParser; @@ -35,6 +31,7 @@ import com.microsoft.azure.storage.OperationContext; import com.microsoft.azure.storage.StorageErrorCodeStrings; import com.microsoft.azure.storage.StorageException; +import com.microsoft.azure.storage.core.JsonUtilities; import com.microsoft.azure.storage.core.SR; /** @@ -50,12 +47,10 @@ final class TableDeserializer { /** * Reserved for internal use. Parses the operation response as a collection of entities. Reads entity data from the * specified input stream using the specified class type and optionally projects each entity result with the - * specified resolver into an {@link ODataPayload} containing a collection of {@link TableResult} objects. . + * specified resolver into an {@link ODataPayload} containing a collection of {@link TableResult} objects. * * @param inStream * The InputStream to read the data to parse from. - * @param format - * The {@link TablePayloadFormat} to use for parsing. * @param clazzType * The class type T implementing {@link TableEntity} for the entities returned. Set to * null to ignore the returned entities and copy only response properties into the @@ -75,51 +70,111 @@ final class TableDeserializer { * if an error occurs while constructing the result. * @throws IllegalAccessException * if an error occurs in reflection while parsing the result. - * @throws XMLStreamException - * if an error occurs while accessing the stream with AtomPub. * @throws StorageException * if a storage service error occurs. * @throws IOException - * if an error occurs while accessing the stream with Json. + * if an error occurs while accessing the stream. * @throws JsonParseException * if an error occurs while parsing the stream. */ - @SuppressWarnings("deprecation") + @SuppressWarnings("unchecked") static ODataPayload parseQueryResponse(final InputStream inStream, final TableRequestOptions options, final Class clazzType, final EntityResolver resolver, - final OperationContext opContext) throws InstantiationException, IllegalAccessException, - XMLStreamException, StorageException, JsonParseException, IOException { - ODataPayload payload; - if (options.getTablePayloadFormat() == TablePayloadFormat.AtomPub) { - payload = parseAtomQueryResponse(inStream, clazzType, resolver, options, opContext); + final OperationContext opContext) throws JsonParseException, IOException, InstantiationException, + IllegalAccessException, StorageException { + ODataPayload corePayload = null; + ODataPayload resolvedPayload = null; + ODataPayload commonPayload = null; + + JsonParser parser = createJsonParserFromStream(inStream); + + try { + + if (resolver != null) { + resolvedPayload = new ODataPayload(); + commonPayload = resolvedPayload; + } + else { + corePayload = new ODataPayload(); + commonPayload = corePayload; + } + + if (!parser.hasCurrentToken()) { + parser.nextToken(); + } + + JsonUtilities.assertIsStartObjectJsonToken(parser); + + // move into data + parser.nextToken(); + + // if there is a clazz type and if JsonNoMetadata, create a classProperties dictionary to use for type inference once + // instead of querying the cache many times + HashMap classProperties = null; + if (options.getTablePayloadFormat() == TablePayloadFormat.JsonNoMetadata && clazzType != null) { + classProperties = PropertyPair.generatePropertyPairs(clazzType); + } + + while (parser.getCurrentToken() != null) { + if (parser.getCurrentToken() == JsonToken.FIELD_NAME + && parser.getCurrentName().equals(ODataConstants.VALUE)) { + // move to start of array + parser.nextToken(); + + JsonUtilities.assertIsStartArrayJsonToken(parser); + + // go to properties + parser.nextToken(); + + while (parser.getCurrentToken() == JsonToken.START_OBJECT) { + final TableResult res = parseJsonEntity(parser, clazzType, classProperties, resolver, options, + opContext); + if (corePayload != null) { + corePayload.tableResults.add(res); + } + + if (resolver != null) { + resolvedPayload.results.add((R) res.getResult()); + } + else { + corePayload.results.add((T) res.getResult()); + } + + parser.nextToken(); + } + + JsonUtilities.assertIsEndArrayJsonToken(parser); + } + + parser.nextToken(); + } } - else { - payload = parseJsonQueryResponse(inStream, clazzType, resolver, options, opContext); + finally { + parser.close(); } - return payload; + + return commonPayload; } /** - * Reserved for internal use. Parses the operation response as a collection of entities. Reads entity data from the - * specified input stream using the specified class type and optionally projects each entity result with the - * specified resolver into an {@link ODataPayload} containing a collection of {@link TableResult} objects. . + * Reserved for internal use. Parses the operation response as an entity. Reads entity data from the specified + * JsonParser using the specified class type and optionally projects the entity result with the + * specified resolver into a {@link TableResult} object. * - * @param inStream - * The InputStream to read the data to parse from. - * @param format - * The {@link TablePayloadFormat} to use for parsing. - * @param options - * A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout - * settings for the operation. + * @param parser + * The JsonParser to read the data to parse from. * @param httpStatusCode * The HTTP status code returned with the operation response. * @param clazzType - * The class type T implementing {@link TableEntity} for the entities returned. Set to - * null to ignore the returned entities and copy only response properties into the - * {@link TableResult} objects. + * The class type T implementing {@link TableEntity} for the entity returned. Set to + * null to ignore the returned entity and copy only response properties into the + * {@link TableResult} object. * @param resolver - * An {@link EntityResolver} instance to project the entities into instances of type R. Set - * to null to return the entities as instances of the class type T. + * An {@link EntityResolver} instance to project the entity into an instance of type R. Set + * to null to return the entitys as instance of the class type T. + * @param options + * A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout + * settings for the operation. * @param opContext * An {@link OperationContext} object used to track the execution of the operation. * @return @@ -128,28 +183,28 @@ static ODataPayload parseQueryResponse(final Input * if an error occurs while constructing the result. * @throws IllegalAccessException * if an error occurs in reflection while parsing the result. - * @throws XMLStreamException - * if an error occurs while accessing the stream with AtomPub. * @throws StorageException * if a storage service error occurs. * @throws IOException - * if an error occurs while accessing the stream with Json. + * if an error occurs while accessing the stream. * @throws JsonParseException * if an error occurs while parsing the stream. */ - @SuppressWarnings("deprecation") static TableResult parseSingleOpResponse(final InputStream inStream, final TableRequestOptions options, final int httpStatusCode, final Class clazzType, - final EntityResolver resolver, final OperationContext opContext) throws InstantiationException, - IllegalAccessException, XMLStreamException, StorageException, IOException, JsonParseException { - TableResult res; - if (options.getTablePayloadFormat() == TablePayloadFormat.AtomPub) { - res = parseSingleOpAtomResponse(inStream, httpStatusCode, clazzType, resolver, options, opContext); + final EntityResolver resolver, final OperationContext opContext) throws JsonParseException, IOException, + InstantiationException, IllegalAccessException, StorageException { + JsonParser parser = createJsonParserFromStream(inStream); + + try { + final TableResult res = parseJsonEntity(parser, clazzType, + null /*HashMap classProperties*/, resolver, options, opContext); + res.setHttpStatusCode(httpStatusCode); + return res; } - else { - res = parseSingleOpJsonResponse(inStream, httpStatusCode, clazzType, resolver, options, opContext); + finally { + parser.close(); } - return res; } /** @@ -186,7 +241,6 @@ static TableResult parseSingleOpResponse(final InputS * @throws JsonParseException * if an error occurs while parsing the stream. */ - @SuppressWarnings("deprecation") private static TableResult parseJsonEntity(final JsonParser parser, final Class clazzType, HashMap classProperties, final EntityResolver resolver, final TableRequestOptions options, final OperationContext opContext) throws JsonParseException, @@ -199,7 +253,7 @@ private static TableResult parseJsonEntity(final Json parser.nextToken(); } - ODataUtilities.assertIsStartObjectJsonToken(parser); + JsonUtilities.assertIsStartObjectJsonToken(parser); parser.nextToken(); @@ -352,517 +406,6 @@ else if (clazzType != null) { return res; } - /** - * Reserved for internal use. Parses the operation response as a collection of entities. Reads entity data from the - * specified input stream using the specified class type and optionally projects each entity result with the - * specified resolver into an {@link ODataPayload} containing a collection of {@link TableResult} objects. - * - * @param inStream - * The InputStream to read the data to parse from. - * @param clazzType - * The class type T implementing {@link TableEntity} for the entities returned. Set to - * null to ignore the returned entities and copy only response properties into the - * {@link TableResult} objects. - * @param resolver - * An {@link EntityResolver} instance to project the entities into instances of type R. Set - * to null to return the entities as instances of the class type T. - * @param options - * A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout - * settings for the operation. - * @param opContext - * An {@link OperationContext} object used to track the execution of the operation. - * @return - * An {@link ODataPayload} containing a collection of {@link TableResult} objects with the parsed operation - * response. - * @throws InstantiationException - * if an error occurs while constructing the result. - * @throws IllegalAccessException - * if an error occurs in reflection while parsing the result. - * @throws StorageException - * if a storage service error occurs. - * @throws IOException - * if an error occurs while accessing the stream. - * @throws JsonParseException - * if an error occurs while parsing the stream. - */ - @SuppressWarnings("unchecked") - private static ODataPayload parseJsonQueryResponse(final InputStream inStream, - final Class clazzType, final EntityResolver resolver, final TableRequestOptions options, - final OperationContext opContext) throws InstantiationException, IllegalAccessException, StorageException, - JsonParseException, IOException { - ODataPayload corePayload = null; - ODataPayload resolvedPayload = null; - ODataPayload commonPayload = null; - - JsonParser parser = createJsonParserFromStream(inStream); - - try { - - if (resolver != null) { - resolvedPayload = new ODataPayload(); - commonPayload = resolvedPayload; - } - else { - corePayload = new ODataPayload(); - commonPayload = corePayload; - } - - if (!parser.hasCurrentToken()) { - parser.nextToken(); - } - - ODataUtilities.assertIsStartObjectJsonToken(parser); - - // move into data - parser.nextToken(); - - // if there is a clazz type and if JsonNoMetadata, create a classProperties dictionary to use for type inference once - // instead of querying the cache many times - HashMap classProperties = null; - if (options.getTablePayloadFormat() == TablePayloadFormat.JsonNoMetadata && clazzType != null) { - classProperties = PropertyPair.generatePropertyPairs(clazzType); - } - - while (parser.getCurrentToken() != null) { - if (parser.getCurrentToken() == JsonToken.FIELD_NAME - && parser.getCurrentName().equals(ODataConstants.VALUE)) { - // move to start of array - parser.nextToken(); - - ODataUtilities.assertIsStartArrayJsonToken(parser); - - // go to properties - parser.nextToken(); - - while (parser.getCurrentToken() == JsonToken.START_OBJECT) { - final TableResult res = parseJsonEntity(parser, clazzType, classProperties, resolver, options, - opContext); - if (corePayload != null) { - corePayload.tableResults.add(res); - } - - if (resolver != null) { - resolvedPayload.results.add((R) res.getResult()); - } - else { - corePayload.results.add((T) res.getResult()); - } - - parser.nextToken(); - } - - ODataUtilities.assertIsEndArrayJsonToken(parser); - } - - parser.nextToken(); - } - } - finally { - parser.close(); - } - - return commonPayload; - } - - /** - * Reserved for internal use. Parses the operation response as an entity. Reads entity data from the specified - * JsonParser using the specified class type and optionally projects the entity result with the - * specified resolver into a {@link TableResult} object. - * - * @param parser - * The JsonParser to read the data to parse from. - * @param httpStatusCode - * The HTTP status code returned with the operation response. - * @param clazzType - * The class type T implementing {@link TableEntity} for the entity returned. Set to - * null to ignore the returned entity and copy only response properties into the - * {@link TableResult} object. - * @param resolver - * An {@link EntityResolver} instance to project the entity into an instance of type R. Set - * to null to return the entitys as instance of the class type T. - * @param options - * A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout - * settings for the operation. - * @param opContext - * An {@link OperationContext} object used to track the execution of the operation. - * @return - * A {@link TableResult} object with the parsed operation response. - * @throws InstantiationException - * if an error occurs while constructing the result. - * @throws IllegalAccessException - * if an error occurs in reflection while parsing the result. - * @throws StorageException - * if a storage service error occurs. - * @throws IOException - * if an error occurs while accessing the stream. - * @throws JsonParseException - * if an error occurs while parsing the stream. - */ - private static TableResult parseSingleOpJsonResponse(final InputStream inStream, - final int httpStatusCode, final Class clazzType, final EntityResolver resolver, - final TableRequestOptions options, final OperationContext opContext) throws InstantiationException, - IllegalAccessException, StorageException, JsonParseException, IOException { - JsonParser parser = createJsonParserFromStream(inStream); - - try { - final TableResult res = parseJsonEntity(parser, clazzType, - null /*HashMap classProperties*/, resolver, options, opContext); - res.setHttpStatusCode(httpStatusCode); - return res; - } - finally { - parser.close(); - } - } - - /** - * Reserved for internal use. Parses the operation response as an entity. Parses the result returned in the - * specified stream in AtomPub format into a {@link TableResult} containing an entity of the specified class type - * projected using the specified resolver. - * - * @param xmlr - * An XMLStreamReader on the input stream. - * @param clazzType - * The class type T implementing {@link TableEntity} for the entity returned. Set to - * null to ignore the returned entity and copy only response properties into the - * {@link TableResult} object. - * @param resolver - * An {@link EntityResolver} instance to project the entity into an instance of type R. Set - * to null to return the entity as an instance of the class type T. - * @param options - * A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout - * settings for the operation. - * @param opContext - * An {@link OperationContext} object used to track the execution of the operation. - * @return - * A {@link TableResult} containing the parsed entity result of the operation. - * - * @throws XMLStreamException - * if an error occurs while accessing the stream. - * @throws InstantiationException - * if an error occurs while constructing the result. - * @throws IllegalAccessException - * if an error occurs in reflection while parsing the result. - * @throws StorageException - * if a storage service error occurs. - */ - @SuppressWarnings("deprecation") - private static TableResult parseAtomEntity(final XMLStreamReader xmlr, - final Class clazzType, final EntityResolver resolver, final TableRequestOptions options, - final OperationContext opContext) throws XMLStreamException, InstantiationException, - IllegalAccessException, StorageException { - int eventType = xmlr.getEventType(); - final TableResult res = new TableResult(); - - xmlr.require(XMLStreamConstants.START_ELEMENT, null, ODataConstants.ENTRY); - - String etag = xmlr.getAttributeValue(ODataConstants.DATA_SERVICES_METADATA_NS, ODataConstants.ETAG); - - res.setEtag(etag); - - while (xmlr.hasNext()) { - eventType = xmlr.next(); - if (eventType == XMLStreamConstants.CHARACTERS) { - xmlr.getText(); - continue; - } - - final String name = xmlr.getName().toString(); - - if (eventType == XMLStreamConstants.START_ELEMENT) { - if (name.equals(ODataConstants.BRACKETED_ATOM_NS + ODataConstants.ID)) { - DeserializationHelper.readElementFromXMLReader(xmlr, ODataConstants.ID); - } - else if (name.equals(ODataConstants.BRACKETED_DATA_SERVICES_METADATA_NS + ODataConstants.PROPERTIES)) { - // Do read properties - if (resolver == null && clazzType == null) { - return res; - } - else { - res.setProperties(readAtomProperties(xmlr, options, opContext)); - break; - } - } - } - } - - // Move to end Content - eventType = xmlr.next(); - if (eventType == XMLStreamConstants.CHARACTERS) { - eventType = xmlr.next(); - } - - xmlr.require(XMLStreamConstants.END_ELEMENT, null, ODataConstants.CONTENT); - - eventType = xmlr.next(); - if (eventType == XMLStreamConstants.CHARACTERS) { - eventType = xmlr.next(); - } - - xmlr.require(XMLStreamConstants.END_ELEMENT, null, ODataConstants.ENTRY); - - String rowKey = null; - String partitionKey = null; - Date timestamp = null; - - // Remove core properties from map and set individually - EntityProperty tempProp = res.getProperties().remove(TableConstants.PARTITION_KEY); - if (tempProp != null) { - partitionKey = tempProp.getValueAsString(); - } - - tempProp = res.getProperties().remove(TableConstants.ROW_KEY); - if (tempProp != null) { - rowKey = tempProp.getValueAsString(); - } - - tempProp = res.getProperties().remove(TableConstants.TIMESTAMP); - if (tempProp != null) { - tempProp.setDateBackwardCompatibility(false); - timestamp = tempProp.getValueAsDate(); - } - - if (resolver != null) { - // Call resolver - res.setResult(resolver.resolve(partitionKey, rowKey, timestamp, res.getProperties(), res.getEtag())); - } - else if (clazzType != null) { - // Generate new entity and return - final T entity = clazzType.newInstance(); - entity.setEtag(res.getEtag()); - - entity.setPartitionKey(partitionKey); - entity.setRowKey(rowKey); - entity.setTimestamp(timestamp); - - entity.readEntity(res.getProperties(), opContext); - - res.setResult(entity); - } - - return res; - } - - /** - * Reserved for internal use. Parses the operation response as a collection of entities. Reads entity data from the - * specified input stream using the specified class type and optionally projects each entity result with the - * specified resolver into an {@link ODataPayload} containing a collection of {@link TableResult} objects. - * - * @param inStream - * The InputStream to read the data to parse from. - * @param clazzType - * The class type T implementing {@link TableEntity} for the entities returned. Set to - * null to ignore the returned entities and copy only response properties into the - * {@link TableResult} objects. - * @param resolver - * An {@link EntityResolver} instance to project the entities into instances of type R. Set - * to null to return the entities as instances of the class type T. - * @param options - * A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout - * settings for the operation. - * @param opContext - * An {@link OperationContext} object used to track the execution of the operation. - * @return - * An {@link ODataPayload} containing a collection of {@link TableResult} objects with the parsed operation - * response. - * - * @throws XMLStreamException - * if an error occurs while accessing the stream. - * @throws InstantiationException - * if an error occurs while constructing the result. - * @throws IllegalAccessException - * if an error occurs in reflection while parsing the result. - * @throws StorageException - * if a storage service error occurs. - */ - @SuppressWarnings("unchecked") - private static ODataPayload parseAtomQueryResponse(final InputStream inStream, - final Class clazzType, final EntityResolver resolver, final TableRequestOptions options, - final OperationContext opContext) throws XMLStreamException, InstantiationException, - IllegalAccessException, StorageException { - ODataPayload corePayload = null; - ODataPayload resolvedPayload = null; - ODataPayload commonPayload = null; - - if (resolver != null) { - resolvedPayload = new ODataPayload(); - commonPayload = resolvedPayload; - } - else { - corePayload = new ODataPayload(); - commonPayload = corePayload; - } - - final XMLStreamReader xmlr = DeserializationHelper.createXMLStreamReaderFromStream(inStream); - int eventType = xmlr.getEventType(); - xmlr.require(XMLStreamConstants.START_DOCUMENT, null, null); - eventType = xmlr.next(); - - xmlr.require(XMLStreamConstants.START_ELEMENT, null, ODataConstants.FEED); - // skip feed chars - eventType = xmlr.next(); - - while (xmlr.hasNext()) { - eventType = xmlr.next(); - - if (eventType == XMLStreamConstants.CHARACTERS) { - xmlr.getText(); - continue; - } - - final String name = xmlr.getName().toString(); - - if (eventType == XMLStreamConstants.START_ELEMENT) { - if (name.equals(ODataConstants.BRACKETED_ATOM_NS + ODataConstants.ENTRY)) { - final TableResult res = parseAtomEntity(xmlr, clazzType, resolver, options, opContext); - if (corePayload != null) { - corePayload.tableResults.add(res); - } - - if (resolver != null) { - resolvedPayload.results.add((R) res.getResult()); - } - else { - corePayload.results.add((T) res.getResult()); - } - } - } - else if (eventType == XMLStreamConstants.END_ELEMENT - && name.equals(ODataConstants.BRACKETED_ATOM_NS + ODataConstants.FEED)) { - break; - } - } - - xmlr.require(XMLStreamConstants.END_ELEMENT, null, ODataConstants.FEED); - return commonPayload; - } - - /** - * Reserved for internal use. Parses the operation response as an entity. Reads entity data from the specified - * XMLStreamReader using the specified class type and optionally projects the entity result with the - * specified resolver into a {@link TableResult} object. - * - * @param xmlr - * The XMLStreamReader to read the data to parse from. - * @param httpStatusCode - * The HTTP status code returned with the operation response. - * @param clazzType - * The class type T implementing {@link TableEntity} for the entity returned. Set to - * null to ignore the returned entity and copy only response properties into the - * {@link TableResult} object. - * @param resolver - * An {@link EntityResolver} instance to project the entity into an instance of type R. Set - * to null to return the entities as instance of the class type T. - * @param options - * A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout - * settings for the operation. - * @param opContext - * An {@link OperationContext} object used to track the execution of the operation. - * @return - * A {@link TableResult} object with the parsed operation response. - * - * @throws XMLStreamException - * if an error occurs while accessing the stream. - * @throws InstantiationException - * if an error occurs while constructing the result. - * @throws IllegalAccessException - * if an error occurs in reflection while parsing the result. - * @throws StorageException - * if a storage service error occurs. - */ - private static TableResult parseSingleOpAtomResponse(final InputStream inStream, - final int httpStatusCode, final Class clazzType, final EntityResolver resolver, - final TableRequestOptions options, final OperationContext opContext) - throws XMLStreamException, InstantiationException, IllegalAccessException, StorageException { - XMLStreamReader xmlr = DeserializationHelper.createXMLStreamReaderFromStream(inStream); - - try { - xmlr.require(XMLStreamConstants.START_DOCUMENT, null, null); - xmlr.next(); - - final TableResult res = parseAtomEntity(xmlr, clazzType, resolver, options, opContext); - res.setHttpStatusCode(httpStatusCode); - return res; - } - finally { - xmlr.close(); - } - } - - /** - * Reserved for internal use. Reads the properties of an entity from the stream into a map of property names to - * typed values. Reads the entity data as an AtomPub Entry Resource from the specified {@link XMLStreamReader} into - * a map of String property names to {@link EntityProperty} data typed values. - * - * @param xmlr - * The XMLStreamReader to read the data from. - * @param options - * A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout - * settings for the operation. - * @param opContext - * An {@link OperationContext} object used to track the execution of the operation. - * - * @return - * A java.util.HashMap containing a map of String property names to - * {@link EntityProperty} data typed values found in the entity data. - * @throws XMLStreamException - * if an error occurs accessing the stream. - */ - private static HashMap readAtomProperties(final XMLStreamReader xmlr, - final TableRequestOptions options, final OperationContext opContext) throws XMLStreamException { - int eventType = xmlr.getEventType(); - xmlr.require(XMLStreamConstants.START_ELEMENT, null, ODataConstants.PROPERTIES); - final HashMap properties = new HashMap(); - - while (xmlr.hasNext()) { - eventType = xmlr.next(); - if (eventType == XMLStreamConstants.CHARACTERS) { - xmlr.getText(); - continue; - } - - if (eventType == XMLStreamConstants.START_ELEMENT - && xmlr.getNamespaceURI().equals(ODataConstants.DATA_SERVICES_NS)) { - final String key = xmlr.getLocalName(); - String val = Constants.EMPTY_STRING; - String edmType = null; - - if (xmlr.getAttributeCount() > 0) { - edmType = xmlr.getAttributeValue(ODataConstants.DATA_SERVICES_METADATA_NS, ODataConstants.TYPE); - - if (xmlr.getAttributeValue(ODataConstants.DATA_SERVICES_METADATA_NS, ODataConstants.NULL) != null) { - val = null; - } - } - - // move to chars - eventType = xmlr.next(); - - if (eventType == XMLStreamConstants.CHARACTERS) { - val = xmlr.getText(); - - // end element - eventType = xmlr.next(); - } - - xmlr.require(XMLStreamConstants.END_ELEMENT, null, key); - - final EntityProperty newProp = new EntityProperty(val, EdmType.parse(edmType)); - newProp.setDateBackwardCompatibility(options.getDateBackwardCompatibility()); - properties.put(key, newProp); - } - else if (eventType == XMLStreamConstants.END_ELEMENT - && xmlr.getName().toString() - .equals(ODataConstants.BRACKETED_DATA_SERVICES_METADATA_NS + ODataConstants.PROPERTIES)) { - // End read properties - break; - } - } - - xmlr.require(XMLStreamConstants.END_ELEMENT, null, ODataConstants.PROPERTIES); - return properties; - } - private static String getETagFromTimestamp(String timestampString) throws UnsupportedEncodingException { timestampString = URLEncoder.encode(timestampString, Constants.UTF8_CHARSET); return "W/\"datetime'" + timestampString + "'\""; diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/table/TableEntity.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/table/TableEntity.java index 2c695c74d..b7ff5cc43 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/table/TableEntity.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/table/TableEntity.java @@ -148,13 +148,12 @@ public void readEntity(HashMap properties, OperationCont public void setRowKey(String rowKey); /** - * Sets the Timestamp value for the entity. + * Sets the Timestamp value for the entity. Note that timestamp is a read-only property on the service and should + * not be set by the user. * * @param timeStamp * A java.util.Date which specifies the Timestamp value to set for the entity. - * @deprecated as of 3.0.0. The timestamp property is a read-only property, set by the service only. */ - @Deprecated public void setTimestamp(Date timeStamp); /** diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/table/TableEntitySerializer.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/table/TableEntitySerializer.java index 7de22252e..1e9c48492 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/table/TableEntitySerializer.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/table/TableEntitySerializer.java @@ -17,17 +17,12 @@ import java.io.IOException; import java.io.OutputStream; import java.io.StringWriter; -import java.util.Date; import java.util.HashMap; import java.util.Map.Entry; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.XMLStreamWriter; - import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonGenerationException; import com.fasterxml.jackson.core.JsonGenerator; -import com.microsoft.azure.storage.Constants; import com.microsoft.azure.storage.OperationContext; import com.microsoft.azure.storage.StorageException; import com.microsoft.azure.storage.core.Utility; @@ -42,7 +37,8 @@ final class TableEntitySerializer { private static JsonFactory jsonFactory = new JsonFactory(); /** - * Reserved for internal use. Writes an entity to the stream, leaving the stream open for additional writing. + * Reserved for internal use. Writes an entity to the stream as a JSON resource, leaving the stream open + * for additional writing. * * @param outStream * The OutputStream to write the entity to. @@ -55,27 +51,29 @@ final class TableEntitySerializer { * true and a reference to an entity within a table when false. * @param opContext * An {@link OperationContext} object used to track the execution of the operation. - * @throws XMLStreamException - * if an error occurs while accessing the stream with AtomPub. + * * @throws StorageException * if a Storage service error occurs. * @throws IOException - * if an error occurs while accessing the stream with Json. + * if an error occurs while accessing the stream. */ - @SuppressWarnings("deprecation") static void writeSingleEntityToStream(final OutputStream outStream, final TablePayloadFormat format, final TableEntity entity, final boolean isTableEntry, final OperationContext opContext) - throws XMLStreamException, StorageException, IOException { - if (format == TablePayloadFormat.AtomPub) { - writeSingleAtomEntity(outStream, entity, isTableEntry, opContext); + throws StorageException, IOException { + JsonGenerator generator = jsonFactory.createGenerator(outStream); + + try { + // write to stream + writeJsonEntity(generator, format, entity, isTableEntry, opContext); } - else { - writeSingleJsonEntity(outStream, format, entity, isTableEntry, opContext); + finally { + generator.close(); } } /** - * Reserved for internal use. Writes an entity to the stream, leaving the stream open for additional writing. + * Reserved for internal use. Writes an entity to the stream as a JSON resource, leaving the stream open + * for additional writing. * * @param strWriter * The StringWriter to write the entity to. @@ -88,207 +86,28 @@ static void writeSingleEntityToStream(final OutputStream outStream, final TableP * true and a reference to an entity within a table when false. * @param opContext * An {@link OperationContext} object used to track the execution of the operation. - * @throws XMLStreamException - * if an error occurs while accessing the stream with AtomPub. + * * @throws StorageException * if a Storage service error occurs. * @throws IOException - * if an error occurs while accessing the stream with Json. + * if an error occurs while accessing the stream. */ - @SuppressWarnings("deprecation") static void writeSingleEntityToString(final StringWriter strWriter, final TablePayloadFormat format, final TableEntity entity, final boolean isTableEntry, final OperationContext opContext) - throws XMLStreamException, StorageException, IOException { - if (format == TablePayloadFormat.AtomPub) { - writeSingleAtomEntity(strWriter, entity, isTableEntry, opContext); - } - else { - writeSingleJsonEntity(strWriter, format, entity, isTableEntry, opContext); - } - } - - /** - * Reserved for internal use. Writes an entity to the stream as an AtomPub Entry Resource, leaving the stream open - * for additional writing. - * - * @param entity - * The instance implementing {@link TableEntity} to write to the output stream. - * @param isTableEntry - * A flag indicating the entity is a reference to a table at the top level of the storage service when - * true and a reference to an entity within a table when false. - * @param xmlw - * The XMLStreamWriter to write the entity to. - * @param opContext - * An {@link OperationContext} object used to track the execution of the operation. - * - * @throws XMLStreamException - * if an error occurs accessing the stream. - * @throws StorageException - * if a Storage service error occurs. - */ - @SuppressWarnings("deprecation") - private static void writeAtomEntity(final TableEntity entity, final boolean isTableEntry, - final XMLStreamWriter xmlw, final OperationContext opContext) throws XMLStreamException, StorageException { - HashMap properties = entity.writeEntity(opContext); - if (properties == null) { - properties = new HashMap(); - } - - if (!isTableEntry) { - Utility.assertNotNull(TableConstants.PARTITION_KEY, entity.getPartitionKey()); - Utility.assertNotNull(TableConstants.ROW_KEY, entity.getRowKey()); - Utility.assertNotNull(TableConstants.TIMESTAMP, entity.getTimestamp()); - } - - // Begin entry - xmlw.writeStartElement("entry"); - xmlw.writeNamespace("d", ODataConstants.DATA_SERVICES_NS); - xmlw.writeNamespace("m", ODataConstants.DATA_SERVICES_METADATA_NS); - - // default namespace - xmlw.writeNamespace(null, ODataConstants.ATOM_NS); - - // Content - xmlw.writeStartElement(ODataConstants.CONTENT); - xmlw.writeAttribute(ODataConstants.TYPE, ODataConstants.ODATA_CONTENT_TYPE); - - // m:properties - xmlw.writeStartElement("m", ODataConstants.PROPERTIES, ODataConstants.DATA_SERVICES_METADATA_NS); - - if (!isTableEntry) { - // d:PartitionKey - xmlw.writeStartElement("d", TableConstants.PARTITION_KEY, ODataConstants.DATA_SERVICES_NS); - xmlw.writeAttribute("xml", "xml", "space", "preserve"); - xmlw.writeCharacters(entity.getPartitionKey()); - xmlw.writeEndElement(); - - // d:RowKey - xmlw.writeStartElement("d", TableConstants.ROW_KEY, ODataConstants.DATA_SERVICES_NS); - xmlw.writeAttribute("xml", "xml", "space", "preserve"); - xmlw.writeCharacters(entity.getRowKey()); - xmlw.writeEndElement(); - - // d:Timestamp - if (entity.getTimestamp() == null) { - entity.setTimestamp(new Date()); - } + throws StorageException, IOException { + JsonGenerator generator = jsonFactory.createGenerator(strWriter); - xmlw.writeStartElement("d", TableConstants.TIMESTAMP, ODataConstants.DATA_SERVICES_NS); - xmlw.writeAttribute("m", ODataConstants.DATA_SERVICES_METADATA_NS, ODataConstants.TYPE, - EdmType.DATE_TIME.toString()); - xmlw.writeCharacters(Utility.getJavaISO8601Time(entity.getTimestamp())); - xmlw.writeEndElement(); + try { + // write to stream + writeJsonEntity(generator, format, entity, isTableEntry, opContext); } - - for (final Entry ent : properties.entrySet()) { - if (ent.getKey().equals(TableConstants.PARTITION_KEY) || ent.getKey().equals(TableConstants.ROW_KEY) - || ent.getKey().equals(TableConstants.TIMESTAMP) || ent.getKey().equals("Etag")) { - continue; - } - - EntityProperty currProp = ent.getValue(); - - // d:PropName - xmlw.writeStartElement("d", ent.getKey(), ODataConstants.DATA_SERVICES_NS); - - if (currProp.getEdmType() == EdmType.STRING) { - xmlw.writeAttribute("xml", "xml", "space", "preserve"); - } - else if (currProp.getEdmType().toString().length() != 0) { - String edmTypeString = currProp.getEdmType().toString(); - if (edmTypeString.length() != 0) { - xmlw.writeAttribute("m", ODataConstants.DATA_SERVICES_METADATA_NS, ODataConstants.TYPE, - edmTypeString); - } - } - - if (currProp.getIsNull()) { - xmlw.writeAttribute("m", ODataConstants.DATA_SERVICES_METADATA_NS, ODataConstants.NULL, Constants.TRUE); - } - - // Write Value - xmlw.writeCharacters(currProp.getValueAsString()); - // End d:PropName - xmlw.writeEndElement(); + finally { + generator.close(); } - - // End m:properties - xmlw.writeEndElement(); - - // End content - xmlw.writeEndElement(); - - // End entry - xmlw.writeEndElement(); } /** - * Reserved for internal use. Writes a single entity to the specified OutputStream as a complete XML - * document. - * - * @param outStream - * The OutputStream to write the entity to. - * @param entity - * The instance implementing {@link TableEntity} to write to the output stream. - * @param isTableEntry - * A flag indicating the entity is a reference to a table at the top level of the storage service when - * true and a reference to an entity within a table when false. - * @param opContext - * An {@link OperationContext} object used to track the execution of the operation. - * @throws XMLStreamException - * if an error occurs creating or accessing the stream. - * @throws StorageException - * if a Storage service error occurs. - */ - private static void writeSingleAtomEntity(final OutputStream outStream, final TableEntity entity, - final boolean isTableEntry, final OperationContext opContext) throws XMLStreamException, StorageException { - XMLStreamWriter xmlw = Utility.createXMLStreamWriter(outStream, Constants.UTF8_CHARSET); - - // default is UTF8 - xmlw.writeStartDocument(Constants.UTF8_CHARSET, "1.0"); - - writeAtomEntity(entity, isTableEntry, xmlw, opContext); - - // end doc - xmlw.writeEndDocument(); - xmlw.flush(); - } - - /** - * Reserved for internal use. Writes a single entity to the specified OutputStream as a complete XML - * document. - * - * @param entity - * The instance implementing {@link TableEntity} to write to the output stream. - * @param isTableEntry - * A flag indicating the entity is a reference to a table at the top level of the storage service when - * true and a reference to an entity within a table when false. - * @param opContext - * An {@link OperationContext} object used to track the execution of the operation. - * @param outStream - * The OutputStream to write the entity to. - * - * @throws XMLStreamException - * if an error occurs creating or accessing the stream. - * @throws StorageException - * if a Storage service error occurs. - */ - private static void writeSingleAtomEntity(final StringWriter strWriter, final TableEntity entity, - final boolean isTableEntry, final OperationContext opContext) throws XMLStreamException, StorageException { - final XMLStreamWriter xmlw = Utility.createXMLStreamWriter(strWriter); - - // default is UTF8 - xmlw.writeStartDocument(Constants.UTF8_CHARSET, "1.0"); - - writeAtomEntity(entity, isTableEntry, xmlw, opContext); - - // end doc - xmlw.writeEndDocument(); - xmlw.flush(); - } - - /** - * Reserved for internal use. Writes an entity to the specified JsonGenerator as an JSON resource + * Reserved for internal use. Writes an entity to the specified JsonGenerator as a JSON resource * * @param generator * The JsonGenerator to write the entity to. @@ -376,76 +195,6 @@ else if (currProp.getEdmType() == EdmType.DOUBLE && currProp.getIsNull() == fals generator.writeEndObject(); } - /** - * Reserved for internal use. Writes an entity to the stream as an JSON resource, leaving the stream open - * for additional writing. - * - * @param outStream - * The OutputStream to write the entity to. - * @param format - * The {@link TablePayloadFormat} to use for parsing. - * @param entity - * The instance implementing {@link TableEntity} to write to the output stream. - * @param isTableEntry - * A flag indicating the entity is a reference to a table at the top level of the storage service when - * true and a reference to an entity within a table when false. - * @param opContext - * An {@link OperationContext} object used to track the execution of the operation. - * - * @throws StorageException - * if a Storage service error occurs. - * @throws IOException - * if an error occurs while accessing the stream. - */ - private static void writeSingleJsonEntity(final OutputStream outStream, TablePayloadFormat format, - final TableEntity entity, final boolean isTableEntry, final OperationContext opContext) - throws StorageException, IOException { - JsonGenerator generator = jsonFactory.createGenerator(outStream); - - try { - // write to stream - writeJsonEntity(generator, format, entity, isTableEntry, opContext); - } - finally { - generator.close(); - } - } - - /** - * Reserved for internal use. Writes an entity to the stream as an JSON resource, leaving the stream open - * for additional writing. - * - * @param strWriter - * The StringWriter to write the entity to. - * @param format - * The {@link TablePayloadFormat} to use for parsing. - * @param entity - * The instance implementing {@link TableEntity} to write to the output stream. - * @param isTableEntry - * A flag indicating the entity is a reference to a table at the top level of the storage service when - * true and a reference to an entity within a table when false. - * @param opContext - * An {@link OperationContext} object used to track the execution of the operation. - * - * @throws StorageException - * if a Storage service error occurs. - * @throws IOException - * if an error occurs while accessing the stream. - */ - private static void writeSingleJsonEntity(final StringWriter strWriter, TablePayloadFormat format, - final TableEntity entity, final boolean isTableEntry, final OperationContext opContext) - throws StorageException, IOException { - JsonGenerator generator = jsonFactory.createGenerator(strWriter); - - try { - // write to stream - writeJsonEntity(generator, format, entity, isTableEntry, opContext); - } - finally { - generator.close(); - } - } - private static void writeJsonProperty(JsonGenerator generator, Entry prop) throws JsonGenerationException, IOException { EdmType edmType = prop.getValue().getEdmType(); diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/table/TableOperation.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/table/TableOperation.java index 4257e2e08..86a103e0a 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/table/TableOperation.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/table/TableOperation.java @@ -21,8 +21,6 @@ import java.io.InputStream; import java.net.HttpURLConnection; -import javax.xml.stream.XMLStreamException; - import com.fasterxml.jackson.core.JsonParseException; import com.microsoft.azure.storage.Constants; import com.microsoft.azure.storage.OperationContext; @@ -453,11 +451,6 @@ public StorageExtendedErrorInformation parseErrorDetails() { return putRequest; } - catch (XMLStreamException e) { - // The request was not even made. There was an error while trying to read the entity. Just throw. - StorageException translatedException = StorageException.translateClientException(e); - throw translatedException; - } catch (IOException e) { // The request was not even made. There was an error while trying to read the entity. Just throw. StorageException translatedException = StorageException.translateClientException(e); @@ -552,11 +545,6 @@ public StorageExtendedErrorInformation parseErrorDetails() { return putRequest; } - catch (XMLStreamException e) { - // The request was not even made. There was an error while trying to read the entity. Just throw. - StorageException translatedException = StorageException.translateClientException(e); - throw translatedException; - } catch (IOException e) { // The request was not even made. There was an error while trying to read the entity. Just throw. StorageException translatedException = StorageException.translateClientException(e); @@ -650,11 +638,6 @@ public StorageExtendedErrorInformation parseErrorDetails() { return putRequest; } - catch (XMLStreamException e) { - // The request was not even made. There was an error while trying to read the entity. Just throw. - StorageException translatedException = StorageException.translateClientException(e); - throw translatedException; - } catch (IOException e) { // The request was not even made. There was an error while trying to read the entity. Just throw. StorageException translatedException = StorageException.translateClientException(e); @@ -812,8 +795,6 @@ protected synchronized final TableOperationType getOperationType() { * @return * The {@link TableResult} representing the result of the operation. * - * @throws XMLStreamException - * if an error occurs accessing the {@link InputStream} with AtomPub. * @throws InstantiationException * if an error occurs in object construction. * @throws IllegalAccessException @@ -826,8 +807,8 @@ protected synchronized final TableOperationType getOperationType() { * if an error occurs while parsing the Json, if Json is used. */ protected TableResult parseResponse(final InputStream inStream, final int httpStatusCode, String etagFromHeader, - final OperationContext opContext, final TableRequestOptions options) throws XMLStreamException, - InstantiationException, IllegalAccessException, StorageException, JsonParseException, IOException { + final OperationContext opContext, final TableRequestOptions options) throws InstantiationException, + IllegalAccessException, StorageException, JsonParseException, IOException { TableResult resObj; if (this.opType == TableOperationType.INSERT && this.echoContent) { diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/table/TablePayloadFormat.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/table/TablePayloadFormat.java index 7aea50301..355621250 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/table/TablePayloadFormat.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/table/TablePayloadFormat.java @@ -19,15 +19,6 @@ * Describes the payload formats supported for Tables. */ public enum TablePayloadFormat { - - /** - * Use AtomPub. - * - * @deprecated Deprecated as of 0.7.0 in favor of Json format. - */ - @Deprecated - AtomPub, - /** * Use JSON with full metadata. */ diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/table/TableRequest.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/table/TableRequest.java index 5a4cc28ad..e6a9cd262 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/table/TableRequest.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/table/TableRequest.java @@ -156,7 +156,8 @@ private static HttpURLConnection coreCreate(final URI rootUri, final TableReques opContext); setAcceptHeaderForHttpWebRequest(retConnection, tableOptions.getTablePayloadFormat()); - setContentTypeForHttpWebRequest(retConnection, tableOptions.getTablePayloadFormat()); + retConnection.setRequestProperty(Constants.HeaderConstants.CONTENT_TYPE, + TableConstants.HeaderConstants.JSON_CONTENT_TYPE); retConnection.setRequestProperty(TableConstants.HeaderConstants.MAX_DATA_SERVICE_VERSION, TableConstants.HeaderConstants.MAX_DATA_SERVICE_VERSION_VALUE); @@ -455,14 +456,9 @@ public static HttpURLConnection getAcl(final URI rootUri, final TableRequestOpti return retConnection; } - @SuppressWarnings("deprecation") private static void setAcceptHeaderForHttpWebRequest(HttpURLConnection retConnection, TablePayloadFormat payloadFormat) { - if (payloadFormat == TablePayloadFormat.AtomPub) { - retConnection.setRequestProperty(Constants.HeaderConstants.ACCEPT, - TableConstants.HeaderConstants.ATOM_ACCEPT_TYPE); - } - else if (payloadFormat == TablePayloadFormat.JsonFullMetadata) { + if (payloadFormat == TablePayloadFormat.JsonFullMetadata) { retConnection.setRequestProperty(Constants.HeaderConstants.ACCEPT, TableConstants.HeaderConstants.JSON_FULL_METADATA_ACCEPT_TYPE); } @@ -476,19 +472,6 @@ else if (payloadFormat == TablePayloadFormat.JsonNoMetadata) { } } - @SuppressWarnings("deprecation") - private static void setContentTypeForHttpWebRequest(HttpURLConnection retConnection, - TablePayloadFormat payloadFormat) { - if (payloadFormat == TablePayloadFormat.AtomPub) { - retConnection.setRequestProperty(Constants.HeaderConstants.CONTENT_TYPE, - TableConstants.HeaderConstants.ATOM_CONTENT_TYPE); - } - else { - retConnection.setRequestProperty(Constants.HeaderConstants.CONTENT_TYPE, - TableConstants.HeaderConstants.JSON_CONTENT_TYPE); - } - } - /** * Private Default Constructor. */ diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/table/TableResult.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/table/TableResult.java index 86d9cc76d..8be6cf699 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/table/TableResult.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/table/TableResult.java @@ -157,7 +157,6 @@ protected void setResult(final Object result) { * An instance of an object implementing {@link TableEntity} to associate with the table operation. * @throws UnsupportedEncodingException */ - @SuppressWarnings("deprecation") protected void updateResultObject(final TableEntity ent) throws UnsupportedEncodingException { this.result = ent; ent.setEtag(this.etag); diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/table/TableServiceEntity.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/table/TableServiceEntity.java index be4c89e5b..014059ec9 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/table/TableServiceEntity.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/table/TableServiceEntity.java @@ -428,13 +428,12 @@ public void setRowKey(final String rowKey) { } /** - * Sets the timeStamp value for the entity. + * Sets the timeStamp value for the entity. Note that the timestamp property is a read-only property, + * set by the service only. * * @param timeStamp * A java.util.Date containing the timeStamp value for the entity. - * @deprecated as of 3.0.0. The timestamp property is a read-only property, set by the service only. */ - @Deprecated @Override public void setTimestamp(final Date timeStamp) { this.timeStamp = timeStamp; diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/table/TableStorageErrorDeserializer.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/table/TableStorageErrorDeserializer.java index 7f9fbfb00..85317004d 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/table/TableStorageErrorDeserializer.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/table/TableStorageErrorDeserializer.java @@ -20,17 +20,13 @@ import java.io.Reader; import java.util.HashMap; -import javax.xml.stream.XMLStreamConstants; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.XMLStreamReader; - import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; import com.microsoft.azure.storage.Constants; import com.microsoft.azure.storage.StorageExtendedErrorInformation; -import com.microsoft.azure.storage.core.SR; +import com.microsoft.azure.storage.core.JsonUtilities; import com.microsoft.azure.storage.core.StorageRequest; /*** @@ -38,46 +34,6 @@ */ final class TableStorageErrorDeserializer { - /** - * Gets the Extended Error information. - * - * @return the Extended Error information. - * - * @param reader - * the input stream to read error details from. - * @param format - * The {@link TablePayloadFormat} to use for parsing - * @throws XMLStreamException - * if an error occurs while accessing the stream with AtomPub. - * @throws IOException - * if an error occurs while accessing the stream with Json. - * @throws JsonParseException - * if an error occurs while parsing the stream. - */ - @SuppressWarnings("deprecation") - public static StorageExtendedErrorInformation getExtendedErrorInformation(final Reader reader, - final TablePayloadFormat format) throws JsonParseException, IOException, XMLStreamException { - if (format == TablePayloadFormat.AtomPub) { - XMLStreamReader xmlr = DeserializationHelper.createXMLStreamReaderFromReader(reader); - try { - return parseAtomResponse(xmlr); - } - finally { - xmlr.close(); - } - } - else { - JsonFactory jsonFactory = new JsonFactory(); - JsonParser parser = jsonFactory.createParser(reader); - try { - return parseJsonResponse(parser); - } - finally { - parser.close(); - } - } - } - /** * Parse the table extended error information from the response body. * @@ -86,245 +42,138 @@ public static StorageExtendedErrorInformation getExtendedErrorInformation(final * @return The {@link StorageExtendedErrorInformation} parsed from the body or null if parsing fails or the request * contains no body. */ - @SuppressWarnings({"deprecation" }) public static StorageExtendedErrorInformation parseErrorDetails(StorageRequest request) { try { if (request == null || request.getConnection().getErrorStream() == null) { return null; } - - final TablePayloadFormat format; - final String type = request.getConnection().getHeaderField(Constants.HeaderConstants.CONTENT_TYPE); - if (type != null && type.startsWith("application/json")) { - format = TablePayloadFormat.Json; - } else { - format = TablePayloadFormat.AtomPub; - } - return getExtendedErrorInformation(new InputStreamReader(request.getConnection().getErrorStream()), format); + return getExtendedErrorInformation(new InputStreamReader(request.getConnection().getErrorStream()), TablePayloadFormat.Json); } catch (Exception e) { return null; } } /** - * Parses the error exception details from the Json-formatted response. + * Gets the Extended Error information. * - * @param parser - * the {@link JsonParser} to use for parsing + * @return the Extended Error information. + * + * @param reader + * the input stream to read error details from. + * @param format + * The {@link TablePayloadFormat} to use for parsing * @throws IOException * if an error occurs while accessing the stream with Json. * @throws JsonParseException * if an error occurs while parsing the stream. */ - private static HashMap parseJsonErrorException(JsonParser parser) throws JsonParseException, - IOException { - HashMap additionalDetails = new HashMap(); - - parser.nextToken(); - ODataUtilities.assertIsStartObjectJsonToken(parser); - - parser.nextToken(); - ODataUtilities.assertIsFieldNameJsonToken(parser); + public static StorageExtendedErrorInformation getExtendedErrorInformation(final Reader reader, + final TablePayloadFormat format) throws JsonParseException, IOException { + JsonFactory jsonFactory = new JsonFactory(); + JsonParser parser = jsonFactory.createParser(reader); + try { + final StorageExtendedErrorInformation errorInfo = new StorageExtendedErrorInformation(); - while (parser.getCurrentToken() != JsonToken.END_OBJECT) { - if (parser.getCurrentName().equals(TableConstants.ErrorConstants.ERROR_MESSAGE)) { - parser.nextToken(); - additionalDetails.put(TableConstants.ErrorConstants.ERROR_MESSAGE, - new String[] { parser.getValueAsString() }); - } - else if (parser.getCurrentName().equals(TableConstants.ErrorConstants.ERROR_EXCEPTION_TYPE)) { - parser.nextToken(); - additionalDetails.put(TableConstants.ErrorConstants.ERROR_EXCEPTION_TYPE, - new String[] { parser.getValueAsString() }); - } - else if (parser.getCurrentName().equals(TableConstants.ErrorConstants.ERROR_EXCEPTION_STACK_TRACE)) { + if (!parser.hasCurrentToken()) { parser.nextToken(); - additionalDetails - .put(Constants.ERROR_EXCEPTION_STACK_TRACE, new String[] { parser.getValueAsString() }); } - parser.nextToken(); - } - - return additionalDetails; - } - /** - * Parses the extended error information from the Json-formatted response. - * - * @throws IOException - * if an error occurs while accessing the stream with Json. - * @throws JsonParseException - * if an error occurs while parsing the stream. - */ - private static StorageExtendedErrorInformation parseJsonResponse(JsonParser parser) throws JsonParseException, - IOException { - final StorageExtendedErrorInformation errorInfo = new StorageExtendedErrorInformation(); + JsonUtilities.assertIsStartObjectJsonToken(parser); - if (!parser.hasCurrentToken()) { parser.nextToken(); - } + JsonUtilities.assertIsFieldNameJsonToken(parser); + JsonUtilities.assertIsExpectedFieldName(parser, "odata.error"); - ODataUtilities.assertIsStartObjectJsonToken(parser); + // start getting extended error information + parser.nextToken(); + JsonUtilities.assertIsStartObjectJsonToken(parser); - parser.nextToken(); - ODataUtilities.assertIsFieldNameJsonToken(parser); - ODataUtilities.assertIsExpectedFieldName(parser, "odata.error"); + // get code + parser.nextValue(); + JsonUtilities.assertIsExpectedFieldName(parser, TableConstants.ErrorConstants.ERROR_CODE); + errorInfo.setErrorCode(parser.getValueAsString()); - // start getting extended error information - parser.nextToken(); - ODataUtilities.assertIsStartObjectJsonToken(parser); + // get message + parser.nextToken(); + JsonUtilities.assertIsFieldNameJsonToken(parser); + JsonUtilities.assertIsExpectedFieldName(parser, TableConstants.ErrorConstants.ERROR_MESSAGE); - // get code - parser.nextValue(); - ODataUtilities.assertIsExpectedFieldName(parser, TableConstants.ErrorConstants.ERROR_CODE); - errorInfo.setErrorCode(parser.getValueAsString()); + parser.nextToken(); + JsonUtilities.assertIsStartObjectJsonToken(parser); - // get message - parser.nextToken(); - ODataUtilities.assertIsFieldNameJsonToken(parser); - ODataUtilities.assertIsExpectedFieldName(parser, TableConstants.ErrorConstants.ERROR_MESSAGE); + parser.nextValue(); + JsonUtilities.assertIsExpectedFieldName(parser, "lang"); - parser.nextToken(); - ODataUtilities.assertIsStartObjectJsonToken(parser); + parser.nextValue(); + JsonUtilities.assertIsExpectedFieldName(parser, "value"); + errorInfo.setErrorMessage(parser.getValueAsString()); - parser.nextValue(); - ODataUtilities.assertIsExpectedFieldName(parser, "lang"); + parser.nextToken(); + JsonUtilities.assertIsEndObjectJsonToken(parser); - parser.nextValue(); - ODataUtilities.assertIsExpectedFieldName(parser, "value"); - errorInfo.setErrorMessage(parser.getValueAsString()); + parser.nextToken(); - parser.nextToken(); - ODataUtilities.assertIsEndObjectJsonToken(parser); + // get innererror if it exists + if (parser.getCurrentToken() == JsonToken.FIELD_NAME) { + JsonUtilities.assertIsExpectedFieldName(parser, TableConstants.ErrorConstants.INNER_ERROR); + errorInfo.getAdditionalDetails().putAll(parseJsonErrorException(parser)); + parser.nextToken(); + } - parser.nextToken(); + // end code object + JsonUtilities.assertIsEndObjectJsonToken(parser); - // get innererror if it exists - if (parser.getCurrentToken() == JsonToken.FIELD_NAME) { - ODataUtilities.assertIsExpectedFieldName(parser, TableConstants.ErrorConstants.INNER_ERROR); - errorInfo.getAdditionalDetails().putAll(parseJsonErrorException(parser)); + // end odata.error object parser.nextToken(); - } + JsonUtilities.assertIsEndObjectJsonToken(parser); - // end code object - ODataUtilities.assertIsEndObjectJsonToken(parser); - - // end odata.error object - parser.nextToken(); - ODataUtilities.assertIsEndObjectJsonToken(parser); - - return errorInfo; + return errorInfo; + } + finally { + parser.close(); + } } - + /** - * Parses the error exception details from the AtomPub-formatted response. + * Parses the error exception details from the Json-formatted response. * - * @param xmlr - * the XMLStreamReader to read from - * @throws XMLStreamException - * if an xml exception occurs + * @param parser + * the {@link JsonParser} to use for parsing + * @throws IOException + * if an error occurs while accessing the stream with Json. + * @throws JsonParseException + * if an error occurs while parsing the stream. */ - private static HashMap parseAtomErrorException(XMLStreamReader xmlr) throws XMLStreamException { - int eventType = xmlr.getEventType(); - - xmlr.require(XMLStreamConstants.START_ELEMENT, null, TableConstants.ErrorConstants.INNER_ERROR); - + private static HashMap parseJsonErrorException(JsonParser parser) throws JsonParseException, + IOException { HashMap additionalDetails = new HashMap(); - while (xmlr.hasNext()) { - eventType = xmlr.next(); - if (eventType == XMLStreamConstants.CHARACTERS) { - continue; - } - - final String name = xmlr.getName().getLocalPart().toString(); + parser.nextToken(); + JsonUtilities.assertIsStartObjectJsonToken(parser); - if (eventType == XMLStreamConstants.START_ELEMENT - && name.equals(TableConstants.ErrorConstants.ERROR_MESSAGE)) { - final String errorExceptionMessage = DeserializationHelper.readElementFromXMLReader(xmlr, name); - additionalDetails.put(Constants.ERROR_EXCEPTION_MESSAGE, new String[] { errorExceptionMessage }); + parser.nextToken(); + JsonUtilities.assertIsFieldNameJsonToken(parser); + while (parser.getCurrentToken() != JsonToken.END_OBJECT) { + if (parser.getCurrentName().equals(TableConstants.ErrorConstants.ERROR_MESSAGE)) { + parser.nextToken(); + additionalDetails.put(TableConstants.ErrorConstants.ERROR_MESSAGE, + new String[] { parser.getValueAsString() }); } - else if (eventType == XMLStreamConstants.START_ELEMENT - && name.equals(TableConstants.ErrorConstants.ERROR_EXCEPTION_STACK_TRACE)) { - final String errorExceptionStack = DeserializationHelper.readElementFromXMLReader(xmlr, name); - additionalDetails.put(Constants.ERROR_EXCEPTION_STACK_TRACE, new String[] { errorExceptionStack }); - } - else if (eventType == XMLStreamConstants.START_ELEMENT - && name.equals(TableConstants.ErrorConstants.ERROR_EXCEPTION_TYPE)) { - final String errorExceptionStack = DeserializationHelper.readElementFromXMLReader(xmlr, name); + else if (parser.getCurrentName().equals(TableConstants.ErrorConstants.ERROR_EXCEPTION_TYPE)) { + parser.nextToken(); additionalDetails.put(TableConstants.ErrorConstants.ERROR_EXCEPTION_TYPE, - new String[] { errorExceptionStack }); + new String[] { parser.getValueAsString() }); } - else if (eventType == XMLStreamConstants.END_ELEMENT) { - break; + else if (parser.getCurrentName().equals(TableConstants.ErrorConstants.ERROR_EXCEPTION_STACK_TRACE)) { + parser.nextToken(); + additionalDetails + .put(Constants.ERROR_EXCEPTION_STACK_TRACE, new String[] { parser.getValueAsString() }); } + parser.nextToken(); } - xmlr.require(XMLStreamConstants.END_ELEMENT, null, null); - return additionalDetails; } - - /** - * Parses the extended error information from the AtomPub-formatted response. - * - * @param xmlr - * the XMLStreamReader to read from - * @throws XMLStreamException - * if an xml exception occurs - */ - private static StorageExtendedErrorInformation parseAtomResponse(XMLStreamReader xmlr) throws XMLStreamException { - final StorageExtendedErrorInformation errorInfo = new StorageExtendedErrorInformation(); - - String tempParseString; - - // Start document - int eventType = xmlr.getEventType(); - xmlr.require(XMLStreamConstants.START_DOCUMENT, null, null); - - // 1. get Error Root Header - eventType = xmlr.next(); - xmlr.require(XMLStreamConstants.START_ELEMENT, null, null); - if (!xmlr.getName().getLocalPart().toString().equals(TableConstants.ErrorConstants.ERROR_ROOT_ELEMENT)) { - throw new XMLStreamException(SR.EXPECTED_START_ELEMENT_TO_EQUAL_ERROR); - } - - while (xmlr.hasNext()) { - eventType = xmlr.next(); - if (eventType == XMLStreamConstants.CHARACTERS) { - continue; - } - - if (eventType == XMLStreamConstants.END_ELEMENT) { - break; - } - - String name = xmlr.getName().getLocalPart().toString(); - - if (eventType == XMLStreamConstants.START_ELEMENT) { - - if (name.equals(TableConstants.ErrorConstants.ERROR_CODE)) { - errorInfo.setErrorCode(DeserializationHelper.readElementFromXMLReader(xmlr, name)); - } - else if (name.equals(TableConstants.ErrorConstants.ERROR_MESSAGE)) { - errorInfo.setErrorMessage(DeserializationHelper.readElementFromXMLReader(xmlr, name)); - } - else if (name.equals(TableConstants.ErrorConstants.INNER_ERROR)) { - // get error exception - errorInfo.getAdditionalDetails().putAll(parseAtomErrorException(xmlr)); - } - else { - // get additional details - tempParseString = DeserializationHelper.readElementFromXMLReader(xmlr, name); - - errorInfo.getAdditionalDetails().put(name, new String[] { tempParseString }); - - xmlr.require(XMLStreamConstants.END_ELEMENT, null, null); - } - } - } - return errorInfo; - } } diff --git a/pom.xml b/pom.xml index abd8001f5..6cb1b6dad 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ 4.0.0 com.microsoft.azure azure-storage - 3.1.0 + 4.0.0 jar Microsoft Azure Storage Client SDK @@ -43,12 +43,6 @@ - - junit - junit - 4.12 - test - com.fasterxml.jackson.core jackson-core @@ -64,6 +58,18 @@ commons-lang3 3.4 + + junit + junit + 4.12 + test + + + org.slf4j + slf4j-simple + 1.7.5 + test +