This is an automated email from the ASF dual-hosted git repository.
mmerli pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/pulsar-client-python.git
The following commit(s) were added to refs/heads/main by this push:
new 87a3506 Add docs and tests for AuthenticationOauth2 (#120)
87a3506 is described below
commit 87a3506d38ea1a0dd15743e34cf2b3ca9d538164
Author: Yunze Xu <[email protected]>
AuthorDate: Wed May 24 02:30:00 2023 +0800
Add docs and tests for AuthenticationOauth2 (#120)
### Modifications
Add tests to verify the changes of
https://github.com/apache/pulsar-client-cpp/pull/249 work for the Python
client.
Add docs to describe valid JSON fields used to create an
`AuthenticationOauth2` instance.
---
.github/workflows/ci-pr-validation.yaml | 8 +++
build-support/docker-compose-pulsar-oauth2.yml | 46 +++++++++++++
pulsar/__init__.py | 35 +++++++++-
tests/oauth2_test.py | 90 ++++++++++++++++++++++++++
4 files changed, 176 insertions(+), 3 deletions(-)
diff --git a/.github/workflows/ci-pr-validation.yaml
b/.github/workflows/ci-pr-validation.yaml
index aae8551..098e342 100644
--- a/.github/workflows/ci-pr-validation.yaml
+++ b/.github/workflows/ci-pr-validation.yaml
@@ -70,6 +70,14 @@ jobs:
WHEEL=$(find dist -name '*.whl')
pip3 install ${WHEEL}[avro]
+ - name: Run Oauth2 tests
+ run: |
+ docker compose -f ./build-support/docker-compose-pulsar-oauth2.yml
up -d
+ # Wait until the namespace is created, currently there is no good
way to check it via CLI
+ sleep 10
+ python3 tests/oauth2_test.py
+ docker compose -f ./build-support/docker-compose-pulsar-oauth2.yml
down
+
- name: Start Pulsar service
run: ./build-support/pulsar-test-service-start.sh
diff --git a/build-support/docker-compose-pulsar-oauth2.yml
b/build-support/docker-compose-pulsar-oauth2.yml
new file mode 100644
index 0000000..0ab818e
--- /dev/null
+++ b/build-support/docker-compose-pulsar-oauth2.yml
@@ -0,0 +1,46 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+version: '3'
+networks:
+ pulsar:
+ driver: bridge
+services:
+ standalone:
+ image: apachepulsar/pulsar:latest
+ container_name: standalone
+ hostname: local
+ restart: "no"
+ networks:
+ - pulsar
+ environment:
+ - metadataStoreUrl=zk:localhost:2181
+ - clusterName=standalone-oauth2
+ - advertisedAddress=localhost
+ - advertisedListeners=external:pulsar://localhost:6650
+ - PULSAR_MEM=-Xms512m -Xmx512m -XX:MaxDirectMemorySize=256m
+ - PULSAR_PREFIX_authenticationEnabled=true
+ -
PULSAR_PREFIX_authenticationProviders=org.apache.pulsar.broker.authentication.AuthenticationProviderToken
+ -
PULSAR_PREFIX_tokenPublicKey=data:;base64,MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2tZd/4gJda3U2Pc3tpgRAN7JPGWx/Gn17v/0IiZlNNRbP/Mmf0Vc6G1qsnaRaWNWOR+t6/a6ekFHJMikQ1N2X6yfz4UjMc8/G2FDPRmWjA+GURzARjVhxc/BBEYGoD0Kwvbq/u9CZm2QjlKrYaLfg3AeB09j0btNrDJ8rBsNzU6AuzChRvXj9IdcE/A/4N/UQ+S9cJ4UXP6NJbToLwajQ5km+CnxdGE6nfB7LWHvOFHjn9C2Rb9e37CFlmeKmIVFkagFM0gbmGOb6bnGI8Bp/VNGV0APef4YaBvBTqwoZ1Z4aDHy5eRxXfAMdtBkBupmBXqL6bpd15XRYUbu/7ck9QIDAQAB
+ -
PULSAR_PREFIX_brokerClientAuthenticationPlugin=org.apache.pulsar.client.impl.auth.oauth2.AuthenticationOAuth2
+ -
PULSAR_PREFIX_brokerClientAuthenticationParameters={"issuerUrl":"https://dev-kt-aa9ne.us.auth0.com","audience":"https://dev-kt-aa9ne.us.auth0.com/api/v2/","privateKey":"data:application/json;base64,ewogICAgICAgICAgICAiY2xpZW50X2lkIjoiWGQyM1JIc1VudlVsUDd3Y2hqTllPYUlmYXpnZUhkOXgiLAogICAgICAgICAgICAiY2xpZW50X3NlY3JldCI6InJUN3BzN1dZOHVoZFZ1QlRLV1prdHR3TGRRb3RtZEVsaWFNNXJMZm1nTmlidnF6aVotZzA3Wkg1Mk5fcG9HQWIiCiAgICAgICAgfQ=="}
+ ports:
+ - "6650:6650"
+ - "8080:8080"
+ command: bash -c "bin/apply-config-from-env.py conf/standalone.conf &&
exec bin/pulsar standalone -nss -nfw"
diff --git a/pulsar/__init__.py b/pulsar/__init__.py
index b1b8a9d..f7c05e2 100644
--- a/pulsar/__init__.py
+++ b/pulsar/__init__.py
@@ -289,15 +289,44 @@ class AuthenticationOauth2(Authentication):
"""
Oauth2 Authentication implementation
"""
- def __init__(self, auth_params_string):
+ def __init__(self, auth_params_string: str):
"""
Create the Oauth2 authentication provider instance.
+ You can create the instance by setting the necessary fields in the
JSON string.
+
+ .. code-block:: python
+
+ auth = AuthenticationOauth2('{"issuer_url": "xxx", "private_key":
"yyy"}')
+
+ The valid JSON fields are:
+
+ * issuer_url (required)
+ The URL of the authentication provider which allows the Pulsar
client to obtain an
+ access token.
+ * private_key (required)
+ The URL to the JSON credentials file. It supports the following
pattern formats:
+
+ * ``/path/to/file``
+ * ``file:///path/to/file``
+ * ``file:/path/to/file``
+ * ``data:application/json;base64,<base64-encoded-value>``
+
+ The file content or the based64 encoded value is the encoded JSON
string that contains
+ the following fields:
+
+ * ``client_id``
+ * ``client_secret``
+ * audience
+ The OAuth 2.0 "resource server" identifier for a Pulsar cluster.
+ * scope
+ The scope of an access request.
+
Parameters
----------
-
- auth_params_string: str
+ auth_params_string : str
JSON encoded configuration for Oauth2 client
+
"""
_check_type(str, auth_params_string, 'auth_params_string')
self.auth = _pulsar.AuthenticationOauth2.create(auth_params_string)
diff --git a/tests/oauth2_test.py b/tests/oauth2_test.py
new file mode 100644
index 0000000..1411ebc
--- /dev/null
+++ b/tests/oauth2_test.py
@@ -0,0 +1,90 @@
+#!/usr/bin/env python3
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+from unittest import TestCase, main
+from pulsar import AuthenticationOauth2, AuthenticationError, Client
+import base64
+import os
+
+# This test should run against the standalone that is set up with
+# build-support/docker-compose-pulsar-oauth2.yml
+class Oauth2Test(TestCase):
+
+ service_url = 'pulsar://localhost:6650'
+
+ def test_invalid_private_key(self):
+ def test_create_client(auth_params_string):
+ client = Client(self.service_url,
authentication=AuthenticationOauth2(auth_params_string))
+ with self.assertRaises(AuthenticationError):
+ client.create_producer('oauth2-test-base64')
+ client.close()
+
+ test_create_client('{"private_key":"xxx:yyy"}')
+ test_create_client('{"private_key":"data:"}')
+ test_create_client('{"private_key":"data:application/x-pem"}')
+ test_create_client('{"private_key":"data:application/json;xxx"}')
+
+ def test_key_file(self):
+ path = (os.path.dirname(os.path.abspath(__file__))
+ + '/test-conf/cpp_credentials_file.json')
+ auth = AuthenticationOauth2(f'''{{
+ "issuer_url": "https://dev-kt-aa9ne.us.auth0.com",
+ "private_key": "{path}",
+ "audience": "https://dev-kt-aa9ne.us.auth0.com/api/v2/"
+ }}''')
+ client = Client(self.service_url, authentication=auth)
+ producer = client.create_producer('oauth2-test-base64')
+ producer.close()
+ client.close()
+
+ def test_base64(self):
+ credentials = '''{
+ "client_id":"Xd23RHsUnvUlP7wchjNYOaIfazgeHd9x",
+
"client_secret":"rT7ps7WY8uhdVuBTKWZkttwLdQotmdEliaM5rLfmgNibvqziZ-g07ZH52N_poGAb"
+ }'''
+ base64_credentials = base64.b64encode(credentials.encode()).decode()
+ auth = AuthenticationOauth2(f'''{{
+ "issuer_url": "https://dev-kt-aa9ne.us.auth0.com",
+ "private_key": "data:application/json;base64,{base64_credentials}",
+ "audience": "https://dev-kt-aa9ne.us.auth0.com/api/v2/"
+ }}''')
+ client = Client(self.service_url, authentication=auth)
+ producer = client.create_producer('oauth2-test-base64')
+ producer.close()
+ client.close()
+
+ def test_wrong_secret(self):
+ credentials = '''{
+ "client_id": "my-id",
+ "client_secret":"my-secret"
+ }'''
+ base64_credentials = base64.b64encode(credentials.encode()).decode()
+ auth = AuthenticationOauth2(f'''{{
+ "issuer_url": "https://dev-kt-aa9ne.us.auth0.com",
+ "private_key": "data:application/json;base64,{base64_credentials}",
+ "audience": "https://dev-kt-aa9ne.us.auth0.com/api/v2/"
+ }}''')
+ client = Client(self.service_url, authentication=auth)
+ with self.assertRaises(AuthenticationError):
+ client.create_producer('oauth2-test-base64')
+ client.close()
+
+if __name__ == '__main__':
+ main()