Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for ContainerRegistryAudience in ACR #23434

Merged
merged 3 commits into from
Aug 11, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.azure.containers.containerregistry.ContainerRegistryAsyncClient;
import com.azure.containers.containerregistry.ContainerRegistryClient;
import com.azure.containers.containerregistry.ContainerRegistryClientBuilder;
import com.azure.containers.containerregistry.models.ContainerRegistryAudience;
import com.azure.core.credential.TokenCredential;
import com.azure.core.management.AzureEnvironment;
import com.azure.core.management.profile.AzureProfile;
Expand Down Expand Up @@ -72,6 +73,7 @@ public ServiceTest(TOptions options) {
tokenCredential = new DefaultAzureCredentialBuilder().build();
ContainerRegistryClientBuilder builder = new ContainerRegistryClientBuilder()
.endpoint(registryEndpoint)
.audience(ContainerRegistryAudience.AZURERESOURCEMANAGERPUBLICCLOUD)
.credential(tokenCredential);

this.containerRegistryClient = builder.buildClient();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,22 @@ The [Azure Identity library][identity] provides easy Azure Active Directory supp
Note all the below samples assume you have an endpoint, which is the URL including the name of the login server and the `https://` prefix.
More information at [Azure Container Registry portal][container_registry_create_portal]

<!-- embedme ./src/samples/java/com/azure/containers/containerregistry/ReadmeSamples.java#L31-L35 -->
<!-- embedme ./src/samples/java/com/azure/containers/containerregistry/ReadmeSamples.java#L30-L35 -->
```Java
DefaultAzureCredential credential = new DefaultAzureCredentialBuilder().build();
ContainerRegistryClient client = new ContainerRegistryClientBuilder()
.endpoint(endpoint)
.audience(ContainerRegistryAudience.AZURERESOURCEMANAGERPUBLICCLOUD)
.credential(credential)
.buildClient();
```

<!-- embedme ./src/samples/java/com/azure/containers/containerregistry/ReadmeSamples.java#L39-L43 -->
<!-- embedme ./src/samples/java/com/azure/containers/containerregistry/ReadmeSamples.java#L39-L44 -->
```Java
DefaultAzureCredential credential = new DefaultAzureCredentialBuilder().build();
ContainerRegistryAsyncClient client = new ContainerRegistryClientBuilder()
.endpoint(endpoint)
.audience(ContainerRegistryAudience.AZURERESOURCEMANAGERPUBLICCLOUD)
.credential(credential)
.buildAsyncClient();
```
Expand All @@ -62,18 +64,12 @@ To authenticate with a registry in a [National Cloud](https://docs.microsoft.com
- Set the authorityHost in the credential builder.
- Set the authenticationScope in ContainerRegistryClientBuilder.

<!-- embedme ./src/samples/java/com/azure/containers/containerregistry/ReadmeSamples.java#L183-L197 -->
<!-- embedme ./src/samples/java/com/azure/containers/containerregistry/ReadmeSamples.java#L193-L201 -->
```Java
AzureProfile profile = new AzureProfile(AzureEnvironment.AZURE_US_GOVERNMENT);
TokenCredential credentials = new DefaultAzureCredentialBuilder()
.authorityHost(profile.getEnvironment().getActiveDirectoryEndpoint())
.build();

final String authenticationScope = "https://management.usgovcloudapi.net/.default";
ContainerRegistryClient containerRegistryClient = new ContainerRegistryClientBuilder()
.endpoint(getEndpoint())
.credential(credentials)
.authenticationScope(authenticationScope)
.audience(ContainerRegistryAudience.AZURECHINA)
.buildClient();

containerRegistryClient
Expand All @@ -87,17 +83,19 @@ The user must use this setting on a registry that has been enabled for anonymous
In this mode, the user can only call listRepositoryNames method and its overload. All the other calls will fail.
For more information please read [Anonymous Pull Access](https://docs.microsoft.com/azure/container-registry/container-registry-faq#how-do-i-enable-anonymous-pull-access)

<!-- embedme ./src/samples/java/com/azure/containers/containerregistry/ReadmeSamples.java#L73-L75 -->
<!-- embedme ./src/samples/java/com/azure/containers/containerregistry/ReadmeSamples.java#L76-L79 -->
```Java
ContainerRegistryClient client = new ContainerRegistryClientBuilder()
.endpoint(endpoint)
.audience(ContainerRegistryAudience.AZURERESOURCEMANAGERPUBLICCLOUD)
.buildClient();
```

<!-- embedme ./src/samples/java/com/azure/containers/containerregistry/ReadmeSamples.java#L79-L81 -->
<!-- embedme ./src/samples/java/com/azure/containers/containerregistry/ReadmeSamples.java#L83-L86 -->
```Java
ContainerRegistryAsyncClient client = new ContainerRegistryClientBuilder()
.endpoint(endpoint)
.audience(ContainerRegistryAudience.AZURERESOURCEMANAGERPUBLICCLOUD)
.buildAsyncClient();
```

Expand Down Expand Up @@ -129,11 +127,12 @@ For more information please see [Container Registry Concepts](https://docs.micro

Iterate through the collection of repositories in the registry.

<!-- embedme ./src/samples/java/com/azure/containers/containerregistry/ReadmeSamples.java#L47-L53 -->
<!-- embedme ./src/samples/java/com/azure/containers/containerregistry/ReadmeSamples.java#L48-L55 -->
```Java
DefaultAzureCredential credential = new DefaultAzureCredentialBuilder().build();
ContainerRegistryClient client = new ContainerRegistryClientBuilder()
.endpoint(endpoint)
.audience(ContainerRegistryAudience.AZURERESOURCEMANAGERPUBLICCLOUD)
.credential(credential)
.buildClient();

Expand All @@ -142,10 +141,11 @@ client.listRepositoryNames().forEach(repository -> System.out.println(repository

### List tags with anonymous access

<!-- embedme ./src/samples/java/com/azure/containers/containerregistry/ReadmeSamples.java#L140-L151 -->
<!-- embedme ./src/samples/java/com/azure/containers/containerregistry/ReadmeSamples.java#L147-L159 -->
```Java
ContainerRegistryClient anonymousClient = new ContainerRegistryClientBuilder()
.endpoint(endpoint)
.audience(ContainerRegistryAudience.AZURERESOURCEMANAGERPUBLICCLOUD)
.buildClient();

RegistryArtifact image = anonymousClient.getArtifact(repositoryName, digest);
Expand All @@ -160,12 +160,13 @@ for (ArtifactTagProperties tag : tags) {

### Set artifact properties

<!-- embedme ./src/samples/java/com/azure/containers/containerregistry/ReadmeSamples.java#L119-L132 -->
<!-- embedme ./src/samples/java/com/azure/containers/containerregistry/ReadmeSamples.java#L125-L139 -->
```Java
TokenCredential defaultCredential = new DefaultAzureCredentialBuilder().build();

ContainerRegistryClient client = new ContainerRegistryClientBuilder()
.endpoint(endpoint)
.audience(ContainerRegistryAudience.AZURERESOURCEMANAGERPUBLICCLOUD)
.credential(defaultCredential)
.buildClient();

Expand All @@ -180,12 +181,13 @@ image.updateTagProperties(

### Delete Images

<!-- embedme ./src/samples/java/com/azure/containers/containerregistry/ReadmeSamples.java#L85-L113 -->
<!-- embedme ./src/samples/java/com/azure/containers/containerregistry/ReadmeSamples.java#L90-L119 -->
```Java
TokenCredential defaultCredential = new DefaultAzureCredentialBuilder().build();

ContainerRegistryClient client = new ContainerRegistryClientBuilder()
.endpoint(endpoint)
.audience(ContainerRegistryAudience.AZURERESOURCEMANAGERPUBLICCLOUD)
.credential(defaultCredential)
.buildClient();

Expand Down Expand Up @@ -214,13 +216,14 @@ for (String repositoryName : client.listRepositoryNames()) {
```

### Delete repository with anonymous access throws
<!-- embedme ./src/samples/java/com/azure/containers/containerregistry/ReadmeSamples.java#L155-L167 -->
<!-- embedme ./src/samples/java/com/azure/containers/containerregistry/ReadmeSamples.java#L163-L176 -->
```Java
final String endpoint = getEndpoint();
final String repositoryName = getRepositoryName();

ContainerRegistryClient anonymousClient = new ContainerRegistryClientBuilder()
.endpoint(endpoint)
.audience(ContainerRegistryAudience.AZURERESOURCEMANAGERPUBLICCLOUD)
.buildClient();

try {
Expand All @@ -236,11 +239,12 @@ try {
All container registry service operations will throw a
[HttpResponseException][HttpResponseException] on failure.

<!-- embedme ./src/samples/java/com/azure/containers/containerregistry/ReadmeSamples.java#L59-L69 -->
<!-- embedme ./src/samples/java/com/azure/containers/containerregistry/ReadmeSamples.java#L61-L72 -->
```Java
DefaultAzureCredential credential = new DefaultAzureCredentialBuilder().build();
ContainerRepository containerRepository = new ContainerRegistryClientBuilder()
.endpoint(endpoint)
.audience(ContainerRegistryAudience.AZURERESOURCEMANAGERPUBLICCLOUD)
.credential(credential)
.buildClient()
.getRepository(repositoryName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License.
package com.azure.containers.containerregistry;

import com.azure.containers.containerregistry.models.ContainerRegistryAudience;
import com.azure.core.annotation.ServiceClientBuilder;
import com.azure.core.credential.TokenCredential;
import com.azure.core.http.HttpClient;
Expand Down Expand Up @@ -29,7 +30,7 @@
* #buildClient() buildClient} and {@link #buildAsyncClient() buildAsyncClient} respectively to construct an instance of
* the desired client.
*
* <p>The client needs the service endpoint of the Azure Container Registry and Azure access credentials.
* <p>The client needs the service endpoint of the Azure Container Registry, Audience for ACR that you want to target and Azure access credentials to use for authentication.
* <p><strong>Instantiating an asynchronous Container Registry client</strong></p>
* {@codesnippet com.azure.containers.containerregistry.ContainerRegistryAsyncClient.instantiation}
*
Expand Down Expand Up @@ -84,7 +85,7 @@ public final class ContainerRegistryClientBuilder {
private HttpLogOptions httpLogOptions;
private RetryPolicy retryPolicy;
private ContainerRegistryServiceVersion version;
private String authenticationScope;
private ContainerRegistryAudience audience;

/**
* Sets the service endpoint for the Azure Container Registry instance.
Expand All @@ -105,19 +106,15 @@ public ContainerRegistryClientBuilder endpoint(String endpoint) {
}

/**
* Sets the authentication scope to be used for getting AAD credentials.
* Sets the audience for the Azure Container Registry service.
*
* <p> To connect to a different cloud, set this value to "&lt;resource-id&gt;/.default",
* where &lt;resource-id&gt; is one of the Resource IDs listed at
* https://docs.microsoft.com/azure/active-directory/managed-identities-azure-resources/services-support-managed-identities#azure-resource-manager.
* For example, to connect to the Azure Germany cloud, {@code authenticationScope} is set to "https://management.microsoftazure.de/.default".
* </p>
*
* @param authenticationScope ARM management scope associated with the given registry.
* @param audience ARM management scope associated with the given registry.
* @throws NullPointerException If {@code audience} is null.
* @return The updated {@link ContainerRegistryClientBuilder} object.
*/
public ContainerRegistryClientBuilder authenticationScope(String authenticationScope) {
this.authenticationScope = authenticationScope;
public ContainerRegistryClientBuilder audience(ContainerRegistryAudience audience) {
Objects.requireNonNull(audience, "audience can't be null");
pallavit marked this conversation as resolved.
Show resolved Hide resolved
this.audience = audience;
pallavit marked this conversation as resolved.
Show resolved Hide resolved
return this;
}

Expand All @@ -126,7 +123,6 @@ public ContainerRegistryClientBuilder authenticationScope(String authenticationS
*
* @param credential Azure token credentials used to authenticate HTTP requests.
* @return The updated {@link ContainerRegistryClientBuilder} object.
* @throws NullPointerException if credential is null.
*/
public ContainerRegistryClientBuilder credential(TokenCredential credential) {
this.credential = credential;
Expand Down Expand Up @@ -271,9 +267,12 @@ public ContainerRegistryClientBuilder addPolicy(HttpPipelinePolicy policy) {
*
* @return A {@link ContainerRegistryAsyncClient} with the options set from the builder.
* @throws NullPointerException If {@code endpoint} has not been set. You can set it by calling {@link #endpoint(String)}.
* @throws NullPointerException If {@code credential} or {@code httpPipeline} has not been set.
* @throws NullPointerException If {@code audience} has not been set. You can set it by calling {@link #audience(ContainerRegistryAudience)}.
alzimmermsft marked this conversation as resolved.
Show resolved Hide resolved
*/
public ContainerRegistryAsyncClient buildAsyncClient() {
Objects.requireNonNull(endpoint, "endpoint can't be null");
Objects.requireNonNull(audience, "audience can't be null");

// Service version
ContainerRegistryServiceVersion serviceVersion = (version != null)
? version
Expand All @@ -296,7 +295,7 @@ private HttpPipeline getHttpPipeline() {
this.configuration,
this.retryPolicy,
this.credential,
this.authenticationScope,
this.audience,
this.perCallPolicies,
this.perRetryPolicies,
this.httpClient,
Expand All @@ -314,7 +313,7 @@ private HttpPipeline getHttpPipeline() {
*
* @return A {@link ContainerRegistryClient} with the options set from the builder.
* @throws NullPointerException If {@code endpoint} has not been set. You can set it by calling {@link #endpoint(String)}.
* @throws NullPointerException If {@code credential} or {@code httpPipeline} has not been set.
* @throws NullPointerException If {@code audience} has not been set. You can set it by calling {@link #audience(ContainerRegistryAudience)}.
*/
public ContainerRegistryClient buildClient() {
return new ContainerRegistryClient(buildAsyncClient());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import com.azure.containers.containerregistry.implementation.authentication.ContainerRegistryTokenService;
import com.azure.containers.containerregistry.implementation.models.AcrErrorsException;
import com.azure.containers.containerregistry.models.ContainerRegistryAudience;
import com.azure.core.credential.TokenCredential;
import com.azure.core.exception.ClientAuthenticationException;
import com.azure.core.exception.HttpResponseException;
Expand Down Expand Up @@ -185,7 +186,7 @@ static HttpPipeline buildHttpPipeline(
Configuration configuration,
RetryPolicy retryPolicy,
TokenCredential credential,
String authenticationScope,
ContainerRegistryAudience audience,
List<HttpPipelinePolicy> perCallPolicies,
List<HttpPipelinePolicy> perRetryPolicies,
HttpClient httpClient,
Expand Down Expand Up @@ -222,7 +223,7 @@ static HttpPipeline buildHttpPipeline(

ContainerRegistryTokenService tokenService = new ContainerRegistryTokenService(
credential,
authenticationScope,
audience,
endpoint,
new HttpPipelineBuilder()
.policies(credentialPolicies.toArray(new HttpPipelinePolicy[0]))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

package com.azure.containers.containerregistry.implementation.authentication;

import com.azure.containers.containerregistry.models.ContainerRegistryAudience;
import com.azure.core.credential.AccessToken;
import com.azure.core.credential.TokenCredential;
import com.azure.core.credential.TokenRequestContext;
Expand All @@ -17,17 +18,16 @@ public class ContainerRegistryRefreshTokenCredential {
private final TokenCredential aadTokenCredential;
private final TokenServiceImpl tokenService;
private final String authenticationScope;
public static final String AAD_DEFAULT_SCOPE = "https://management.azure.com/.default";

/**
* Creates an instance of RefreshTokenCredential with default scheme "Bearer".
* @param tokenService the container registry token service that calls the token rest APIs.
* @param aadTokenCredential the ARM access token.
*/
ContainerRegistryRefreshTokenCredential(TokenServiceImpl tokenService, TokenCredential aadTokenCredential, String authenticationScope) {
ContainerRegistryRefreshTokenCredential(TokenServiceImpl tokenService, TokenCredential aadTokenCredential, ContainerRegistryAudience audience) {
this.tokenService = tokenService;
this.aadTokenCredential = aadTokenCredential;
this.authenticationScope = authenticationScope == null ? AAD_DEFAULT_SCOPE : authenticationScope;
this.authenticationScope = audience + "/.default";
alzimmermsft marked this conversation as resolved.
Show resolved Hide resolved
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

package com.azure.containers.containerregistry.implementation.authentication;

import com.azure.containers.containerregistry.models.ContainerRegistryAudience;
import com.azure.core.credential.AccessToken;
import com.azure.core.credential.TokenCredential;
import com.azure.core.credential.TokenRequestContext;
Expand Down Expand Up @@ -30,11 +31,11 @@ public class ContainerRegistryTokenService implements TokenCredential {
* @param pipeline the pipeline to be used for the rest calls to the service.
* @param serializerAdapter the serializer adapter to be used for the rest calls to the service.
*/
public ContainerRegistryTokenService(TokenCredential aadTokenCredential, String authenticationScope, String url, HttpPipeline pipeline, SerializerAdapter serializerAdapter) {
public ContainerRegistryTokenService(TokenCredential aadTokenCredential, ContainerRegistryAudience audience, String url, HttpPipeline pipeline, SerializerAdapter serializerAdapter) {
this.tokenService = new TokenServiceImpl(url, pipeline, serializerAdapter);

if (aadTokenCredential != null) {
this.refreshTokenCache = new AccessTokenCacheImpl(new ContainerRegistryRefreshTokenCredential(tokenService, aadTokenCredential, authenticationScope));
this.refreshTokenCache = new AccessTokenCacheImpl(new ContainerRegistryRefreshTokenCredential(tokenService, aadTokenCredential, audience));
} else {
isAnonymousAccess = true;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.containers.containerregistry.models;

import com.azure.core.util.ExpandableStringEnum;
import com.fasterxml.jackson.annotation.JsonCreator;

import java.util.Collection;

/** Defines values for ContainerRegistryAudience. */
public class ContainerRegistryAudience extends ExpandableStringEnum<ContainerRegistryAudience> {
/** Static value AzureResourceManagerChina for ContainerRegistryAudience. */
public static final ContainerRegistryAudience AZURERESOURCEMANAGERCHINA = fromString("https://management.chinacloudapi.cn");
pallavit marked this conversation as resolved.
Show resolved Hide resolved

/** Static value AzureResourceManagerGermany for ContainerRegistryAudience. */
public static final ContainerRegistryAudience AZURERESOURCEMANAGERGERMANY = fromString("https://management.microsoftazure.de");

/** Static value AzureResourceManagerGovernment for ContainerRegistryAudience. */
public static final ContainerRegistryAudience AZURERESOURCEMANAGERGOVERNMENT = fromString("https://management.usgovcloudapi.net");
pallavit marked this conversation as resolved.
Show resolved Hide resolved

/** Static value AzureResourceManagerPublicCloud for ContainerRegistryAudience. */
public static final ContainerRegistryAudience AZURERESOURCEMANAGERPUBLICCLOUD = fromString("https://management.azure.com");
pallavit marked this conversation as resolved.
Show resolved Hide resolved

/**
* Creates or finds a ContainerRegistryAudience from its string representation.
*
* @param name a name to look for.
* @return the corresponding ArtifactArchitecture.
pallavit marked this conversation as resolved.
Show resolved Hide resolved
*/
@JsonCreator
public static ContainerRegistryAudience fromString(String name) {
return fromString(name, ContainerRegistryAudience.class);
}

/** @return known ArtifactArchitecture values. */
pallavit marked this conversation as resolved.
Show resolved Hide resolved
public static Collection<ContainerRegistryAudience> values() {
return values(ContainerRegistryAudience.class);
}
}
Loading