ldap_client_test.go: refactor to use the LDAP server on the K8s cluster
This commit is contained in:
parent
8d75825635
commit
939b6b12cc
@ -76,20 +76,31 @@ spec:
|
|||||||
/tmp/csr.json \
|
/tmp/csr.json \
|
||||||
| cfssljson -bare dex
|
| cfssljson -bare dex
|
||||||
|
|
||||||
|
# Cheat and add 127.0.0.1 as an IP SAN so we can use the ldaps port through port forwarding.
|
||||||
echo "generating LDAP server certificate..."
|
echo "generating LDAP server certificate..."
|
||||||
cfssl gencert \
|
cfssl gencert \
|
||||||
-ca ca.pem -ca-key ca-key.pem \
|
-ca ca.pem -ca-key ca-key.pem \
|
||||||
-config /tmp/cfssl-default.json \
|
-config /tmp/cfssl-default.json \
|
||||||
-profile www \
|
-profile www \
|
||||||
-cn "ldap.tools.svc.cluster.local" \
|
-cn "ldap.tools.svc.cluster.local" \
|
||||||
-hostname "ldap.tools.svc.cluster.local" \
|
-hostname "ldap.tools.svc.cluster.local,127.0.0.1" \
|
||||||
/tmp/csr.json \
|
/tmp/csr.json \
|
||||||
| cfssljson -bare ldap
|
| cfssljson -bare ldap
|
||||||
|
|
||||||
chmod -R 777 /var/certs
|
chmod -R 777 /var/certs
|
||||||
|
|
||||||
|
echo
|
||||||
echo "generated certificates:"
|
echo "generated certificates:"
|
||||||
ls -l /var/certs
|
ls -l /var/certs
|
||||||
|
echo
|
||||||
|
echo "CA cert..."
|
||||||
|
cat ca.pem | openssl x509 -text
|
||||||
|
echo
|
||||||
|
echo "Dex cert..."
|
||||||
|
cat dex.pem | openssl x509 -text
|
||||||
|
echo
|
||||||
|
echo "LDAP cert..."
|
||||||
|
cat ldap.pem | openssl x509 -text
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
- name: certs
|
- name: certs
|
||||||
mountPath: /var/certs
|
mountPath: /var/certs
|
||||||
@ -100,8 +111,8 @@ spec:
|
|||||||
args:
|
args:
|
||||||
- -c
|
- -c
|
||||||
- |
|
- |
|
||||||
kubectl get secrets -n tools certs -o jsonpath='created: {.metadata.creationTimestamp}' || \
|
kubectl create secret generic -n tools certs --from-file=/var/certs \
|
||||||
kubectl create secret generic -n tools certs --from-file=/var/certs
|
--dry-run=client --output yaml | kubectl apply -f -
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
- name: certs
|
- name: certs
|
||||||
mountPath: /var/certs
|
mountPath: /var/certs
|
||||||
|
@ -64,6 +64,7 @@ stringData:
|
|||||||
sn: Walrus
|
sn: Walrus
|
||||||
givenName: Wally
|
givenName: Wally
|
||||||
mail: wally.ldap@example.com
|
mail: wally.ldap@example.com
|
||||||
|
mail: wally.alternate@example.com
|
||||||
uid: wally
|
uid: wally
|
||||||
uidNumber: 1001
|
uidNumber: 1001
|
||||||
gidNumber: 1001
|
gidNumber: 1001
|
||||||
|
@ -7,11 +7,9 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path"
|
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@ -20,29 +18,36 @@ import (
|
|||||||
"k8s.io/apiserver/pkg/authentication/authenticator"
|
"k8s.io/apiserver/pkg/authentication/authenticator"
|
||||||
"k8s.io/apiserver/pkg/authentication/user"
|
"k8s.io/apiserver/pkg/authentication/user"
|
||||||
|
|
||||||
"go.pinniped.dev/internal/certauthority"
|
|
||||||
"go.pinniped.dev/internal/upstreamldap"
|
"go.pinniped.dev/internal/upstreamldap"
|
||||||
"go.pinniped.dev/test/library"
|
"go.pinniped.dev/test/library"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestLDAPSearch(t *testing.T) {
|
func TestLDAPSearch(t *testing.T) {
|
||||||
// Unlike most other integration tests, you can run this test with no special setup, as long
|
env := library.IntegrationEnv(t)
|
||||||
// as you have Docker. It does not depend on Kubernetes.
|
|
||||||
library.SkipUnlessIntegration(t)
|
// Note that these tests depend on the values hard-coded in the LDIF file in test/deploy/tools/ldap.yaml.
|
||||||
|
// It requires the test LDAP server from the tools deployment.
|
||||||
|
if len(env.ToolsNamespace) == 0 {
|
||||||
|
t.Skip("Skipping test because it requires the test LDAP server in the tools namespace.")
|
||||||
|
}
|
||||||
|
|
||||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
cancelFunc() // this will send SIGKILL to the docker process, just in case
|
cancelFunc() // this will send SIGKILL to the subprocess, just in case
|
||||||
})
|
})
|
||||||
|
|
||||||
port := localhostPort(t)
|
hostPorts := findRecentlyUnusedLocalhostPorts(t, 2)
|
||||||
caBundle := dockerRunLDAPServer(ctx, t, port)
|
ldapHostPort := hostPorts[0]
|
||||||
|
unusedHostPort := hostPorts[1]
|
||||||
|
|
||||||
|
// Expose the the test LDAP server's TLS port on the localhost.
|
||||||
|
startKubectlPortForward(ctx, t, ldapHostPort, "ldaps", "ldap", env.ToolsNamespace)
|
||||||
|
|
||||||
provider := func(editFunc func(p *upstreamldap.Provider)) *upstreamldap.Provider {
|
provider := func(editFunc func(p *upstreamldap.Provider)) *upstreamldap.Provider {
|
||||||
provider := &upstreamldap.Provider{
|
provider := &upstreamldap.Provider{
|
||||||
Name: "test-ldap-provider",
|
Name: "test-ldap-provider",
|
||||||
Host: "127.0.0.1:" + port,
|
Host: "127.0.0.1:" + ldapHostPort,
|
||||||
CABundle: caBundle,
|
CABundle: []byte(env.SupervisorUpstreamLDAP.CABundle),
|
||||||
BindUsername: "cn=admin,dc=pinniped,dc=dev",
|
BindUsername: "cn=admin,dc=pinniped,dc=dev",
|
||||||
BindPassword: "password",
|
BindPassword: "password",
|
||||||
UserSearch: &upstreamldap.UserSearch{
|
UserSearch: &upstreamldap.UserSearch{
|
||||||
@ -58,8 +63,7 @@ func TestLDAPSearch(t *testing.T) {
|
|||||||
return provider
|
return provider
|
||||||
}
|
}
|
||||||
|
|
||||||
pinnyPassword := "password123" // from the LDIF file below
|
pinnyPassword := env.SupervisorUpstreamLDAP.TestUserPassword
|
||||||
wallyPassword := "password456" // from the LDIF file below
|
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
@ -79,15 +83,6 @@ func TestLDAPSearch(t *testing.T) {
|
|||||||
User: &user.DefaultInfo{Name: "pinny", UID: "1000", Groups: []string{}},
|
User: &user.DefaultInfo{Name: "pinny", UID: "1000", Groups: []string{}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "happy path as a different user",
|
|
||||||
username: "wally",
|
|
||||||
password: wallyPassword,
|
|
||||||
provider: provider(nil),
|
|
||||||
wantAuthResponse: &authenticator.Response{
|
|
||||||
User: &user.DefaultInfo{Name: "wally", UID: "1001", Groups: []string{}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "using a different user search base",
|
name: "using a different user search base",
|
||||||
username: "pinny",
|
username: "pinny",
|
||||||
@ -239,8 +234,8 @@ func TestLDAPSearch(t *testing.T) {
|
|||||||
name: "when the server is unreachable",
|
name: "when the server is unreachable",
|
||||||
username: "pinny",
|
username: "pinny",
|
||||||
password: pinnyPassword,
|
password: pinnyPassword,
|
||||||
provider: provider(func(p *upstreamldap.Provider) { p.Host = "127.0.0.1:27534" }), // hopefully this port is not in use on the host running tests
|
provider: provider(func(p *upstreamldap.Provider) { p.Host = "127.0.0.1:" + unusedHostPort }),
|
||||||
wantError: `error dialing host "127.0.0.1:27534": LDAP Result Code 200 "Network Error": dial tcp 127.0.0.1:27534: connect: connection refused`,
|
wantError: fmt.Sprintf(`error dialing host "127.0.0.1:%s": LDAP Result Code 200 "Network Error": dial tcp 127.0.0.1:%s: connect: connection refused`, unusedHostPort, unusedHostPort),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "when the server is not parsable",
|
name: "when the server is not parsable",
|
||||||
@ -254,33 +249,33 @@ func TestLDAPSearch(t *testing.T) {
|
|||||||
username: "pinny",
|
username: "pinny",
|
||||||
password: pinnyPassword,
|
password: pinnyPassword,
|
||||||
provider: provider(func(p *upstreamldap.Provider) { p.CABundle = []byte("invalid-pem") }),
|
provider: provider(func(p *upstreamldap.Provider) { p.CABundle = []byte("invalid-pem") }),
|
||||||
wantError: fmt.Sprintf(`error dialing host "127.0.0.1:%s": LDAP Result Code 200 "Network Error": could not parse CA bundle`, port),
|
wantError: fmt.Sprintf(`error dialing host "127.0.0.1:%s": LDAP Result Code 200 "Network Error": could not parse CA bundle`, ldapHostPort),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "when the CA bundle does not cause the host to be trusted",
|
name: "when the CA bundle does not cause the host to be trusted",
|
||||||
username: "pinny",
|
username: "pinny",
|
||||||
password: pinnyPassword,
|
password: pinnyPassword,
|
||||||
provider: provider(func(p *upstreamldap.Provider) { p.CABundle = nil }),
|
provider: provider(func(p *upstreamldap.Provider) { p.CABundle = nil }),
|
||||||
wantError: fmt.Sprintf(`error dialing host "127.0.0.1:%s": LDAP Result Code 200 "Network Error": x509: certificate signed by unknown authority`, port),
|
wantError: fmt.Sprintf(`error dialing host "127.0.0.1:%s": LDAP Result Code 200 "Network Error": x509: certificate signed by unknown authority`, ldapHostPort),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "when the UsernameAttribute attribute has multiple values in the entry",
|
name: "when the UsernameAttribute attribute has multiple values in the entry",
|
||||||
username: "wally.ldap@example.com",
|
username: "wally.ldap@example.com",
|
||||||
password: wallyPassword,
|
password: "unused-because-error-is-before-bind",
|
||||||
provider: provider(func(p *upstreamldap.Provider) { p.UserSearch.UsernameAttribute = "mail" }),
|
provider: provider(func(p *upstreamldap.Provider) { p.UserSearch.UsernameAttribute = "mail" }),
|
||||||
wantError: `found 2 values for attribute "mail" while searching for user "wally.ldap@example.com", but expected 1 result`,
|
wantError: `found 2 values for attribute "mail" while searching for user "wally.ldap@example.com", but expected 1 result`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "when the UIDAttribute attribute has multiple values in the entry",
|
name: "when the UIDAttribute attribute has multiple values in the entry",
|
||||||
username: "wally",
|
username: "wally",
|
||||||
password: wallyPassword,
|
password: "unused-because-error-is-before-bind",
|
||||||
provider: provider(func(p *upstreamldap.Provider) { p.UserSearch.UIDAttribute = "mail" }),
|
provider: provider(func(p *upstreamldap.Provider) { p.UserSearch.UIDAttribute = "mail" }),
|
||||||
wantError: `found 2 values for attribute "mail" while searching for user "wally", but expected 1 result`,
|
wantError: `found 2 values for attribute "mail" while searching for user "wally", but expected 1 result`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "when the UsernameAttribute attribute is not found in the entry",
|
name: "when the UsernameAttribute attribute is not found in the entry",
|
||||||
username: "wally",
|
username: "wally",
|
||||||
password: wallyPassword,
|
password: "unused-because-error-is-before-bind",
|
||||||
provider: provider(func(p *upstreamldap.Provider) {
|
provider: provider(func(p *upstreamldap.Provider) {
|
||||||
p.UserSearch.Filter = "cn={}"
|
p.UserSearch.Filter = "cn={}"
|
||||||
p.UserSearch.UsernameAttribute = "attr-does-not-exist"
|
p.UserSearch.UsernameAttribute = "attr-does-not-exist"
|
||||||
@ -290,7 +285,7 @@ func TestLDAPSearch(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "when the UIDAttribute attribute is not found in the entry",
|
name: "when the UIDAttribute attribute is not found in the entry",
|
||||||
username: "wally",
|
username: "wally",
|
||||||
password: wallyPassword,
|
password: "unused-because-error-is-before-bind",
|
||||||
provider: provider(func(p *upstreamldap.Provider) { p.UserSearch.UIDAttribute = "attr-does-not-exist" }),
|
provider: provider(func(p *upstreamldap.Provider) { p.UserSearch.UIDAttribute = "attr-does-not-exist" }),
|
||||||
wantError: `found 0 values for attribute "attr-does-not-exist" while searching for user "wally", but expected 1 result`,
|
wantError: `found 0 values for attribute "attr-does-not-exist" while searching for user "wally", but expected 1 result`,
|
||||||
},
|
},
|
||||||
@ -379,81 +374,73 @@ func TestLDAPSearch(t *testing.T) {
|
|||||||
switch {
|
switch {
|
||||||
case tt.wantError != "":
|
case tt.wantError != "":
|
||||||
require.EqualError(t, err, tt.wantError)
|
require.EqualError(t, err, tt.wantError)
|
||||||
require.False(t, authenticated)
|
require.False(t, authenticated, "expected the user not to be authenticated, but they were")
|
||||||
require.Nil(t, authResponse)
|
require.Nil(t, authResponse)
|
||||||
case tt.wantUnauthenticated:
|
case tt.wantUnauthenticated:
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.False(t, authenticated)
|
require.False(t, authenticated, "expected the user not to be authenticated, but they were")
|
||||||
require.Nil(t, authResponse)
|
require.Nil(t, authResponse)
|
||||||
default:
|
default:
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.True(t, authenticated)
|
require.True(t, authenticated, "expected the user to be authenticated, but they were not")
|
||||||
require.Equal(t, tt.wantAuthResponse, authResponse)
|
require.Equal(t, tt.wantAuthResponse, authResponse)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func localhostPort(t *testing.T) string {
|
func startKubectlPortForward(ctx context.Context, t *testing.T, hostPort, remotePort, serviceName, namespace string) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
startLongRunningCommandAndWaitForInitialOutput(ctx, t,
|
||||||
unusedPortGrabbingListener, err := net.Listen("tcp", "127.0.0.1:0")
|
"kubectl",
|
||||||
require.NoError(t, err)
|
[]string{
|
||||||
|
"port-forward",
|
||||||
recentlyClaimedHostAndPort := unusedPortGrabbingListener.Addr().String()
|
fmt.Sprintf("service/%s", serviceName),
|
||||||
require.NoError(t, unusedPortGrabbingListener.Close())
|
fmt.Sprintf("%s:%s", hostPort, remotePort),
|
||||||
|
"-n", namespace,
|
||||||
splitHostAndPort := strings.Split(recentlyClaimedHostAndPort, ":")
|
},
|
||||||
require.Len(t, splitHostAndPort, 2)
|
"Forwarding from ",
|
||||||
|
"stdout",
|
||||||
return splitHostAndPort[1]
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func dockerRunLDAPServer(ctx context.Context, t *testing.T, hostPort string) []byte {
|
func findRecentlyUnusedLocalhostPorts(t *testing.T, howManyPorts int) []string {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
_, err := exec.LookPath("docker")
|
listeners := []net.Listener{}
|
||||||
require.NoError(t, err)
|
for i := 0; i < howManyPorts; i++ {
|
||||||
|
unusedPortGrabbingListener, err := net.Listen("tcp", "127.0.0.1:0")
|
||||||
ca, err := certauthority.New("Test LDAP CA", time.Hour*24)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
certPEM, keyPEM, err := ca.IssueServerCertPEM(nil, []net.IP{net.ParseIP("127.0.0.1")}, time.Hour*24)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
tempDir, err := ioutil.TempDir("", "pinniped-test-*")
|
|
||||||
require.NoError(t, err)
|
|
||||||
t.Cleanup(func() {
|
|
||||||
err := os.Remove(tempDir)
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
})
|
listeners = append(listeners, unusedPortGrabbingListener)
|
||||||
|
|
||||||
writeToNewTempFile(t, tempDir, "cert.pem", certPEM)
|
|
||||||
writeToNewTempFile(t, tempDir, "key.pem", keyPEM)
|
|
||||||
writeToNewTempFile(t, tempDir, "ca.pem", ca.Bundle())
|
|
||||||
writeToNewTempFile(t, tempDir, "test.ldif", []byte(testLDIF))
|
|
||||||
|
|
||||||
dockerArgs := []string{
|
|
||||||
"run",
|
|
||||||
"-e", "BITNAMI_DEBUG=true",
|
|
||||||
"-e", "LDAP_ADMIN_USERNAME=admin",
|
|
||||||
"-e", "LDAP_ADMIN_PASSWORD=password",
|
|
||||||
"-e", "LDAP_ENABLE_TLS=yes",
|
|
||||||
"-e", "LDAP_TLS_CERT_FILE=/inputs/cert.pem",
|
|
||||||
"-e", "LDAP_TLS_KEY_FILE=/inputs/key.pem",
|
|
||||||
"-e", "LDAP_TLS_CA_FILE=/inputs/ca.pem",
|
|
||||||
"-e", "LDAP_CUSTOM_LDIF_DIR=/inputs",
|
|
||||||
"-e", "LDAP_ROOT=dc=pinniped,dc=dev",
|
|
||||||
"-v", tempDir + ":/inputs",
|
|
||||||
"-p", hostPort + ":1636",
|
|
||||||
"-m", "64m",
|
|
||||||
"--rm", // automatically delete the container when finished
|
|
||||||
"docker.io/bitnami/openldap",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Log("Starting:", "docker", strings.Join(dockerArgs, " "))
|
ports := make([]string, len(listeners))
|
||||||
|
for i, listener := range listeners {
|
||||||
|
splitHostAndPort := strings.Split(listener.Addr().String(), ":")
|
||||||
|
require.Len(t, splitHostAndPort, 2)
|
||||||
|
ports[i] = splitHostAndPort[1]
|
||||||
|
}
|
||||||
|
|
||||||
cmd := exec.CommandContext(ctx, "docker", dockerArgs...)
|
for _, listener := range listeners {
|
||||||
|
require.NoError(t, listener.Close())
|
||||||
|
}
|
||||||
|
|
||||||
|
return ports
|
||||||
|
}
|
||||||
|
|
||||||
|
func startLongRunningCommandAndWaitForInitialOutput(
|
||||||
|
ctx context.Context,
|
||||||
|
t *testing.T,
|
||||||
|
command string,
|
||||||
|
args []string,
|
||||||
|
waitForOutputToContain string,
|
||||||
|
waitForOutputOnFd string, // can be either "stdout" or "stderr"
|
||||||
|
) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
t.Logf("Starting: %s %s", command, strings.Join(args, " "))
|
||||||
|
|
||||||
|
cmd := exec.CommandContext(ctx, command, args...)
|
||||||
|
|
||||||
var stdoutBuf, stderrBuf syncBuffer
|
var stdoutBuf, stderrBuf syncBuffer
|
||||||
cmd.Stdout = &stdoutBuf
|
cmd.Stdout = &stdoutBuf
|
||||||
@ -461,14 +448,25 @@ func dockerRunLDAPServer(ctx context.Context, t *testing.T, hostPort string) []b
|
|||||||
cmd.Stdout = io.MultiWriter(os.Stdout, &stdoutBuf)
|
cmd.Stdout = io.MultiWriter(os.Stdout, &stdoutBuf)
|
||||||
cmd.Stderr = io.MultiWriter(os.Stderr, &stderrBuf)
|
cmd.Stderr = io.MultiWriter(os.Stderr, &stderrBuf)
|
||||||
|
|
||||||
err = cmd.Start()
|
var watchOn *syncBuffer
|
||||||
|
switch waitForOutputOnFd {
|
||||||
|
case "stdout":
|
||||||
|
watchOn = &stdoutBuf
|
||||||
|
case "stderr":
|
||||||
|
watchOn = &stderrBuf
|
||||||
|
default:
|
||||||
|
t.Fatalf("oops bad argument")
|
||||||
|
}
|
||||||
|
|
||||||
|
err := cmd.Start()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
// docker requires an interrupt signal to end the container.
|
// If the cancellation of ctx was already scheduled in a t.Cleanup, then this
|
||||||
// This t.Cleanup is registered after the one that cancels the context, so this one will happen first.
|
// t.Cleanup is registered after the one, so this one will happen first.
|
||||||
|
// Cancelling ctx will send SIGKILL, which will act as a backup in case
|
||||||
|
// the process ignored this SIGINT.
|
||||||
err := cmd.Process.Signal(os.Interrupt)
|
err := cmd.Process.Signal(os.Interrupt)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
time.Sleep(time.Second) // give a moment before we move on, because we'll send SIGKILL in a later t.Cleanup
|
|
||||||
})
|
})
|
||||||
|
|
||||||
earlyTerminationCh := make(chan bool, 1)
|
earlyTerminationCh := make(chan bool, 1)
|
||||||
@ -479,9 +477,8 @@ func dockerRunLDAPServer(ctx context.Context, t *testing.T, hostPort string) []b
|
|||||||
|
|
||||||
terminatedEarly := false
|
terminatedEarly := false
|
||||||
require.Eventually(t, func() bool {
|
require.Eventually(t, func() bool {
|
||||||
t.Log("Waiting for slapd to start...")
|
t.Logf(`Waiting for %s to emit output: "%s"`, command, waitForOutputToContain)
|
||||||
// This substring is contained in the last line of output before the server starts.
|
if strings.Contains(watchOn.String(), waitForOutputToContain) {
|
||||||
if strings.Contains(stderrBuf.String(), " slapd starting\n") {
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
@ -491,136 +488,9 @@ func dockerRunLDAPServer(ctx context.Context, t *testing.T, hostPort string) []b
|
|||||||
default: // ignore when this non-blocking read found no message
|
default: // ignore when this non-blocking read found no message
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}, 2*time.Minute, time.Second)
|
}, 1*time.Minute, 1*time.Second)
|
||||||
|
|
||||||
require.Falsef(t, terminatedEarly, "docker command ended sooner than expected")
|
require.Falsef(t, terminatedEarly, "subcommand ended sooner than expected")
|
||||||
|
|
||||||
t.Log("Detected LDAP server has started successfully")
|
t.Logf("Detected that %s has started successfully", command)
|
||||||
return ca.Bundle()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeToNewTempFile(t *testing.T, dir string, filename string, contents []byte) {
|
|
||||||
t.Helper()
|
|
||||||
|
|
||||||
filePath := path.Join(dir, filename)
|
|
||||||
|
|
||||||
err := ioutil.WriteFile(filePath, contents, 0600)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
t.Cleanup(func() {
|
|
||||||
err := os.Remove(filePath)
|
|
||||||
require.NoError(t, err)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
var testLDIF = `
|
|
||||||
# ** CAUTION: Blank lines separate entries in the LDIF format! Do not remove them! ***
|
|
||||||
# Here's a good explanation of LDIF:
|
|
||||||
# https://www.digitalocean.com/community/tutorials/how-to-use-ldif-files-to-make-changes-to-an-openldap-system
|
|
||||||
|
|
||||||
# pinniped.dev (organization, root)
|
|
||||||
dn: dc=pinniped,dc=dev
|
|
||||||
objectClass: dcObject
|
|
||||||
objectClass: organization
|
|
||||||
dc: pinniped
|
|
||||||
o: example
|
|
||||||
|
|
||||||
# users, pinniped.dev (organization unit)
|
|
||||||
dn: ou=users,dc=pinniped,dc=dev
|
|
||||||
objectClass: organizationalUnit
|
|
||||||
ou: users
|
|
||||||
|
|
||||||
# groups, pinniped.dev (organization unit)
|
|
||||||
dn: ou=groups,dc=pinniped,dc=dev
|
|
||||||
objectClass: organizationalUnit
|
|
||||||
ou: groups
|
|
||||||
|
|
||||||
# beach-groups, groups, pinniped.dev (organization unit)
|
|
||||||
dn: ou=beach-groups,ou=groups,dc=pinniped,dc=dev
|
|
||||||
objectClass: organizationalUnit
|
|
||||||
ou: beach-groups
|
|
||||||
|
|
||||||
# pinny, users, pinniped.dev (user)
|
|
||||||
dn: cn=pinny,ou=users,dc=pinniped,dc=dev
|
|
||||||
objectClass: inetOrgPerson
|
|
||||||
objectClass: posixAccount
|
|
||||||
objectClass: shadowAccount
|
|
||||||
cn: pinny
|
|
||||||
sn: Seal
|
|
||||||
givenName: Pinny
|
|
||||||
mail: pinny.ldap@example.com
|
|
||||||
userPassword: password123
|
|
||||||
uid: pinny
|
|
||||||
uidNumber: 1000
|
|
||||||
gidNumber: 1000
|
|
||||||
homeDirectory: /home/pinny
|
|
||||||
loginShell: /bin/bash
|
|
||||||
gecos: pinny-the-seal
|
|
||||||
|
|
||||||
# wally, users, pinniped.dev
|
|
||||||
dn: cn=wally,ou=users,dc=pinniped,dc=dev
|
|
||||||
objectClass: inetOrgPerson
|
|
||||||
objectClass: posixAccount
|
|
||||||
objectClass: shadowAccount
|
|
||||||
cn: wally
|
|
||||||
sn: Walrus
|
|
||||||
givenName: Wally
|
|
||||||
mail: wally.ldap@example.com
|
|
||||||
mail: wally.alternate@example.com
|
|
||||||
userPassword: password456
|
|
||||||
uid: wally
|
|
||||||
uidNumber: 1001
|
|
||||||
gidNumber: 1001
|
|
||||||
homeDirectory: /home/wally
|
|
||||||
loginShell: /bin/bash
|
|
||||||
gecos: wally-the-walrus
|
|
||||||
|
|
||||||
# olive, users, pinniped.dev (user without password)
|
|
||||||
dn: cn=olive,ou=users,dc=pinniped,dc=dev
|
|
||||||
objectClass: inetOrgPerson
|
|
||||||
objectClass: posixAccount
|
|
||||||
objectClass: shadowAccount
|
|
||||||
cn: olive
|
|
||||||
sn: Boston Terrier
|
|
||||||
givenName: Olive
|
|
||||||
mail: olive.ldap@example.com
|
|
||||||
uid: olive
|
|
||||||
uidNumber: 1002
|
|
||||||
gidNumber: 1002
|
|
||||||
homeDirectory: /home/olive
|
|
||||||
loginShell: /bin/bash
|
|
||||||
gecos: olive-the-dog
|
|
||||||
|
|
||||||
# ball-game-players, beach-groups, groups, pinniped.dev (group of users)
|
|
||||||
dn: cn=ball-game-players,ou=beach-groups,ou=groups,dc=pinniped,dc=dev
|
|
||||||
cn: ball-game-players
|
|
||||||
objectClass: groupOfNames
|
|
||||||
member: cn=pinny,ou=users,dc=pinniped,dc=dev
|
|
||||||
member: cn=olive,ou=users,dc=pinniped,dc=dev
|
|
||||||
|
|
||||||
# seals, groups, pinniped.dev (group of users)
|
|
||||||
dn: cn=seals,ou=groups,dc=pinniped,dc=dev
|
|
||||||
cn: seals
|
|
||||||
objectClass: groupOfNames
|
|
||||||
member: cn=pinny,ou=users,dc=pinniped,dc=dev
|
|
||||||
|
|
||||||
# walruses, groups, pinniped.dev (group of users)
|
|
||||||
dn: cn=walruses,ou=groups,dc=pinniped,dc=dev
|
|
||||||
cn: walruses
|
|
||||||
objectClass: groupOfNames
|
|
||||||
member: cn=wally,ou=users,dc=pinniped,dc=dev
|
|
||||||
|
|
||||||
# pinnipeds, users, pinniped.dev (group of groups)
|
|
||||||
dn: cn=pinnipeds,ou=groups,dc=pinniped,dc=dev
|
|
||||||
cn: pinnipeds
|
|
||||||
objectClass: groupOfNames
|
|
||||||
member: cn=seals,ou=groups,dc=pinniped,dc=dev
|
|
||||||
member: cn=walruses,ou=groups,dc=pinniped,dc=dev
|
|
||||||
|
|
||||||
# mammals, groups, pinniped.dev (group of both groups and users)
|
|
||||||
dn: cn=mammals,ou=groups,dc=pinniped,dc=dev
|
|
||||||
cn: mammals
|
|
||||||
objectClass: groupOfNames
|
|
||||||
member: cn=pinninpeds,ou=groups,dc=pinniped,dc=dev
|
|
||||||
member: cn=olive,ou=users,dc=pinniped,dc=dev
|
|
||||||
`
|
|
||||||
|
@ -236,7 +236,7 @@ func loadEnvVars(t *testing.T, result *TestEnv) {
|
|||||||
|
|
||||||
result.SupervisorUpstreamLDAP = TestLDAPUpstream{
|
result.SupervisorUpstreamLDAP = TestLDAPUpstream{
|
||||||
Host: needEnv(t, "PINNIPED_TEST_LDAP_HOST"),
|
Host: needEnv(t, "PINNIPED_TEST_LDAP_HOST"),
|
||||||
CABundle: needEnv(t, "PINNIPED_TEST_LDAP_LDAPS_CA_BUNDLE"),
|
CABundle: base64Decoded(t, needEnv(t, "PINNIPED_TEST_LDAP_LDAPS_CA_BUNDLE")),
|
||||||
BindUsername: needEnv(t, "PINNIPED_TEST_LDAP_BIND_ACCOUNT_USERNAME"),
|
BindUsername: needEnv(t, "PINNIPED_TEST_LDAP_BIND_ACCOUNT_USERNAME"),
|
||||||
BindPassword: needEnv(t, "PINNIPED_TEST_LDAP_BIND_ACCOUNT_PASSWORD"),
|
BindPassword: needEnv(t, "PINNIPED_TEST_LDAP_BIND_ACCOUNT_PASSWORD"),
|
||||||
UserSearchBase: needEnv(t, "PINNIPED_TEST_LDAP_USERS_SEARCH_BASE"),
|
UserSearchBase: needEnv(t, "PINNIPED_TEST_LDAP_USERS_SEARCH_BASE"),
|
||||||
|
Loading…
Reference in New Issue
Block a user