Skip to content

Stg102/put block right branch#48390

Open
browndav-msft wants to merge 6 commits intoAzure:feature/storage/stg102basefrom
browndav-msft:stg102/putBlockRightBranch
Open

Stg102/put block right branch#48390
browndav-msft wants to merge 6 commits intoAzure:feature/storage/stg102basefrom
browndav-msft:stg102/putBlockRightBranch

Conversation

@browndav-msft
Copy link
Member

changes to this putBlock have been moved here because I selected main as the branch to merge into instead of stg102

@github-actions github-actions bot added the Storage Storage Service (Queues, Blobs, Files) label Mar 12, 2026
Copy link
Member

@ibrandes ibrandes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm! make sure to add the sanitization line back to the test base and re-record to get ci to pass though.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds SAS-focused tests in azure-storage-blob to validate Block Blob operations (upload/copy/put block+commit) succeed when a user-delegation SAS token has create-only permission, and updates the recorded test assets tag accordingly.

Changes:

  • Add sync tests covering create-only permission for upload, copy-from-URL, and stageBlock+commitBlockList flows.
  • Add async equivalents for the same create-only permission scenarios.
  • Update assets.json tag to point to new/updated recordings.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 7 comments.

File Description
sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/SasClientTests.java Adds new sync SAS tests for create-only permission covering upload/copy/commit block list scenarios.
sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/SasAsyncClientTests.java Adds new async SAS tests for create-only permission covering upload/copy/commit block list scenarios.
sdk/storage/azure-storage-blob/assets.json Updates the assets tag for test recordings.

Comment on lines +1306 to +1366
BlobServiceClient oauthService = getOAuthServiceClient();
String oauthContainerName = cc.getBlobContainerName();
BlobContainerClient oauthContainer = oauthService.getBlobContainerClient(oauthContainerName);

String oauthBlobName = generateBlobName();
OffsetDateTime expiryTime = testResourceNamer.now().plusDays(1);

UserDelegationKey key = oauthService.getUserDelegationKey(null, expiryTime);
key.setSignedTenantId(testResourceNamer.recordValueFromConfig(key.getSignedTenantId()));
String saoid = testResourceNamer.randomUuid();

BlobSasPermission permissions = new BlobSasPermission().setCreatePermission(true);
BlobServiceSasSignatureValues sasValues
= new BlobServiceSasSignatureValues(expiryTime, permissions).setPreauthorizedAgentObjectId(saoid);

String sasWithPermissions = oauthContainer.generateUserDelegationSas(sasValues, key);
BlockBlobClient blockClient
= instrument(new SpecializedBlobClientBuilder().endpoint(oauthContainer.getBlobContainerUrl())
.blobName(oauthBlobName)
.sasToken(sasWithPermissions)).buildBlockBlobClient();

assertDoesNotThrow(() -> blockClient.upload(DATA.getDefaultInputStream(), DATA.getDefaultDataSize()));
}

