Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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"
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,17 @@
import com.azure.storage.common.sas.SasProtocol;

import java.time.OffsetDateTime;
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;

import static com.azure.storage.common.implementation.SasImplUtils.formatQueryParameterDate;
import static com.azure.storage.common.implementation.SasImplUtils.formatRequestHeadersForSasSigning;
import static com.azure.storage.common.implementation.SasImplUtils.formatRequestQueryParametersForSasSigning;
import static com.azure.storage.common.implementation.SasImplUtils.tryAppendQueryParameter;

/**
* This class provides helper methods for common blob service sas patterns.
*
* RESERVED FOR INTERNAL USE.
*/
public class BlobSasImplUtil {
Expand Down Expand Up @@ -58,44 +60,27 @@ public class BlobSasImplUtil {
.get(Constants.PROPERTY_AZURE_STORAGE_SAS_SERVICE_VERSION, BlobServiceVersion.getLatest().getVersion());

private SasProtocol protocol;

private OffsetDateTime startTime;

private OffsetDateTime expiryTime;

private String permissions;

private SasIpRange sasIpRange;

private String containerName;

private String blobName;

private String resource;

private String snapshotId;

private String versionId;

private String identifier;

private String cacheControl;

private String contentDisposition;

private String contentEncoding;

private String contentLanguage;

private String contentType;

private String authorizedAadObjectId;

private String correlationId;

private String encryptionScope;

private String delegatedUserObjectId;
private Map<String, String> requestHeaders;
private Map<String, String> requestQueryParameters;

/**
* Creates a new {@link BlobSasImplUtil} with the specified parameters
Expand Down Expand Up @@ -143,6 +128,8 @@ public BlobSasImplUtil(BlobServiceSasSignatureValues sasValues, String container
this.correlationId = sasValues.getCorrelationId();
this.encryptionScope = encryptionScope;
this.delegatedUserObjectId = sasValues.getDelegatedUserObjectId();
this.requestHeaders = sasValues.getRequestHeaders();
this.requestQueryParameters = sasValues.getRequestQueryParameters();
}

/**
Expand Down Expand Up @@ -272,31 +259,34 @@ private String encode(UserDelegationKey userDelegationKey, String signature) {
tryAppendQueryParameter(sb, Constants.UrlConstants.SAS_SIGNED_PERMISSIONS, this.permissions);
tryAppendQueryParameter(sb, Constants.UrlConstants.SAS_SIGNATURE, signature);
tryAppendQueryParameter(sb, Constants.UrlConstants.SAS_ENCRYPTION_SCOPE, this.encryptionScope);
tryAppendQueryParameter(sb, Constants.UrlConstants.SAS_REQUEST_HEADERS,
formatRequestHeadersForSasSigning(this.requestHeaders));
tryAppendQueryParameter(sb, Constants.UrlConstants.SAS_REQUEST_QUERY_PARAMETERS,
formatRequestQueryParametersForSasSigning(this.requestQueryParameters));
tryAppendQueryParameter(sb, Constants.UrlConstants.SAS_CACHE_CONTROL, this.cacheControl);
tryAppendQueryParameter(sb, Constants.UrlConstants.SAS_CONTENT_DISPOSITION, this.contentDisposition);
tryAppendQueryParameter(sb, Constants.UrlConstants.SAS_CONTENT_ENCODING, this.contentEncoding);
tryAppendQueryParameter(sb, Constants.UrlConstants.SAS_CONTENT_LANGUAGE, this.contentLanguage);
tryAppendQueryParameter(sb, Constants.UrlConstants.SAS_CONTENT_TYPE, this.contentType);

return sb.toString();

}

/**
* Ensures that the builder's properties are in a consistent state.
*
* 1. If there is no version, use latest.
* <p> 1. If there is no version, use latest.
* 2. If there is no identifier set, ensure expiryTime and permissions are set.
* 3. Resource name is chosen by:
* a. If "BlobName" is _not_ set, it is a container resource.
* b. Otherwise, if "SnapshotId" is set, it is a blob snapshot resource.
* c. Otherwise, if "VersionId" is set, it is a blob version resource.
* d. Otherwise, it is a blob resource.
* 4. Reparse permissions depending on what the resource is. If it is an unrecognized resource, do nothing.
* 4. Reparse permissions depending on what the resource is. If it is an unrecognized resource, do nothing. </p>
*
* Taken from:
* https://github.com/Azure/azure-storage-blob-go/blob/master/azblob/sas_service.go#L33
* https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/storage/Azure.Storage.Blobs/src/Sas/BlobSasBuilder.cs
* <a href="https://github.com/Azure/azure-storage-blob-go/blob/master/azblob/sas_service.go#L33">sas_service.go</a>
* <a href="https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/storage/Azure.Storage.Blobs/src/Sas/BlobSasBuilder.cs">BlobSasBuilder.cs</a>
*/
public void ensureState() {
if (identifier == null) {
Expand Down Expand Up @@ -443,6 +433,30 @@ private String stringToSign(final UserDelegationKey key, String canonicalName) {
this.contentEncoding == null ? "" : this.contentEncoding,
this.contentLanguage == null ? "" : this.contentLanguage,
this.contentType == null ? "" : this.contentType);
} else if (VERSION.compareTo(BlobServiceVersion.V2026_02_06.getVersion()) <= 0) {
return String.join("\n", this.permissions == null ? "" : this.permissions,
this.startTime == null ? "" : Constants.ISO_8601_UTC_DATE_FORMATTER.format(this.startTime),
this.expiryTime == null ? "" : Constants.ISO_8601_UTC_DATE_FORMATTER.format(this.expiryTime),
canonicalName, key.getSignedObjectId() == null ? "" : key.getSignedObjectId(),
key.getSignedTenantId() == null ? "" : key.getSignedTenantId(),
key.getSignedStart() == null ? "" : Constants.ISO_8601_UTC_DATE_FORMATTER.format(key.getSignedStart()),
key.getSignedExpiry() == null
? ""
: Constants.ISO_8601_UTC_DATE_FORMATTER.format(key.getSignedExpiry()),
key.getSignedService() == null ? "" : key.getSignedService(),
key.getSignedVersion() == null ? "" : key.getSignedVersion(),
this.authorizedAadObjectId == null ? "" : this.authorizedAadObjectId,
"", /* suoid - empty since this applies to HNS only accounts. */
this.correlationId == null ? "" : this.correlationId, "", /* new schema 2025-07-05 */
this.delegatedUserObjectId == null ? "" : this.delegatedUserObjectId,
this.sasIpRange == null ? "" : this.sasIpRange.toString(),
this.protocol == null ? "" : this.protocol.toString(), VERSION, resource,
versionSegment == null ? "" : versionSegment, this.encryptionScope == null ? "" : this.encryptionScope,
this.cacheControl == null ? "" : this.cacheControl,
this.contentDisposition == null ? "" : this.contentDisposition,
this.contentEncoding == null ? "" : this.contentEncoding,
this.contentLanguage == null ? "" : this.contentLanguage,
this.contentType == null ? "" : this.contentType);
} else {
return String.join("\n", this.permissions == null ? "" : this.permissions,
this.startTime == null ? "" : Constants.ISO_8601_UTC_DATE_FORMATTER.format(this.startTime),
Expand All @@ -462,6 +476,10 @@ private String stringToSign(final UserDelegationKey key, String canonicalName) {
this.sasIpRange == null ? "" : this.sasIpRange.toString(),
this.protocol == null ? "" : this.protocol.toString(), VERSION, resource,
versionSegment == null ? "" : versionSegment, this.encryptionScope == null ? "" : this.encryptionScope,
this.requestHeaders == null ? "" : formatRequestHeadersForSasSigning(this.requestHeaders),
this.requestQueryParameters == null
? ""
: formatRequestQueryParametersForSasSigning(this.requestQueryParameters),
this.cacheControl == null ? "" : this.cacheControl,
this.contentDisposition == null ? "" : this.contentDisposition,
this.contentEncoding == null ? "" : this.contentEncoding,
Expand All @@ -472,15 +490,16 @@ private String stringToSign(final UserDelegationKey key, String canonicalName) {

/**
* Gets the resource string for SAS token signing.
* @return
*
* @return The resource string.
*/
public String getResource() {
return this.resource;
}

/**
* Gets the permissions string for SAS token signing.
* @return
* @return The permissions string.
*/
public String getPermissions() {
return this.permissions;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import com.azure.storage.common.sas.SasProtocol;

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

/**
* Used to initialize parameters for a Shared Access Signature (SAS) for an Azure Blob Storage service. Once all the
Expand Down Expand Up @@ -83,6 +84,8 @@ public final class BlobServiceSasSignatureValues {
private String correlationId;
private String encryptionScope;
private String delegatedUserObjectId;
private Map<String, String> requestHeaders;
private Map<String, String> requestQueryParameters;

/**
* Creates an object with empty values for all fields.
Expand Down Expand Up @@ -600,6 +603,50 @@ public BlobServiceSasSignatureValues setDelegatedUserObjectId(String delegatedUs
return this;
}

/**
* Optional. Beginning in version 2026-04-06, this value specifies Custom Request Headers to include in the SAS.
* Any usage of the SAS must include these headers and values in the request.
*
* @return The custom request headers to be set when the SAS is used.
*/
public Map<String, String> getRequestHeaders() {
return requestHeaders;
}

/**
* Optional. Beginning in version 2026-04-06, this value specifies Custom Request Headers to include in the SAS.
* Any usage of the SAS must include these headers and values in the request.
*
* @param requestHeaders The custom request headers to be set when the SAS is used.
* @return the updated BlobServiceSasSignatureValues object
*/
public BlobServiceSasSignatureValues setRequestHeaders(Map<String, String> requestHeaders) {
this.requestHeaders = requestHeaders;
return this;
}

/**
* Optional. Beginning in version 2026-04-06, this value specifies Custom Request Query Parameters to include in
* the SAS. Any usage of the SAS must include these query parameters and values in the request.
*
* @return The custom query parameters to be set when the SAS is used.
*/
public Map<String, String> getRequestQueryParameters() {
return requestQueryParameters;
}

/**
* Optional. Beginning in version 2026-04-06, this value specifies Custom Request Query Parameters to include in
* the SAS. Any usage of the SAS must include these query parameters and values in the request.
*
* @param requestQueryParameters The custom query parameters to be set when the SAS is used.
* @return the updated BlobServiceSasSignatureValues object
*/
public BlobServiceSasSignatureValues setRequestQueryParameters(Map<String, String> requestQueryParameters) {
this.requestQueryParameters = requestQueryParameters;
return this;
}

/**
* Uses an account's shared key credential to sign these signature values to produce the proper SAS query
* parameters.
Expand Down Expand Up @@ -713,8 +760,8 @@ public BlobServiceSasQueryParameters generateSasQueryParameters(UserDelegationKe
* 3. Reparse permissions depending on what the resource is. If it is an unrecognised resource, do nothing.
* <p>
* Taken from:
* https://github.com/Azure/azure-storage-blob-go/blob/master/azblob/sas_service.go#L33
* https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/storage/Azure.Storage.Blobs/src/Sas/BlobSasBuilder.cs
* <a href="https://github.com/Azure/azure-storage-blob-go/blob/master/azblob/sas_service.go#L33">sas_service.go</a>
* <a href="https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/storage/Azure.Storage.Blobs/src/Sas/BlobSasBuilder.cs">BlobSasBuilder.cs</a>
*/
private void ensureState() {
if (CoreUtils.isNullOrEmpty(blobName)) {
Expand Down
Loading
Loading