Skip to content

Commit 3f21e08

Browse files
authored
STG100 - Queue User Delegation SAS (#46311)
* 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 * initial implementation * async implementations and tests * fixing typo * style * adding send message with user delegation sas test and service tests * expanding tid and oid acronyms * adding new test and recordings * removing change in blobsasimplutil * updating recordings
1 parent 24f03d7 commit 3f21e08

18 files changed

+1491
-23
lines changed

sdk/storage/azure-storage-queue/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-queue",
5-
"Tag": "java/storage/azure-storage-queue_06da25b415"
5+
"Tag": "java/storage/azure-storage-queue_2e02d46abe"
66
}

sdk/storage/azure-storage-queue/src/main/java/com/azure/storage/queue/QueueAsyncClient.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import com.azure.storage.queue.models.QueueStorageException;
3838
import com.azure.storage.queue.models.SendMessageResult;
3939
import com.azure.storage.queue.models.UpdateMessageResult;
40+
import com.azure.storage.queue.models.UserDelegationKey;
4041
import com.azure.storage.queue.sas.QueueServiceSasSignatureValues;
4142
import reactor.core.publisher.Flux;
4243
import reactor.core.publisher.Mono;
@@ -1516,4 +1517,36 @@ public String generateSas(QueueServiceSasSignatureValues queueServiceSasSignatur
15161517
return new QueueSasImplUtil(queueServiceSasSignatureValues, getQueueName())
15171518
.generateSas(SasImplUtils.extractSharedKeyCredential(getHttpPipeline()), stringToSignHandler, context);
15181519
}
1520+
1521+
/**
1522+
* Generates a user delegation SAS for the queue using the specified {@link QueueServiceSasSignatureValues}.
1523+
* <p>See {@link QueueServiceSasSignatureValues} for more information on how to construct a user delegation SAS.</p>
1524+
*
1525+
* @param queueServiceSasSignatureValues {@link QueueServiceSasSignatureValues}
1526+
* @param userDelegationKey A {@link UserDelegationKey} object used to sign the SAS values.
1527+
*
1528+
* @return A {@code String} representing the SAS query parameters.
1529+
*/
1530+
public String generateUserDelegationSas(QueueServiceSasSignatureValues queueServiceSasSignatureValues,
1531+
UserDelegationKey userDelegationKey) {
1532+
return generateUserDelegationSas(queueServiceSasSignatureValues, userDelegationKey, null, Context.NONE);
1533+
}
1534+
1535+
/**
1536+
* Generates a user delegation SAS for the queue using the specified {@link QueueServiceSasSignatureValues}.
1537+
* <p>See {@link QueueServiceSasSignatureValues} for more information on how to construct a user delegation SAS.</p>
1538+
*
1539+
* @param queueServiceSasSignatureValues {@link QueueServiceSasSignatureValues}
1540+
* @param userDelegationKey A {@link UserDelegationKey} object used to sign the SAS values.
1541+
* @param stringToSignHandler For debugging purposes only. Returns the string to sign that was used to generate the
1542+
* signature.
1543+
* @param context Additional context that is passed through the code when generating a SAS.
1544+
*
1545+
* @return A {@code String} representing the SAS query parameters.
1546+
*/
1547+
public String generateUserDelegationSas(QueueServiceSasSignatureValues queueServiceSasSignatureValues,
1548+
UserDelegationKey userDelegationKey, Consumer<String> stringToSignHandler, Context context) {
1549+
return new QueueSasImplUtil(queueServiceSasSignatureValues, getQueueName())
1550+
.generateUserDelegationSas(userDelegationKey, accountName, stringToSignHandler, context);
1551+
}
15191552
}