@Test
@RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2026-04-06")
public void transferBlobWithCreatePermission() {
BlobServiceClient oauthService = getOAuthServiceClient();
String containerName = cc.getBlobContainerName();
BlobContainerClient oauthContainer = oauthService.getBlobContainerClient(containerName);

String sourceBlobName = generateBlobName();
String destinationBlobName = generateBlobName();
OffsetDateTime expiryTime = testResourceNamer.now().plusDays(1);

// Upload source blob via OAuth client
BlockBlobClient sourceBlob = oauthContainer.getBlobClient(sourceBlobName).getBlockBlobClient();
sourceBlob.upload(DATA.getDefaultInputStream(), DATA.getDefaultDataSize());

UserDelegationKey key = oauthService.getUserDelegationKey(null, expiryTime);
key.setSignedTenantId(testResourceNamer.recordValueFromConfig(key.getSignedTenantId()));
String saoid = testResourceNamer.randomUuid();

// Create-only permission for destination blob
BlobSasPermission destinationPermissions = new BlobSasPermission().setCreatePermission(true);
BlobServiceSasSignatureValues sasValues = new BlobServiceSasSignatureValues(expiryTime, destinationPermissions)
.setPreauthorizedAgentObjectId(saoid);
String createPermissionsOnly = oauthContainer.generateUserDelegationSas(sasValues, key);
BlockBlobClient destinationClient
= instrument(new SpecializedBlobClientBuilder().endpoint(oauthContainer.getBlobContainerUrl())
.blobName(destinationBlobName)
.sasToken(createPermissionsOnly)).buildBlockBlobClient();

// Read permission for source blob
BlobSasPermission readPermission = new BlobSasPermission().setReadPermission(true);
BlobServiceSasSignatureValues readValues
= new BlobServiceSasSignatureValues(expiryTime, readPermission).setPreauthorizedAgentObjectId(saoid);
String readSas = oauthContainer.generateUserDelegationSas(readValues, key);
String sourceUrl = sourceBlob.getBlobUrl() + "?" + readSas;

assertDoesNotThrow(() -> destinationClient.copyFromUrl(sourceUrl));
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These new tests use user delegation key acquisition via getOAuthServiceClient(), which is subject to RBAC replication lag in live runs. In this class, other user-delegation SAS tests are consistently wrapped in liveTestScenarioWithRetry(...) (see blobSasUserDelegation, containerSasUserDelegation, etc.); these should follow the same pattern to avoid intermittent 403s/timeouts in CI.

Suggested change
BlobServiceClient oauthService = getOAuthServiceClient();
String oauthContainerName = cc.getBlobContainerName();
BlobContainerClient oauthContainer = oauthService.getBlobContainerClient(oauthContainerName);
String oauthBlobName = generateBlobName();
OffsetDateTime expiryTime = testResourceNamer.now().plusDays(1);
UserDelegationKey key = oauthService.getUserDelegationKey(null, expiryTime);
key.setSignedTenantId(testResourceNamer.recordValueFromConfig(key.getSignedTenantId()));
String saoid = testResourceNamer.randomUuid();
BlobSasPermission permissions = new BlobSasPermission().setCreatePermission(true);
BlobServiceSasSignatureValues sasValues
= new BlobServiceSasSignatureValues(expiryTime, permissions).setPreauthorizedAgentObjectId(saoid);
String sasWithPermissions = oauthContainer.generateUserDelegationSas(sasValues, key);
BlockBlobClient blockClient
= instrument(new SpecializedBlobClientBuilder().endpoint(oauthContainer.getBlobContainerUrl())
.blobName(oauthBlobName)
.sasToken(sasWithPermissions)).buildBlockBlobClient();
assertDoesNotThrow(() -> blockClient.upload(DATA.getDefaultInputStream(), DATA.getDefaultDataSize()));
}
@Test
@RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2026-04-06")
public void transferBlobWithCreatePermission() {
BlobServiceClient oauthService = getOAuthServiceClient();
String containerName = cc.getBlobContainerName();
BlobContainerClient oauthContainer = oauthService.getBlobContainerClient(containerName);
String sourceBlobName = generateBlobName();
String destinationBlobName = generateBlobName();
OffsetDateTime expiryTime = testResourceNamer.now().plusDays(1);
// Upload source blob via OAuth client
BlockBlobClient sourceBlob = oauthContainer.getBlobClient(sourceBlobName).getBlockBlobClient();
sourceBlob.upload(DATA.getDefaultInputStream(), DATA.getDefaultDataSize());
UserDelegationKey key = oauthService.getUserDelegationKey(null, expiryTime);
key.setSignedTenantId(testResourceNamer.recordValueFromConfig(key.getSignedTenantId()));
String saoid = testResourceNamer.randomUuid();
// Create-only permission for destination blob
BlobSasPermission destinationPermissions = new BlobSasPermission().setCreatePermission(true);
BlobServiceSasSignatureValues sasValues = new BlobServiceSasSignatureValues(expiryTime, destinationPermissions)
.setPreauthorizedAgentObjectId(saoid);
String createPermissionsOnly = oauthContainer.generateUserDelegationSas(sasValues, key);
BlockBlobClient destinationClient
= instrument(new SpecializedBlobClientBuilder().endpoint(oauthContainer.getBlobContainerUrl())
.blobName(destinationBlobName)
.sasToken(createPermissionsOnly)).buildBlockBlobClient();
// Read permission for source blob
BlobSasPermission readPermission = new BlobSasPermission().setReadPermission(true);
BlobServiceSasSignatureValues readValues
= new BlobServiceSasSignatureValues(expiryTime, readPermission).setPreauthorizedAgentObjectId(saoid);
String readSas = oauthContainer.generateUserDelegationSas(readValues, key);
String sourceUrl = sourceBlob.getBlobUrl() + "?" + readSas;
assertDoesNotThrow(() -> destinationClient.copyFromUrl(sourceUrl));
liveTestScenarioWithRetry(rbacRetry -> {
BlobServiceClient oauthService = getOAuthServiceClient();
String oauthContainerName = cc.getBlobContainerName();
BlobContainerClient oauthContainer = oauthService.getBlobContainerClient(oauthContainerName);
String oauthBlobName = generateBlobName();
OffsetDateTime expiryTime = testResourceNamer.now().plusDays(1);
UserDelegationKey key = oauthService.getUserDelegationKey(null, expiryTime);
key.setSignedTenantId(testResourceNamer.recordValueFromConfig(key.getSignedTenantId()));
String saoid = testResourceNamer.randomUuid();
BlobSasPermission permissions = new BlobSasPermission().setCreatePermission(true);
BlobServiceSasSignatureValues sasValues
= new BlobServiceSasSignatureValues(expiryTime, permissions).setPreauthorizedAgentObjectId(saoid);
String sasWithPermissions = oauthContainer.generateUserDelegationSas(sasValues, key);
BlockBlobClient blockClient
= instrument(new SpecializedBlobClientBuilder().endpoint(oauthContainer.getBlobContainerUrl())
.blobName(oauthBlobName)
.sasToken(sasWithPermissions)).buildBlockBlobClient();
assertDoesNotThrow(() -> blockClient.upload(DATA.getDefaultInputStream(), DATA.getDefaultDataSize()));
});
}
@Test
@RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2026-04-06")
public void transferBlobWithCreatePermission() {
liveTestScenarioWithRetry(rbacRetry -> {
BlobServiceClient oauthService = getOAuthServiceClient();
String containerName = cc.getBlobContainerName();
BlobContainerClient oauthContainer = oauthService.getBlobContainerClient(containerName);
String sourceBlobName = generateBlobName();
String destinationBlobName = generateBlobName();
OffsetDateTime expiryTime = testResourceNamer.now().plusDays(1);
// Upload source blob via OAuth client
BlockBlobClient sourceBlob = oauthContainer.getBlobClient(sourceBlobName).getBlockBlobClient();
sourceBlob.upload(DATA.getDefaultInputStream(), DATA.getDefaultDataSize());
UserDelegationKey key = oauthService.getUserDelegationKey(null, expiryTime);
key.setSignedTenantId(testResourceNamer.recordValueFromConfig(key.getSignedTenantId()));
String saoid = testResourceNamer.randomUuid();
// Create-only permission for destination blob
BlobSasPermission destinationPermissions = new BlobSasPermission().setCreatePermission(true);
BlobServiceSasSignatureValues sasValues
= new BlobServiceSasSignatureValues(expiryTime, destinationPermissions)
.setPreauthorizedAgentObjectId(saoid);
String createPermissionsOnly = oauthContainer.generateUserDelegationSas(sasValues, key);
BlockBlobClient destinationClient
= instrument(new SpecializedBlobClientBuilder().endpoint(oauthContainer.getBlobContainerUrl())
.blobName(destinationBlobName)
.sasToken(createPermissionsOnly)).buildBlockBlobClient();
// Read permission for source blob
BlobSasPermission readPermission = new BlobSasPermission().setReadPermission(true);
BlobServiceSasSignatureValues readValues
= new BlobServiceSasSignatureValues(expiryTime, readPermission).setPreauthorizedAgentObjectId(saoid);
String readSas = oauthContainer.generateUserDelegationSas(readValues, key);
String sourceUrl = sourceBlob.getBlobUrl() + "?" + readSas;
assertDoesNotThrow(() -> destinationClient.copyFromUrl(sourceUrl));
});

Copilot uses AI. Check for mistakes.
Comment on lines +592 to +617
BlobServiceAsyncClient oauthService = getOAuthServiceAsyncClient();
BlobContainerAsyncClient oauthContainer = oauthService.getBlobContainerAsyncClient(cc.getBlobContainerName());

String oauthBlobName = generateBlobName();
OffsetDateTime expiryTime = testResourceNamer.now().plusDays(1);

Mono<Void> response = oauthService.getUserDelegationKey(null, expiryTime).flatMap(key -> {
key.setSignedTenantId(testResourceNamer.recordValueFromConfig(key.getSignedTenantId()));
String saoid = testResourceNamer.randomUuid();

BlobSasPermission permissions = new BlobSasPermission().setCreatePermission(true);
BlobServiceSasSignatureValues sasValues
= new BlobServiceSasSignatureValues(expiryTime, permissions).setPreauthorizedAgentObjectId(saoid);

String sasWithPermissions = oauthContainer.generateUserDelegationSas(sasValues, key);

BlockBlobAsyncClient blockClient
= instrument(new SpecializedBlobClientBuilder().endpoint(oauthContainer.getBlobContainerUrl())
.blobName(oauthBlobName)
.sasToken(sasWithPermissions)).buildBlockBlobAsyncClient();

return blockClient.upload(DATA.getDefaultFlux(), DATA.getDefaultDataSize()).then();
});

StepVerifier.create(response).verifyComplete();

Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These newly added user-delegation SAS tests aren't wrapped in liveTestScenarioWithRetry(...). This suite generally wraps user-delegation SAS scenarios to mitigate RBAC replication lag (numerous existing tests in this file do so); without the retry wrapper these are more likely to flake in live CI.

Suggested change
BlobServiceAsyncClient oauthService = getOAuthServiceAsyncClient();
BlobContainerAsyncClient oauthContainer = oauthService.getBlobContainerAsyncClient(cc.getBlobContainerName());
String oauthBlobName = generateBlobName();
OffsetDateTime expiryTime = testResourceNamer.now().plusDays(1);
Mono<Void> response = oauthService.getUserDelegationKey(null, expiryTime).flatMap(key -> {
key.setSignedTenantId(testResourceNamer.recordValueFromConfig(key.getSignedTenantId()));
String saoid = testResourceNamer.randomUuid();
BlobSasPermission permissions = new BlobSasPermission().setCreatePermission(true);
BlobServiceSasSignatureValues sasValues
= new BlobServiceSasSignatureValues(expiryTime, permissions).setPreauthorizedAgentObjectId(saoid);
String sasWithPermissions = oauthContainer.generateUserDelegationSas(sasValues, key);
BlockBlobAsyncClient blockClient
= instrument(new SpecializedBlobClientBuilder().endpoint(oauthContainer.getBlobContainerUrl())
.blobName(oauthBlobName)
.sasToken(sasWithPermissions)).buildBlockBlobAsyncClient();
return blockClient.upload(DATA.getDefaultFlux(), DATA.getDefaultDataSize()).then();
});
StepVerifier.create(response).verifyComplete();
liveTestScenarioWithRetry(() -> {
BlobServiceAsyncClient oauthService = getOAuthServiceAsyncClient();
BlobContainerAsyncClient oauthContainer = oauthService.getBlobContainerAsyncClient(cc.getBlobContainerName());
String oauthBlobName = generateBlobName();
OffsetDateTime expiryTime = testResourceNamer.now().plusDays(1);
Mono<Void> response = oauthService.getUserDelegationKey(null, expiryTime).flatMap(key -> {
key.setSignedTenantId(testResourceNamer.recordValueFromConfig(key.getSignedTenantId()));
String saoid = testResourceNamer.randomUuid();
BlobSasPermission permissions = new BlobSasPermission().setCreatePermission(true);
BlobServiceSasSignatureValues sasValues
= new BlobServiceSasSignatureValues(expiryTime, permissions).setPreauthorizedAgentObjectId(saoid);
String sasWithPermissions = oauthContainer.generateUserDelegationSas(sasValues, key);
BlockBlobAsyncClient blockClient
= instrument(new SpecializedBlobClientBuilder().endpoint(oauthContainer.getBlobContainerUrl())
.blobName(oauthBlobName)
.sasToken(sasWithPermissions)).buildBlockBlobAsyncClient();
return blockClient.upload(DATA.getDefaultFlux(), DATA.getDefaultDataSize()).then();
});
StepVerifier.create(response).verifyComplete();
});

Copilot uses AI. Check for mistakes.
Comment on lines +623 to +661
BlobServiceAsyncClient oauthService = getOAuthServiceAsyncClient();
String containerName = ccAsync.getBlobContainerName();
BlobContainerAsyncClient oauthContainer = oauthService.getBlobContainerAsyncClient(containerName);

String sourceBlobName = generateBlobName();
String destinationBlobName = generateBlobName();
OffsetDateTime expiryTime = testResourceNamer.now().plusDays(1);

// Upload source blob via OAuth client
BlockBlobAsyncClient sourceBlob = oauthContainer.getBlobAsyncClient(sourceBlobName).getBlockBlobAsyncClient();
sourceBlob.upload(DATA.getDefaultFlux(), DATA.getDefaultDataSize()).block();

Mono<Void> response = oauthService.getUserDelegationKey(null, expiryTime).flatMap(key -> {

key.setSignedTenantId(testResourceNamer.recordValueFromConfig(key.getSignedTenantId()));
String saoid = testResourceNamer.randomUuid();

// Create-only permission for destination blob
BlobSasPermission destinationPermissions = new BlobSasPermission().setCreatePermission(true);
BlobServiceSasSignatureValues sasValues
= new BlobServiceSasSignatureValues(expiryTime, destinationPermissions)
.setPreauthorizedAgentObjectId(saoid);
String createPermissionsOnly = oauthContainer.generateUserDelegationSas(sasValues, key);
BlockBlobAsyncClient destinationClient
= instrument(new SpecializedBlobClientBuilder().endpoint(oauthContainer.getBlobContainerUrl())
.blobName(destinationBlobName)
.sasToken(createPermissionsOnly)).buildBlockBlobAsyncClient();

// Read permission for source blob
BlobSasPermission readPermission = new BlobSasPermission().setReadPermission(true);
BlobServiceSasSignatureValues readValues
= new BlobServiceSasSignatureValues(expiryTime, readPermission).setPreauthorizedAgentObjectId(saoid);
String readSas = oauthContainer.generateUserDelegationSas(readValues, key);
String sourceUrl = sourceBlob.getBlobUrl() + "?" + readSas;

return destinationClient.copyFromUrl(sourceUrl).then();
});

StepVerifier.create(response).verifyComplete();
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new user-delegation SAS copy scenario should be wrapped in liveTestScenarioWithRetry(...) for consistency with the rest of the file and to reduce live-test flakiness from RBAC replication lag when acquiring user delegation keys.

Suggested change
BlobServiceAsyncClient oauthService = getOAuthServiceAsyncClient();
String containerName = ccAsync.getBlobContainerName();
BlobContainerAsyncClient oauthContainer = oauthService.getBlobContainerAsyncClient(containerName);
String sourceBlobName = generateBlobName();
String destinationBlobName = generateBlobName();
OffsetDateTime expiryTime = testResourceNamer.now().plusDays(1);
// Upload source blob via OAuth client
BlockBlobAsyncClient sourceBlob = oauthContainer.getBlobAsyncClient(sourceBlobName).getBlockBlobAsyncClient();
sourceBlob.upload(DATA.getDefaultFlux(), DATA.getDefaultDataSize()).block();
Mono<Void> response = oauthService.getUserDelegationKey(null, expiryTime).flatMap(key -> {
key.setSignedTenantId(testResourceNamer.recordValueFromConfig(key.getSignedTenantId()));
String saoid = testResourceNamer.randomUuid();
// Create-only permission for destination blob
BlobSasPermission destinationPermissions = new BlobSasPermission().setCreatePermission(true);
BlobServiceSasSignatureValues sasValues
= new BlobServiceSasSignatureValues(expiryTime, destinationPermissions)
.setPreauthorizedAgentObjectId(saoid);
String createPermissionsOnly = oauthContainer.generateUserDelegationSas(sasValues, key);
BlockBlobAsyncClient destinationClient
= instrument(new SpecializedBlobClientBuilder().endpoint(oauthContainer.getBlobContainerUrl())
.blobName(destinationBlobName)
.sasToken(createPermissionsOnly)).buildBlockBlobAsyncClient();
// Read permission for source blob
BlobSasPermission readPermission = new BlobSasPermission().setReadPermission(true);
BlobServiceSasSignatureValues readValues
= new BlobServiceSasSignatureValues(expiryTime, readPermission).setPreauthorizedAgentObjectId(saoid);
String readSas = oauthContainer.generateUserDelegationSas(readValues, key);
String sourceUrl = sourceBlob.getBlobUrl() + "?" + readSas;
return destinationClient.copyFromUrl(sourceUrl).then();
});
StepVerifier.create(response).verifyComplete();
liveTestScenarioWithRetry(() -> {
BlobServiceAsyncClient oauthService = getOAuthServiceAsyncClient();
String containerName = ccAsync.getBlobContainerName();
BlobContainerAsyncClient oauthContainer = oauthService.getBlobContainerAsyncClient(containerName);
String sourceBlobName = generateBlobName();
String destinationBlobName = generateBlobName();
OffsetDateTime expiryTime = testResourceNamer.now().plusDays(1);
// Upload source blob via OAuth client
BlockBlobAsyncClient sourceBlob
= oauthContainer.getBlobAsyncClient(sourceBlobName).getBlockBlobAsyncClient();
sourceBlob.upload(DATA.getDefaultFlux(), DATA.getDefaultDataSize()).block();
Mono<Void> response = oauthService.getUserDelegationKey(null, expiryTime).flatMap(key -> {
key.setSignedTenantId(testResourceNamer.recordValueFromConfig(key.getSignedTenantId()));
String saoid = testResourceNamer.randomUuid();
// Create-only permission for destination blob
BlobSasPermission destinationPermissions = new BlobSasPermission().setCreatePermission(true);
BlobServiceSasSignatureValues sasValues
= new BlobServiceSasSignatureValues(expiryTime, destinationPermissions)
.setPreauthorizedAgentObjectId(saoid);
String createPermissionsOnly = oauthContainer.generateUserDelegationSas(sasValues, key);
BlockBlobAsyncClient destinationClient
= instrument(new SpecializedBlobClientBuilder().endpoint(oauthContainer.getBlobContainerUrl())
.blobName(destinationBlobName)
.sasToken(createPermissionsOnly)).buildBlockBlobAsyncClient();
// Read permission for source blob
BlobSasPermission readPermission = new BlobSasPermission().setReadPermission(true);
BlobServiceSasSignatureValues readValues = new BlobServiceSasSignatureValues(expiryTime, readPermission)
.setPreauthorizedAgentObjectId(saoid);
String readSas = oauthContainer.generateUserDelegationSas(readValues, key);
String sourceUrl = sourceBlob.getBlobUrl() + "?" + readSas;
return destinationClient.copyFromUrl(sourceUrl).then();
});
StepVerifier.create(response).verifyComplete();
});

Copilot uses AI. Check for mistakes.
Comment on lines +667 to +701
BlobServiceAsyncClient oauthService = getOAuthServiceAsyncClient();
String containerName = ccAsync.getBlobContainerName();
BlobContainerAsyncClient oauthContainer = oauthService.getBlobContainerAsyncClient(containerName);
String blockId = Base64.getEncoder().encodeToString("blockid".getBytes(StandardCharsets.UTF_8));
List<String> blockIds = new ArrayList<>();
blockIds.add(blockId);

String destinationBlobName = generateBlobName();
OffsetDateTime expiryTime = testResourceNamer.now().plusDays(1);

Mono<Void> response = oauthService.getUserDelegationKey(null, expiryTime).flatMap(key -> {

key.setSignedTenantId(testResourceNamer.recordValueFromConfig(key.getSignedTenantId()));
String saoid = testResourceNamer.randomUuid();

// Create-only permission for destination blob
BlobSasPermission destinationPermissions = new BlobSasPermission().setCreatePermission(true);
BlobServiceSasSignatureValues sasValues
= new BlobServiceSasSignatureValues(expiryTime, destinationPermissions)
.setPreauthorizedAgentObjectId(saoid);
String createPermissionsOnly = oauthContainer.generateUserDelegationSas(sasValues, key);
BlockBlobAsyncClient destinationClient
= instrument(new SpecializedBlobClientBuilder().endpoint(oauthContainer.getBlobContainerUrl())
.blobName(destinationBlobName)
.sasToken(createPermissionsOnly)).buildBlockBlobAsyncClient();

Flux<ByteBuffer> data = DATA.getDefaultFlux();

return destinationClient.stageBlock(blockId, data, DATA.getDefaultDataSize())
.then(destinationClient.commitBlockList(blockIds, false))
.then();
});

StepVerifier.create(response).verifyComplete();

Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new user-delegation SAS block staging/commit test should use the same liveTestScenarioWithRetry(...) wrapper as other user-delegation SAS tests in this file to avoid intermittent failures in live runs due to RBAC replication lag.

Suggested change
BlobServiceAsyncClient oauthService = getOAuthServiceAsyncClient();
String containerName = ccAsync.getBlobContainerName();
BlobContainerAsyncClient oauthContainer = oauthService.getBlobContainerAsyncClient(containerName);
String blockId = Base64.getEncoder().encodeToString("blockid".getBytes(StandardCharsets.UTF_8));
List<String> blockIds = new ArrayList<>();
blockIds.add(blockId);
String destinationBlobName = generateBlobName();
OffsetDateTime expiryTime = testResourceNamer.now().plusDays(1);
Mono<Void> response = oauthService.getUserDelegationKey(null, expiryTime).flatMap(key -> {
key.setSignedTenantId(testResourceNamer.recordValueFromConfig(key.getSignedTenantId()));
String saoid = testResourceNamer.randomUuid();
// Create-only permission for destination blob
BlobSasPermission destinationPermissions = new BlobSasPermission().setCreatePermission(true);
BlobServiceSasSignatureValues sasValues
= new BlobServiceSasSignatureValues(expiryTime, destinationPermissions)
.setPreauthorizedAgentObjectId(saoid);
String createPermissionsOnly = oauthContainer.generateUserDelegationSas(sasValues, key);
BlockBlobAsyncClient destinationClient
= instrument(new SpecializedBlobClientBuilder().endpoint(oauthContainer.getBlobContainerUrl())
.blobName(destinationBlobName)
.sasToken(createPermissionsOnly)).buildBlockBlobAsyncClient();
Flux<ByteBuffer> data = DATA.getDefaultFlux();
return destinationClient.stageBlock(blockId, data, DATA.getDefaultDataSize())
.then(destinationClient.commitBlockList(blockIds, false))
.then();
});
StepVerifier.create(response).verifyComplete();
liveTestScenarioWithRetry(() -> {
BlobServiceAsyncClient oauthService = getOAuthServiceAsyncClient();
String containerName = ccAsync.getBlobContainerName();
BlobContainerAsyncClient oauthContainer = oauthService.getBlobContainerAsyncClient(containerName);
String blockId = Base64.getEncoder().encodeToString("blockid".getBytes(StandardCharsets.UTF_8));
List<String> blockIds = new ArrayList<>();
blockIds.add(blockId);
String destinationBlobName = generateBlobName();
OffsetDateTime expiryTime = testResourceNamer.now().plusDays(1);
Mono<Void> response = oauthService.getUserDelegationKey(null, expiryTime).flatMap(key -> {
key.setSignedTenantId(testResourceNamer.recordValueFromConfig(key.getSignedTenantId()));
String saoid = testResourceNamer.randomUuid();
// Create-only permission for destination blob
BlobSasPermission destinationPermissions = new BlobSasPermission().setCreatePermission(true);
BlobServiceSasSignatureValues sasValues
= new BlobServiceSasSignatureValues(expiryTime, destinationPermissions)
.setPreauthorizedAgentObjectId(saoid);
String createPermissionsOnly = oauthContainer.generateUserDelegationSas(sasValues, key);
BlockBlobAsyncClient destinationClient
= instrument(new SpecializedBlobClientBuilder().endpoint(oauthContainer.getBlobContainerUrl())
.blobName(destinationBlobName)
.sasToken(createPermissionsOnly)).buildBlockBlobAsyncClient();
Flux<ByteBuffer> data = DATA.getDefaultFlux();
return destinationClient.stageBlock(blockId, data, DATA.getDefaultDataSize())
.then(destinationClient.commitBlockList(blockIds, false))
.then();
});
StepVerifier.create(response).verifyComplete();
});

Copilot uses AI. Check for mistakes.
Comment on lines +1333 to +1366
BlobServiceClient oauthService = getOAuthServiceClient();
String containerName = cc.getBlobContainerName();
BlobContainerClient oauthContainer = oauthService.getBlobContainerClient(containerName);

String sourceBlobName = generateBlobName();
String destinationBlobName = generateBlobName();
OffsetDateTime expiryTime = testResourceNamer.now().plusDays(1);

// Upload source blob via OAuth client
BlockBlobClient sourceBlob = oauthContainer.getBlobClient(sourceBlobName).getBlockBlobClient();
sourceBlob.upload(DATA.getDefaultInputStream(), DATA.getDefaultDataSize());

UserDelegationKey key = oauthService.getUserDelegationKey(null, expiryTime);
key.setSignedTenantId(testResourceNamer.recordValueFromConfig(key.getSignedTenantId()));
String saoid = testResourceNamer.randomUuid();

// Create-only permission for destination blob
BlobSasPermission destinationPermissions = new BlobSasPermission().setCreatePermission(true);
BlobServiceSasSignatureValues sasValues = new BlobServiceSasSignatureValues(expiryTime, destinationPermissions)
.setPreauthorizedAgentObjectId(saoid);
String createPermissionsOnly = oauthContainer.generateUserDelegationSas(sasValues, key);
BlockBlobClient destinationClient
= instrument(new SpecializedBlobClientBuilder().endpoint(oauthContainer.getBlobContainerUrl())
.blobName(destinationBlobName)
.sasToken(createPermissionsOnly)).buildBlockBlobClient();

// Read permission for source blob
BlobSasPermission readPermission = new BlobSasPermission().setReadPermission(true);
BlobServiceSasSignatureValues readValues
= new BlobServiceSasSignatureValues(expiryTime, readPermission).setPreauthorizedAgentObjectId(saoid);
String readSas = oauthContainer.generateUserDelegationSas(readValues, key);
String sourceUrl = sourceBlob.getBlobUrl() + "?" + readSas;

assertDoesNotThrow(() -> destinationClient.copyFromUrl(sourceUrl));
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new user-delegation SAS copy scenario should be wrapped in liveTestScenarioWithRetry(...) (as other user delegation SAS tests in this class are) to mitigate intermittent failures from RBAC replication lag when acquiring/using user delegation keys in live CI.

Suggested change
BlobServiceClient oauthService = getOAuthServiceClient();
String containerName = cc.getBlobContainerName();
BlobContainerClient oauthContainer = oauthService.getBlobContainerClient(containerName);
String sourceBlobName = generateBlobName();
String destinationBlobName = generateBlobName();
OffsetDateTime expiryTime = testResourceNamer.now().plusDays(1);
// Upload source blob via OAuth client
BlockBlobClient sourceBlob = oauthContainer.getBlobClient(sourceBlobName).getBlockBlobClient();
sourceBlob.upload(DATA.getDefaultInputStream(), DATA.getDefaultDataSize());
UserDelegationKey key = oauthService.getUserDelegationKey(null, expiryTime);
key.setSignedTenantId(testResourceNamer.recordValueFromConfig(key.getSignedTenantId()));
String saoid = testResourceNamer.randomUuid();
// Create-only permission for destination blob
BlobSasPermission destinationPermissions = new BlobSasPermission().setCreatePermission(true);
BlobServiceSasSignatureValues sasValues = new BlobServiceSasSignatureValues(expiryTime, destinationPermissions)
.setPreauthorizedAgentObjectId(saoid);
String createPermissionsOnly = oauthContainer.generateUserDelegationSas(sasValues, key);
BlockBlobClient destinationClient
= instrument(new SpecializedBlobClientBuilder().endpoint(oauthContainer.getBlobContainerUrl())
.blobName(destinationBlobName)
.sasToken(createPermissionsOnly)).buildBlockBlobClient();
// Read permission for source blob
BlobSasPermission readPermission = new BlobSasPermission().setReadPermission(true);
BlobServiceSasSignatureValues readValues
= new BlobServiceSasSignatureValues(expiryTime, readPermission).setPreauthorizedAgentObjectId(saoid);
String readSas = oauthContainer.generateUserDelegationSas(readValues, key);
String sourceUrl = sourceBlob.getBlobUrl() + "?" + readSas;
assertDoesNotThrow(() -> destinationClient.copyFromUrl(sourceUrl));
liveTestScenarioWithRetry(() -> {
BlobServiceClient oauthService = getOAuthServiceClient();
String containerName = cc.getBlobContainerName();
BlobContainerClient oauthContainer = oauthService.getBlobContainerClient(containerName);
String sourceBlobName = generateBlobName();
String destinationBlobName = generateBlobName();
OffsetDateTime expiryTime = testResourceNamer.now().plusDays(1);
// Upload source blob via OAuth client
BlockBlobClient sourceBlob = oauthContainer.getBlobClient(sourceBlobName).getBlockBlobClient();
sourceBlob.upload(DATA.getDefaultInputStream(), DATA.getDefaultDataSize());
UserDelegationKey key = oauthService.getUserDelegationKey(null, expiryTime);
key.setSignedTenantId(testResourceNamer.recordValueFromConfig(key.getSignedTenantId()));
String saoid = testResourceNamer.randomUuid();
// Create-only permission for destination blob
BlobSasPermission destinationPermissions = new BlobSasPermission().setCreatePermission(true);
BlobServiceSasSignatureValues sasValues = new BlobServiceSasSignatureValues(expiryTime, destinationPermissions)
.setPreauthorizedAgentObjectId(saoid);
String createPermissionsOnly = oauthContainer.generateUserDelegationSas(sasValues, key);
BlockBlobClient destinationClient
= instrument(new SpecializedBlobClientBuilder().endpoint(oauthContainer.getBlobContainerUrl())
.blobName(destinationBlobName)
.sasToken(createPermissionsOnly)).buildBlockBlobClient();
// Read permission for source blob
BlobSasPermission readPermission = new BlobSasPermission().setReadPermission(true);
BlobServiceSasSignatureValues readValues
= new BlobServiceSasSignatureValues(expiryTime, readPermission).setPreauthorizedAgentObjectId(saoid);
String readSas = oauthContainer.generateUserDelegationSas(readValues, key);
String sourceUrl = sourceBlob.getBlobUrl() + "?" + readSas;
assertDoesNotThrow(() -> destinationClient.copyFromUrl(sourceUrl));
});

Copilot uses AI. Check for mistakes.
Comment on lines +1372 to +1398
BlobServiceClient oauthService = getOAuthServiceClient();
String containerName = cc.getBlobContainerName();
BlobContainerClient oauthContainer = oauthService.getBlobContainerClient(containerName);
String blockId = Base64.getEncoder().encodeToString("blockid".getBytes(StandardCharsets.UTF_8));
List<String> blockIds = new ArrayList<String>();
blockIds.add(blockId);

String destinationBlobName = generateBlobName();
OffsetDateTime expiryTime = testResourceNamer.now().plusDays(1);

UserDelegationKey key = oauthService.getUserDelegationKey(null, expiryTime);
key.setSignedTenantId(testResourceNamer.recordValueFromConfig(key.getSignedTenantId()));
String saoid = testResourceNamer.randomUuid();

// Create-only permission for destination blob
BlobSasPermission destinationPermissions = new BlobSasPermission().setCreatePermission(true);
BlobServiceSasSignatureValues sasValues = new BlobServiceSasSignatureValues(expiryTime, destinationPermissions)
.setPreauthorizedAgentObjectId(saoid);
String createPermissionsOnly = oauthContainer.generateUserDelegationSas(sasValues, key);
BlockBlobClient destinationClient
= instrument(new SpecializedBlobClientBuilder().endpoint(oauthContainer.getBlobContainerUrl())
.blobName(destinationBlobName)
.sasToken(createPermissionsOnly)).buildBlockBlobClient();

destinationClient.stageBlock(blockId, DATA.getDefaultInputStream(), DATA.getDefaultDataSize());

assertDoesNotThrow(() -> destinationClient.commitBlockList(blockIds, false));
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new stageBlock/commitBlockList user-delegation SAS test should follow the existing pattern in this class and run inside liveTestScenarioWithRetry(...) to reduce live-test flakiness due to RBAC replication lag.

Suggested change
BlobServiceClient oauthService = getOAuthServiceClient();
String containerName = cc.getBlobContainerName();
BlobContainerClient oauthContainer = oauthService.getBlobContainerClient(containerName);
String blockId = Base64.getEncoder().encodeToString("blockid".getBytes(StandardCharsets.UTF_8));
List<String> blockIds = new ArrayList<String>();
blockIds.add(blockId);
String destinationBlobName = generateBlobName();
OffsetDateTime expiryTime = testResourceNamer.now().plusDays(1);
UserDelegationKey key = oauthService.getUserDelegationKey(null, expiryTime);
key.setSignedTenantId(testResourceNamer.recordValueFromConfig(key.getSignedTenantId()));
String saoid = testResourceNamer.randomUuid();
// Create-only permission for destination blob
BlobSasPermission destinationPermissions = new BlobSasPermission().setCreatePermission(true);
BlobServiceSasSignatureValues sasValues = new BlobServiceSasSignatureValues(expiryTime, destinationPermissions)
.setPreauthorizedAgentObjectId(saoid);
String createPermissionsOnly = oauthContainer.generateUserDelegationSas(sasValues, key);
BlockBlobClient destinationClient
= instrument(new SpecializedBlobClientBuilder().endpoint(oauthContainer.getBlobContainerUrl())
.blobName(destinationBlobName)
.sasToken(createPermissionsOnly)).buildBlockBlobClient();
destinationClient.stageBlock(blockId, DATA.getDefaultInputStream(), DATA.getDefaultDataSize());
assertDoesNotThrow(() -> destinationClient.commitBlockList(blockIds, false));
liveTestScenarioWithRetry(() -> {
BlobServiceClient oauthService = getOAuthServiceClient();
String containerName = cc.getBlobContainerName();
BlobContainerClient oauthContainer = oauthService.getBlobContainerClient(containerName);
String blockId = Base64.getEncoder().encodeToString("blockid".getBytes(StandardCharsets.UTF_8));
List<String> blockIds = new ArrayList<String>();
blockIds.add(blockId);
String destinationBlobName = generateBlobName();
OffsetDateTime expiryTime = testResourceNamer.now().plusDays(1);
UserDelegationKey key = oauthService.getUserDelegationKey(null, expiryTime);
key.setSignedTenantId(testResourceNamer.recordValueFromConfig(key.getSignedTenantId()));
String saoid = testResourceNamer.randomUuid();
// Create-only permission for destination blob
BlobSasPermission destinationPermissions = new BlobSasPermission().setCreatePermission(true);
BlobServiceSasSignatureValues sasValues =
new BlobServiceSasSignatureValues(expiryTime, destinationPermissions)
.setPreauthorizedAgentObjectId(saoid);
String createPermissionsOnly = oauthContainer.generateUserDelegationSas(sasValues, key);
BlockBlobClient destinationClient =
instrument(new SpecializedBlobClientBuilder().endpoint(oauthContainer.getBlobContainerUrl())
.blobName(destinationBlobName)
.sasToken(createPermissionsOnly)).buildBlockBlobClient();
destinationClient.stageBlock(blockId, DATA.getDefaultInputStream(), DATA.getDefaultDataSize());
assertDoesNotThrow(() -> destinationClient.commitBlockList(blockIds, false));
});

Copilot uses AI. Check for mistakes.
import com.azure.storage.blob.sas.BlobSasPermission;
import com.azure.storage.blob.sas.BlobServiceSasSignatureValues;
import com.azure.storage.blob.specialized.AppendBlobClient;
import com.azure.storage.blob.specialized.BlockBlobAsyncClient;
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused import BlockBlobAsyncClient is introduced here but never used in this test class. This will fail Checkstyle's UnusedImports rule; please remove the import (or switch the new sync tests to actually use the async client if that was intended).

Suggested change
import com.azure.storage.blob.specialized.BlockBlobAsyncClient;

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Storage Storage Service (Queues, Blobs, Files)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants