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

[pulsar-client]Add a optional params scope for pulsar oauth2 client #11931

Conversation

tuteng
Copy link
Member

@tuteng tuteng commented Sep 6, 2021

Motivation

In some scenarios (e.g. azure cloud), when the client exchanges tokens with the server, an optional scope parameter is required, this pr fixes this issue, to ensure compatibility, when the user does not fill in this parameter, all behavior is the same as before.

Modifications

  • Add an optional parameter scope when exchanges token

Verifying this change

  • Make sure that the change passes the CI checks.

(Please pick either of the following options)

This change is a trivial rework / code cleanup without any test coverage.

(or)

This change is already covered by existing tests, such as (please describe tests).

(or)

This change added tests and can be verified as follows:

(example:)

  • Added integration tests for end-to-end deployment with large payloads (10MB)
  • Extended integration test for recovery after broker failure

Does this pull request potentially affect one of the following parts:

If yes was chosen, please highlight the changes

  • Dependencies (does it add or upgrade a dependency): (yes / no)
  • The public API: (yes / no)
  • The schema: (yes / no / don't know)
  • The default values of configurations: (yes / no)
  • The wire protocol: (yes / no)
  • The rest endpoints: (yes / no)
  • The admin cli options: (yes / no)
  • Anything that affects deployment: (yes / no / don't know)

Documentation

Check the box below and label this PR (if you have committer privilege).

Need to update docs?

@tuteng tuteng self-assigned this Sep 6, 2021
@tuteng tuteng added release/2.8.2 type/enhancement The enhancements for the existing features or docs. e.g. reduce memory usage of the delayed messages doc-required Your PR changes impact docs and you will update later. labels Sep 6, 2021
@tuteng tuteng added this to the 2.9.0 milestone Sep 6, 2021
@tuteng
Copy link
Member Author

tuteng commented Sep 6, 2021

/pulsarbot run-failure-checks

@Anonymitaet
Copy link
Member

@tuteng Thanks for your contribution. Please do not forget to add docs accordingly to allow users to know your great code changes. And you can ping me to review the docs, thanks.

@tuteng
Copy link
Member Author

tuteng commented Sep 7, 2021

/pulsarbot run-failure-checks

Copy link
Contributor

@eolivelli eolivelli left a comment

Choose a reason for hiding this comment

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

very useful, I left a comment about the test.
please take a look

@@ -86,6 +86,7 @@ public void testConfigure() throws Exception {
params.put("privateKey", "data:base64,e30=");
params.put("issuerUrl", "http://localhost");
params.put("audience", "http://localhost");
params.put("scope", "test-scope");
Copy link
Contributor

Choose a reason for hiding this comment

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

can you please add more testing about this value ?
I see this only added here and there is not follow up verification

Copy link
Member Author

Choose a reason for hiding this comment

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

@addisonj I have added some tests, please take a look at this, thanks

Copy link
Member

@michaeljmarshall michaeljmarshall left a comment

Choose a reason for hiding this comment

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

Good addition. Left some comments that I think we should address before merging this.

@@ -76,6 +77,9 @@ public TokenResult exchangeClientCredentials(ClientCredentialsExchangeRequest re
bodyMap.put("client_id", req.getClientId());
bodyMap.put("client_secret", req.getClientSecret());
bodyMap.put("audience", req.getAudience());
Copy link
Member

Choose a reason for hiding this comment

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

It seems to me that the audience field originates from the Auth0 implementation, and is not necessary in every case. Since it is optional and since the bodyMap does not allow for null values, I think we should only put this field in the bodyMap if it is non empty and non null.

Copy link
Member Author

@tuteng tuteng Sep 8, 2021

Choose a reason for hiding this comment

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

I haven't seen the audience related specification yet, if I want to change this configuration, there might be other parts that need to be updated, for example https://github.com/apache/pulsar/blob/master/pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/FlowBase.java#L61, maybe other language need to be updated as well, I'm worried about breaking client compatibility, I want to fixed it in the next pr

Copy link
Member

Choose a reason for hiding this comment

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

I'll submit a PR to change the behavior. My main point is that the RFC for this flow (https://datatracker.ietf.org/doc/html/rfc6749#section-4.4.2) does not mention audience. The only IDP I know of that requires the audience field is Auth0. As such, we shouldn't require that audience be set. I don't think it will break client compatibility to demote a field from being required to optional.

Copy link
Member

Choose a reason for hiding this comment

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

I submitted the PR here: #11988

Comment on lines 48 to 49
* @param audience the audience identifier
* @param scope the scope
Copy link
Member

Choose a reason for hiding this comment

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

I think it'd be helpful to update these comments. The audience field seems tied to auth0, not oauth2.0 itself, so you might even reference the auth0 spec: https://auth0.com/docs/authorization/flows/call-your-api-using-the-client-credentials-flow. The scope is an optional part of the oauth2.0 spec defined here: https://datatracker.ietf.org/doc/html/rfc6749#section-4.4.2.

It'd also be helpful to know what is optional and what is required.

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed scope, but without changing the audience

Copy link
Member

Choose a reason for hiding this comment

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

Makes sense, thanks.

@sijie
Copy link
Member

sijie commented Sep 8, 2021

@eolivelli please review @tuteng 's latest change.

@tuteng
Copy link
Member Author

tuteng commented Sep 8, 2021

/pulsarbot run-failure-checks

when(defaultAsyncHttpClient.preparePost(url.toString())).thenReturn(boundRequestBuilder);
when(boundRequestBuilder.setHeader("Accept", "application/json")).thenReturn(boundRequestBuilder);
when(boundRequestBuilder.setHeader("Content-Type", "application/x-www-form-urlencoded")).thenReturn(boundRequestBuilder);
when(boundRequestBuilder.setBody(body)).thenReturn(boundRequestBuilder);
Copy link
Contributor

Choose a reason for hiding this comment

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

@tuteng Could you please provide more context about this test? I do not fully understand the purpose of this test. Here we only return the boundRequestBuilder if the body equals the body map, but the request always has the scope, so that we will always return null here. But looks like the test will not touch any response handling steps.

Copy link
Member Author

@tuteng tuteng Sep 8, 2021

Choose a reason for hiding this comment

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

The purpose of this test is to test that the scope is correctly encoded in the request body, because it is a unit test, so there is no way to get the real response, In this unit test, only this part of the logic in this function exchangeClientCredentials can be tested

       Map<String, String> bodyMap = new TreeMap<>();
        bodyMap.put("grant_type", "client_credentials");
        bodyMap.put("client_id", req.getClientId());
        bodyMap.put("client_secret", req.getClientSecret());
        bodyMap.put("audience", req.getAudience());
        if (!StringUtils.isBlank(req.getScope())) {
            bodyMap.put("scope", req.getScope());
        }
        String body = buildClientCredentialsBody(bodyMap);

because it has no way to actually send an http request to a server, all http-related operations are mocked,
So there is the following mock call:

when(boundRequestBuilder.setBody(body)).thenReturn(boundRequestBuilder);

Note that there is a body that is mocked here, and the test will pass when it has the same value as the body in the following function, otherwise an exception will be thrown, the body in the following function is generated in the exchangeClientCredentials function:

Response res = httpClient.preparePost(tokenUrl.toString())
                    .setHeader("Accept", "application/json")
                    .setHeader("Content-Type", "application/x-www-form-urlencoded")
                    .setBody(body)
                    .execute()
                    .get();

This is the current way to verify that the scope is encoded correctly

@codelipenghui

Copy link
Contributor

Choose a reason for hiding this comment

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

I see. Thanks for the explanation

bodyMap.put("client_id", request.getClientId());
bodyMap.put("client_secret", request.getClientSecret());
bodyMap.put("audience", request.getAudience());
if (!StringUtils.isBlank(request.getScope())) {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: the request.getScope() is always "test-scope", so looks like a unnecessary check.

Copy link
Member

@lhotari lhotari left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Contributor

@eolivelli eolivelli left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Member

@michaeljmarshall michaeljmarshall left a comment

Choose a reason for hiding this comment

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

LGTM

@tuteng tuteng merged commit ac5114f into apache:master Sep 9, 2021
codelipenghui pushed a commit that referenced this pull request Sep 9, 2021
…11931)

### Motivation

In some scenarios (e.g. azure cloud), when the client exchanges tokens with the server, an optional scope parameter is required, this pr fixes this issue, to ensure compatibility, when the user does not fill in this parameter, all behavior is the same as before.

### Modifications

* Add an optional parameter scope when exchanges token

### Verifying this change

- [x] Make sure that the change passes the CI checks.

(cherry picked from commit ac5114f)
codelipenghui pushed a commit that referenced this pull request Sep 9, 2021
…11931)

### Motivation

In some scenarios (e.g. azure cloud), when the client exchanges tokens with the server, an optional scope parameter is required, this pr fixes this issue, to ensure compatibility, when the user does not fill in this parameter, all behavior is the same as before.

### Modifications

* Add an optional parameter scope when exchanges token

### Verifying this change

- [x] Make sure that the change passes the CI checks.

(cherry picked from commit ac5114f)
@codelipenghui codelipenghui added the cherry-picked/branch-2.7 Archived: 2.7 is end of life label Sep 9, 2021
BewareMyPower added a commit that referenced this pull request Oct 9, 2021
…ion (#12305)

### Motivation

It's a C++ client catchup for #11931.

### Modifications

- Add a `scope_` field to `ClientCredentialFlow` and load it from `ParamMap` object whose key is `scope`.
- Refactor `ClientCredentialFlow` to simplify code and make it testable:
  - Use only one constructor instead of two overloaded constructors that might look confused
  - Add a `generateJsonBody` public method for generating JSON body for post fields in `authenticate` so that it can be tested.
  - Add a `KeyFile` class like what Java client does to load client id and client secret from `ParamMap` or file.

### Verifying this change

- [x] Make sure that the change passes the CI checks.

This change added test `AuthPluginTest.testOauth2RequestBody` for the cases that scope exists or doesn't exist.
tuteng pushed a commit that referenced this pull request Oct 11, 2021
…ion (#12305)

It's a C++ client catchup for #11931.

- Add a `scope_` field to `ClientCredentialFlow` and load it from `ParamMap` object whose key is `scope`.
- Refactor `ClientCredentialFlow` to simplify code and make it testable:
  - Use only one constructor instead of two overloaded constructors that might look confused
  - Add a `generateJsonBody` public method for generating JSON body for post fields in `authenticate` so that it can be tested.
  - Add a `KeyFile` class like what Java client does to load client id and client secret from `ParamMap` or file.

- [x] Make sure that the change passes the CI checks.

This change added test `AuthPluginTest.testOauth2RequestBody` for the cases that scope exists or doesn't exist.

(cherry picked from commit 44dcc04)
codelipenghui pushed a commit that referenced this pull request Oct 11, 2021
…ion (#12305)

It's a C++ client catchup for #11931.

- Add a `scope_` field to `ClientCredentialFlow` and load it from `ParamMap` object whose key is `scope`.
- Refactor `ClientCredentialFlow` to simplify code and make it testable:
  - Use only one constructor instead of two overloaded constructors that might look confused
  - Add a `generateJsonBody` public method for generating JSON body for post fields in `authenticate` so that it can be tested.
  - Add a `KeyFile` class like what Java client does to load client id and client secret from `ParamMap` or file.

- [x] Make sure that the change passes the CI checks.

This change added test `AuthPluginTest.testOauth2RequestBody` for the cases that scope exists or doesn't exist.

(cherry picked from commit 44dcc04)
@Anonymitaet Anonymitaet removed the doc-required Your PR changes impact docs and you will update later. label Oct 18, 2021
bharanic-dev pushed a commit to bharanic-dev/pulsar that referenced this pull request Mar 18, 2022
…pache#11931)

### Motivation

In some scenarios (e.g. azure cloud), when the client exchanges tokens with the server, an optional scope parameter is required, this pr fixes this issue, to ensure compatibility, when the user does not fill in this parameter, all behavior is the same as before.

### Modifications

* Add an optional parameter scope when exchanges token

### Verifying this change

- [x] Make sure that the change passes the CI checks.
bharanic-dev pushed a commit to bharanic-dev/pulsar that referenced this pull request Mar 18, 2022
…ion (apache#12305)

### Motivation

It's a C++ client catchup for apache#11931.

### Modifications

- Add a `scope_` field to `ClientCredentialFlow` and load it from `ParamMap` object whose key is `scope`.
- Refactor `ClientCredentialFlow` to simplify code and make it testable:
  - Use only one constructor instead of two overloaded constructors that might look confused
  - Add a `generateJsonBody` public method for generating JSON body for post fields in `authenticate` so that it can be tested.
  - Add a `KeyFile` class like what Java client does to load client id and client secret from `ParamMap` or file.

### Verifying this change

- [x] Make sure that the change passes the CI checks.

This change added test `AuthPluginTest.testOauth2RequestBody` for the cases that scope exists or doesn't exist.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cherry-picked/branch-2.7 Archived: 2.7 is end of life cherry-picked/branch-2.8 Archived: 2.8 is end of life release/2.7.4 release/2.8.2 release/2.9.0 type/enhancement The enhancements for the existing features or docs. e.g. reduce memory usage of the delayed messages
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants