Skip to content

Added support for logging string to sign in Storage and ability to pass in account name when generating a UserDelegationKey#18075

Merged
gapra-msft merged 17 commits into
Azure:masterfrom
gapra-msft:storage/logStringToSign
Dec 17, 2020
Merged

Added support for logging string to sign in Storage and ability to pass in account name when generating a UserDelegationKey#18075
gapra-msft merged 17 commits into
Azure:masterfrom
gapra-msft:storage/logStringToSign

Conversation

@gapra-msft

@gapra-msft gapra-msft commented Dec 10, 2020

Copy link
Copy Markdown
Member

Resolves #17935 and
Resolves #6713

@ghost ghost added the Storage Storage Service (Queues, Blobs, Files) label Dec 10, 2020
UserDelegationKey userDelegationKey) {
return new BlobSasImplUtil(blobServiceSasSignatureValues, getBlobContainerName())
.generateUserDelegationSas(userDelegationKey, getAccountName());
.generateUserDelegationSas(userDelegationKey, getAccountName(), Context.NONE);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Will add here


when:
bc.setMetadata(metadata)
bc.setMetadataWithResponse(metadata, null, null, new com.azure.core.util.Context("Log-String-To-Sign", true))

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

undo

final String canonicalName = getCanonicalName(storageSharedKeyCredentials.getAccountName());
final String signature = storageSharedKeyCredentials.computeHmac256(stringToSign(canonicalName));
final String stringToSign = stringToSign(canonicalName);
if (context != null && Boolean.TRUE.equals(context.getData("Log-String-To-Sign").orElse(false))) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I would use a different key such as azure-log-string-to-sign to ensure there is no overlap and to indicate it's Azure SDK specific

Comment on lines +179 to +184
if (context != null && Boolean.TRUE.equals(context.getData("Log-String-To-Sign").orElse(false))) {
logger.info("The string to sign computed by the SDK is: {}{}", stringToSign,
System.getProperty("line.separator"));
logger.warning("Please remember to disable 'Log-String-To-Sign' before going to production as this "
+ "string can potentially contain PII.");
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Since this is in multiple spots let's make it a method so if it needs to be updated only one location needs modification

private static String convertExceptionMessage(String message, HttpResponse response) {
if (response != null) {
if (response.getStatusCode() == 403) {
return message + "\nIf you are using a StorageSharedKeyCredential, and the server returned an "

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should we use String.format here with line separators instead of \n or are we using '\n' as that would be what is returned by the service.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I think the service returns \n so its fine if we leave this as \n

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Changed to %n as CI was angry

@gapra-msft gapra-msft changed the title Added support for logging string to sign in Storage Added support for logging string to sign in Storage and ability to pass in account name when generating a UserDelegationKey Dec 10, 2020
@gapra-msft
gapra-msft marked this pull request as ready for review December 14, 2020 18:35

private String buildStringToSign(URL requestURL, String httpMethod, Map<String, String> headers) {
private String buildStringToSign(URL requestURL, String httpMethod, Map<String, String> headers,
boolean logStringToSign) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why not accept the context itself instead of a boolean?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The context in the pipeline policy isnt an AzureContext, its like a HttpPipelineContext so I'd need to do that weird conversion somewhere. Would you prefer I do it in the pipeline policy?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

HttpPipelineContext is the combination of HttpRequest and Context where the Context APIs are wrapped and hidden by the HttpPipelineContext APIs, in the end it should contain all information from the Context.

*
* @param blobServiceSasSignatureValues {@link BlobServiceSasSignatureValues}
* @param userDelegationKey A {@link UserDelegationKey} object used to sign the SAS values.
* @see BlobServiceAsyncClient#getUserDelegationKey(OffsetDateTime, OffsetDateTime) for more information on how to

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I would change this to an {@link} as @see serves a different purpose to link to a class with its own special section. This is an example: https://azuresdkdocs.blob.core.windows.net/$web/java/azure-core-http-netty/1.7.0/com/azure/core/http/netty/NettyAsyncHttpClientBuilder.html

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed

* @param accountName The account name.
* @param context Additional context that is passed through the code when generating a SAS.
*
* @return A {@code String} representing all SAS query parameters.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Does it represent all SAS query parameters or is it the SAS query parameters string?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

It should be 'the'. I've fixed it.


/**
* Generates a service SAS for the container using the specified {@link BlobServiceSasSignatureValues}
* Note : The client must be authenticated via {@link StorageSharedKeyCredential}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This will generate on the same line as the sentence above due to Javadocs being HTML, let's make this another <p> so get a line break.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed

*
* @param blobServiceSasSignatureValues {@link BlobServiceSasSignatureValues}
* @param userDelegationKey A {@link UserDelegationKey} object used to sign the SAS values.
* @see BlobServiceClient#getUserDelegationKey(OffsetDateTime, OffsetDateTime) for more information on how to get a

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same comment as the other Javadoc with inlined @see

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed

*
* @return A {@code String} representing all SAS query parameters.
*/
public String generateUserDelegationSas(BlobServiceSasSignatureValues blobServiceSasSignatureValues,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is there going to be an issue that this method assumes the container name from the BlobContainerClient instance but then allows for any account name to be passed? This could result in cases where the container doesn't exist in the other account, and given that to me this method feels closer to a static utility which allows generating a SAS for any account and container given the UserDelegationKey.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I think thats a valid concern. The reason we wanted to put account name here is specifically for situations where a user passes in a custom domain name, and we can't parse an account name as a result.

*
* @return A {@code String} representing all SAS query parameters.
*/
public String generateUserDelegationSas(BlobServiceSasSignatureValues blobServiceSasSignatureValues,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same comment as BlobContainerClient around this being a mix of external values and values on the instance, it ends up being an in-between of static utility to generate any SAS given the delegation key and requested permissions but also being information specific to the blob such as container, name, version, and snapshot.


private String buildStringToSign(URL requestURL, String httpMethod, Map<String, String> headers) {
private String buildStringToSign(URL requestURL, String httpMethod, Map<String, String> headers,
boolean logStringToSign) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

HttpPipelineContext is the combination of HttpRequest and Context where the Context APIs are wrapped and hidden by the HttpPipelineContext APIs, in the end it should contain all information from the Context.

public static void logStringToSign(ClientLogger logger, String stringToSign, Context context) {
if (context != null && Boolean.TRUE.equals(context.getData(Constants.STORAGE_LOG_STRING_TO_SIGN).orElse(false))) {
logger.info("The string to sign computed by the SDK is: {}{}", stringToSign,
System.getProperty("line.separator"));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should use System.lineSeparator() instead as it is a convenience wrapper for this.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed


if (logStringToSign) {
StorageImplUtils.logStringToSign(logger, stringToSign,
new Context(Constants.STORAGE_LOG_STRING_TO_SIGN, true));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Let's make this a constant so we don't need to instantiate a new instance every time this code path is executed

public static String convertStorageExceptionMessage(String message, HttpResponse response) {
if (response != null) {
if (response.getStatusCode() == 403) {
return String.format("If you are using a StorageSharedKeyCredential, and the server returned an "

@alzimmermsft alzimmermsft Dec 17, 2020

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Let's make this base message a constant as all parameters for the formatting are constant, that way we don't need to make additional calls to String.format and wait for this to be optimized by JIT

if (context != null && Boolean.TRUE.equals(context.getData(Constants.STORAGE_LOG_STRING_TO_SIGN).orElse(false))) {
logger.info("The string to sign computed by the SDK is: {}{}", stringToSign,
System.lineSeparator());
logger.warning("Please remember to disable '{}' before going to production as this "

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Let's make this log message a constant string as it doesn't have any instance data

@gapra-msft
gapra-msft merged commit 49cddb6 into Azure:master Dec 17, 2020
@gapra-msft
gapra-msft deleted the storage/logStringToSign branch December 17, 2020 23:55
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.

[FEATURE REQ] Storage log string to sign Create an overload for user delegation SAS that allows you to pass in an account name

3 participants