Skip to content

Commit 08284c0

Browse files
ibrandesCopilot
andauthored
STG100 - Blob Principal Bound Identity SAS (#46282)
* adding implementation and happy path test * adding more tests and recordings, and moving token extraction to common * re-adding recordings after pull * fixing string to sign test * forgot that token can't be recorded * reverting assets and moving query param * adding old assets tag back because im silly * Apply suggestion from @Copilot Co-authored-by: Copilot <[email protected]> * Apply suggestion from @Copilot Co-authored-by: Copilot <[email protected]> * Apply suggestion from @Copilot Co-authored-by: Copilot <[email protected]> * consolidating string to sign logic --------- Co-authored-by: Copilot <[email protected]>
1 parent f32d8a6 commit 08284c0

File tree

9 files changed

+416
-49
lines changed

9 files changed

+416
-49
lines changed

sdk/storage/azure-storage-blob/assets.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
"AssetsRepo": "Azure/azure-sdk-assets",
33
"AssetsRepoPrefixPath": "java",
44
"TagPrefix": "java/storage/azure-storage-blob",
5-
"Tag": "java/storage/azure-storage-blob_098e26235c"
5+
"Tag": "java/storage/azure-storage-blob_26d22d7fb8"
66
}

sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/implementation/util/BlobSasImplUtil.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ public class BlobSasImplUtil {
9595

9696
private String encryptionScope;
9797

98+
private String delegatedUserObjectId;
99+
98100
/**
99101
* Creates a new {@link BlobSasImplUtil} with the specified parameters
100102
*
@@ -140,6 +142,7 @@ public BlobSasImplUtil(BlobServiceSasSignatureValues sasValues, String container
140142
this.authorizedAadObjectId = sasValues.getPreauthorizedAgentObjectId();
141143
this.correlationId = sasValues.getCorrelationId();
142144
this.encryptionScope = encryptionScope;
145+
this.delegatedUserObjectId = sasValues.getDelegatedUserObjectId();
143146
}
144147

145148
/**
@@ -262,6 +265,8 @@ private String encode(UserDelegationKey userDelegationKey, String signature) {
262265
tryAppendQueryParameter(sb, Constants.UrlConstants.SAS_PREAUTHORIZED_AGENT_OBJECT_ID,
263266
this.authorizedAadObjectId);
264267
tryAppendQueryParameter(sb, Constants.UrlConstants.SAS_CORRELATION_ID, this.correlationId);
268+
tryAppendQueryParameter(sb, Constants.UrlConstants.SAS_DELEGATED_USER_OBJECT_ID,
269+
this.delegatedUserObjectId);
265270
}
266271
tryAppendQueryParameter(sb, Constants.UrlConstants.SAS_SIGNED_RESOURCE, this.resource);
267272
tryAppendQueryParameter(sb, Constants.UrlConstants.SAS_SIGNED_PERMISSIONS, this.permissions);
@@ -453,7 +458,7 @@ private String stringToSign(final UserDelegationKey key, String canonicalName) {
453458
this.authorizedAadObjectId == null ? "" : this.authorizedAadObjectId,
454459
"", /* suoid - empty since this applies to HNS only accounts. */
455460
this.correlationId == null ? "" : this.correlationId, "", /* new schema 2025-07-05 */
456-
"", /* new schema 2025-07-05 */
461+
this.delegatedUserObjectId == null ? "" : this.delegatedUserObjectId,
457462
this.sasIpRange == null ? "" : this.sasIpRange.toString(),
458463
this.protocol == null ? "" : this.protocol.toString(), VERSION, resource,
459464
versionSegment == null ? "" : versionSegment, this.encryptionScope == null ? "" : this.encryptionScope,

sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/sas/BlobServiceSasSignatureValues.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ public final class BlobServiceSasSignatureValues {
8282
private String preauthorizedAgentObjectId; /* saoid */
8383
private String correlationId;
8484
private String encryptionScope;
85+
private String delegatedUserObjectId;
8586

8687
/**
8788
* Creates an object with empty values for all fields.
@@ -575,6 +576,30 @@ public BlobServiceSasSignatureValues setCorrelationId(String correlationId) {
575576
return this;
576577
}
577578

579+
/**
580+
* Optional. Beginning in version 2025-07-05, this value specifies the Entra ID of the user that is authorized to
581+
* use the resulting SAS URL. The resulting SAS URL must be used in conjunction with an Entra ID token that has been
582+
* issued to the user specified in this value.
583+
*
584+
* @return The Entra ID of the user that is authorized to use the resulting SAS URL.
585+
*/
586+
public String getDelegatedUserObjectId() {
587+
return delegatedUserObjectId;
588+
}
589+
590+
/**
591+
* Optional. Beginning in version 2025-07-05, this value specifies the Entra ID of the user that is authorized to
592+
* use the resulting SAS URL. The resulting SAS URL must be used in conjunction with an Entra ID token that has been
593+
* issued to the user specified in this value.
594+
*
595+
* @param delegatedUserObjectId The Entra ID of the user that is authorized to use the resulting SAS URL.
596+
* @return the updated BlobServiceSasSignatureValues object
597+
*/
598+
public BlobServiceSasSignatureValues setDelegatedUserObjectId(String delegatedUserObjectId) {
599+
this.delegatedUserObjectId = delegatedUserObjectId;
600+
return this;
601+
}
602+
578603
/**
579604
* Uses an account's shared key credential to sign these signature values to produce the proper SAS query
580605
* parameters.
@@ -754,6 +779,7 @@ private String stringToSign(final UserDelegationKey key, String canonicalName) {
754779
key.getSignedExpiry() == null ? "" : Constants.ISO_8601_UTC_DATE_FORMATTER.format(key.getSignedExpiry()),
755780
key.getSignedService() == null ? "" : key.getSignedService(),
756781
key.getSignedVersion() == null ? "" : key.getSignedVersion(),
782+
this.delegatedUserObjectId == null ? "" : this.delegatedUserObjectId,
757783
this.sasIpRange == null ? "" : this.sasIpRange.toString(),
758784
this.protocol == null ? "" : this.protocol.toString(),
759785
VERSION_DEPRECATED_USER_DELEGATION_SAS_STRING_TO_SIGN, /* Pin down to version so old string to sign works. */

sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobTestBase.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -772,15 +772,19 @@ protected void liveTestScenarioWithRetry(Runnable runnable) {
772772
}
773773

774774
int retry = 0;
775-
while (retry < 5) {
775+
776+
// Try up to 5 times (4 retries + 1 final attempt)
777+
while (retry < 4) {
776778
try {
777779
runnable.run();
778-
break;
780+
return; // success
779781
} catch (Exception ex) {
780782
retry++;
781783
sleepIfRunningAgainstService(5000);
782784
}
783785
}
786+
// Final attempt (5th try)
787+
runnable.run();
784788
}
785789

786790
protected HttpPipelinePolicy getPerCallVersionPolicy() {

0 commit comments

Comments
 (0)