Skip to content

Commit

Permalink
Added token credential for queue
Browse files Browse the repository at this point in the history
  • Loading branch information
sima-zhu committed Jul 29, 2019
1 parent 7ca6b86 commit 9d849c8
Show file tree
Hide file tree
Showing 6 changed files with 182 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,22 @@

import com.azure.core.implementation.util.ImplUtils;

import java.util.HashMap;
import java.util.Map;

/**
* Holds a SAS token used for authenticating requests.
*/
public final class SASTokenCredential {
// Required SAS token pieces
private static final String SIGNED_VERSION = "sv";
private static final String SIGNED_SERVICES = "ss";
private static final String SIGNED_RESOURCE_TYPES = "srt";
private static final String SIGNED_PERMISSIONS = "sp";
private static final String SIGNED_EXPIRY = "se";
private static final String SIGNATURE = "sig";

// Optional SAS token pieces
private static final String SIGNED_START = "st";
private static final String SIGNED_PROTOCOL = "spr";
private static final String SIGNED_IP = "sip";

private final String sasToken;

/**
* Creates a SAS token credential from the passed SAS token.
*
* @param sasToken SAS token used to authenticate requests with the service.
*/
public SASTokenCredential(String sasToken) {
private SASTokenCredential(String sasToken) {
this.sasToken = sasToken;
}

Expand All @@ -42,55 +32,41 @@ public String sasToken() {
}

/**
* Creates a SAS token credential from the passed URL query string
* @param query URL query used to build the SAS token
* @return a SAS token credential if the query param contains all the necessary pieces
* Creates a SAS token credential from the passed SAS token.
*
* @param sasToken SAS token
* @return a SAS token credential if {@code sasToken} is not {@code null} or empty, otherwise null.
*/
public static SASTokenCredential fromQuery(String query) {
if (ImplUtils.isNullOrEmpty(query)) {
public static SASTokenCredential fromSASTokenString(String sasToken) {
if (ImplUtils.isNullOrEmpty(sasToken)) {
return null;
}

HashMap<String, String> queryParams = new HashMap<>();
for (String queryParam : query.split("&")) {
String key = queryParam.split("=", 2)[0];
queryParams.put(key, queryParam);
}
return new SASTokenCredential(sasToken);
}

if (queryParams.size() < 6
|| !queryParams.containsKey(SIGNED_VERSION)
|| !queryParams.containsKey(SIGNED_SERVICES)
|| !queryParams.containsKey(SIGNED_RESOURCE_TYPES)
|| !queryParams.containsKey(SIGNED_PERMISSIONS)
|| !queryParams.containsKey(SIGNED_EXPIRY)
|| !queryParams.containsKey(SIGNATURE)) {
/**
* Creates a SAS token credential from the passed query string parameters.
*
* @param queryParameters 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) {
if (ImplUtils.isNullOrEmpty(queryParameters) || !queryParameters.containsKey(SIGNATURE)) {
return null;
}

StringBuilder sasTokenBuilder = new StringBuilder(queryParams.get(SIGNED_VERSION))
.append("&").append(queryParams.get(SIGNED_SERVICES))
.append("&").append(queryParams.get(SIGNED_RESOURCE_TYPES))
.append("&").append(queryParams.get(SIGNED_PERMISSIONS));
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> kvp : queryParameters.entrySet()) {
if (sb.length() != 0) {
sb.append("&");

// SIGNED_START is optional
if (queryParams.containsKey(SIGNED_START)) {
sasTokenBuilder.append("&").append(queryParams.get(SIGNED_START));
}

sasTokenBuilder.append("&").append(queryParams.get(SIGNED_EXPIRY));
}

// SIGNED_IP is optional
if (queryParams.containsKey(SIGNED_IP)) {
sasTokenBuilder.append("&").append(queryParams.get(SIGNED_IP));
sb.append(kvp.getKey()).append("=").append(kvp.getValue());
}

// SIGNED_PROTOCOL is optional
if (queryParams.containsKey(SIGNED_PROTOCOL)) {
sasTokenBuilder.append("&").append(queryParams.get(SIGNED_PROTOCOL));
}

sasTokenBuilder.append("&").append(queryParams.get(SIGNATURE));

return new SASTokenCredential(sasTokenBuilder.toString());
return new SASTokenCredential(sb.toString());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
// Licensed under the MIT License.
package com.azure.storage.queue;

import com.azure.core.credentials.TokenCredential;
import com.azure.core.http.HttpClient;
import com.azure.core.http.HttpPipeline;
import com.azure.core.http.policy.AddDatePolicy;
import com.azure.core.http.policy.BearerTokenAuthenticationPolicy;
import com.azure.core.http.policy.HttpLogDetailLevel;
import com.azure.core.http.policy.HttpLoggingPolicy;
import com.azure.core.http.policy.HttpPipelinePolicy;
Expand All @@ -26,6 +28,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;

/**
Expand Down Expand Up @@ -83,6 +86,7 @@ public final class QueueClientBuilder {
private HttpLogDetailLevel logLevel;
private RetryPolicy retryPolicy;
private Configuration configuration;
private TokenCredential tokenCredential;

/**
* Creates a builder instance that is able to configure and construct {@link QueueClient QueueClients}
Expand Down Expand Up @@ -134,7 +138,7 @@ public QueueAsyncClient buildAsyncClient() {
Objects.requireNonNull(endpoint);
Objects.requireNonNull(queueName);

if (sasTokenCredential == null && sharedKeyCredential == null) {
if (sasTokenCredential == null && sharedKeyCredential == null && tokenCredential == null) {
LOGGER.asError().log("Credentials are required for authorization");
throw new IllegalArgumentException("Credentials are required for authorization");
}
Expand All @@ -151,7 +155,9 @@ public QueueAsyncClient buildAsyncClient() {

if (sharedKeyCredential != null) {
policies.add(new SharedKeyCredentialPolicy(sharedKeyCredential));
} else {
} else if (tokenCredential != null) {
policies.add(new BearerTokenAuthenticationPolicy(tokenCredential, String.format("%s/.default", endpoint)));
} else if (sasTokenCredential != null) {
policies.add(new SASTokenCredentialPolicy(sasTokenCredential));
}

Expand All @@ -177,7 +183,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#fromQuery(String) fromQuery} in an
* <p>Query parameters of the endpoint will be parsed using {@link SASTokenCredential#fromQueryParameters(Map)} 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 @@ -197,9 +203,10 @@ public QueueClientBuilder endpoint(String endpoint) {
}

// Attempt to get the SAS token from the URL passed
SASTokenCredential credential = SASTokenCredential.fromQuery(fullURL.getQuery());
if (credential != null) {
this.sasTokenCredential = credential;
this.sasTokenCredential = SASTokenCredential.fromQueryParameters(Utility.parseQueryString(fullURL.getQuery()));
if (this.sasTokenCredential != null) {
this.sharedKeyCredential = null;
this.tokenCredential = null;
}
} catch (MalformedURLException ex) {
LOGGER.asError().log("The Azure Storage Queue endpoint url is malformed. Endpoint: " + endpoint);
Expand Down Expand Up @@ -230,6 +237,8 @@ public QueueClientBuilder queueName(String queueName) {
*/
public QueueClientBuilder credential(SASTokenCredential credential) {
this.sasTokenCredential = Objects.requireNonNull(credential);
this.sharedKeyCredential = null;
this.tokenCredential = null;
return this;
}

Expand All @@ -242,6 +251,21 @@ public QueueClientBuilder credential(SASTokenCredential credential) {
*/
public QueueClientBuilder credential(SharedKeyCredential credential) {
this.sharedKeyCredential = Objects.requireNonNull(credential);
this.sasTokenCredential = null;
this.tokenCredential = null;
return this;
}

/**
* Sets the {@link TokenCredential} used to authenticate requests sent to the Queue service.
* @param credential authorization credential
* @return the updated QueueServiceClientBuilder object
* @throws NullPointerException If {@code credential} is {@code null}
*/
public QueueClientBuilder credential(TokenCredential credential) {
this.tokenCredential = Objects.requireNonNull(credential);
this.sharedKeyCredential = null;
this.sasTokenCredential = null;
return this;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
// Licensed under the MIT License.
package com.azure.storage.queue;

import com.azure.core.credentials.TokenCredential;
import com.azure.core.http.HttpClient;
import com.azure.core.http.HttpPipeline;
import com.azure.core.http.policy.AddDatePolicy;
import com.azure.core.http.policy.BearerTokenAuthenticationPolicy;
import com.azure.core.http.policy.HttpLogDetailLevel;
import com.azure.core.http.policy.HttpLoggingPolicy;
import com.azure.core.http.policy.HttpPipelinePolicy;
Expand All @@ -23,6 +25,7 @@
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
Expand Down Expand Up @@ -81,6 +84,7 @@ public final class QueueServiceClientBuilder {
private HttpLogDetailLevel logLevel;
private RetryPolicy retryPolicy;
private Configuration configuration;
private TokenCredential tokenCredential;

/**
* Creates a builder instance that is able to configure and construct {@link QueueServiceClient QueueServiceClients}
Expand Down Expand Up @@ -110,7 +114,7 @@ public QueueServiceClientBuilder() {
public QueueServiceAsyncClient buildAsyncClient() {
Objects.requireNonNull(endpoint);

if (sasTokenCredential == null && sharedKeyCredential == null) {
if (sasTokenCredential == null && sharedKeyCredential == null && tokenCredential == null) {
LOGGER.asError().log("Credentials are required for authorization");
throw new IllegalArgumentException("Credentials are required for authorization");
}
Expand All @@ -127,7 +131,9 @@ public QueueServiceAsyncClient buildAsyncClient() {

if (sharedKeyCredential != null) {
policies.add(new SharedKeyCredentialPolicy(sharedKeyCredential));
} else {
} else if (tokenCredential != null) {
policies.add(new BearerTokenAuthenticationPolicy(tokenCredential, String.format("%s/.default", endpoint)));
} else if (sasTokenCredential != null) {
policies.add(new SASTokenCredentialPolicy(sasTokenCredential));
}

Expand Down Expand Up @@ -169,7 +175,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#fromQuery(String) fromQuery} in an
* <p>Query parameters of the endpoint will be parsed using {@link SASTokenCredential#fromQueryParameters(Map)} 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 @@ -183,9 +189,10 @@ public QueueServiceClientBuilder endpoint(String endpoint) {
this.endpoint = new URL(fullURL.getProtocol() + "://" + fullURL.getHost());

// Attempt to get the SAS token from the URL passed
SASTokenCredential credential = SASTokenCredential.fromQuery(fullURL.getQuery());
if (credential != null) {
this.sasTokenCredential = credential;
this.sasTokenCredential = SASTokenCredential.fromQueryParameters(Utility.parseQueryString(fullURL.getQuery()));
if (this.sasTokenCredential != null) {
this.sharedKeyCredential = null;
this.tokenCredential = null;
}
} catch (MalformedURLException ex) {
LOGGER.asError().log("The Azure Storage Queue endpoint url is malformed.");
Expand All @@ -204,6 +211,8 @@ public QueueServiceClientBuilder endpoint(String endpoint) {
*/
public QueueServiceClientBuilder credential(SASTokenCredential credential) {
this.sasTokenCredential = Objects.requireNonNull(credential);
this.sharedKeyCredential = null;
this.tokenCredential = null;
return this;
}

Expand All @@ -216,9 +225,23 @@ public QueueServiceClientBuilder credential(SASTokenCredential credential) {
*/
public QueueServiceClientBuilder credential(SharedKeyCredential credential) {
this.sharedKeyCredential = Objects.requireNonNull(credential);
this.sasTokenCredential = null;
this.tokenCredential = null;
return this;
}

/**
* Sets the {@link TokenCredential} used to authenticate requests sent to the Queue service.
* @param credential authorization credential
* @return the updated QueueServiceClientBuilder object
* @throws NullPointerException If {@code credential} is {@code null}
*/
public QueueServiceClientBuilder credential(TokenCredential credential) {
this.tokenCredential = Objects.requireNonNull(credential);
this.sharedKeyCredential = null;
this.sasTokenCredential = null;
return this;
}

/**
* Creates a {@link SharedKeyCredential} from the {@code connectionString} used to authenticate requests sent to the
Expand Down
Loading

0 comments on commit 9d849c8

Please sign in to comment.