sdk/storage/azure-storage-queue/src/main/java/com/azure/storage/queue/QueueClient.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import com.azure.storage.queue.implementation.models.QueuesGetAccessPolicyHeaders;
3333
import com.azure.storage.queue.implementation.models.QueuesGetPropertiesHeaders;
3434
import com.azure.storage.queue.implementation.models.SendMessageResultWrapper;
35+
import com.azure.storage.queue.models.UserDelegationKey;
3536
import com.azure.storage.queue.implementation.util.ModelHelper;
3637
import com.azure.storage.queue.implementation.util.QueueSasImplUtil;
3738
import com.azure.storage.queue.models.PeekedMessageItem;
@@ -1459,4 +1460,36 @@ public String generateSas(QueueServiceSasSignatureValues queueServiceSasSignatur
14591460
return new QueueSasImplUtil(queueServiceSasSignatureValues, getQueueName())
14601461
.generateSas(SasImplUtils.extractSharedKeyCredential(getHttpPipeline()), stringToSignHandler, context);
14611462
}
1463+
1464+
/**
1465+
* Generates a user delegation SAS for the queue using the specified {@link QueueServiceSasSignatureValues}.
1466+
* <p>See {@link QueueServiceSasSignatureValues} for more information on how to construct a user delegation SAS.</p>
1467+
*
1468+
* @param queueServiceSasSignatureValues {@link QueueServiceSasSignatureValues}
1469+
* @param userDelegationKey A {@link UserDelegationKey} object used to sign the SAS values.
1470+
*
1471+
* @return A {@code String} representing the SAS query parameters.
1472+
*/
1473+
public String generateUserDelegationSas(QueueServiceSasSignatureValues queueServiceSasSignatureValues,
1474+
UserDelegationKey userDelegationKey) {
1475+
return generateUserDelegationSas(queueServiceSasSignatureValues, userDelegationKey, null, Context.NONE);
1476+
}
1477+
1478+
/**
1479+
* Generates a user delegation SAS for the queue using the specified {@link QueueServiceSasSignatureValues}.
1480+
* <p>See {@link QueueServiceSasSignatureValues} for more information on how to construct a user delegation SAS.</p>
1481+
*
1482+
* @param queueServiceSasSignatureValues {@link QueueServiceSasSignatureValues}
1483+
* @param userDelegationKey A {@link UserDelegationKey} object used to sign the SAS values.
1484+
* @param stringToSignHandler For debugging purposes only. Returns the string to sign that was used to generate the
1485+
* signature.
1486+
* @param context Additional context that is passed through the code when generating a SAS.
1487+
*
1488+
* @return A {@code String} representing the SAS query parameters.
1489+
*/
1490+
public String generateUserDelegationSas(QueueServiceSasSignatureValues queueServiceSasSignatureValues,
1491+
UserDelegationKey userDelegationKey, Consumer<String> stringToSignHandler, Context context) {
1492+
return new QueueSasImplUtil(queueServiceSasSignatureValues, getQueueName())
1493+
.generateUserDelegationSas(userDelegationKey, accountName, stringToSignHandler, context);
1494+
}
14621495
}

sdk/storage/azure-storage-queue/src/main/java/com/azure/storage/queue/QueueServiceAsyncClient.java

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.azure.core.annotation.ReturnType;
66
import com.azure.core.annotation.ServiceClient;
77
import com.azure.core.annotation.ServiceMethod;
8+
import com.azure.core.credential.TokenCredential;
89
import com.azure.core.http.HttpPipeline;
910
import com.azure.core.http.rest.PagedFlux;
1011
import com.azure.core.http.rest.PagedResponse;
@@ -15,20 +16,24 @@
1516
import com.azure.core.util.logging.ClientLogger;
1617
import com.azure.storage.common.StorageSharedKeyCredential;
1718
import com.azure.storage.common.implementation.AccountSasImplUtil;
19+
import com.azure.storage.common.implementation.Constants;
1820
import com.azure.storage.common.implementation.SasImplUtils;
1921
import com.azure.storage.common.implementation.StorageImplUtils;
2022
import com.azure.storage.common.sas.AccountSasSignatureValues;
2123
import com.azure.storage.queue.implementation.AzureQueueStorageImpl;
24+
import com.azure.storage.queue.models.KeyInfo;
2225
import com.azure.storage.queue.models.QueueCorsRule;
2326
import com.azure.storage.queue.models.QueueItem;
2427
import com.azure.storage.queue.models.QueueMessageDecodingError;
2528
import com.azure.storage.queue.models.QueueServiceProperties;
2629
import com.azure.storage.queue.models.QueueServiceStatistics;
2730
import com.azure.storage.queue.models.QueueStorageException;
2831
import com.azure.storage.queue.models.QueuesSegmentOptions;
32+
import com.azure.storage.queue.models.UserDelegationKey;
2933
import reactor.core.publisher.Mono;
3034

