This is an automated email from the ASF dual-hosted git repository.

zhangjintao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix-ingress-controller.git


The following commit(s) were added to refs/heads/master by this push:
     new 33d42c31 feat: add ldap-auth authorization method (#1588)
33d42c31 is described below

commit 33d42c31dbc70e43b08d9d4c95ca7ecd8a572633
Author: machinly <[email protected]>
AuthorDate: Tue Jan 31 17:00:36 2023 +0800

    feat: add ldap-auth authorization method (#1588)
---
 Makefile                                           |  13 +-
 pkg/kube/apisix/apis/config/v2/types.go            |  30 ++-
 .../apisix/apis/config/v2/zz_generated.deepcopy.go |  64 ++++++
 .../apisix/translation/apisix_consumer.go          |   6 +
 .../apisix/translation/apisix_consumer_test.go     |  21 ++
 pkg/providers/apisix/translation/apisix_plugin.go  |  22 +++
 .../apisix/translation/apisix_plugin_test.go       |  55 ++++++
 pkg/providers/apisix/translation/apisix_route.go   |   4 +
 pkg/types/apisix/v1/plugin_types.go                |   7 +
 pkg/types/apisix/v1/zz_generated.deepcopy.go       |  16 ++
 samples/deploy/crd/v1/ApisixConsumer.yaml          |  21 ++
 samples/deploy/crd/v1/ApisixRoute.yaml             |  12 ++
 .../suite-plugins-authentication/ldap.go           | 218 +++++++++++++++++++++
 test/e2e/testdata/ldap/cmd.sh                      |  36 ++++
 test/e2e/testdata/ldap/docker-compose.yaml         |  33 ++++
 15 files changed, 553 insertions(+), 5 deletions(-)

diff --git a/Makefile b/Makefile
index 04e38989..ad32b818 100644
--- a/Makefile
+++ b/Makefile
@@ -135,7 +135,7 @@ unit-test:
 
 ### e2e-test:             Run e2e test cases (in existing clusters directly)
 .PHONY: e2e-test
-e2e-test: ginkgo-check pack-images e2e-wolf-rbac install install-gateway-api
+e2e-test: ginkgo-check pack-images e2e-wolf-rbac e2e-ldap install 
install-gateway-api
        cd test/e2e \
                && go mod download \
                && export REGISTRY=$(REGISTRY) \
@@ -266,6 +266,17 @@ ifneq ("$(E2E_FOCUS)", "")
        && ./test/e2e/testdata/wolf-rbac/cmd.sh start
 endif
 
+.PHONY: e2e-ldap
+e2e-ldap:
+ifeq ("$(E2E_FOCUS)", "")
+       chmod +x ./test/e2e/testdata/ldap/cmd.sh && 
./test/e2e/testdata/ldap/cmd.sh start
+endif
+ifneq ("$(E2E_FOCUS)", "")
+       echo $(E2E_FOCUS) | grep -E 
'suite-plugins-authentication|consumer|ldap' || exit 0 \
+       && chmod +x ./test/e2e/testdata/ldap/cmd.sh \
+       && ./test/e2e/testdata/ldap/cmd.sh start
+endif
+
 ### kind-load-images:    Load the images to the kind cluster
 .PHONY: kind-load-images
 kind-load-images:
diff --git a/pkg/kube/apisix/apis/config/v2/types.go 
b/pkg/kube/apisix/apis/config/v2/types.go
index 5f223f62..1f92785c 100644
--- a/pkg/kube/apisix/apis/config/v2/types.go
+++ b/pkg/kube/apisix/apis/config/v2/types.go
@@ -199,10 +199,11 @@ func (p *ApisixRoutePluginConfig) DeepCopy() 
*ApisixRoutePluginConfig {
 // ApisixRouteAuthentication is the authentication-related
 // configuration in ApisixRoute.
 type ApisixRouteAuthentication struct {
-       Enable  bool                             `json:"enable" yaml:"enable"`
-       Type    string                           `json:"type" yaml:"type"`
-       KeyAuth ApisixRouteAuthenticationKeyAuth `json:"keyAuth,omitempty" 
yaml:"keyAuth,omitempty"`
-       JwtAuth ApisixRouteAuthenticationJwtAuth `json:"jwtAuth,omitempty" 
yaml:"jwtAuth,omitempty"`
+       Enable   bool                              `json:"enable" yaml:"enable"`
+       Type     string                            `json:"type" yaml:"type"`
+       KeyAuth  ApisixRouteAuthenticationKeyAuth  `json:"keyAuth,omitempty" 
yaml:"keyAuth,omitempty"`
+       JwtAuth  ApisixRouteAuthenticationJwtAuth  `json:"jwtAuth,omitempty" 
yaml:"jwtAuth,omitempty"`
+       LDAPAuth ApisixRouteAuthenticationLDAPAuth `json:"ldapAuth,omitempty" 
yaml:"ldapAuth,omitempty"`
 }
 
 // ApisixRouteAuthenticationKeyAuth is the keyAuth-related
@@ -219,6 +220,15 @@ type ApisixRouteAuthenticationJwtAuth struct {
        Cookie string `json:"cookie,omitempty" yaml:"cookie,omitempty"`
 }
 
+// ApisixRouteAuthenticationLDAPAuth is the LDAP auth related
+// configuration in ApisixRouteAuthentication.
+type ApisixRouteAuthenticationLDAPAuth struct {
+       BaseDN  string `json:"base_dn,omitempty" yaml:"base_dn,omitempty"`
+       LDAPURI string `json:"ldap_uri,omitempty" yaml:"ldap_uri,omitempty"`
+       UseTLS  bool   `json:"use_tls,omitempty" yaml:"use_tls,omitempty"`
+       UID     string `json:"uid,omitempty" yaml:"uid,omitempty"`
+}
+
 // ApisixRouteStream is the configuration for level 4 route
 type ApisixRouteStream struct {
        // The rule name, cannot be empty.
@@ -356,6 +366,7 @@ type ApisixConsumerAuthParameter struct {
        WolfRBAC  *ApisixConsumerWolfRBAC  `json:"wolfRBAC,omitempty" 
yaml:"wolfRBAC"`
        JwtAuth   *ApisixConsumerJwtAuth   `json:"jwtAuth,omitempty" 
yaml:"jwtAuth"`
        HMACAuth  *ApisixConsumerHMACAuth  `json:"hmacAuth,omitempty" 
yaml:"hmacAuth"`
+       LDAPAuth  *ApisixConsumerLDAPAuth  `json:"ldapAuth,omitempty" 
yaml:"ldapAuth"`
 }
 
 // ApisixConsumerBasicAuth defines the configuration for basic auth.
@@ -430,6 +441,17 @@ type ApisixConsumerHMACAuthValue struct {
        MaxReqBody          int64    `json:"max_req_body,omitempty" 
yaml:"max_req_body,omitempty"`
 }
 
+// ApisixConsumerLDAPAuth defines the configuration for the ldap auth.
+type ApisixConsumerLDAPAuth struct {
+       SecretRef *corev1.LocalObjectReference `json:"secretRef" yaml:"secret"`
+       Value     *ApisixConsumerLDAPAuthValue `json:"value,omitempty" 
yaml:"value,omitempty"`
+}
+
+// ApisixConsumerLDAPAuthValue defines the in-place configuration for ldap 
auth.
+type ApisixConsumerLDAPAuthValue struct {
+       UserDN string `json:"user_dn" yaml:"user_dn"`
+}
+
 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
 // ApisixConsumerList contains a list of ApisixConsumer.
 type ApisixConsumerList struct {
diff --git a/pkg/kube/apisix/apis/config/v2/zz_generated.deepcopy.go 
b/pkg/kube/apisix/apis/config/v2/zz_generated.deepcopy.go
index f83b3648..d12816d3 100644
--- a/pkg/kube/apisix/apis/config/v2/zz_generated.deepcopy.go
+++ b/pkg/kube/apisix/apis/config/v2/zz_generated.deepcopy.go
@@ -308,6 +308,11 @@ func (in *ApisixConsumerAuthParameter) DeepCopyInto(out 
*ApisixConsumerAuthParam
                *out = new(ApisixConsumerHMACAuth)
                (*in).DeepCopyInto(*out)
        }
+       if in.LDAPAuth != nil {
+               in, out := &in.LDAPAuth, &out.LDAPAuth
+               *out = new(ApisixConsumerLDAPAuth)
+               (*in).DeepCopyInto(*out)
+       }
        return
 }
 
@@ -494,6 +499,48 @@ func (in *ApisixConsumerKeyAuthValue) DeepCopy() 
*ApisixConsumerKeyAuthValue {
        return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, 
writing into out. in must be non-nil.
+func (in *ApisixConsumerLDAPAuth) DeepCopyInto(out *ApisixConsumerLDAPAuth) {
+       *out = *in
+       if in.SecretRef != nil {
+               in, out := &in.SecretRef, &out.SecretRef
+               *out = new(v1.LocalObjectReference)
+               **out = **in
+       }
+       if in.Value != nil {
+               in, out := &in.Value, &out.Value
+               *out = new(ApisixConsumerLDAPAuthValue)
+               **out = **in
+       }
+       return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, 
creating a new ApisixConsumerLDAPAuth.
+func (in *ApisixConsumerLDAPAuth) DeepCopy() *ApisixConsumerLDAPAuth {
+       if in == nil {
+               return nil
+       }
+       out := new(ApisixConsumerLDAPAuth)
+       in.DeepCopyInto(out)
+       return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, 
writing into out. in must be non-nil.
+func (in *ApisixConsumerLDAPAuthValue) DeepCopyInto(out 
*ApisixConsumerLDAPAuthValue) {
+       *out = *in
+       return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, 
creating a new ApisixConsumerLDAPAuthValue.
+func (in *ApisixConsumerLDAPAuthValue) DeepCopy() *ApisixConsumerLDAPAuthValue 
{
+       if in == nil {
+               return nil
+       }
+       out := new(ApisixConsumerLDAPAuthValue)
+       in.DeepCopyInto(out)
+       return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, 
writing into out. in must be non-nil.
 func (in *ApisixConsumerList) DeepCopyInto(out *ApisixConsumerList) {
        *out = *in
@@ -804,6 +851,7 @@ func (in *ApisixRouteAuthentication) DeepCopyInto(out 
*ApisixRouteAuthentication
        *out = *in
        out.KeyAuth = in.KeyAuth
        out.JwtAuth = in.JwtAuth
+       out.LDAPAuth = in.LDAPAuth
        return
 }
 
@@ -849,6 +897,22 @@ func (in *ApisixRouteAuthenticationKeyAuth) DeepCopy() 
*ApisixRouteAuthenticatio
        return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, 
writing into out. in must be non-nil.
+func (in *ApisixRouteAuthenticationLDAPAuth) DeepCopyInto(out 
*ApisixRouteAuthenticationLDAPAuth) {
+       *out = *in
+       return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, 
creating a new ApisixRouteAuthenticationLDAPAuth.
+func (in *ApisixRouteAuthenticationLDAPAuth) DeepCopy() 
*ApisixRouteAuthenticationLDAPAuth {
+       if in == nil {
+               return nil
+       }
+       out := new(ApisixRouteAuthenticationLDAPAuth)
+       in.DeepCopyInto(out)
+       return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, 
writing into out. in must be non-nil.
 func (in *ApisixRouteHTTP) DeepCopyInto(out *ApisixRouteHTTP) {
        *out = *in
diff --git a/pkg/providers/apisix/translation/apisix_consumer.go 
b/pkg/providers/apisix/translation/apisix_consumer.go
index 802662b8..c27eae64 100644
--- a/pkg/providers/apisix/translation/apisix_consumer.go
+++ b/pkg/providers/apisix/translation/apisix_consumer.go
@@ -100,6 +100,12 @@ func (t *translator) TranslateApisixConsumerV2(ac 
*configv2.ApisixConsumer) (*ap
                        return nil, fmt.Errorf("invaild hmac auth config: %s", 
err)
                }
                plugins["hmac-auth"] = cfg
+       } else if ac.Spec.AuthParameter.LDAPAuth != nil {
+               cfg, err := t.translateConsumerLDAPAuthPluginV2(ac.Namespace, 
ac.Spec.AuthParameter.LDAPAuth)
+               if err != nil {
+                       return nil, fmt.Errorf("invalid ldap auth config: %s", 
err)
+               }
+               plugins["ldap-auth"] = cfg
        }
 
        consumer := apisixv1.NewDefaultConsumer()
diff --git a/pkg/providers/apisix/translation/apisix_consumer_test.go 
b/pkg/providers/apisix/translation/apisix_consumer_test.go
index 82c45450..c85e5dbd 100644
--- a/pkg/providers/apisix/translation/apisix_consumer_test.go
+++ b/pkg/providers/apisix/translation/apisix_consumer_test.go
@@ -277,6 +277,27 @@ func TestTranslateApisixConsumerV2(t *testing.T) {
        assert.Equal(t, "foo", cfg5.AccessKey)
        assert.Equal(t, "bar", cfg5.SecretKey)
 
+       ac = &configv2.ApisixConsumer{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      "jack",
+                       Namespace: "qa",
+               },
+               Spec: configv2.ApisixConsumerSpec{
+                       AuthParameter: configv2.ApisixConsumerAuthParameter{
+                               LDAPAuth: &configv2.ApisixConsumerLDAPAuth{
+                                       Value: 
&configv2.ApisixConsumerLDAPAuthValue{
+                                               UserDN: 
"cn=user01,ou=users,dc=example,dc=org",
+                                       },
+                               },
+                       },
+               },
+       }
+       consumer, err = (&translator{}).TranslateApisixConsumerV2(ac)
+       assert.Nil(t, err)
+       assert.Len(t, consumer.Plugins, 1)
+       cfg6 := consumer.Plugins["ldap-auth"].(*apisixv1.LDAPAuthConsumerConfig)
+       assert.Equal(t, "cn=user01,ou=users,dc=example,dc=org", cfg6.UserDN)
+
        // No test test cases for secret references as we already test them
        // in plugin_test.go.
 }
diff --git a/pkg/providers/apisix/translation/apisix_plugin.go 
b/pkg/providers/apisix/translation/apisix_plugin.go
index 9a93281f..96142b91 100644
--- a/pkg/providers/apisix/translation/apisix_plugin.go
+++ b/pkg/providers/apisix/translation/apisix_plugin.go
@@ -520,3 +520,25 @@ func (t *translator) 
translateConsumerHMACAuthPluginV2(consumerNamespace string,
                MaxReqBody:          maxReqBody,
        }, nil
 }
+
+func (t *translator) translateConsumerLDAPAuthPluginV2(consumerNamespace 
string, cfg *configv2.ApisixConsumerLDAPAuth) 
(*apisixv1.LDAPAuthConsumerConfig, error) {
+       if cfg.Value != nil {
+               return &apisixv1.LDAPAuthConsumerConfig{
+                       UserDN: cfg.Value.UserDN,
+               }, nil
+       }
+
+       sec, err := 
t.SecretLister.Secrets(consumerNamespace).Get(cfg.SecretRef.Name)
+       if err != nil {
+               return nil, err
+       }
+
+       userDNRaw, ok := sec.Data["user_dn"]
+       if !ok || len(userDNRaw) == 0 {
+               return nil, _errKeyNotFoundOrInvalid
+       }
+
+       return &apisixv1.LDAPAuthConsumerConfig{
+               UserDN: string(userDNRaw),
+       }, nil
+}
diff --git a/pkg/providers/apisix/translation/apisix_plugin_test.go 
b/pkg/providers/apisix/translation/apisix_plugin_test.go
index 7c334873..e39b9bdd 100644
--- a/pkg/providers/apisix/translation/apisix_plugin_test.go
+++ b/pkg/providers/apisix/translation/apisix_plugin_test.go
@@ -1039,3 +1039,58 @@ func TestTranslateConsumerHMACAuthPluginWithSecretRef(t 
*testing.T) {
        close(processCh)
        close(stopCh)
 }
+
+func TestTranslateConsumerLDAPAuthPluginWithInPlaceValue(t *testing.T) {
+       ldapAuth := &configv2.ApisixConsumerLDAPAuth{
+               Value: &configv2.ApisixConsumerLDAPAuthValue{
+                       UserDN: "cn=user01,ou=users,dc=example,dc=org",
+               },
+       }
+       cfg, err := 
(&translator{}).translateConsumerLDAPAuthPluginV2("default", ldapAuth)
+       assert.Nil(t, err)
+       assert.Equal(t, "cn=user01,ou=users,dc=example,dc=org", cfg.UserDN)
+}
+
+func TestTranslateConsumerLDAPAuthPluginWithSecretRef(t *testing.T) {
+       sec := &corev1.Secret{
+               ObjectMeta: metav1.ObjectMeta{
+                       Name: "fatpa-ldap-auth",
+               },
+               Data: map[string][]byte{
+                       "user_dn": 
[]byte("cn=user01,ou=users,dc=example,dc=org"),
+               },
+       }
+
+       client := fake.NewSimpleClientset()
+       informersFactory := informers.NewSharedInformerFactory(client, 0)
+       secretInformer := informersFactory.Core().V1().Secrets().Informer()
+       secretLister := informersFactory.Core().V1().Secrets().Lister()
+       processCh := make(chan struct{})
+       stopCh := make(chan struct{})
+       secretInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
+               AddFunc: func(_ interface{}) {
+                       processCh <- struct{}{}
+               },
+               UpdateFunc: func(_, _ interface{}) {
+                       processCh <- struct{}{}
+               },
+       })
+
+       go secretInformer.Run(stopCh)
+
+       tr := &translator{&TranslatorOptions{
+               SecretLister: secretLister,
+       }, translation.NewTranslator(nil)}
+
+       _, err := 
client.CoreV1().Secrets("default").Create(context.Background(), sec, 
metav1.CreateOptions{})
+       assert.Nil(t, err)
+
+       <-processCh
+
+       ldapAuth := &configv2.ApisixConsumerLDAPAuth{
+               SecretRef: &corev1.LocalObjectReference{Name: 
"fatpa-ldap-auth"},
+       }
+       cfg, err := tr.translateConsumerLDAPAuthPluginV2("default", ldapAuth)
+       assert.Nil(t, err)
+       assert.Equal(t, "cn=user01,ou=users,dc=example,dc=org", cfg.UserDN)
+}
diff --git a/pkg/providers/apisix/translation/apisix_route.go 
b/pkg/providers/apisix/translation/apisix_route.go
index 6d1c3f26..ebcac1ba 100644
--- a/pkg/providers/apisix/translation/apisix_route.go
+++ b/pkg/providers/apisix/translation/apisix_route.go
@@ -285,6 +285,8 @@ func (t *translator) translateHTTPRouteV2(ctx 
*translation.TranslateContext, ar
                                pluginMap["jwt-auth"] = 
part.Authentication.JwtAuth
                        case "hmacAuth":
                                pluginMap["hmac-auth"] = 
make(map[string]interface{})
+                       case "ldapAuth":
+                               pluginMap["ldap-auth"] = 
part.Authentication.LDAPAuth
                        default:
                                pluginMap["basic-auth"] = 
make(map[string]interface{})
                        }
@@ -670,6 +672,8 @@ func (t *translator) generateHTTPRouteV2DeleteMark(ctx 
*translation.TranslateCon
                                pluginMap["jwt-auth"] = 
part.Authentication.JwtAuth
                        case "hmacAuth":
                                pluginMap["hmac-auth"] = 
make(map[string]interface{})
+                       case "ldapAuth":
+                               pluginMap["ldap-auth"] = 
part.Authentication.LDAPAuth
                        default:
                                pluginMap["basic-auth"] = 
make(map[string]interface{})
                        }
diff --git a/pkg/types/apisix/v1/plugin_types.go 
b/pkg/types/apisix/v1/plugin_types.go
index 73eda73e..dd6bf9b9 100644
--- a/pkg/types/apisix/v1/plugin_types.go
+++ b/pkg/types/apisix/v1/plugin_types.go
@@ -110,6 +110,13 @@ type HMACAuthConsumerConfig struct {
        MaxReqBody          int64    `json:"max_req_body,omitempty" 
yaml:"max_req_body,omitempty"`
 }
 
+// LDAPAuthConsumerConfig is the rule config for ldap-auth plugin
+// used in Consumer object.
+// +k8s:deepcopy-gen=true
+type LDAPAuthConsumerConfig struct {
+       UserDN string `json:"user_dn"`
+}
+
 // BasicAuthRouteConfig is the rule config for basic-auth plugin
 // used in Route object.
 // +k8s:deepcopy-gen=true
diff --git a/pkg/types/apisix/v1/zz_generated.deepcopy.go 
b/pkg/types/apisix/v1/zz_generated.deepcopy.go
index ea41a3ca..df2e8216 100644
--- a/pkg/types/apisix/v1/zz_generated.deepcopy.go
+++ b/pkg/types/apisix/v1/zz_generated.deepcopy.go
@@ -271,6 +271,22 @@ func (in *KeyAuthConsumerConfig) DeepCopy() 
*KeyAuthConsumerConfig {
        return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, 
writing into out. in must be non-nil.
+func (in *LDAPAuthConsumerConfig) DeepCopyInto(out *LDAPAuthConsumerConfig) {
+       *out = *in
+       return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, 
creating a new LDAPAuthConsumerConfig.
+func (in *LDAPAuthConsumerConfig) DeepCopy() *LDAPAuthConsumerConfig {
+       if in == nil {
+               return nil
+       }
+       out := new(LDAPAuthConsumerConfig)
+       in.DeepCopyInto(out)
+       return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, 
writing into out. in must be non-nil.
 func (in *Metadata) DeepCopyInto(out *Metadata) {
        *out = *in
diff --git a/samples/deploy/crd/v1/ApisixConsumer.yaml 
b/samples/deploy/crd/v1/ApisixConsumer.yaml
index 90346304..e69f030f 100644
--- a/samples/deploy/crd/v1/ApisixConsumer.yaml
+++ b/samples/deploy/crd/v1/ApisixConsumer.yaml
@@ -220,6 +220,7 @@ spec:
                     - required: ["wolfRBAC"]
                     - required: ["jwtAuth"]
                     - required: ["hmacAuth"]
+                    - required: ["ldapAuth"]
                   properties:
                     basicAuth:
                       type: object
@@ -366,3 +367,23 @@ spec:
                               minLength: 1
                           required:
                             - name
+                    ldapAuth:
+                      type: object
+                      oneOf:
+                        - required: ["value"]
+                        - required: ["secretRef"]
+                      properties:
+                        value:
+                          type: object
+                          properties:
+                            user_dn:
+                              type: string
+                          required:
+                            - user_dn
+                        secretRef:
+                          type: object
+                          properties:
+                            name:
+                              type: string
+                              minLength: 1
+                          required:
diff --git a/samples/deploy/crd/v1/ApisixRoute.yaml 
b/samples/deploy/crd/v1/ApisixRoute.yaml
index 4d1f9870..55dd1e5f 100644
--- a/samples/deploy/crd/v1/ApisixRoute.yaml
+++ b/samples/deploy/crd/v1/ApisixRoute.yaml
@@ -524,6 +524,7 @@ spec:
                               - "jwtAuth"
                               - "wolfRBAC"
                               - "hmacAuth"
+                              - "ldapAuth"
                           keyAuth:
                             type: object
                             properties:
@@ -538,6 +539,17 @@ spec:
                                 type: string
                               cookie:
                                 type: string
+                          ldapAuth:
+                            type: object
+                            properties:
+                              base_dn:
+                                type: string
+                              ldap_uri:
+                                type: string
+                              use_tls:
+                                type: boolean
+                              uid:
+                                type: string
                         required:
                           - enable
                 stream:
diff --git a/test/e2e/suite-plugins/suite-plugins-authentication/ldap.go 
b/test/e2e/suite-plugins/suite-plugins-authentication/ldap.go
new file mode 100644
index 00000000..107e42e9
--- /dev/null
+++ b/test/e2e/suite-plugins/suite-plugins-authentication/ldap.go
@@ -0,0 +1,218 @@
+// 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 plugins
+
+import (
+       "fmt"
+       "net/http"
+       "os/exec"
+       "time"
+
+       "github.com/onsi/ginkgo/v2"
+       "github.com/stretchr/testify/assert"
+
+       "github.com/apache/apisix-ingress-controller/test/e2e/scaffold"
+)
+
+var _ = ginkgo.Describe("suite-plugins-authentication: ApisixConsumer with 
ldap", func() {
+       suites := func(scaffoldFunc func() *scaffold.Scaffold) {
+               s := scaffoldFunc()
+               getLDAPServerURL := func() (string, error) {
+                       cmd := exec.Command("sh", "testdata/ldap/cmd.sh", "ip")
+                       ip, err := cmd.Output()
+                       if err != nil {
+                               return "", err
+                       }
+                       if len(ip) == 0 {
+                               return "", fmt.Errorf("ldap-server start 
failed")
+                       }
+                       return fmt.Sprintf("%s:1389", string(ip)), nil
+               }
+
+               ginkgo.It("ApisixRoute with ldapAuth consumer", func() {
+                       ac := `
+apiVersion: apisix.apache.org/v2
+kind: ApisixConsumer
+metadata:
+  name: jack
+spec:
+  authParameter:
+    ldapAuth:
+      value:
+        user_dn: "cn=jack,ou=users,dc=ldap,dc=example,dc=org"
+`
+                       assert.Nil(ginkgo.GinkgoT(), 
s.CreateVersionedApisixResource(ac), "creating ldapAuth ApisixConsumer")
+
+                       // Wait until the ApisixConsumer create event was 
delivered.
+                       time.Sleep(6 * time.Second)
+
+                       grs, err := s.ListApisixConsumers()
+                       assert.Nil(ginkgo.GinkgoT(), err, "listing consumer")
+                       assert.Len(ginkgo.GinkgoT(), grs, 1)
+                       assert.Len(ginkgo.GinkgoT(), grs[0].Plugins, 1)
+                       ldapAuth, _ := 
grs[0].Plugins["ldap-auth"].(map[string]interface{})
+                       assert.Equal(ginkgo.GinkgoT(), ldapAuth["user_dn"], 
"cn=jack,ou=users,dc=ldap,dc=example,dc=org")
+
+                       ldapSvr, err := getLDAPServerURL()
+                       assert.Nil(ginkgo.GinkgoT(), err, "check ldap server")
+                       backendSvc, backendPorts := s.DefaultHTTPBackend()
+                       ar := fmt.Sprintf(`
+apiVersion: apisix.apache.org/v2
+kind: ApisixRoute
+metadata:
+ name: httpbin-route
+spec:
+  http:
+  - name: rule1
+    match:
+      hosts:
+      - httpbin.org
+      paths:
+        - /ip
+    backends:
+    - serviceName: %s
+      servicePort: %d
+    authentication:
+      enable: true
+      type: ldapAuth
+      ldapAuth: 
+        ldap_uri: %s
+        base_dn: "ou=users,dc=ldap,dc=example,dc=org"
+        use_tls: false
+        uid: "cn"
+`, backendSvc, backendPorts[0], ldapSvr)
+                       assert.Nil(ginkgo.GinkgoT(), 
s.CreateResourceFromString(ar), "Creating ApisixRoute with ldapAuth")
+                       assert.Nil(ginkgo.GinkgoT(), 
s.EnsureNumApisixRoutesCreated(1), "Checking number of routes")
+                       assert.Nil(ginkgo.GinkgoT(), 
s.EnsureNumApisixUpstreamsCreated(1), "Checking number of upstreams")
+
+                       msg401CourseMissing := s.NewAPISIXClient().GET("/ip").
+                               WithHeader("Host", "httpbin.org").
+                               Expect().
+                               Status(http.StatusUnauthorized).
+                               Body().
+                               Raw()
+                       assert.Contains(ginkgo.GinkgoT(), msg401CourseMissing, 
"Missing authorization in request")
+
+                       msg401CouseInvalid := s.NewAPISIXClient().GET("/ip").
+                               WithHeader("Host", "httpbin.org").
+                               WithBasicAuth("jack", "invalid").
+                               Expect().
+                               Status(http.StatusUnauthorized).
+                               Body().
+                               Raw()
+                       assert.Contains(ginkgo.GinkgoT(), msg401CouseInvalid, 
"Invalid user authorization")
+
+                       _ = s.NewAPISIXClient().GET("/ip").
+                               WithHeader("Host", "httpbin.org").
+                               WithBasicAuth("jack", "jackPassword").
+                               Expect().
+                               Status(http.StatusOK)
+               })
+
+               ginkgo.It("ApisixRoute with ldapAuth consumer using secret", 
func() {
+                       secret := `
+apiVersion: v1
+kind: Secret
+metadata:
+  name: ldap
+data:
+  user_dn: Y249amFjayxvdT11c2VycyxkYz1sZGFwLGRjPWV4YW1wbGUsZGM9b3Jn
+`
+                       assert.Nil(ginkgo.GinkgoT(), 
s.CreateResourceFromString(secret), "creating ldapAuth secret for 
ApisixConsumer")
+
+                       ac := `
+apiVersion: apisix.apache.org/v2
+kind: ApisixConsumer
+metadata:
+  name: jack
+spec:
+  authParameter:
+    ldapAuth:
+      secretRef:
+        name: ldap
+`
+                       assert.Nil(ginkgo.GinkgoT(), 
s.CreateVersionedApisixResource(ac), "creating ldapAuth ApisixConsumer")
+
+                       // Wait until the ApisixConsumer create event was 
delivered.
+                       time.Sleep(6 * time.Second)
+
+                       grs, err := s.ListApisixConsumers()
+                       assert.Nil(ginkgo.GinkgoT(), err, "listing consumer")
+                       assert.Len(ginkgo.GinkgoT(), grs, 1)
+                       assert.Len(ginkgo.GinkgoT(), grs[0].Plugins, 1)
+                       ldapAuth, _ := 
grs[0].Plugins["ldap-auth"].(map[string]interface{})
+                       assert.Equal(ginkgo.GinkgoT(), ldapAuth["user_dn"], 
"cn=jack,ou=users,dc=ldap,dc=example,dc=org")
+
+                       ldapSvr, err := getLDAPServerURL()
+                       assert.Nil(ginkgo.GinkgoT(), err, "check ldap server")
+                       backendSvc, backendPorts := s.DefaultHTTPBackend()
+                       ar := fmt.Sprintf(`
+apiVersion: apisix.apache.org/v2
+kind: ApisixRoute
+metadata:
+ name: httpbin-route
+spec:
+  http:
+  - name: rule1
+    match:
+      hosts:
+      - httpbin.org
+      paths:
+        - /ip
+    backends:
+    - serviceName: %s
+      servicePort: %d
+    authentication:
+      enable: true
+      type: ldapAuth
+      ldapAuth: 
+        ldap_uri: %s
+        base_dn: "ou=users,dc=ldap,dc=example,dc=org"
+        use_tls: false
+        uid: "cn"
+`, backendSvc, backendPorts[0], ldapSvr)
+                       assert.Nil(ginkgo.GinkgoT(), 
s.CreateVersionedApisixResource(ar), "Creating ApisixRoute with ldapAuth")
+                       assert.Nil(ginkgo.GinkgoT(), 
s.EnsureNumApisixRoutesCreated(1), "Checking number of routes")
+                       assert.Nil(ginkgo.GinkgoT(), 
s.EnsureNumApisixUpstreamsCreated(1), "Checking number of upstreams")
+
+                       msg401CouseMissing := s.NewAPISIXClient().GET("/ip").
+                               WithHeader("Host", "httpbin.org").
+                               Expect().
+                               Status(http.StatusUnauthorized).
+                               Body().
+                               Raw()
+                       assert.Contains(ginkgo.GinkgoT(), msg401CouseMissing, 
"Missing authorization in request")
+
+                       msg401CourseInvalid := s.NewAPISIXClient().GET("/ip").
+                               WithHeader("Host", "httpbin.org").
+                               WithBasicAuth("jack", "invalid").
+                               Expect().
+                               Status(http.StatusUnauthorized).
+                               Body().
+                               Raw()
+                       assert.Contains(ginkgo.GinkgoT(), msg401CourseInvalid, 
"Invalid user authorization")
+
+                       _ = s.NewAPISIXClient().GET("/ip").
+                               WithHeader("Host", "httpbin.org").
+                               WithBasicAuth("jack", "jackPassword").
+                               Expect().
+                               Status(http.StatusOK)
+               })
+       }
+
+       ginkgo.Describe("suite-plugins-authentication: scaffold v2", func() {
+               suites(scaffold.NewDefaultV2Scaffold)
+       })
+})
diff --git a/test/e2e/testdata/ldap/cmd.sh b/test/e2e/testdata/ldap/cmd.sh
new file mode 100755
index 00000000..7f2f5927
--- /dev/null
+++ b/test/e2e/testdata/ldap/cmd.sh
@@ -0,0 +1,36 @@
+#!/bin/sh
+
+#
+# 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.
+#
+
+cd test/e2e/testdata/ldap/
+
+OPTION=$1
+
+if  [ $OPTION = "ip" ]; then
+    echo -n `docker inspect -f '{{range 
.NetworkSettings.Networks}}{{.Gateway}}{{end}}' openldap`
+elif [ $OPTION = "start" ]; then
+    docker-compose -f 'docker-compose.yaml'  -p 'openldap' down
+
+    # start openldap
+    docker-compose -f 'docker-compose.yaml'  -p 'openldap' up -d
+
+elif [ $OPTION = "stop" ]; then
+    docker-compose -f  'docker-compose.yaml'  -p 'openldap' down
+else
+    echo "argument is one of [ip, start, stop]"
+fi
diff --git a/test/e2e/testdata/ldap/docker-compose.yaml 
b/test/e2e/testdata/ldap/docker-compose.yaml
new file mode 100644
index 00000000..364aef39
--- /dev/null
+++ b/test/e2e/testdata/ldap/docker-compose.yaml
@@ -0,0 +1,33 @@
+#
+# 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'
+
+services:
+  openldap:
+    container_name: openldap
+    image: docker.io/bitnami/openldap:2.6
+    ports:
+      - '1389:1389'
+    environment:
+      - LDAP_PORT_NUMBER=1389
+      - LDAP_ENABLE_TLS=no
+      - LDAP_ADMIN_USERNAME=admin
+      - LDAP_ADMIN_PASSWORD=admin
+      - LDAP_ROOT=dc=ldap,dc=example,dc=org
+      - LDAP_USERS=jack
+      - LDAP_PASSWORDS=jackPassword

Reply via email to