You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pulsar.apache.org by lh...@apache.org on 2021/12/03 17:29:00 UTC
[pulsar] 01/04: [Java Client] Make Audience Field Optional in OAuth2 Client Credentials (#11988)
This is an automated email from the ASF dual-hosted git repository.
lhotari pushed a commit to branch branch-2.9
in repository https://gitbox.apache.org/repos/asf/pulsar.git
commit 06c344f2a60ffa995be81669059be1cd6ef6851e
Author: Michael Marshall <mi...@datastax.com>
AuthorDate: Thu Dec 2 22:55:44 2021 -0600
[Java Client] Make Audience Field Optional in OAuth2 Client Credentials (#11988)
* [Java Client] Make Audience Field in Client Credentials Optional
* Update site2/website-next/docs
* Remove update to 2.8.1 docs
* Update more docs based on code review
(cherry picked from commit b2b5463a574806b611c4065a0a27c154e7cbe0bd)
---
.../auth/oauth2/AuthenticationFactoryOAuth2.java | 6 +++---
.../impl/auth/oauth2/ClientCredentialsFlow.java | 4 ++--
.../pulsar/client/impl/auth/oauth2/README.md | 4 ++--
.../impl/auth/oauth2/protocol/TokenClient.java | 25 ++++++++++++----------
.../impl/auth/oauth2/AuthenticationOAuth2Test.java | 13 +++++++++++
.../impl/auth/oauth2/protocol/TokenClientTest.java | 18 +++-------------
site2/docs/security-oauth2.md | 4 ++--
site2/website-next/docs/security-oauth2.md | 4 ++--
8 files changed, 41 insertions(+), 37 deletions(-)
diff --git a/pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/AuthenticationFactoryOAuth2.java b/pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/AuthenticationFactoryOAuth2.java
index 707fcaf..cf56774 100644
--- a/pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/AuthenticationFactoryOAuth2.java
+++ b/pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/AuthenticationFactoryOAuth2.java
@@ -33,7 +33,7 @@ public final class AuthenticationFactoryOAuth2 {
*
* @param issuerUrl the issuer URL
* @param credentialsUrl the credentials URL
- * @param audience the audience identifier
+ * @param audience An optional field. The audience identifier used by some Identity Providers, like Auth0.
* @return an Authentication object
*/
public static Authentication clientCredentials(URL issuerUrl, URL credentialsUrl, String audience) {
@@ -45,9 +45,9 @@ public final class AuthenticationFactoryOAuth2 {
*
* @param issuerUrl the issuer URL
* @param credentialsUrl the credentials URL
- * @param audience the audience identifier
+ * @param audience An optional field. The audience identifier used by some Identity Providers, like Auth0.
* @param scope An optional field. The value of the scope parameter is expressed as a list of space-delimited,
- * case-sensitive strings. The strings are defined by the authorization server.
+ * case-sensitive strings. The strings are defined by the authorization server.
* If the value contains multiple space-delimited strings, their order does not matter,
* and each string adds an additional access range to the requested scope.
* From here: https://datatracker.ietf.org/doc/html/rfc6749#section-4.4.2
diff --git a/pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/ClientCredentialsFlow.java b/pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/ClientCredentialsFlow.java
index b011e85..bf0c289 100644
--- a/pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/ClientCredentialsFlow.java
+++ b/pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/ClientCredentialsFlow.java
@@ -118,10 +118,10 @@ class ClientCredentialsFlow extends FlowBase {
*/
public static ClientCredentialsFlow fromParameters(Map<String, String> params) {
URL issuerUrl = parseParameterUrl(params, CONFIG_PARAM_ISSUER_URL);
- String audience = parseParameterString(params, CONFIG_PARAM_AUDIENCE);
String privateKeyUrl = parseParameterString(params, CONFIG_PARAM_KEY_FILE);
- // This is an optional parameter
+ // These are optional parameters, so we only perform a get
String scope = params.get(CONFIG_PARAM_SCOPE);
+ String audience = params.get(CONFIG_PARAM_AUDIENCE);
return ClientCredentialsFlow.builder()
.issuerUrl(issuerUrl)
.audience(audience)
diff --git a/pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/README.md b/pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/README.md
index 55ffe58..b8b1237 100644
--- a/pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/README.md
+++ b/pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/README.md
@@ -46,7 +46,7 @@ The following parameters are supported:
| `type` | Oauth 2.0 auth type. Optional. | default: `client_credentials` |
| `issuerUrl` | URL of the provider which allows Pulsar to obtain an access token. Required. | `https://accounts.google.com` |
| `privateKey` | URL to a JSON credentials file (in JSON format; see below). Required. | See "Supported Pattern Formats" |
-| `audience` | An OAuth 2.0 "resource server" identifier for the Pulsar cluster. Required. | `https://broker.example.com` |
+| `audience` | An OAuth 2.0 "resource server" identifier for the Pulsar cluster. Required by some Identity Providers. Optional for client. | `https://broker.example.com` |
### Supported Pattern Formats of `privateKey`
The `privateKey` parameter supports the following three pattern formats, and contains client Credentials:
@@ -88,7 +88,7 @@ curl --request POST \
In which,
- `issuerUrl` parameter in this plugin is mapped to `--url https://dev-kt-aa9ne.us.auth0.com`
- `privateKey` file parameter in this plugin should at least contains fields `client_id` and `client_secret`.
-- `audience` parameter in this plugin is mapped to `"audience":"https://dev-kt-aa9ne.us.auth0.com/api/v2/"`
+- `audience` parameter in this plugin is mapped to `"audience":"https://dev-kt-aa9ne.us.auth0.com/api/v2/"`. This field is only used by some identity providers.
## Pulsar Client Config
You can use the provider with the following Pulsar clients.
diff --git a/pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/protocol/TokenClient.java b/pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/protocol/TokenClient.java
index f8667e8..c2b9779 100644
--- a/pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/protocol/TokenClient.java
+++ b/pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/protocol/TokenClient.java
@@ -73,10 +73,21 @@ public class TokenClient implements ClientCredentialsExchanger {
/**
* Constructing http request parameters.
- * @param bodyMap List of parameters to be requested.
+ * @param req object with relevant request parameters
* @return Generate the final request body from a map.
*/
- String buildClientCredentialsBody(Map<String, String> bodyMap) {
+ String buildClientCredentialsBody(ClientCredentialsExchangeRequest req) {
+ Map<String, String> bodyMap = new TreeMap<>();
+ bodyMap.put("grant_type", "client_credentials");
+ bodyMap.put("client_id", req.getClientId());
+ bodyMap.put("client_secret", req.getClientSecret());
+ // Only set audience and scope if they are non-empty.
+ if (!StringUtils.isBlank(req.getAudience())) {
+ bodyMap.put("audience", req.getAudience());
+ }
+ if (!StringUtils.isBlank(req.getScope())) {
+ bodyMap.put("scope", req.getScope());
+ }
return bodyMap.entrySet().stream()
.map(e -> {
try {
@@ -96,15 +107,7 @@ public class TokenClient implements ClientCredentialsExchanger {
*/
public TokenResult exchangeClientCredentials(ClientCredentialsExchangeRequest req)
throws TokenExchangeException, IOException {
- 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);
+ String body = buildClientCredentialsBody(req);
try {
diff --git a/pulsar-client/src/test/java/org/apache/pulsar/client/impl/auth/oauth2/AuthenticationOAuth2Test.java b/pulsar-client/src/test/java/org/apache/pulsar/client/impl/auth/oauth2/AuthenticationOAuth2Test.java
index ac14dd2..3ae578c 100644
--- a/pulsar-client/src/test/java/org/apache/pulsar/client/impl/auth/oauth2/AuthenticationOAuth2Test.java
+++ b/pulsar-client/src/test/java/org/apache/pulsar/client/impl/auth/oauth2/AuthenticationOAuth2Test.java
@@ -86,6 +86,19 @@ public class AuthenticationOAuth2Test {
params.put("privateKey", "data:base64,e30=");
params.put("issuerUrl", "http://localhost");
params.put("audience", "http://localhost");
+ params.put("scope", "http://localhost");
+ ObjectMapper mapper = new ObjectMapper();
+ String authParams = mapper.writeValueAsString(params);
+ this.auth.configure(authParams);
+ assertNotNull(this.auth.flow);
+ }
+
+ @Test
+ public void testConfigureWithoutOptionalParams() throws Exception {
+ Map<String, String> params = new HashMap<>();
+ params.put("type", "client_credentials");
+ params.put("privateKey", "data:base64,e30=");
+ params.put("issuerUrl", "http://localhost");
ObjectMapper mapper = new ObjectMapper();
String authParams = mapper.writeValueAsString(params);
this.auth.configure(authParams);
diff --git a/pulsar-client/src/test/java/org/apache/pulsar/client/impl/auth/oauth2/protocol/TokenClientTest.java b/pulsar-client/src/test/java/org/apache/pulsar/client/impl/auth/oauth2/protocol/TokenClientTest.java
index 1617359..da70d6c 100644
--- a/pulsar-client/src/test/java/org/apache/pulsar/client/impl/auth/oauth2/protocol/TokenClientTest.java
+++ b/pulsar-client/src/test/java/org/apache/pulsar/client/impl/auth/oauth2/protocol/TokenClientTest.java
@@ -47,19 +47,13 @@ public class TokenClientTest {
DefaultAsyncHttpClient defaultAsyncHttpClient = mock(DefaultAsyncHttpClient.class);
URL url = new URL("http://localhost");
TokenClient tokenClient = new TokenClient(url, defaultAsyncHttpClient);
- Map<String, String> bodyMap = new TreeMap<>();
ClientCredentialsExchangeRequest request = ClientCredentialsExchangeRequest.builder()
.audience("test-audience")
.clientId("test-client-id")
.clientSecret("test-client-secret")
.scope("test-scope")
.build();
- bodyMap.put("grant_type", "client_credentials");
- bodyMap.put("client_id", request.getClientId());
- bodyMap.put("client_secret", request.getClientSecret());
- bodyMap.put("audience", request.getAudience());
- bodyMap.put("scope", request.getScope());
- String body = tokenClient.buildClientCredentialsBody(bodyMap);
+ String body = tokenClient.buildClientCredentialsBody(request);
BoundRequestBuilder boundRequestBuilder = mock(BoundRequestBuilder.class);
Response response = mock(Response.class);
ListenableFuture<Response> listenableFuture = mock(ListenableFuture.class);
@@ -80,22 +74,16 @@ public class TokenClientTest {
@Test
@SuppressWarnings("unchecked")
- public void exchangeClientCredentialsSuccessByNoScopeTest() throws
+ public void exchangeClientCredentialsSuccessWithoutOptionalClientCredentialsTest() throws
IOException, TokenExchangeException, ExecutionException, InterruptedException {
DefaultAsyncHttpClient defaultAsyncHttpClient = mock(DefaultAsyncHttpClient.class);
URL url = new URL("http://localhost");
TokenClient tokenClient = new TokenClient(url, defaultAsyncHttpClient);
- Map<String, String> bodyMap = new TreeMap<>();
ClientCredentialsExchangeRequest request = ClientCredentialsExchangeRequest.builder()
- .audience("test-audience")
.clientId("test-client-id")
.clientSecret("test-client-secret")
.build();
- bodyMap.put("grant_type", "client_credentials");
- bodyMap.put("client_id", request.getClientId());
- bodyMap.put("client_secret", request.getClientSecret());
- bodyMap.put("audience", request.getAudience());
- String body = tokenClient.buildClientCredentialsBody(bodyMap);
+ String body = tokenClient.buildClientCredentialsBody(request);
BoundRequestBuilder boundRequestBuilder = mock(BoundRequestBuilder.class);
Response response = mock(Response.class);
ListenableFuture<Response> listenableFuture = mock(ListenableFuture.class);
diff --git a/site2/docs/security-oauth2.md b/site2/docs/security-oauth2.md
index 7ea9f35..35d0b5f 100644
--- a/site2/docs/security-oauth2.md
+++ b/site2/docs/security-oauth2.md
@@ -28,7 +28,7 @@ The following table lists parameters supported for the `client credentials` auth
| `type` | Oauth 2.0 authentication type. | `client_credentials` (default) | Optional |
| `issuerUrl` | URL of the authentication provider which allows the Pulsar client to obtain an access token | `https://accounts.google.com` | Required |
| `privateKey` | URL to a JSON credentials file | Support the following pattern formats: <br> <li> `file:///path/to/file` <li>`file:/path/to/file` <li> `data:application/json;base64,<base64-encoded value>` | Required |
-| `audience` | An OAuth 2.0 "resource server" identifier for the Pulsar cluster | `https://broker.example.com` | Required |
+| `audience` | An OAuth 2.0 "resource server" identifier for the Pulsar cluster | `https://broker.example.com` | Optional |
The credentials file contains service account credentials used with the client authentication type. The following shows an example of a credentials file `credentials_file.json`.
@@ -63,7 +63,7 @@ In the above example, the mapping relationship is shown as below.
- The `issuerUrl` parameter in this plugin is mapped to `--url https://dev-kt-aa9ne.us.auth0.com`.
- The `privateKey` file parameter in this plugin should at least contains the `client_id` and `client_secret` fields.
-- The `audience` parameter in this plugin is mapped to `"audience":"https://dev-kt-aa9ne.us.auth0.com/api/v2/"`.
+- The `audience` parameter in this plugin is mapped to `"audience":"https://dev-kt-aa9ne.us.auth0.com/api/v2/"`. This field is only used by some identity providers.
## Client Configuration
diff --git a/site2/website-next/docs/security-oauth2.md b/site2/website-next/docs/security-oauth2.md
index 19c3789..820a696 100644
--- a/site2/website-next/docs/security-oauth2.md
+++ b/site2/website-next/docs/security-oauth2.md
@@ -32,7 +32,7 @@ The following table lists parameters supported for the `client credentials` auth
| `type` | Oauth 2.0 authentication type. | `client_credentials` (default) | Optional |
| `issuerUrl` | URL of the authentication provider which allows the Pulsar client to obtain an access token | `https://accounts.google.com` | Required |
| `privateKey` | URL to a JSON credentials file | Support the following pattern formats: <br /> <li> `file:///path/to/file` </li><li>`file:/path/to/file` </li><li> `data:application/json;base64,<base64-encoded value>` </li>| Required |
-| `audience` | An OAuth 2.0 "resource server" identifier for the Pulsar cluster | `https://broker.example.com` | Required |
+| `audience` | An OAuth 2.0 "resource server" identifier for the Pulsar cluster | `https://broker.example.com` | Optional |
The credentials file contains service account credentials used with the client authentication type. The following shows an example of a credentials file `credentials_file.json`.
@@ -71,7 +71,7 @@ In the above example, the mapping relationship is shown as below.
- The `issuerUrl` parameter in this plugin is mapped to `--url https://dev-kt-aa9ne.us.auth0.com`.
- The `privateKey` file parameter in this plugin should at least contains the `client_id` and `client_secret` fields.
-- The `audience` parameter in this plugin is mapped to `"audience":"https://dev-kt-aa9ne.us.auth0.com/api/v2/"`.
+- The `audience` parameter in this plugin is mapped to `"audience":"https://dev-kt-aa9ne.us.auth0.com/api/v2/"`. This field is only used by some identity providers.
## Client Configuration