3135
import java.time.Duration;
36+
import java.time.OffsetDateTime;
3237
import java.util.ArrayList;
3338
import java.util.List;
3439
import java.util.Map;
@@ -709,4 +714,57 @@ public String generateAccountSas(AccountSasSignatureValues accountSasSignatureVa
709714
return new AccountSasImplUtil(accountSasSignatureValues, null)
710715
.generateSas(SasImplUtils.extractSharedKeyCredential(getHttpPipeline()), stringToSignHandler, context);
711716
}
717+
718+
/**
719+
* Gets a user delegation key for use with this account's queue storage. Note: This method call is only valid when
720+
* using {@link TokenCredential} in this object's {@link HttpPipeline}.
721+
*
722+
* @param start Start time for the key's validity. Null indicates immediate start.
723+
* @param expiry Expiration of the key's validity.
724+
* @return A {@link Mono} containing the user delegation key.
725+
* @throws IllegalArgumentException If {@code start} isn't null and is after {@code expiry}.
726+
* @throws NullPointerException If {@code expiry} is null.
727+
*/
728+
@ServiceMethod(returns = ReturnType.SINGLE)
729+
public Mono<UserDelegationKey> getUserDelegationKey(OffsetDateTime start, OffsetDateTime expiry) {
730+
return getUserDelegationKeyWithResponse(start, expiry).flatMap(FluxUtil::toMono);
731+
}
732+
733+
/**
734+
* Gets a user delegation key for use with this account's queue storage. Note: This method call is only valid when
735+
* using {@link TokenCredential} in this object's {@link HttpPipeline}.
736+
*
737+
* @param start Start time for the key's validity. Null indicates immediate start.
738+
* @param expiry Expiration of the key's validity.
739+
* @return A {@link Mono} containing a {@link Response} whose {@link Response#getValue() value} containing the user
740+
* delegation key.
741+
* @throws IllegalArgumentException If {@code start} isn't null and is after {@code expiry}.
742+
* @throws NullPointerException If {@code expiry} is null.
743+
*/
744+
@ServiceMethod(returns = ReturnType.SINGLE)
745+
public Mono<Response<UserDelegationKey>> getUserDelegationKeyWithResponse(OffsetDateTime start,
746+
OffsetDateTime expiry) {
747+
try {
748+
return withContext(context -> getUserDelegationKeyWithResponse(start, expiry, context));
749+
} catch (RuntimeException ex) {
750+
return monoError(LOGGER, ex);
751+
}
752+
}
753+
754+
Mono<Response<UserDelegationKey>> getUserDelegationKeyWithResponse(OffsetDateTime start, OffsetDateTime expiry,
755+
Context context) {
756+
StorageImplUtils.assertNotNull("expiry", expiry);
757+
if (start != null && !start.isBefore(expiry)) {
758+
throw LOGGER.logExceptionAsError(
759+
new IllegalArgumentException("`start` must be null or a datetime before `expiry`."));
760+
}
761+
context = context == null ? Context.NONE : context;
762+
763+
return client.getServices()
764+
.getUserDelegationKeyWithResponseAsync(
765+
new KeyInfo().setStart(start == null ? "" : Constants.ISO_8601_UTC_DATE_FORMATTER.format(start))
766+
.setExpiry(Constants.ISO_8601_UTC_DATE_FORMATTER.format(expiry)),
767+
null, null, context)
768+
.map(rb -> new SimpleResponse<>(rb, rb.getValue()));
769+
}
712770
}

sdk/storage/azure-storage-queue/src/main/java/com/azure/storage/queue/QueueServiceClient.java

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.azure.core.annotation.ReturnType;
66
import com.azure.core.annotation.ServiceClient;
77
import com.azure.core.annotation.ServiceMethod;
8+
import com.azure.core.credential.TokenCredential;
89
import com.azure.core.http.HttpPipeline;
910
import com.azure.core.http.rest.PagedIterable;
1011
import com.azure.core.http.rest.PagedResponse;
@@ -15,29 +16,37 @@
1516
import com.azure.core.util.logging.ClientLogger;
1617
import com.azure.storage.common.StorageSharedKeyCredential;
1718
import com.azure.storage.common.implementation.AccountSasImplUtil;
19+
import com.azure.storage.common.implementation.Constants;
1820
import com.azure.storage.common.implementation.SasImplUtils;
21+
import com.azure.storage.common.implementation.StorageImplUtils;
1922
import com.azure.storage.common.sas.AccountSasSignatureValues;
2023
import com.azure.storage.queue.implementation.AzureQueueStorageImpl;
24+
import com.azure.storage.queue.models.KeyInfo;
2125
import com.azure.storage.queue.implementation.models.ServicesGetStatisticsHeaders;
26+
import com.azure.storage.queue.implementation.models.ServicesGetUserDelegationKeyHeaders;
2227
import com.azure.storage.queue.models.QueueCorsRule;
2328
import com.azure.storage.queue.models.QueueItem;
2429
import com.azure.storage.queue.models.QueueMessageDecodingError;
2530
import com.azure.storage.queue.models.QueueServiceProperties;
2631
import com.azure.storage.queue.models.QueueServiceStatistics;
2732
import com.azure.storage.queue.models.QueueStorageException;
2833
import com.azure.storage.queue.models.QueuesSegmentOptions;
34+
import com.azure.storage.queue.models.UserDelegationKey;
2935
import reactor.core.publisher.Mono;
3036

