diff --git a/.github/workflows/apisix-e2e-test.yml b/.github/workflows/apisix-e2e-test.yml index f5a59178..280122cc 100644 --- a/.github/workflows/apisix-e2e-test.yml +++ b/.github/workflows/apisix-e2e-test.yml @@ -108,6 +108,9 @@ jobs: node $(pwd)/adc.js -v echo "ADC_BIN=node $(pwd)/adc.js" >> $GITHUB_ENV + - name: Start OpenLDAP server + run: make e2e-ldap + - name: Run E2E test suite shell: bash env: diff --git a/.github/workflows/e2e-test-k8s.yml b/.github/workflows/e2e-test-k8s.yml index 6393fc26..b4151ddf 100644 --- a/.github/workflows/e2e-test-k8s.yml +++ b/.github/workflows/e2e-test-k8s.yml @@ -107,6 +107,10 @@ jobs: node $(pwd)/adc.js -v echo "ADC_BIN=node $(pwd)/adc.js" >> $GITHUB_ENV + + - name: Start OpenLDAP server + run: make e2e-ldap + - name: Run E2E test suite shell: bash env: diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml index 3b6615d7..c264c5be 100644 --- a/.github/workflows/e2e-test.yml +++ b/.github/workflows/e2e-test.yml @@ -107,6 +107,9 @@ jobs: node $(pwd)/adc.js -v echo "ADC_BIN=node $(pwd)/adc.js" >> $GITHUB_ENV + - name: Start OpenLDAP server + run: make e2e-ldap + - name: Run E2E test suite shell: bash env: diff --git a/Makefile b/Makefile index d462fe27..c33247de 100644 --- a/Makefile +++ b/Makefile @@ -310,6 +310,18 @@ ifndef ignore-not-found ignore-not-found = false 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 + + .PHONY: install-gateway-api install-gateway-api: ## Install Gateway API CRDs into the K8s cluster specified in ~/.kube/config. kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/$(GATEAY_API_VERSION)/experimental-install.yaml diff --git a/test/e2e/crds/v2/consumer.go b/test/e2e/crds/v2/consumer.go index b7b59ef9..b4cf4566 100644 --- a/test/e2e/crds/v2/consumer.go +++ b/test/e2e/crds/v2/consumer.go @@ -23,10 +23,14 @@ import ( "encoding/base64" "fmt" "net/http" + "os" + "os/exec" + "path/filepath" "time" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + "github.com/stretchr/testify/assert" "k8s.io/apimachinery/pkg/types" apiv2 "github.com/apache/apisix-ingress-controller/api/v2" @@ -585,4 +589,88 @@ spec: Expect(err).ShouldNot(HaveOccurred(), "deleting Secret") }) }) + Context("Test LDAPAuth", func() { + getLDAPServerURL := func() (string, error) { + wd, _ := os.Getwd() + path := filepath.Join(wd, "testdata", "ldap", "cmd.sh") + cmd := exec.Command("sh", path, "ip") + ip, err := cmd.Output() + errr := fmt.Sprintf("%s failed", path) + if err != nil { + return "", fmt.Errorf(errr+" : %v", err) + } + if len(ip) == 0 { + return "", fmt.Errorf("ldap-server start failed") + } + return fmt.Sprintf("%s:1389", string(ip)), nil + } + request := func(path string, username, password string) int { + return s.NewAPISIXClient().GET(path).WithBasicAuth(username, password).WithHost("httpbin").Expect().Raw().StatusCode + } + It("ApisixRoute with ldapAuth consumer using secret", func() { + secret := ` +apiVersion: v1 +kind: Secret +metadata: + name: ldap +data: + user_dn: Y249amFjayxvdT11c2VycyxkYz1sZGFwLGRjPWV4YW1wbGUsZGM9b3Jn +` + assert.Nil(GinkgoT(), s.CreateResourceFromString(secret), "creating ldapAuth secret for ApisixConsumer") + + ac := ` +apiVersion: apisix.apache.org/v2 +kind: ApisixConsumer +metadata: + name: jack +spec: + ingressClassName: %s + authParameter: + ldapAuth: + secretRef: + name: ldap +` + + By("apply ApisixConsumer") + applier.MustApplyAPIv2(types.NamespacedName{Namespace: s.Namespace(), Name: "jack"}, + &apiv2.ApisixConsumer{}, fmt.Sprintf(ac, s.Namespace())) + + ldapSvr, err := getLDAPServerURL() + assert.Nil(GinkgoT(), err, "check ldap server") + ar := fmt.Sprintf(` +apiVersion: apisix.apache.org/v2 +kind: ApisixRoute +metadata: + name: httpbin-route +spec: + ingressClassName: %s + http: + - name: rule1 + match: + hosts: + - httpbin + paths: + - /get + backends: + - serviceName: httpbin-service-e2e-test + servicePort: 80 + authentication: + enable: true + type: ldapAuth + ldapAuth: + ldap_uri: %s + base_dn: "ou=users,dc=ldap,dc=example,dc=org" + use_tls: false + uid: "cn" +`, s.Namespace(), ldapSvr) + applier.MustApplyAPIv2(types.NamespacedName{Namespace: s.Namespace(), Name: "httpbin-route"}, + &apiv2.ApisixRoute{}, ar) + + By("verify ApisixRoute with ApisixConsumer") + Eventually(request).WithArguments("/get", "", "").WithTimeout(5 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusUnauthorized)) + + By("verify ApisixRoute with ApisixConsumer") + Eventually(request).WithArguments("/get", "jack", "jackPassword").WithTimeout(5 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusOK)) + }) + }) }) diff --git a/test/e2e/testdata/ldap/cmd.sh b/test/e2e/testdata/ldap/cmd.sh new file mode 100755 index 00000000..59a197e4 --- /dev/null +++ b/test/e2e/testdata/ldap/cmd.sh @@ -0,0 +1,46 @@ +!/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 +COMPOSE_CMD="" + +if command -v "docker-compose" > /dev/null 2>&1; then + COMPOSE_CMD="docker-compose" +elif command -v "docker" > /dev/null 2>&1; then + COMPOSE_CMD="docker compose" +else + echo "docker-compose or docker compose not found" + exit 1 +fi + +if [ $OPTION = "ip" ]; then + echo -n `docker inspect -f '{{range .NetworkSettings.Networks}}{{.Gateway}}{{end}}' openldap` +elif [ $OPTION = "start" ]; then + $COMPOSE_CMD -f 'docker-compose.yaml' -p 'openldap' down + + # start openldap + $COMPOSE_CMD -f 'docker-compose.yaml' -p 'openldap' up -d + +elif [ $OPTION = "stop" ]; then + $COMPOSE_CMD -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..d36e5e7e --- /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/bitnamilegacy/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