From a48ecb7f72abea147bdc37da17e0d481ff3e1049 Mon Sep 17 00:00:00 2001 From: Sima Zhu Date: Tue, 30 Jul 2019 16:44:10 -0700 Subject: [PATCH] Addressed feedback, --- .../credentials/SASTokenCredential.java | 83 ++++++++++++++++++- .../storage/queue/QueueClientBuilder.java | 4 +- .../queue/QueueServiceClientBuilder.java | 4 +- .../java/com/azure/storage/queue/Utility.java | 71 +--------------- .../javadoc/QueueJavaDocCodeSamples.java | 6 +- .../QueueServiceJavaDocCodeSamples.java | 6 +- 6 files changed, 90 insertions(+), 84 deletions(-) diff --git a/storage/client/queue/src/main/java/com/azure/storage/common/credentials/SASTokenCredential.java b/storage/client/queue/src/main/java/com/azure/storage/common/credentials/SASTokenCredential.java index 1018fedf94e6d..54ae9349a389c 100644 --- a/storage/client/queue/src/main/java/com/azure/storage/common/credentials/SASTokenCredential.java +++ b/storage/client/queue/src/main/java/com/azure/storage/common/credentials/SASTokenCredential.java @@ -5,13 +5,18 @@ import com.azure.core.implementation.util.ImplUtils; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.util.Locale; import java.util.Map; +import java.util.TreeMap; /** * Holds a SAS token used for authenticating requests. */ public final class SASTokenCredential { private static final String SIGNATURE = "sig"; + private static final String UTF8_CHARSET = "UTF-8"; private final String sasToken; @@ -48,11 +53,12 @@ public static SASTokenCredential fromSASTokenString(String sasToken) { /** * Creates a SAS token credential from the passed query string parameters. * - * @param queryParameters URL query parameters + * @param queryParameterString URL query parameters * @return a SAS token credential if {@code queryParameters} is not {@code null} and has * the signature ("sig") query parameter, otherwise returns {@code null}. */ - public static SASTokenCredential fromQueryParameters(Map queryParameters) { + public static SASTokenCredential fromQueryParameters(String queryParameterString) { + Map queryParameters = parseQueryString(queryParameterString); if (ImplUtils.isNullOrEmpty(queryParameters) || !queryParameters.containsKey(SIGNATURE)) { return null; } @@ -69,4 +75,77 @@ public static SASTokenCredential fromQueryParameters(Map queryPa return new SASTokenCredential(sb.toString()); } + + /** + * Parses the query string into a key-value pair map that maintains key, query parameter key, order. + * + * @param queryString Query string to parse + * @return a mapping of query string pieces as key-value pairs. + */ + private static TreeMap parseQueryString(final String queryString) { + TreeMap pieces = new TreeMap<>(String::compareTo); + + if (ImplUtils.isNullOrEmpty(queryString)) { + return pieces; + } + + for (String kvp : queryString.split("&")) { + int equalIndex = kvp.indexOf("="); + String key = urlDecode(kvp.substring(0, equalIndex)).toLowerCase(Locale.ROOT); + String value = urlDecode(kvp.substring(equalIndex + 1)); + + pieces.putIfAbsent(key, value); + } + + return pieces; + } + + /** + * Performs a safe decoding of the passed string, taking care to preserve each {@code +} character rather than + * replacing it with a space character. + * + * @param stringToDecode String value to decode + * @return the decoded string value + * @throws RuntimeException If the UTF-8 charset isn't supported + */ + static String urlDecode(final String stringToDecode) { + if (ImplUtils.isNullOrEmpty(stringToDecode)) { + return ""; + } + + if (stringToDecode.contains("+")) { + StringBuilder outBuilder = new StringBuilder(); + + int startDex = 0; + for (int m = 0; m < stringToDecode.length(); m++) { + if (stringToDecode.charAt(m) == '+') { + if (m > startDex) { + outBuilder.append(decode(stringToDecode.substring(startDex, m))); + } + + outBuilder.append("+"); + startDex = m + 1; + } + } + + if (startDex != stringToDecode.length()) { + outBuilder.append(decode(stringToDecode.substring(startDex))); + } + + return outBuilder.toString(); + } else { + return decode(stringToDecode); + } + } + + /* + * Helper method to reduce duplicate calls of URLDecoder.decode + */ + private static String decode(final String stringToDecode) { + try { + return URLDecoder.decode(stringToDecode, UTF8_CHARSET); + } catch (UnsupportedEncodingException ex) { + throw new RuntimeException(ex); + } + } } diff --git a/storage/client/queue/src/main/java/com/azure/storage/queue/QueueClientBuilder.java b/storage/client/queue/src/main/java/com/azure/storage/queue/QueueClientBuilder.java index 5899797f8cdd3..c9b5b3382835a 100644 --- a/storage/client/queue/src/main/java/com/azure/storage/queue/QueueClientBuilder.java +++ b/storage/client/queue/src/main/java/com/azure/storage/queue/QueueClientBuilder.java @@ -185,7 +185,7 @@ public QueueAsyncClient buildAsyncClient() { *

The first path segment, if the endpoint contains path segments, will be assumed to be the name of the queue * that the client will interact with.

* - *

Query parameters of the endpoint will be parsed using {@link SASTokenCredential#fromQueryParameters(Map)} fromQuery} in an + *

Query parameters of the endpoint will be parsed using {@link SASTokenCredential#fromQueryParameters(String)} fromQuery} in an * attempt to generate a {@link SASTokenCredential} to authenticate requests sent to the service.

* * @param endpoint The URL of the Azure Storage Queue instance to send service requests to and receive responses from. @@ -205,7 +205,7 @@ public QueueClientBuilder endpoint(String endpoint) { } // Attempt to get the SAS token from the URL passed - this.sasTokenCredential = SASTokenCredential.fromQueryParameters(Utility.parseQueryString(fullURL.getQuery())); + this.sasTokenCredential = SASTokenCredential.fromQueryParameters(fullURL.getQuery()); if (this.sasTokenCredential != null) { this.sharedKeyCredential = null; this.tokenCredential = null; diff --git a/storage/client/queue/src/main/java/com/azure/storage/queue/QueueServiceClientBuilder.java b/storage/client/queue/src/main/java/com/azure/storage/queue/QueueServiceClientBuilder.java index 238964e7b548a..e948d25782cfe 100644 --- a/storage/client/queue/src/main/java/com/azure/storage/queue/QueueServiceClientBuilder.java +++ b/storage/client/queue/src/main/java/com/azure/storage/queue/QueueServiceClientBuilder.java @@ -176,7 +176,7 @@ public QueueServiceClient buildClient() { /** * Sets the endpoint for the Azure Storage Queue instance that the client will interact with. * - *

Query parameters of the endpoint will be parsed using {@link SASTokenCredential#fromQueryParameters(Map)} fromQuery} in an + *

Query parameters of the endpoint will be parsed using {@link SASTokenCredential#fromQueryParameters(String)} fromQuery} in an * attempt to generate a {@link SASTokenCredential} to authenticate requests sent to the service.

* * @param endpoint The URL of the Azure Storage Queue instance to send service requests to and receive responses from. @@ -190,7 +190,7 @@ public QueueServiceClientBuilder endpoint(String endpoint) { this.endpoint = new URL(fullURL.getProtocol() + "://" + fullURL.getHost()); // Attempt to get the SAS token from the URL passed - this.sasTokenCredential = SASTokenCredential.fromQueryParameters(Utility.parseQueryString(fullURL.getQuery())); + this.sasTokenCredential = SASTokenCredential.fromQueryParameters(fullURL.getQuery()); if (this.sasTokenCredential != null) { this.sharedKeyCredential = null; this.tokenCredential = null; diff --git a/storage/client/queue/src/main/java/com/azure/storage/queue/Utility.java b/storage/client/queue/src/main/java/com/azure/storage/queue/Utility.java index ad4454b9a6000..9e016078076a9 100644 --- a/storage/client/queue/src/main/java/com/azure/storage/queue/Utility.java +++ b/storage/client/queue/src/main/java/com/azure/storage/queue/Utility.java @@ -9,80 +9,11 @@ import java.util.TreeMap; -public final class Utility { +final class Utility { - private static final String UTF8_CHARSET = "UTF-8"; - /** - *Parses the query string into a key-value pair map that maintains key, query parameter key, order. - * - * @param queryString Query string to parse - * @return a mapping of query string pieces as key-value pairs. - */ - public static TreeMap parseQueryString(final String queryString) { - TreeMap pieces = new TreeMap<>(String::compareTo); - if (ImplUtils.isNullOrEmpty(queryString)) { - return pieces; - } - for (String kvp : queryString.split("&")) { - int equalIndex = kvp.indexOf("="); - String key = urlDecode(kvp.substring(0, equalIndex)).toLowerCase(Locale.ROOT); - String value = urlDecode(kvp.substring(equalIndex + 1)); - pieces.putIfAbsent(key, value); - } - return pieces; - } - - /** - * Performs a safe decoding of the passed string, taking care to preserve each {@code +} character rather than - * replacing it with a space character. - * - * @param stringToDecode String value to decode - * @return the decoded string value - * @throws RuntimeException If the UTF-8 charset isn't supported - */ - public static String urlDecode(final String stringToDecode) { - if (ImplUtils.isNullOrEmpty(stringToDecode)) { - return ""; - } - - if (stringToDecode.contains("+")) { - StringBuilder outBuilder = new StringBuilder(); - - int startDex = 0; - for (int m = 0; m < stringToDecode.length(); m++) { - if (stringToDecode.charAt(m) == '+') { - if (m > startDex) { - outBuilder.append(decode(stringToDecode.substring(startDex, m))); - } - - outBuilder.append("+"); - startDex = m + 1; - } - } - - if (startDex != stringToDecode.length()) { - outBuilder.append(decode(stringToDecode.substring(startDex))); - } - - return outBuilder.toString(); - } else { - return decode(stringToDecode); - } - } - - /* - * Helper method to reduce duplicate calls of URLDecoder.decode - */ - private static String decode(final String stringToDecode) { - try { - return URLDecoder.decode(stringToDecode, UTF8_CHARSET); - } catch (UnsupportedEncodingException ex) { - throw new RuntimeException(ex); - } - } } diff --git a/storage/client/queue/src/samples/java/com/azure/storage/queue/javadoc/QueueJavaDocCodeSamples.java b/storage/client/queue/src/samples/java/com/azure/storage/queue/javadoc/QueueJavaDocCodeSamples.java index a8cee0a1c0fd3..87306891e4dbf 100644 --- a/storage/client/queue/src/samples/java/com/azure/storage/queue/javadoc/QueueJavaDocCodeSamples.java +++ b/storage/client/queue/src/samples/java/com/azure/storage/queue/javadoc/QueueJavaDocCodeSamples.java @@ -86,8 +86,7 @@ public QueueClient createClientWithCredential() { QueueClient queueClient = new QueueClientBuilder() .endpoint("https://${accountName}.queue.core.windows.net") .queueName("myqueue") - .credential(SASTokenCredential.fromQueryParameters( - Collections.singletonMap("{SASTokenQueryParams}", "{SASTokenQueryValues}"))) + .credential(SASTokenCredential.fromQueryParameters("{SASTokenQueryParams}")) .buildClient(); // END: com.azure.storage.queue.queueClient.instantiation.credential return queueClient; @@ -102,8 +101,7 @@ public QueueAsyncClient createAsyncClientWithCredential() { QueueAsyncClient queueAsyncClient = new QueueClientBuilder() .endpoint("https://{accountName}.queue.core.windows.net") .queueName("myqueue") - .credential(SASTokenCredential.fromQueryParameters( - Collections.singletonMap("{SASTokenQueryParams}", "{SASTokenQueryValues}"))) + .credential(SASTokenCredential.fromQueryParameters("{SASTokenQueryParams}")) .buildAsyncClient(); // END: com.azure.storage.queue.queueAsyncClient.instantiation.credential return queueAsyncClient; diff --git a/storage/client/queue/src/samples/java/com/azure/storage/queue/javadoc/QueueServiceJavaDocCodeSamples.java b/storage/client/queue/src/samples/java/com/azure/storage/queue/javadoc/QueueServiceJavaDocCodeSamples.java index cc001a56d38ea..1da425b7d19c4 100644 --- a/storage/client/queue/src/samples/java/com/azure/storage/queue/javadoc/QueueServiceJavaDocCodeSamples.java +++ b/storage/client/queue/src/samples/java/com/azure/storage/queue/javadoc/QueueServiceJavaDocCodeSamples.java @@ -78,8 +78,7 @@ public QueueServiceClient createClientWithCredential() { // BEGIN: com.azure.storage.queue.queueServiceClient.instantiation.credential QueueServiceClient queueServiceClient = new QueueServiceClientBuilder() .endpoint("https://${accountName}.queue.core.windows.net") - .credential(SASTokenCredential.fromQueryParameters( - Collections.singletonMap("{SASTokenQueryParams}", "{SASTokenQueryValues}"))) + .credential(SASTokenCredential.fromQueryParameters("{SASTokenQueryParams}")) .buildClient(); // END: com.azure.storage.queue.queueServiceClient.instantiation.credential return queueServiceClient; @@ -93,8 +92,7 @@ public QueueServiceAsyncClient createAsyncClientWithCredential() { // BEGIN: com.azure.storage.queue.queueServiceAsyncClient.instantiation.credential QueueServiceAsyncClient queueServiceAsyncClient = new QueueServiceClientBuilder() .endpoint("https://{accountName}.queue.core.windows.net") - .credential(SASTokenCredential.fromQueryParameters( - Collections.singletonMap("{SASTokenQueryParams}", "{SASTokenQueryValues}"))) + .credential(SASTokenCredential.fromQueryParameters("{SASTokenQueryParams}")) .buildAsyncClient(); // END: com.azure.storage.queue.queueServiceAsyncClient.instantiation.credential return queueServiceAsyncClient;