You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pulsar.apache.org by mm...@apache.org on 2022/06/23 19:45:57 UTC
[pulsar-client-go] branch master updated: feat: add basic authentication (#778)
This is an automated email from the ASF dual-hosted git repository.
mmerli pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-client-go.git
The following commit(s) were added to refs/heads/master by this push:
new 2ae909e feat: add basic authentication (#778)
2ae909e is described below
commit 2ae909ecb2d01dffd7517d6fd5aaf1f664c6932f
Author: Zixuan Liu <no...@gmail.com>
AuthorDate: Fri Jun 24 03:45:52 2022 +0800
feat: add basic authentication (#778)
---
Dockerfile | 2 +
integration-tests/.htpasswd | 1 +
integration-tests/license_test.go | 1 +
integration-tests/standalone.conf | 2 +-
pulsar/client.go | 5 +++
pulsar/client_impl_test.go | 47 +++++++++++++++++++++
pulsar/internal/auth/basic.go | 84 ++++++++++++++++++++++++++++++++++++++
pulsar/internal/auth/basic_test.go | 70 +++++++++++++++++++++++++++++++
pulsar/internal/auth/provider.go | 3 ++
9 files changed, 214 insertions(+), 1 deletion(-)
diff --git a/Dockerfile b/Dockerfile
index e12cc80..6dd5817 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -30,6 +30,8 @@ COPY integration-tests/certs /pulsar/certs
COPY integration-tests/tokens /pulsar/tokens
COPY integration-tests/standalone.conf /pulsar/conf
COPY integration-tests/client.conf /pulsar/conf
+COPY integration-tests/.htpasswd /pulsar/conf
+ENV PULSAR_EXTRA_OPTS="-Dpulsar.auth.basic.conf=/pulsar/conf/.htpasswd"
COPY pulsar-test-service-start.sh /pulsar/bin
COPY pulsar-test-service-stop.sh /pulsar/bin
COPY run-ci.sh /pulsar/bin
diff --git a/integration-tests/.htpasswd b/integration-tests/.htpasswd
new file mode 100644
index 0000000..2aa3a47
--- /dev/null
+++ b/integration-tests/.htpasswd
@@ -0,0 +1 @@
+admin:$apr1$FG4AO6aX$KGYPuMoLUou3i6vUkPUUf.
diff --git a/integration-tests/license_test.go b/integration-tests/license_test.go
index e829560..cd6d9d5 100644
--- a/integration-tests/license_test.go
+++ b/integration-tests/license_test.go
@@ -68,6 +68,7 @@ var skip = map[string]bool{
"../pulsar/internal/pulsar_proto/PulsarApi.pb.go": true,
"../.github/workflows/bot.yaml": true,
"../integration-tests/pb/hello.pb.go": true,
+ "../integration-tests/.htpasswd": true,
}
func TestLicense(t *testing.T) {
diff --git a/integration-tests/standalone.conf b/integration-tests/standalone.conf
index a298a61..8cd2828 100644
--- a/integration-tests/standalone.conf
+++ b/integration-tests/standalone.conf
@@ -98,7 +98,7 @@ anonymousUserRole=anonymous
authenticationEnabled=true
# Autentication provider name list, which is comma separated list of class names
-authenticationProviders=org.apache.pulsar.broker.authentication.AuthenticationProviderTls,org.apache.pulsar.broker.authentication.AuthenticationProviderToken
+authenticationProviders=org.apache.pulsar.broker.authentication.AuthenticationProviderTls,org.apache.pulsar.broker.authentication.AuthenticationProviderToken,org.apache.pulsar.broker.authentication.AuthenticationProviderBasic
# Enforce authorization
authorizationEnabled=true
diff --git a/pulsar/client.go b/pulsar/client.go
index f4642c6..bc05b25 100644
--- a/pulsar/client.go
+++ b/pulsar/client.go
@@ -78,6 +78,11 @@ func NewAuthenticationOAuth2(authParams map[string]string) Authentication {
return oauth
}
+// NewAuthenticationBasic Creates Basic Authentication provider
+func NewAuthenticationBasic(username, password string) (Authentication, error) {
+ return auth.NewAuthenticationBasic(username, password)
+}
+
// ClientOptions is used to construct a Pulsar Client instance.
type ClientOptions struct {
// Configure the service URL for the Pulsar service.
diff --git a/pulsar/client_impl_test.go b/pulsar/client_impl_test.go
index 2d83c99..ba8f6eb 100644
--- a/pulsar/client_impl_test.go
+++ b/pulsar/client_impl_test.go
@@ -28,6 +28,8 @@ import (
"testing"
"time"
+ "github.com/stretchr/testify/require"
+
"github.com/apache/pulsar-client-go/pulsar/internal"
"github.com/apache/pulsar-client-go/pulsar/internal/auth"
@@ -1019,3 +1021,48 @@ func TestHTTPOAuth2AuthFailed(t *testing.T) {
client.Close()
}
+
+func TestHTTPBasicAuth(t *testing.T) {
+ basicAuth, err := NewAuthenticationBasic("admin", "123456")
+ require.NoError(t, err)
+ require.NotNil(t, basicAuth)
+
+ client, err := NewClient(ClientOptions{
+ URL: webServiceURL,
+ Authentication: basicAuth,
+ })
+ require.NoError(t, err)
+ require.NotNil(t, client)
+
+ producer, err := client.CreateProducer(ProducerOptions{
+ Topic: newAuthTopicName(),
+ })
+
+ require.NoError(t, err)
+ require.NotNil(t, producer)
+
+ client.Close()
+}
+
+func TestHTTPSBasicAuth(t *testing.T) {
+ basicAuth, err := NewAuthenticationBasic("admin", "123456")
+ require.NoError(t, err)
+ require.NotNil(t, basicAuth)
+
+ client, err := NewClient(ClientOptions{
+ URL: webServiceURLTLS,
+ TLSTrustCertsFilePath: caCertsPath,
+ TLSValidateHostname: true,
+ Authentication: basicAuth,
+ })
+ require.NoError(t, err)
+ require.NotNil(t, client)
+
+ producer, err := client.CreateProducer(ProducerOptions{
+ Topic: newAuthTopicName(),
+ })
+ require.NoError(t, err)
+ require.NotNil(t, producer)
+
+ client.Close()
+}
diff --git a/pulsar/internal/auth/basic.go b/pulsar/internal/auth/basic.go
new file mode 100644
index 0000000..58a87f5
--- /dev/null
+++ b/pulsar/internal/auth/basic.go
@@ -0,0 +1,84 @@
+// 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.
+
+package auth
+
+import (
+ "crypto/tls"
+ "encoding/base64"
+ "errors"
+ "net/http"
+)
+
+type basicAuthProvider struct {
+ rt http.RoundTripper
+ commandAuthToken []byte
+ httpAuthToken string
+}
+
+func NewAuthenticationBasic(username, password string) (Provider, error) {
+ if username == "" {
+ return nil, errors.New("username cannot be empty")
+ }
+ if password == "" {
+ return nil, errors.New("password cannot be empty")
+ }
+
+ commandAuthToken := []byte(username + ":" + password)
+ return &basicAuthProvider{
+ commandAuthToken: commandAuthToken,
+ httpAuthToken: "Basic " + base64.StdEncoding.EncodeToString(commandAuthToken),
+ }, nil
+}
+
+func NewAuthenticationBasicWithParams(params map[string]string) (Provider, error) {
+ return NewAuthenticationBasic(params["username"], params["password"])
+}
+
+func (b *basicAuthProvider) Init() error {
+ return nil
+}
+
+func (b *basicAuthProvider) Name() string {
+ return "basic"
+}
+
+func (b *basicAuthProvider) GetTLSCertificate() (*tls.Certificate, error) {
+ return nil, nil
+}
+
+func (b *basicAuthProvider) GetData() ([]byte, error) {
+ return b.commandAuthToken, nil
+}
+
+func (b *basicAuthProvider) Close() error {
+ return nil
+}
+
+func (b *basicAuthProvider) RoundTrip(req *http.Request) (*http.Response, error) {
+ req.Header.Add("Authorization", b.httpAuthToken)
+ return b.rt.RoundTrip(req)
+}
+
+func (b *basicAuthProvider) Transport() http.RoundTripper {
+ return b.rt
+}
+
+func (b *basicAuthProvider) WithTransport(tr http.RoundTripper) error {
+ b.rt = tr
+ return nil
+}
diff --git a/pulsar/internal/auth/basic_test.go b/pulsar/internal/auth/basic_test.go
new file mode 100644
index 0000000..b212d0b
--- /dev/null
+++ b/pulsar/internal/auth/basic_test.go
@@ -0,0 +1,70 @@
+// 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.
+
+package auth
+
+import (
+ "errors"
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestNewAuthenticationBasicWithParams(t *testing.T) {
+ username := "admin"
+ password := "123456"
+
+ provider, err := NewAuthenticationBasic(username, password)
+ require.NoError(t, err)
+ require.NotNil(t, provider)
+
+ data, err := provider.GetData()
+ require.NoError(t, err)
+ require.Equal(t, []byte(username+":"+password), data)
+
+ s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ _, _ = w.Write([]byte(r.Header.Get("Authorization")))
+ }))
+
+ client := s.Client()
+ err = provider.WithTransport(client.Transport)
+ require.NoError(t, err)
+ client.Transport = provider
+
+ resp, err := client.Get(s.URL)
+ require.NoError(t, err)
+
+ body, err := ioutil.ReadAll(resp.Body)
+ _ = resp.Body.Close()
+ require.NoError(t, err)
+ require.Equal(t, []byte("Basic YWRtaW46MTIzNDU2"), body)
+}
+
+func TestNewAuthenticationBasicWithInvalidParams(t *testing.T) {
+ username := "admin"
+ password := "123456"
+ provider, err := NewAuthenticationBasic("", password)
+ require.Equal(t, errors.New("username cannot be empty"), err)
+ require.Nil(t, provider)
+
+ provider, err = NewAuthenticationBasic(username, "")
+ require.Equal(t, errors.New("password cannot be empty"), err)
+ require.Nil(t, provider)
+}
diff --git a/pulsar/internal/auth/provider.go b/pulsar/internal/auth/provider.go
index 1731490..031ea8d 100644
--- a/pulsar/internal/auth/provider.go
+++ b/pulsar/internal/auth/provider.go
@@ -80,6 +80,9 @@ func NewProvider(name string, params string) (Provider, error) {
case "oauth2", "org.apache.pulsar.client.impl.auth.oauth2.AuthenticationOAuth2":
return NewAuthenticationOAuth2WithParams(m)
+ case "basic", "org.apache.pulsar.client.impl.auth.AuthenticationBasic":
+ return NewAuthenticationBasicWithParams(m)
+
default:
return nil, fmt.Errorf("invalid auth provider '%s'", name)
}