Skip to content

Commit

Permalink
Addressed feedback,
Browse files Browse the repository at this point in the history
  • Loading branch information
sima-zhu committed Jul 30, 2019
1 parent ea937c9 commit a48ecb7
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 84 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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<String, String> queryParameters) {
public static SASTokenCredential fromQueryParameters(String queryParameterString) {
Map<String, String> queryParameters = parseQueryString(queryParameterString);
if (ImplUtils.isNullOrEmpty(queryParameters) || !queryParameters.containsKey(SIGNATURE)) {
return null;
}
Expand All @@ -69,4 +75,77 @@ public static SASTokenCredential fromQueryParameters(Map<String, String> 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<String, String> parseQueryString(final String queryString) {
TreeMap<String, String> 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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ public QueueAsyncClient buildAsyncClient() {
* <p>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.</p>
*
* <p>Query parameters of the endpoint will be parsed using {@link SASTokenCredential#fromQueryParameters(Map)} fromQuery} in an
* <p>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.</p>
*
* @param endpoint The URL of the Azure Storage Queue instance to send service requests to and receive responses from.
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ public QueueServiceClient buildClient() {
/**
* Sets the endpoint for the Azure Storage Queue instance that the client will interact with.
*
* <p>Query parameters of the endpoint will be parsed using {@link SASTokenCredential#fromQueryParameters(Map)} fromQuery} in an
* <p>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.</p>
*
* @param endpoint The URL of the Azure Storage Queue instance to send service requests to and receive responses from.
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, String> parseQueryString(final String queryString) {
TreeMap<String, String> 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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down

0 comments on commit a48ecb7

Please sign in to comment.