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_4ab10936db"
"Tag": "java/storage/azure-storage-blob_8b827141a3"
}
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,12 @@ public enum BlobServiceVersion implements ServiceVersion {
/**
* Service version {@code 2026-04-06}.
*/
V2026_04_06("2026-04-06");
V2026_04_06("2026-04-06"),

/**
* Service version {@code 2026-06-06}.
*/
V2026_06_06("2026-06-06");

private final String version;

Expand All @@ -179,6 +184,6 @@ public String getVersion() {
* @return the latest {@link BlobServiceVersion}
*/
public static BlobServiceVersion getLatest() {
return V2026_04_06;
return V2026_06_06;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,13 @@
import reactor.util.function.Tuples;

import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;

Expand Down Expand Up @@ -583,6 +586,121 @@ public void containerSasFilterBlobsFail() {
StepVerifier.create(client.setTags(tags)).verifyError(BlobStorageException.class);
}

@Test
@RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2026-04-06")
public void createPermissionUpload() {
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();

}

@Test
@RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2026-04-06")
public void transferBlobWithCreatePermission() {
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();
}

@Test
@RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2026-04-06")
public void commitBlockListWithCreatePermission() {
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();

}

// RBAC replication lag
@Test
public void blobUserDelegationSaoid() {
Expand Down Expand Up @@ -1484,4 +1602,5 @@ public void blobSasUserDelegationDelegatedTenantIdFail() {
e -> assertExceptionStatusCodeAndMessage(e, 403, BlobErrorCode.AUTHENTICATION_FAILED));
});
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
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;
import com.azure.storage.blob.specialized.BlockBlobClient;
import com.azure.storage.blob.specialized.SpecializedBlobClientBuilder;
import com.azure.storage.common.implementation.AccountSasImplUtil;
Expand All @@ -41,10 +42,13 @@
import org.junit.jupiter.params.provider.ValueSource;

import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;

Expand Down Expand Up @@ -1296,6 +1300,104 @@ public void blobSasImplUtilCanonicalizedResource(String containerName, String bl
assertEquals(expectedResource, queryParams.getResource());
}

@Test
@RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2026-04-06")
public void createPermissionUpload() {
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));
}

@Test
@RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2026-04-06")
public void commitBlockListWithCreatePermission() {
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));
}

private static Stream<Arguments> blobSasImplUtilCanonicalizedResourceSupplier() {
return Stream.of(
Arguments.of("c", "b", "id", OffsetDateTime.now(), "bs",
Expand Down
4 changes: 2 additions & 2 deletions sdk/storage/azure-storage-common/ci.system.properties
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
AZURE_LIVE_TEST_SERVICE_VERSION=V2026_04_06
AZURE_STORAGE_SAS_SERVICE_VERSION=2026-04-06
AZURE_LIVE_TEST_SERVICE_VERSION=V2026_06_06
AZURE_STORAGE_SAS_SERVICE_VERSION=2026-06-06
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public final class Constants {
public static final String PROPERTY_AZURE_STORAGE_SAS_SERVICE_VERSION = "AZURE_STORAGE_SAS_SERVICE_VERSION";

public static final String SAS_SERVICE_VERSION
= Configuration.getGlobalConfiguration().get(PROPERTY_AZURE_STORAGE_SAS_SERVICE_VERSION, "2026-04-06");
= Configuration.getGlobalConfiguration().get(PROPERTY_AZURE_STORAGE_SAS_SERVICE_VERSION, "2026-06-06");

public static final String ADJUSTED_BLOB_LENGTH_KEY = "adjustedBlobLength";

Expand Down Expand Up @@ -220,7 +220,7 @@ public static final class HeaderConstants {
* @deprecated For SAS Service Version use {@link Constants#SAS_SERVICE_VERSION}.
*/
@Deprecated
public static final String TARGET_STORAGE_VERSION = "2026-04-06";
public static final String TARGET_STORAGE_VERSION = "2026-06-06";

/**
* Error code returned from the service.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,12 @@ public enum DataLakeServiceVersion implements ServiceVersion {
/**
* Service version {@code 2026-04-06}.
*/
V2026_04_06("2026-04-06");
V2026_04_06("2026-04-06"),

/**
* Service version {@code 2026-06-06}.
*/
V2026_06_06("2026-06-06");

private final String version;

Expand All @@ -179,6 +184,6 @@ public String getVersion() {
* @return the latest {@link DataLakeServiceVersion}
*/
public static DataLakeServiceVersion getLatest() {
return V2026_04_06;
return V2026_06_06;
}
}
Loading
Loading