3137
import java.time.Duration;
38+
import java.time.OffsetDateTime;
3239
import java.util.ArrayList;
3340
import java.util.List;
3441
import java.util.Map;
3542
import java.util.Objects;
43+
import java.util.concurrent.Callable;
3644
import java.util.function.BiFunction;
3745
import java.util.function.Consumer;
3846
import java.util.function.Function;
3947
import java.util.function.Supplier;
4048

49+
import static com.azure.storage.common.implementation.StorageImplUtils.sendRequest;
4150
import static com.azure.storage.common.implementation.StorageImplUtils.submitThreadPool;
4251

4352
/**
@@ -693,4 +702,48 @@ public String generateAccountSas(AccountSasSignatureValues accountSasSignatureVa
693702
return new AccountSasImplUtil(accountSasSignatureValues, null)
694703
.generateSas(SasImplUtils.extractSharedKeyCredential(getHttpPipeline()), stringToSignHandler, context);
695704
}
705+
706+
/**
707+
* Gets a user delegation key for use with this account's queue storage. Note: This method call is only valid when
708+
* using {@link TokenCredential} in this object's {@link HttpPipeline}.
709+
*
710+
* @param start Start time for the key's validity. Null indicates immediate start.
711+
* @param expiry Expiration of the key's validity.
712+
* @return The user delegation key.
713+
*/
714+
@ServiceMethod(returns = ReturnType.SINGLE)
715+
public UserDelegationKey getUserDelegationKey(OffsetDateTime start, OffsetDateTime expiry) {
716+
return getUserDelegationKeyWithResponse(start, expiry, null, Context.NONE).getValue();
717+
}
718+
719+
/**
720+
* Gets a user delegation key for use with this account's queue storage. Note: This method call is only valid when
721+
* using {@link TokenCredential} in this object's {@link HttpPipeline}.
722+
*
723+
* @param start Start time for the key's validity. Null indicates immediate start.
724+
* @param expiry Expiration of the key's validity.
725+
* @param timeout An optional timeout value beyond which a {@link RuntimeException} will be raised.
726+
* @param context Additional context that is passed through the Http pipeline during the service call.
727+
* @return A {@link Response} whose {@link Response#getValue() value} contains the user delegation key.
728+
*/
729+
@ServiceMethod(returns = ReturnType.SINGLE)
730+
public Response<UserDelegationKey> getUserDelegationKeyWithResponse(OffsetDateTime start, OffsetDateTime expiry,
731+
Duration timeout, Context context) {
732+
StorageImplUtils.assertNotNull("expiry", expiry);
733+
if (start != null && !start.isBefore(expiry)) {
734+
throw LOGGER.logExceptionAsError(
735+
new IllegalArgumentException("`start` must be null or a datetime before `expiry`."));
736+
}
737+
Context finalContext = context == null ? Context.NONE : context;
738+
Callable<ResponseBase<ServicesGetUserDelegationKeyHeaders, UserDelegationKey>> operation
739+
= () -> this.azureQueueStorage.getServices()
740+
.getUserDelegationKeyWithResponse(
741+
new KeyInfo().setStart(start == null ? "" : Constants.ISO_8601_UTC_DATE_FORMATTER.format(start))
742+
.setExpiry(Constants.ISO_8601_UTC_DATE_FORMATTER.format(expiry)),
743+
null, null, finalContext);
744+
ResponseBase<ServicesGetUserDelegationKeyHeaders, UserDelegationKey> response
745+
= sendRequest(operation, timeout, QueueStorageException.class);
746+
return new SimpleResponse<>(response, response.getValue());
747+
}
748+
696749
}

0 commit comments

Comments
 (0)