Skip to content

Commit

Permalink
Stg91/aad audience (#37204)
Browse files Browse the repository at this point in the history
* adding audiences for each class

* adding support for all client builders to have audience as field

* everything but datalake file api and filesystem api

* fromstring test and bugfixes

* bugfix and recordings besides blobs

* blob recordings

* style

* spelling errors apparently

* more style and rerecording

* removed broken test

* queue recordings

* fileshare recordings

* blob recordings

* comment changes

---------

Co-authored-by: Rabab Ibrahim <[email protected]>
  • Loading branch information
ibrandes and ibrahimrabab authored Oct 19, 2023
1 parent cea7a30 commit 4133bd2
Show file tree
Hide file tree
Showing 48 changed files with 1,553 additions and 39 deletions.
2 changes: 1 addition & 1 deletion sdk/storage/azure-storage-blob/assets.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "java",
"TagPrefix": "java/storage/azure-storage-blob",
"Tag": "java/storage/azure-storage-blob_043be4a1dc"
"Tag": "java/storage/azure-storage-blob_3ba5b9fd92"
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import com.azure.core.util.logging.ClientLogger;
import com.azure.storage.blob.implementation.models.EncryptionScope;
import com.azure.storage.blob.implementation.util.BuilderHelper;
import com.azure.storage.blob.models.BlobAudience;
import com.azure.storage.blob.models.CpkInfo;
import com.azure.storage.blob.models.CustomerProvidedKey;
import com.azure.storage.common.StorageSharedKeyCredential;
Expand Down Expand Up @@ -95,6 +96,7 @@ public final class BlobClientBuilder implements
private ClientOptions clientOptions = new ClientOptions();
private Configuration configuration;
private BlobServiceVersion version;
private BlobAudience audience;

/**
* Creates a builder instance that is able to configure and construct {@link BlobClient BlobClients} and {@link
Expand Down Expand Up @@ -173,7 +175,7 @@ public BlobAsyncClient buildAsyncClient() {
HttpPipeline pipeline = (httpPipeline != null) ? httpPipeline : BuilderHelper.buildPipeline(
storageSharedKeyCredential, tokenCredential, azureSasCredential, sasToken,
endpoint, retryOptions, coreRetryOptions, logOptions,
clientOptions, httpClient, perCallPolicies, perRetryPolicies, configuration, LOGGER);
clientOptions, httpClient, perCallPolicies, perRetryPolicies, configuration, audience, LOGGER);

return new BlobAsyncClient(pipeline, endpoint, serviceVersion, accountName, blobContainerName, blobName,
snapshot, customerProvidedKey, encryptionScope, versionId);
Expand Down Expand Up @@ -614,4 +616,15 @@ public BlobClientBuilder serviceVersion(BlobServiceVersion version) {
this.version = version;
return this;
}

/**
* Sets the Audience to use for authentication with Azure Active Directory (AAD). The audience is not considered
* when using a shared key.
* @param audience {@link BlobAudience} to be used when requesting a token from Azure Active Directory (AAD).
* @return the updated BlobClientBuilder object
*/
public BlobClientBuilder audience(BlobAudience audience) {
this.audience = audience;
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import com.azure.core.util.logging.ClientLogger;
import com.azure.storage.blob.implementation.models.EncryptionScope;
import com.azure.storage.blob.implementation.util.BuilderHelper;
import com.azure.storage.blob.models.BlobAudience;
import com.azure.storage.blob.models.BlobContainerEncryptionScope;
import com.azure.storage.blob.models.CpkInfo;
import com.azure.storage.blob.models.CustomerProvidedKey;
Expand Down Expand Up @@ -93,6 +94,7 @@ public final class BlobContainerClientBuilder implements
private ClientOptions clientOptions = new ClientOptions();
private Configuration configuration;
private BlobServiceVersion version;
private BlobAudience audience;

/**
* Creates a builder instance that is able to configure and construct {@link BlobContainerClient ContainerClients}
Expand Down Expand Up @@ -161,7 +163,7 @@ public BlobContainerAsyncClient buildAsyncClient() {
HttpPipeline pipeline = (httpPipeline != null) ? httpPipeline : BuilderHelper.buildPipeline(
storageSharedKeyCredential, tokenCredential, azureSasCredential, sasToken,
endpoint, retryOptions, coreRetryOptions, logOptions,
clientOptions, httpClient, perCallPolicies, perRetryPolicies, configuration, LOGGER);
clientOptions, httpClient, perCallPolicies, perRetryPolicies, configuration, audience, LOGGER);

return new BlobContainerAsyncClient(pipeline, endpoint, serviceVersion, accountName, blobContainerName,
customerProvidedKey, encryptionScope, blobContainerEncryptionScope);
Expand Down Expand Up @@ -569,4 +571,15 @@ public BlobContainerClientBuilder serviceVersion(BlobServiceVersion version) {
this.version = version;
return this;
}

/**
* Sets the Audience to use for authentication with Azure Active Directory (AAD). The audience is not considered
* when using a shared key.
* @param audience {@link BlobAudience} to be used when requesting a token from Azure Active Directory (AAD).
* @return the updated BlobContainerClientBuilder object
*/
public BlobContainerClientBuilder audience(BlobAudience audience) {
this.audience = audience;
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import com.azure.core.util.logging.ClientLogger;
import com.azure.storage.blob.implementation.models.EncryptionScope;
import com.azure.storage.blob.implementation.util.BuilderHelper;
import com.azure.storage.blob.models.BlobAudience;
import com.azure.storage.blob.models.BlobContainerEncryptionScope;
import com.azure.storage.blob.models.CpkInfo;
import com.azure.storage.blob.models.CustomerProvidedKey;
Expand Down Expand Up @@ -94,6 +95,7 @@ public final class BlobServiceClientBuilder implements
private ClientOptions clientOptions = new ClientOptions();
private Configuration configuration;
private BlobServiceVersion version;
private BlobAudience audience;

/**
* Creates a builder instance that is able to configure and construct {@link BlobServiceClient BlobServiceClients}
Expand Down Expand Up @@ -135,7 +137,7 @@ public BlobServiceAsyncClient buildAsyncClient() {
HttpPipeline pipeline = (httpPipeline != null) ? httpPipeline : BuilderHelper.buildPipeline(
storageSharedKeyCredential, tokenCredential, azureSasCredential, sasToken,
endpoint, retryOptions, coreRetryOptions, logOptions,
clientOptions, httpClient, perCallPolicies, perRetryPolicies, configuration, LOGGER);
clientOptions, httpClient, perCallPolicies, perRetryPolicies, configuration, audience, LOGGER);

boolean foundCredential = false;
for (int i = 0; i < pipeline.getPolicyCount(); i++) {
Expand Down Expand Up @@ -531,4 +533,15 @@ public BlobServiceClientBuilder serviceVersion(BlobServiceVersion version) {
this.version = version;
return this;
}

/**
* Sets the Audience to use for authentication with Azure Active Directory (AAD). The audience is not considered
* when using a shared key.
* @param audience {@link BlobAudience} to be used when requesting a token from Azure Active Directory (AAD).
* @return the updated BlobServiceClientBuilder object
*/
public BlobServiceClientBuilder audience(BlobAudience audience) {
this.audience = audience;
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import com.azure.core.util.tracing.Tracer;
import com.azure.core.util.tracing.TracerProvider;
import com.azure.storage.blob.BlobUrlParts;
import com.azure.storage.blob.models.BlobAudience;
import com.azure.storage.common.StorageSharedKeyCredential;
import com.azure.storage.common.implementation.BuilderUtils;
import com.azure.storage.common.implementation.Constants;
Expand Down Expand Up @@ -77,6 +78,7 @@ public final class BuilderHelper {
* @param perRetryPolicies Additional {@link HttpPipelinePolicy policies} to set in the pipeline per retry.
* @param configuration Configuration store contain environment settings.
* @param logger {@link ClientLogger} used to log any exception.
* @param audience {@link BlobAudience} used to determine the audience of the blob.
* @return A new {@link HttpPipeline} from the passed values.
*/
public static HttpPipeline buildPipeline(
Expand All @@ -85,7 +87,7 @@ public static HttpPipeline buildPipeline(
RequestRetryOptions retryOptions, RetryOptions coreRetryOptions,
HttpLogOptions logOptions, ClientOptions clientOptions, HttpClient httpClient,
List<HttpPipelinePolicy> perCallPolicies, List<HttpPipelinePolicy> perRetryPolicies,
Configuration configuration, ClientLogger logger) {
Configuration configuration, BlobAudience audience, ClientLogger logger) {

CredentialValidator.validateSingleCredentialIsPresent(
storageSharedKeyCredential, tokenCredential, azureSasCredential, sasToken, logger);
Expand Down Expand Up @@ -115,7 +117,10 @@ public static HttpPipeline buildPipeline(
credentialPolicy = new StorageSharedKeyCredentialPolicy(storageSharedKeyCredential);
} else if (tokenCredential != null) {
httpsValidation(tokenCredential, "bearer token", endpoint, logger);
credentialPolicy = new BearerTokenAuthenticationPolicy(tokenCredential, Constants.STORAGE_SCOPE);
String scope = audience != null
? ((audience.toString().endsWith("/") ? audience + ".default" : audience + "/.default"))
: Constants.STORAGE_SCOPE;
credentialPolicy = new BearerTokenAuthenticationPolicy(tokenCredential, scope);
} else if (azureSasCredential != null) {
credentialPolicy = new AzureSasCredentialPolicy(azureSasCredential, false);
} else if (sasToken != null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.storage.blob.models;

import com.azure.core.util.ExpandableStringEnum;

import java.util.Collection;

/**
* The audience to be used when requesting a token from Azure Active Directory (AAD).
* Note: This audience only has an effect when authenticating a TokenCredential.
*/
public class BlobAudience extends ExpandableStringEnum<BlobAudience> {

/**
* Gets default Audience used to acquire a token for authorizing requests to any Azure Storage account.
* If no audience is specified, this resource ID is the default value: "https://storage.azure.com/".
*/
public static final BlobAudience AZURE_PUBLIC_CLOUD = fromString("https://storage.azure.com/");

/**
* Creates a new instance of {@link BlobAudience} without a {@link #toString()} value.
* This constructor shouldn't be called as it will produce a {@link BlobAudience} which doesn't have a String enum
* value.
*
* @deprecated Use one of the constants or the {@link #fromString(String)} factory method.
*/
@Deprecated
public BlobAudience() {
}

/**
* The service endpoint for a given storage account. Use this method to acquire a token for authorizing requests to
* that specific Azure Storage account and service only.
*
* @param storageAccountName The storage account name used to populate the service endpoint.
* @return the audience with the blob service endpoint.
*/
public static BlobAudience createBlobServiceAccountAudience(String storageAccountName) {
return fromString(String.format("https://%s.blob.core.windows.net/", storageAccountName));
}

/**
* The Azure Active Directory audience to use when forming authorization scopes.
* For the Language service, this value corresponds to a URL that identifies the Azure cloud where the resource is
* located.
* For more information see
* <a href="https://learn.microsoft.com/en-us/azure/storage/blobs/authorize-access-azure-active-directory">
* Authorize access to Azure blobs using Azure Active Directory</a>.
*
* @param audience The Azure Active Directory audience to use when forming authorization scopes.
* @return the corresponding BlobAudience.
*/
public static BlobAudience fromString(String audience) {
return fromString(audience, BlobAudience.class);
}

/**
* @return known BlobAudience values.
*/
public static Collection<BlobAudience> values() {
return values(BlobAudience.class);
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import com.azure.storage.blob.BlobUrlParts;
import com.azure.storage.blob.implementation.models.EncryptionScope;
import com.azure.storage.blob.implementation.util.BuilderHelper;
import com.azure.storage.blob.models.BlobAudience;
import com.azure.storage.blob.models.CpkInfo;
import com.azure.storage.blob.models.CustomerProvidedKey;
import com.azure.storage.blob.models.PageRange;
Expand Down Expand Up @@ -103,6 +104,7 @@ public final class SpecializedBlobClientBuilder implements
private ClientOptions clientOptions = new ClientOptions();
private Configuration configuration;
private BlobServiceVersion version;
private BlobAudience audience;

/**
* Creates a {@link AppendBlobClient} based on options set in the Builder. AppendBlobClients are used to perform
Expand Down Expand Up @@ -235,7 +237,7 @@ private HttpPipeline getHttpPipeline() {
return (httpPipeline != null) ? httpPipeline : BuilderHelper.buildPipeline(
storageSharedKeyCredential, tokenCredential, azureSasCredential, sasToken,
endpoint, retryOptions, coreRetryOptions, logOptions,
clientOptions, httpClient, perCallPolicies, perRetryPolicies, configuration, LOGGER);
clientOptions, httpClient, perCallPolicies, perRetryPolicies, configuration, audience, LOGGER);
}

private BlobServiceVersion getServiceVersion() {
Expand Down Expand Up @@ -759,4 +761,15 @@ public SpecializedBlobClientBuilder serviceVersion(BlobServiceVersion version) {
this.version = version;
return this;
}

/**
* Sets the Audience to use for authentication with Azure Active Directory (AAD). The audience is not considered
* when using a shared key.
* @param audience {@link BlobAudience} to be used when requesting a token from Azure Active Directory (AAD).
* @return the updated SpecializedBlobClientBuilder object
*/
public SpecializedBlobClientBuilder audience(BlobAudience audience) {
this.audience = audience;
return this;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package com.azure.storage.blob;

import com.azure.core.credential.TokenCredential;
import com.azure.identity.DefaultAzureCredentialBuilder;
import com.azure.storage.blob.models.BlobAudience;

import java.util.Locale;

/**
* This example shows how to use audience-based authentication with Azure Storage fpr blobs. Audience-based
* authentication requires AAD authentication. The audience is to be used when requesting a token from
* Azure Active Directory (AAD). Note: This audience only has an effect when authenticating a TokenCredential.
*/
public class BlobAudienceExample {

public static void main(String[] args) {
/*
* From the Azure portal, get your Storage account's name.
*/
String accountName = SampleHelper.getAccountName();

/*
* audience will look like: "https://<your storage account>.blob.core.windows.net"
*/
BlobAudience audience = BlobAudience.createBlobServiceAccountAudience(accountName);

/* The credential used is DefaultAzureCredential because it combines commonly used credentials
* in deployment and development and chooses the credential to used based on its running environment.
* More information can be found at: https://learn.microsoft.com/java/api/overview/azure/identity-readme
* AAD authentication is required for audience-based authentication.
*/
TokenCredential tokenCredential = new DefaultAzureCredentialBuilder().build();

/*
* From the Azure portal, get your Storage account blob service URL endpoint.
* The URL typically looks like this:
*/
String endpoint = String.format(Locale.ROOT, "https://%s.blob.core.windows.net", accountName);

/*
* Create a BlobServiceClient object that wraps the service endpoint, credential and a request pipeline.
*/
BlobServiceClient serviceClient = new BlobServiceClientBuilder()
.endpoint(endpoint)
.credential(tokenCredential)
.audience(null) // The default audience is "https://storage.azure.com"
.buildClient();

// This call will succeed because the default audience is "https://storage.azure.com"
serviceClient.getProperties();


/*
Now create a BlobContainerClient that takes a specific audience.
*/
BlobContainerClient containerClient = new BlobContainerClientBuilder()
.endpoint(endpoint)
.credential(tokenCredential)
.audience(audience)
.containerName("myContainer")
.buildClient();

/*
Any calls to the service should successfully work with the specified audience.
*/
containerClient.create();
containerClient.getBlobClient("myBlob").uploadFromFile("path/to/file");

/*
The storage account name must be a valid name. If an incorrect storage account name is specified, authentication
will fail.
*/
BlobAudience badAudience = BlobAudience.createBlobServiceAccountAudience("invalidAccount");
BlobContainerClient badContainerClient = new BlobContainerClientBuilder()
.endpoint(endpoint)
.credential(tokenCredential)
.audience(badAudience) // audience will look like: "https://invalidaccount.blob.core.windows.net"
.containerName("myBadContainer")
.buildClient();

try {
badContainerClient.create();
} catch (Exception e) {
System.out.println("Authentication failed with invalid storage account name.");
}
}
}
Loading

0 comments on commit 4133bd2

Please sign in to comment.