Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion sdk/storage/azure-storage-blob/assets.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "java",
"TagPrefix": "java/storage/azure-storage-blob",
"Tag": "java/storage/azure-storage-blob_f26563826e"
"Tag": "java/storage/azure-storage-blob_6631ad464e"
}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,17 @@ public static Map<String, String[]> parseQueryString(String queryParams) {
return retVals;
}

/**
* Formats request headers for SAS signing.
*
* @param requestHeaders The map of request headers to format.
* @return A formatted string with headers in the format "key:value" separated by newlines, or empty string if
* null/empty. Terminates each pair with a newline (\n).
* @see
* <a href="https://learn.microsoft.com/en-us/rest/api/storageservices/create-user-delegation-sas#version-2026-04-06-and-later-blob-storage-and-data-lake-storage">
* Version 2026-04-06 and later (Blob Storage and Data Lake Storage)</a>
*/

public static String formatRequestHeadersForSasSigning(Map<String, String> requestHeaders) {
if (requestHeaders == null || requestHeaders.isEmpty()) {
return "";
Expand All @@ -128,6 +139,16 @@ public static String formatRequestHeadersForSasSigning(Map<String, String> reque
return sb.toString();
}

/**
* Formats request headers for SAS signing.
*
* @param requestQueryParameters The map of request headers to format.
* @return A formatted string with query params in the format "key:value" separated by newlines, or empty string if
* null/empty. Prepends a newline character. Prefixes each pair with a newline (\n).
* @see
* <a href="https://learn.microsoft.com/en-us/rest/api/storageservices/create-user-delegation-sas#version-2026-04-06-and-later-blob-storage-and-data-lake-storage">
* Version 2026-04-06 and later (Blob Storage and Data Lake Storage)</a>
*/
public static String formatRequestQueryParametersForSasSigning(Map<String, String> requestQueryParameters) {
if (requestQueryParameters == null || requestQueryParameters.isEmpty()) {
return "";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.storage.common.test.shared;
import com.azure.storage.common.sas.SasIpRange;
import com.azure.storage.common.sas.SasProtocol;
import org.junit.jupiter.params.provider.Arguments;
import java.time.OffsetDateTime;
/**
* Helper class to build test arguments for regular SAS string-to-sign tests.
* This is the base class that contains common fields shared by both regular SAS and user delegation SAS.
* All fields default to null, so you only need to set the ones you're testing.
* <p>
* For user delegation SAS tests, use {@link UserDelegationSasTestData} which extends this class.
*/
public class SasTestData {
// Common fields for all SAS types
protected OffsetDateTime startTime;
protected SasIpRange ipRange;
protected SasProtocol protocol;
protected String cacheControl;
protected String disposition;
protected String encoding;
protected String language;
protected String type;
protected String expectedStringToSign;
// Regular SAS specific field
protected String identifier; // Signed identifier for regular SAS
public SasTestData setStartTime(OffsetDateTime startTime) {
this.startTime = startTime;
return this;
}
public SasTestData setIdentifier(String identifier) {
this.identifier = identifier;
return this;
}
public SasTestData setIpRange(SasIpRange ipRange) {
this.ipRange = ipRange;
return this;
}
public SasTestData setProtocol(SasProtocol protocol) {
this.protocol = protocol;
return this;
}
public SasTestData setCacheControl(String cacheControl) {
this.cacheControl = cacheControl;
return this;
}
public SasTestData setDisposition(String disposition) {
this.disposition = disposition;
return this;
}
public SasTestData setEncoding(String encoding) {
this.encoding = encoding;
return this;
}
public SasTestData setLanguage(String language) {
this.language = language;
return this;
}
public SasTestData setType(String type) {
this.type = type;
return this;
}
public SasTestData setExpectedStringToSign(String expectedStringToSign) {
this.expectedStringToSign = expectedStringToSign;
return this;
}
/**
* Converts to Arguments for regular SAS tests.
* Returns arguments in this order:
* startTime, identifier, ipRange, protocol, cacheControl, disposition, encoding, language, type, expectedStringToSign
*
* @return Arguments for parameterized tests matching the signature of regular SAS test methods
*/
public Arguments toArguments() {
return Arguments.of(startTime, identifier, ipRange, protocol, cacheControl, disposition, encoding, language,
type, expectedStringToSign);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.storage.common.test.shared;

import com.azure.storage.common.sas.SasIpRange;
import com.azure.storage.common.sas.SasProtocol;
import org.junit.jupiter.params.provider.Arguments;

import java.time.OffsetDateTime;
import java.util.Map;

/**
* Helper class to build test arguments for User Delegation SAS string-to-sign tests.
* Extends {@link SasTestData} to inherit common SAS fields.
* All fields default to null, so you only need to set the ones you're testing.
* <p>
* Note: User delegation SAS does NOT use the 'identifier' field (that's for regular SAS).
* Request headers and query parameters are only used in user delegation SAS.
* <p>
* For regular SAS tests, use {@link SasTestData} directly.
*/
public class UserDelegationSasTestData extends SasTestData {
// User delegation SAS specific fields
private String keyOid;
private String keyTid;
private OffsetDateTime keyStart;
private OffsetDateTime keyExpiry;
private String keyService;
private String keyVersion;
private String keyValue;
private Map<String, String> requestHeaders;
private Map<String, String> requestQueryParameters;
private String saoid;
private String suoid;
private String cid;

// Override parent setters to return UserDelegationSasTestData for fluent API
@Override
public UserDelegationSasTestData setStartTime(OffsetDateTime startTime) {
super.setStartTime(startTime);
return this;
}

@Override
public UserDelegationSasTestData setIpRange(SasIpRange ipRange) {
super.setIpRange(ipRange);
return this;
}

@Override
public UserDelegationSasTestData setProtocol(SasProtocol protocol) {
super.setProtocol(protocol);
return this;
}

@Override
public UserDelegationSasTestData setCacheControl(String cacheControl) {
super.setCacheControl(cacheControl);
return this;
}

@Override
public UserDelegationSasTestData setDisposition(String disposition) {
super.setDisposition(disposition);
return this;
}

@Override
public UserDelegationSasTestData setEncoding(String encoding) {
super.setEncoding(encoding);
return this;
}

@Override
public UserDelegationSasTestData setLanguage(String language) {
super.setLanguage(language);
return this;
}

@Override
public UserDelegationSasTestData setType(String type) {
super.setType(type);
return this;
}

@Override
public UserDelegationSasTestData setExpectedStringToSign(String expectedStringToSign) {
super.setExpectedStringToSign(expectedStringToSign);
return this;
}

// User delegation SAS specific setters

public UserDelegationSasTestData setKeyOid(String keyOid) {
this.keyOid = keyOid;
return this;
}

public UserDelegationSasTestData setKeyTid(String keyTid) {
this.keyTid = keyTid;
return this;
}

public UserDelegationSasTestData setKeyStart(OffsetDateTime keyStart) {
this.keyStart = keyStart;
return this;
}

public UserDelegationSasTestData setKeyExpiry(OffsetDateTime keyExpiry) {
this.keyExpiry = keyExpiry;
return this;
}

public UserDelegationSasTestData setKeyService(String keyService) {
this.keyService = keyService;
return this;
}

public UserDelegationSasTestData setKeyVersion(String keyVersion) {
this.keyVersion = keyVersion;
return this;
}

public UserDelegationSasTestData setKeyValue(String keyValue) {
this.keyValue = keyValue;
return this;
}

public UserDelegationSasTestData setRequestHeaders(Map<String, String> requestHeaders) {
this.requestHeaders = requestHeaders;
return this;
}

public UserDelegationSasTestData setRequestQueryParameters(Map<String, String> requestQueryParameters) {
this.requestQueryParameters = requestQueryParameters;
return this;
}

public UserDelegationSasTestData setSaoid(String saoid) {
this.saoid = saoid;
return this;
}

public UserDelegationSasTestData setSuoid(String suoid) {
this.suoid = suoid;
return this;
}

public UserDelegationSasTestData setCid(String cid) {
this.cid = cid;
return this;
}

/**
* Converts to Arguments for user delegation SAS tests.
* Returns arguments with or without request headers/query parameters based on the parameter.
*
* @param withHeadersAndParams Whether to include request headers and query parameters in the test data.
* @return Arguments for parameterized tests matching the signature of user delegation SAS test methods
*/
public Arguments toArguments(boolean withHeadersAndParams) {
if (withHeadersAndParams) {
return Arguments.of(startTime, keyOid, keyTid, keyStart, keyExpiry, keyService, keyVersion, keyValue,
ipRange, protocol, cacheControl, disposition, encoding, language, type, requestHeaders,
requestQueryParameters, saoid, suoid, cid, expectedStringToSign);
} else {
return Arguments.of(startTime, keyOid, keyTid, keyStart, keyExpiry, keyService, keyVersion, keyValue,
ipRange, protocol, cacheControl, disposition, encoding, language, type, saoid, suoid, cid, expectedStringToSign);
}
}

/**
* Converts to Arguments for user delegation SAS tests with request headers and query parameters.
* This is a convenience method that calls {@link #toArguments(boolean)} with true.
*
* @return Arguments for parameterized tests with headers and query parameters included
*/
public Arguments toArguments() {
return toArguments(true);
}
}


Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@

public class SasImplUtilsTests {

private static Map<String, String> requestHeaders;
private Map<String, String> requestHeaders;
private Map<String, String> requestQueryParams;

@BeforeEach
public void setup() {
requestHeaders = new HashMap<>();
requestQueryParams = new HashMap<>();
}

@Test
Expand All @@ -31,6 +33,15 @@ public void formatRequestHeadersForSasSigningEmptyReturnsEmptyString() {
assertEquals("", SasImplUtils.formatRequestHeadersForSasSigning(requestHeaders));
}

@Test
public void formatRequestHeadersForSasSigningReturnsWithLastCharAsNewline() {
requestHeaders.put("Some-Header", "someValue");
String headerString = SasImplUtils.formatRequestHeadersForSasSigning(requestHeaders);

assertNotEquals("", headerString);
assertEquals("\n", headerString.substring(headerString.length() - 1));
}

@Test
public void formatRequestHeadersForSasSigningPopulatedHeaders() {
requestHeaders.put(Constants.HeaderConstants.ENCRYPTION_KEY, "encryptionKeyValue");
Expand All @@ -53,4 +64,43 @@ public void formatRequestHeadersForSasSigningPopulatedHeaders() {
assertEquals(4, newLineCount);
assertEquals(sortedExpected, sortedHeaders);
}

@Test
public void formatRequestQueryParamsForSasSigningNullReturnsEmptyString() {
assertEquals("", SasImplUtils.formatRequestQueryParametersForSasSigning(null));
}

@Test
public void formatRequestQueryParamsForSasSigningEmptyReturnsEmptyString() {
assertEquals("", SasImplUtils.formatRequestQueryParametersForSasSigning(requestQueryParams));
}

@Test
public void formatRequestQueryParamsForSasSigningReturnsWithFirstCharAsNewline() {
requestQueryParams.put("someParam", "someValue");

String queryParamString = SasImplUtils.formatRequestQueryParametersForSasSigning(requestQueryParams);

assertNotEquals("", queryParamString);
assertEquals("\n", queryParamString.substring(0, 1));
}

@Test
public void formatRequestQueryParamsForSasSigningPopulatedParams() {
requestQueryParams.put("paramA", "valueA");
requestQueryParams.put("paramB", "valueB");
requestQueryParams.put("paramC", "valueC");
String expected = "\nparamA:valueA\nparamB:valueB\nparamC:valueC";

String queryParams = SasImplUtils.formatRequestQueryParametersForSasSigning(requestQueryParams);
Integer newLineCount
= Arrays.stream(queryParams.split("")).filter(s -> s.equals("\n")).collect(Collectors.toList()).size();
String sortedExpected
= "\n" + Arrays.stream(expected.substring(1).split("\n")).sorted().collect(Collectors.joining("\n"));
String sortedQueryParams
= "\n" + Arrays.stream(queryParams.substring(1).split("\n")).sorted().collect(Collectors.joining("\n"));

assertEquals(3, newLineCount);
assertEquals(sortedExpected, sortedQueryParams);
}
}
2 changes: 1 addition & 1 deletion sdk/storage/azure-storage-file-datalake/assets.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "java",
"TagPrefix": "java/storage/azure-storage-file-datalake",
"Tag": "java/storage/azure-storage-file-datalake_e7c65c4771"
"Tag": "java/storage/azure-storage-file-datalake_cc5d8d21d2"
}
Loading
Loading