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 authentication support for ACR #19926

Merged
merged 1 commit into from
Mar 22, 2021
Merged

Conversation

pallavit
Copy link
Contributor

@pallavit pallavit commented Mar 17, 2021

ACR follows a challenge based authorization and functions as follows

For example
GET {url} /api/v1/acr/repositories translates into the following calls.

Step1: GET /api/v1/acr/repositories
Return Header: 401: www-authenticate header - Bearer realm="{url}",service="{serviceName}",scope="{scope}",error="invalid_token".

Step2: Parse the serviceName, scope from the service.

Step3: POST /api/oauth2/exchange
Request Body : {service, scope, grant-type, aadToken with ARM scope}
Response Body: {acrRefreshToken}

Step4: POST /api/oauth2/token
Request Body: {acrRefreshToken, scope, grant-type}
Response Body: {acrAccessToken}

Step5: GET /api/v1/acr/repositories
Request Header: {Bearer acrTokenAccess}

In order to prevent user from having to do this tedious process we have decided to build a convenience layer on ACR - that does this under the hood by exposing an ACR specific policy.

/**
* A token cache that supports caching a token and refreshing it.
*/
class AccessTokenCache {
Copy link
Contributor Author

@pallavit pallavit Mar 17, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally this class can be exposed via the experimental package and we can consume it.
@g2vinay - I will sync up with you on this. #Resolved

Copy link
Member

@g2vinay g2vinay Mar 18, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As per the offline discussion:
Given that ACR has a separate sts service and issues its own independent tokens with custom expiry.
It makes sense to have independent caching logic for it, as it has the potential to evolve in the future if we choose to cache in the ACR tokens per scope. (That'd be a great optimization from a performance perspective if we pull it off).

The AAD token caching logic in azure-core can evolve too in the future and will give priority to AAD tokens specifically. So, the two implementations are not directly tied together, and having independent caches, allows to optimize and augment the caching logic best for ACR and AAD respectively.

Seems both the caches are implementation details, once ACR caching logic has stabilized and evolved we can always look into merging them together without breaking public API, if possible given their mature state at that time.

#Resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created an issue to track it.


In reply to: 597131284 [](ancestors = 597131284)

import reactor.core.publisher.Mono;

/** An instance of this class provides access to all the operations defined in AccessTokensService. */
public final class AccessTokensImpl {
Copy link
Contributor Author

@pallavit pallavit Mar 17, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

public [](start = 0, length = 6)

Why do we generate these classes as public? #Resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had to hand author the generated files due to the discrepancy between www-encoded form not correctly supported. Also, I could not use the builder pattern since this class is a hierarchial pattern and has only one builder. And it needed the policy that had to be added to the builder to consume this class. Unfortunately ACR's STS goes through the same rest endpoints. Let me know if there is a better way to do it.


In reply to: 596415910 [](ancestors = 596415910)

Copy link
Member

@alzimmermsft alzimmermsft Mar 18, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These classes are generated as public since Java classes cannot be accessible across package boundaries without either being protected or public. #Resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I missed that they were in a different package.


In reply to: 597119525 [](ancestors = 597119525)

Copy link
Member

@srnagar srnagar Mar 19, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pallavit could you please file a bug in autorest.java repo with steps to reproduce and the expected code that should be generated? Ideally, we should not modify any generated code manually as it would be overwritten when we regenerate this code. #Resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There already is a bug and @jianghaolu is aware of it.


In reply to: 597954987 [](ancestors = 597954987)

autorest --java --use=C:/work/autorest.java
```

### Code generation settings
Copy link
Contributor Author

@pallavit pallavit Mar 17, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file and the swagger will change. The swagger is still being worked upon. #Resolved

* Request Header: {Bearer acrTokenAccess}
*
*/
public class ContainerRegistryCredentialsPolicy implements HttpPipelinePolicy {
Copy link
Member

@alzimmermsft alzimmermsft Mar 18, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally, we want to eagerly mark classes as final, equivalent to sealed in .NET. It's a non-breaking change to remove final in the future if required but marking as final is breaking #Resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Also, this class was not meant to be public.


In reply to: 597116660 [](ancestors = 597116660)

import reactor.core.publisher.Mono;

/** An instance of this class provides access to all the operations defined in AccessTokensService. */
public final class AccessTokensImpl {
Copy link
Member

@alzimmermsft alzimmermsft Mar 18, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These classes are generated as public since Java classes cannot be accessible across package boundaries without either being protected or public. #Resolved

package com.azure.containers.containerregistry.authentication;

import com.azure.core.credential.AccessToken;
import com.azure.core.http.*;
Copy link
Member

@alzimmermsft alzimmermsft Mar 18, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Our guidelines don't allow * imports #Resolved

/**
* This package contains classes for the container registry project.
*/
package com.azure.containers.containerregistry.authentication;
Copy link
Member

@srnagar srnagar Mar 19, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see any classes in this package. Is this package-info required? #Resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missed this. I moved all the files from here to the implementation package.


In reply to: 597948739 [](ancestors = 597948739)

Mono<AccessToken> fallback;

Supplier<Mono<AccessToken>> tokenSupplier = () -> {
Objects.requireNonNull(this.tokenRequestContext);
Copy link
Member

@srnagar srnagar Mar 19, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't throw an exception when returning a reactive publisher (Mono/Flux). Instead return Mono.error() or Flux.error() #Resolved


private Supplier<Mono<? extends AccessToken>> retrieveToken(ContainerRegistryTokenRequestContextImpl tokenRequestContext) {
return () -> {
try {
Copy link
Member

@srnagar srnagar Mar 19, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This whole supplier should be wrapped with Mono.defer() to evaluate the need to refresh at subscription-time instead of at assembly-time. #Resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The caller actually does that - retrieveToken call just above this method. Since this is a private method chose not to do it.


In reply to: 597950212 [](ancestors = 597950212)

import reactor.core.publisher.Mono;

/** An instance of this class provides access to all the operations defined in AccessTokensService. */
public final class AccessTokensImpl {
Copy link
Member

@srnagar srnagar Mar 19, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pallavit could you please file a bug in autorest.java repo with steps to reproduce and the expected code that should be generated? Ideally, we should not modify any generated code manually as it would be overwritten when we regenerate this code. #Resolved

* <p>Step5: GET /api/v1/acr/repositories
* Request Header: {Bearer acrTokenAccess}</p>
*/
public class ContainerRegistryCredentialsPolicy implements HttpPipelinePolicy {
Copy link
Member

@srnagar srnagar Mar 19, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally, we make all pipeline policies public so that users add the policy to their own HTTP pipeline. #Resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The policy is not public - just because we are still ironing out what is the right handshake with experimental package. Having said that, this policy may not be public even in future - since its a convenience layer added for the customer.


In reply to: 597957262 [](ancestors = 597957262)

* Token credentials representing the container registry refresh token.
* This token is unique per registry operation.
*/
class ContainerRegistryTokenCredentialImpl {
Copy link
Member

@srnagar srnagar Mar 19, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed offline, will this type be implementing TokenCredential? #Resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it may. As of now since the surface is non-public, we have the flexibility of changing it in future.


In reply to: 597957584 [](ancestors = 597957584)

/**
* A token request context associated with a given container registry token.
*/
class ContainerRegistryTokenRequestContextImpl {
Copy link
Member

@srnagar srnagar Mar 19, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't need to append Impl to class names if they are already in implementation package. #Resolved

@@ -0,0 +1,123 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
Copy link
Member

@srnagar srnagar Mar 19, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this class handwritten? The license header doesn't have this line // Code generated by Microsoft (R) AutoRest Code Generator. which is generally included for all generated files. #Resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, as mentioned in another file, a bug tracking this already exists.


In reply to: 597960498 [](ancestors = 597960498)


## Contributing
<Add contributing steps>
<Instructions on how to contribute>.
Copy link
Member

@alzimmermsft alzimmermsft Mar 19, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be able to copy the template project's section for this #Resolved

// Licensed under the MIT License.

/**
* This package contains classes for the container registry project.
Copy link
Member

@alzimmermsft alzimmermsft Mar 19, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* This package contains classes for the container registry project.
* This package contains authentication classes used by Azure Container Registry.
``` #Resolved

@@ -2,5 +2,6 @@
// Licensed under the MIT License.

module com.azure.containers.containerregistry {
requires transitive com.azure.core;
exports com.azure.containers.containerregistry;
Copy link
Member

@alzimmermsft alzimmermsft Mar 19, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want the classes in com.azure.containers.containerregistry.authentication to be available outside of the project you'll need to add it as an export here. #Resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't want that right now.


In reply to: 598005797 [](ancestors = 598005797)

@pallavit
Copy link
Contributor Author

/check-enforcer evaluate

@pallavit
Copy link
Contributor Author

/check-enforcer evaluate

@pallavit pallavit merged commit 5423e12 into Azure:master Mar 22, 2021
azure-sdk pushed a commit to azure-sdk/azure-sdk-for-java that referenced this pull request Aug 22, 2022
New API Path added "datawarehousequeries"  in sqlpool (Azure#19926)

* Path datawarehousequeries added in sqlpool

* Resolved Conflict - removed space

* Changes in definitions

* Changes in example-getdatawarehousequeries.json

* added object in sqlpool

* Updated datawarehousequeries json

* modified sqlpool definitions

* Removed properties in example json

* added Removed items

* added response in example json for dataWarehouseQueries

* Added properties in definitions

* updated mismatch type

* modifications in id path

* Properties changes

* changed Value to Properties

* properties to value changes

* removed Nextlink

* Removed array

* updated description

* Added type in response

* chnages in responses

* changes in readme

* Null Changes

* Changes in Readme added Datawarehousequeries

* added missing tag in batch

* Changed Value Object

* Reversed type changes

* Camel case changes

* Camel Changes

* Removed Extra Value Properties

* Added Properties

* Description Changes

* resourceClass values

* Added Custom Words

Co-authored-by: VamshiKrishna Chirra (Quadrant Resource) <vamshikrishna.chirra@quadrantresource.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Design and implement AAD auth
4 participants