Skip to content

Commit

Permalink
Added client side checks for account set props (#7426)
Browse files Browse the repository at this point in the history
  • Loading branch information
gapra-msft authored Jan 15, 2020
1 parent ee1925d commit b89d97c
Show file tree
Hide file tree
Showing 6 changed files with 277 additions and 1 deletion.
1 change: 1 addition & 0 deletions sdk/storage/azure-storage-blob/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## 12.3.0-beta.1 (Unreleased)
- Added ability to create service clients anonymously and should only be used to create anonymous container and blob clients. Anonymous service clients will throw on attempting to create network requests.
- Added an overload to listBlobs to include a continuation token.
- Added a check in BlobServiceClient.setAccountProperties to block invalid requests.

## 12.2.0 (2020-01-08)
This package's
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import com.azure.storage.blob.implementation.models.ServiceGetAccountInfoHeaders;
import com.azure.storage.blob.implementation.models.ServicesListBlobContainersSegmentResponse;
import com.azure.storage.blob.models.BlobContainerItem;
import com.azure.storage.blob.models.BlobCorsRule;
import com.azure.storage.blob.models.BlobRetentionPolicy;
import com.azure.storage.blob.models.BlobServiceProperties;
import com.azure.storage.blob.models.BlobServiceStatistics;
import com.azure.storage.blob.models.CpkInfo;
Expand All @@ -38,6 +40,8 @@

import java.time.Duration;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

Expand Down Expand Up @@ -361,6 +365,8 @@ Mono<Response<BlobServiceProperties>> getPropertiesWithResponse(Context context)
* <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/set-blob-service-properties">Azure Docs</a>.
* Note that setting the default service version has no effect when using this client because this client explicitly
* sets the version header on each request, overriding the default.
* <p>This method checks to ensure the properties being sent follow the specifications indicated in the Azure Docs.
* If CORS policies are set, CORS parameters that are not set default to the empty string.</p>
*
* <p><strong>Code Samples</strong></p>
*
Expand All @@ -382,6 +388,8 @@ public Mono<Void> setProperties(BlobServiceProperties properties) {
* <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/set-blob-service-properties">Azure Docs</a>.
* Note that setting the default service version has no effect when using this client because this client explicitly
* sets the version header on each request, overriding the default.
* <p>This method checks to ensure the properties being sent follow the specifications indicated in the Azure Docs.
* If CORS policies are set, CORS parameters that are not set default to the empty string.</p>
* <p><strong>Code Samples</strong></p>
*
* {@codesnippet com.azure.storage.blob.BlobServiceAsyncClient.setPropertiesWithResponse#BlobServiceProperties}
Expand All @@ -399,10 +407,99 @@ public Mono<Response<Void>> setPropertiesWithResponse(BlobServiceProperties prop

Mono<Response<Void>> setPropertiesWithResponse(BlobServiceProperties properties, Context context) {
throwOnAnonymousAccess();
return this.azureBlobStorage.services().setPropertiesWithRestResponseAsync(properties, null, null, context)
BlobServiceProperties finalProperties = null;
if (properties != null) {
finalProperties = new BlobServiceProperties();

// Logging
finalProperties.setLogging(properties.getLogging());
if (finalProperties.getLogging() != null) {
StorageImplUtils.assertNotNull("Logging Version", finalProperties.getLogging().getVersion());
validateRetentionPolicy(finalProperties.getLogging().getRetentionPolicy(), "Logging Retention Policy");
}

// Hour Metrics
finalProperties.setHourMetrics(properties.getHourMetrics());
if (finalProperties.getHourMetrics() != null) {
StorageImplUtils.assertNotNull("HourMetrics Version", finalProperties.getHourMetrics().getVersion());
validateRetentionPolicy(finalProperties.getHourMetrics().getRetentionPolicy(), "HourMetrics Retention "
+ "Policy");
if (finalProperties.getHourMetrics().isEnabled()) {
StorageImplUtils.assertNotNull("HourMetrics IncludeApis",
finalProperties.getHourMetrics().isIncludeApis());
}
}

// Minute Metrics
finalProperties.setMinuteMetrics(properties.getMinuteMetrics());
if (finalProperties.getMinuteMetrics() != null) {
StorageImplUtils.assertNotNull("MinuteMetrics Version",
finalProperties.getMinuteMetrics().getVersion());
validateRetentionPolicy(finalProperties.getMinuteMetrics().getRetentionPolicy(), "MinuteMetrics "
+ "Retention Policy");
if (finalProperties.getMinuteMetrics().isEnabled()) {
StorageImplUtils.assertNotNull("MinuteMetrics IncludeApis",
finalProperties.getHourMetrics().isIncludeApis());
}
}

// CORS
if (properties.getCors() != null) {
List<BlobCorsRule> corsRules = new ArrayList<>();
for (BlobCorsRule rule : properties.getCors()) {
corsRules.add(validatedCorsRule(rule));
}
finalProperties.setCors(corsRules);
}

// Default Service Version
finalProperties.setDefaultServiceVersion(properties.getDefaultServiceVersion());

// Delete Retention Policy
finalProperties.setDeleteRetentionPolicy(properties.getDeleteRetentionPolicy());
validateRetentionPolicy(finalProperties.getDeleteRetentionPolicy(), "DeleteRetentionPolicy Days");

// Static Website
finalProperties.setStaticWebsite(properties.getStaticWebsite());

}

return this.azureBlobStorage.services().setPropertiesWithRestResponseAsync(finalProperties, null, null, context)
.map(response -> new SimpleResponse<>(response, null));
}

/**
* Sets any null fields to "" since the service requires all Cors rules to be set if some are set.
* @param originalRule {@link BlobCorsRule}
* @return The validated {@link BlobCorsRule}
*/
private BlobCorsRule validatedCorsRule(BlobCorsRule originalRule) {
if (originalRule == null) {
return null;
}
BlobCorsRule validRule = new BlobCorsRule();
validRule.setAllowedHeaders(StorageImplUtils.emptyIfNull(originalRule.getAllowedHeaders()));
validRule.setAllowedMethods(StorageImplUtils.emptyIfNull(originalRule.getAllowedMethods()));
validRule.setAllowedOrigins(StorageImplUtils.emptyIfNull(originalRule.getAllowedOrigins()));
validRule.setExposedHeaders(StorageImplUtils.emptyIfNull(originalRule.getExposedHeaders()));
validRule.setMaxAgeInSeconds(originalRule.getMaxAgeInSeconds());
return validRule;
}

/**
* Validates a {@link BlobRetentionPolicy} according to service specs for set properties.
* @param retentionPolicy {@link BlobRetentionPolicy}
* @param policyName The name of the variable for errors.
*/
private void validateRetentionPolicy(BlobRetentionPolicy retentionPolicy, String policyName) {
if (retentionPolicy == null) {
return;
}
if (retentionPolicy.isEnabled()) {
StorageImplUtils.assertInBounds(policyName, retentionPolicy.getDays(), 1, 365);
}
}

/**
* Gets a user delegation key for use with this account's blob storage. Note: This method call is only valid when
* using {@link TokenCredential} in this object's {@link HttpPipeline}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,8 @@ public Response<BlobServiceProperties> getPropertiesWithResponse(Duration timeou
* <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/set-blob-service-properties">Azure Docs</a>.
* Note that setting the default service version has no effect when using this client because this client explicitly
* sets the version header on each request, overriding the default.
* <p>This method checks to ensure the properties being sent follow the specifications indicated in the Azure Docs.
* If CORS policies are set, CORS parameters that are not set default to the empty string.</p>
*
* <p><strong>Code Samples</strong></p>
*
Expand All @@ -247,6 +249,8 @@ public void setProperties(BlobServiceProperties properties) {
* <a href="https://docs.microsoft.com/en-us/rest/api/storageservices/set-blob-service-properties">Azure Docs</a>.
* Note that setting the default service version has no effect when using this client because this client explicitly
* sets the version header on each request, overriding the default.
* <p>This method checks to ensure the properties being sent follow the specifications indicated in the Azure Docs.
* If CORS policies are set, CORS parameters that are not set default to the empty string.</p>
*
* <p><strong>Code Samples</strong></p>
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,23 @@ class ServiceAPITest extends APISpec {
primaryBlobServiceClient.setPropertiesWithResponse(sentProperties, null, null).getStatusCode() == 202
}

def "Set props cors check"() {
setup:
def serviceProperties = primaryBlobServiceClient.getProperties()

// Some properties are not set and this test validates that they are not null when sent to the service
def rule = new BlobCorsRule()
rule.setAllowedOrigins("microsoft.com")
rule.setMaxAgeInSeconds(60)
rule.setAllowedMethods("GET")
rule.setAllowedHeaders("x-ms-version")

serviceProperties.setCors(Collections.singletonList(rule))

expect:
primaryBlobServiceClient.setPropertiesWithResponse(serviceProperties, null, null).getStatusCode() == 202
}

def "Set props error"() {
when:
getServiceClient(primaryCredential, "https://error.blob.core.windows.net")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
{
"networkCallRecords" : [ {
"Method" : "PUT",
"Uri" : "https://gaprastg71.blob.core.windows.net/jtcsetpropscorscheck0serviceapitestsetpropscorscheckd2d100161?restype=container",
"Headers" : {
"x-ms-version" : "2019-02-02",
"User-Agent" : "azsdk-java-azure-storage-blob/12.3.0-beta.1 (11.0.4; Windows 10 10.0)",
"x-ms-client-request-id" : "5d549ef1-ca87-4776-be9f-3b73920314a6"
},
"Response" : {
"x-ms-version" : "2019-02-02",
"Server" : "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0",
"ETag" : "0x8D799E4A7F4D6C3",
"Last-Modified" : "Wed, 15 Jan 2020 17:59:24 GMT",
"retry-after" : "0",
"Content-Length" : "0",
"StatusCode" : "201",
"x-ms-request-id" : "67fe4ef4-f01e-002d-44cd-cb2148000000",
"Date" : "Wed, 15 Jan 2020 17:59:23 GMT",
"x-ms-client-request-id" : "5d549ef1-ca87-4776-be9f-3b73920314a6"
},
"Exception" : null
}, {
"Method" : "PUT",
"Uri" : "https://gaprastg71.blob.core.windows.net?restype=service&comp=properties",
"Headers" : {
"x-ms-version" : "2019-02-02",
"User-Agent" : "azsdk-java-azure-storage-blob/12.3.0-beta.1 (11.0.4; Windows 10 10.0)",
"x-ms-client-request-id" : "ccf3bbf7-7dfa-463f-b195-500ae58eaf72",
"Content-Type" : "application/xml; charset=utf-8"
},
"Response" : {
"x-ms-version" : "2019-02-02",
"Server" : "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0",
"retry-after" : "0",
"Content-Length" : "0",
"StatusCode" : "202",
"x-ms-request-id" : "67fe4ef7-f01e-002d-45cd-cb2148000000",
"Date" : "Wed, 15 Jan 2020 17:59:25 GMT",
"x-ms-client-request-id" : "ccf3bbf7-7dfa-463f-b195-500ae58eaf72"
},
"Exception" : null
}, {
"Method" : "GET",
"Uri" : "https://gaprastg71.blob.core.windows.net?restype=service&comp=properties",
"Headers" : {
"x-ms-version" : "2019-02-02",
"User-Agent" : "azsdk-java-azure-storage-blob/12.3.0-beta.1 (11.0.4; Windows 10 10.0)",
"x-ms-client-request-id" : "ced55d39-dd7a-472c-8ec2-10a9a29fc758"
},
"Response" : {
"Transfer-Encoding" : "chunked",
"x-ms-version" : "2019-02-02",
"Server" : "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0",
"Vary" : "Origin",
"retry-after" : "0",
"StatusCode" : "200",
"x-ms-request-id" : "67fe4f0d-f01e-002d-4dcd-cb2148000000",
"Body" : "<?xml version=\"1.0\" encoding=\"utf-8\"?><StorageServiceProperties><Logging><Version>1.0</Version><Read>false</Read><Write>false</Write><Delete>false</Delete><RetentionPolicy><Enabled>false</Enabled></RetentionPolicy></Logging><HourMetrics><Version>1.0</Version><Enabled>false</Enabled><RetentionPolicy><Enabled>false</Enabled></RetentionPolicy></HourMetrics><MinuteMetrics><Version>1.0</Version><Enabled>false</Enabled><RetentionPolicy><Enabled>false</Enabled></RetentionPolicy></MinuteMetrics><Cors><CorsRule><AllowedMethods>GET</AllowedMethods><AllowedOrigins>microsoft.com</AllowedOrigins><AllowedHeaders>x-ms-version</AllowedHeaders><ExposedHeaders /><MaxAgeInSeconds>60</MaxAgeInSeconds></CorsRule></Cors><DeleteRetentionPolicy><Enabled>false</Enabled></DeleteRetentionPolicy><StaticWebsite><Enabled>false</Enabled></StaticWebsite><DefaultServiceVersion>2018-03-28</DefaultServiceVersion></StorageServiceProperties>",
"Date" : "Wed, 15 Jan 2020 17:59:25 GMT",
"x-ms-client-request-id" : "ced55d39-dd7a-472c-8ec2-10a9a29fc758",
"Content-Type" : "application/xml"
},
"Exception" : null
}, {
"Method" : "PUT",
"Uri" : "https://gaprastg71.blob.core.windows.net?restype=service&comp=properties",
"Headers" : {
"x-ms-version" : "2019-02-02",
"User-Agent" : "azsdk-java-azure-storage-blob/12.3.0-beta.1 (11.0.4; Windows 10 10.0)",
"x-ms-client-request-id" : "e0f37959-0eca-4653-b724-f88b30984501",
"Content-Type" : "application/xml; charset=utf-8"
},
"Response" : {
"x-ms-version" : "2019-02-02",
"Server" : "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0",
"retry-after" : "0",
"Content-Length" : "0",
"StatusCode" : "202",
"x-ms-request-id" : "67fe4f10-f01e-002d-4fcd-cb2148000000",
"Date" : "Wed, 15 Jan 2020 17:59:25 GMT",
"x-ms-client-request-id" : "e0f37959-0eca-4653-b724-f88b30984501"
},
"Exception" : null
}, {
"Method" : "PUT",
"Uri" : "https://gaprastg71.blob.core.windows.net?restype=service&comp=properties",
"Headers" : {
"x-ms-version" : "2019-02-02",
"User-Agent" : "azsdk-java-azure-storage-blob/12.3.0-beta.1 (11.0.4; Windows 10 10.0)",
"x-ms-client-request-id" : "4ec131b7-b87e-454d-8341-e6cc0f870a61",
"Content-Type" : "application/xml; charset=utf-8"
},
"Response" : {
"x-ms-version" : "2019-02-02",
"Server" : "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0",
"retry-after" : "0",
"Content-Length" : "0",
"StatusCode" : "202",
"x-ms-request-id" : "67fe4f12-f01e-002d-50cd-cb2148000000",
"Date" : "Wed, 15 Jan 2020 17:59:25 GMT",
"x-ms-client-request-id" : "4ec131b7-b87e-454d-8341-e6cc0f870a61"
},
"Exception" : null
}, {
"Method" : "GET",
"Uri" : "https://gaprastg71.blob.core.windows.net?prefix=jtcsetpropscorscheck&comp=list",
"Headers" : {
"x-ms-version" : "2019-02-02",
"User-Agent" : "azsdk-java-azure-storage-blob/12.3.0-beta.1 (11.0.4; Windows 10 10.0)",
"x-ms-client-request-id" : "916fd24d-7ccd-4225-a5e2-d4167b3bd8d6"
},
"Response" : {
"Transfer-Encoding" : "chunked",
"x-ms-version" : "2019-02-02",
"Server" : "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0",
"Vary" : "Origin",
"retry-after" : "0",
"StatusCode" : "200",
"x-ms-request-id" : "67fe4f13-f01e-002d-51cd-cb2148000000",
"Body" : "<?xml version=\"1.0\" encoding=\"utf-8\"?><EnumerationResults ServiceEndpoint=\"https://gaprastg71.blob.core.windows.net/\"><Prefix>jtcsetpropscorscheck</Prefix><Containers><Container><Name>jtcsetpropscorscheck0serviceapitestsetpropscorscheckd2d100161</Name><Properties><Last-Modified>Wed, 15 Jan 2020 17:59:24 GMT</Last-Modified><Etag>\"0x8D799E4A7F4D6C3\"</Etag><LeaseStatus>unlocked</LeaseStatus><LeaseState>available</LeaseState><DefaultEncryptionScope>$account-encryption-key</DefaultEncryptionScope><DenyEncryptionScopeOverride>false</DenyEncryptionScopeOverride><HasImmutabilityPolicy>false</HasImmutabilityPolicy><HasLegalHold>false</HasLegalHold></Properties></Container></Containers><NextMarker /></EnumerationResults>",
"Date" : "Wed, 15 Jan 2020 17:59:25 GMT",
"x-ms-client-request-id" : "916fd24d-7ccd-4225-a5e2-d4167b3bd8d6",
"Content-Type" : "application/xml"
},
"Exception" : null
}, {
"Method" : "DELETE",
"Uri" : "https://gaprastg71.blob.core.windows.net/jtcsetpropscorscheck0serviceapitestsetpropscorscheckd2d100161?restype=container",
"Headers" : {
"x-ms-version" : "2019-02-02",
"User-Agent" : "azsdk-java-azure-storage-blob/12.3.0-beta.1 (11.0.4; Windows 10 10.0)",
"x-ms-client-request-id" : "a0d38f04-3502-44b0-a0de-5ff7662e7268"
},
"Response" : {
"x-ms-version" : "2019-02-02",
"Server" : "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0",
"retry-after" : "0",
"Content-Length" : "0",
"StatusCode" : "202",
"x-ms-request-id" : "67fe4f14-f01e-002d-52cd-cb2148000000",
"Date" : "Wed, 15 Jan 2020 17:59:26 GMT",
"x-ms-client-request-id" : "a0d38f04-3502-44b0-a0de-5ff7662e7268"
},
"Exception" : null
} ],
"variables" : [ "jtcsetpropscorscheck0serviceapitestsetpropscorscheckd2d100161" ]
}
Original file line number Diff line number Diff line change
Expand Up @@ -251,4 +251,13 @@ public static String getAccountName(URL url) {
}
return accountName;
}

/**
* Returns an empty string if value is {@code null}, otherwise returns value
* @param value The value to check and return.
* @return The value or empty string.
*/
public static String emptyIfNull(String value) {
return value == null ? "" : value;
}
}

0 comments on commit b89d97c

Please sign in to comment.