From 9ec5e347fffd062ff15ffa09c869a616c364ebf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Wed, 24 Jan 2024 17:24:08 +0100 Subject: [PATCH] Adding LoadLdif --- docs/modules/openldap.md | 17 ++++++++ modules/openldap/openldap.go | 30 +++++++++++++- modules/openldap/openldap_test.go | 69 +++++++++++++++++++++++++++++++ 3 files changed, 115 insertions(+), 1 deletion(-) diff --git a/docs/modules/openldap.md b/docs/modules/openldap.md index ea08a72c51..a74cd6bdb8 100644 --- a/docs/modules/openldap.md +++ b/docs/modules/openldap.md @@ -45,3 +45,20 @@ for OpenLDAP. E.g. `testcontainers.WithImage("bitnami/openldap:2.6.6")`. ### Container Methods The OpenLDAP container exposes the following methods: + +#### ConnectionString + +This method returns the connection string to connect to the OpenLDAP container, using the `1389` port. + + +[Get connection string](../../modules/openldap/openldap_test.go) inside_block:connectionString + + +#### LoadLdif + +This method loads an ldif file in the OpenLDAP server. +It returns and error if there is any problem with the ldif file loading process. + + +[Load ldif](../../modules/openldap/openldap_test.go) inside_block:loadLdif + diff --git a/modules/openldap/openldap.go b/modules/openldap/openldap.go index 19269224bb..23ffe7288c 100644 --- a/modules/openldap/openldap.go +++ b/modules/openldap/openldap.go @@ -2,7 +2,9 @@ package openldap import ( "context" + "errors" "fmt" + "io" "net" "github.com/testcontainers/testcontainers-go" @@ -19,8 +21,12 @@ const ( // OpenLDAPContainer represents the OpenLDAP container type used in the module type OpenLDAPContainer struct { testcontainers.Container + adminUsername string + adminPassword string + rootDn string } +// ConnectionString returns the connection string for the OpenLDAP container func (c *OpenLDAPContainer) ConnectionString(ctx context.Context, args ...string) (string, error) { containerPort, err := c.MappedPort(ctx, "1389/tcp") if err != nil { @@ -36,6 +42,23 @@ func (c *OpenLDAPContainer) ConnectionString(ctx context.Context, args ...string return connStr, nil } +// LoadLdif loads an ldif file into the OpenLDAP container +func (c *OpenLDAPContainer) LoadLdif(ctx context.Context, ldif []byte) error { + err := c.CopyToContainer(ctx, ldif, "/tmp/ldif.ldif", 0o755) + if err != nil { + return err + } + code, output, err := c.Exec(ctx, []string{"ldapadd", "-H", "ldap://localhost:1389", "-x", "-D", fmt.Sprintf("cn=%s,%s", c.adminUsername, c.rootDn), "-w", c.adminPassword, "-f", "/tmp/ldif.ldif"}) + if err != nil { + return err + } + if code != 0 { + data, _ := io.ReadAll(output) + return errors.New(string(data)) + } + return nil +} + // WithAdminUsername sets the initial admin username to be created when the container starts // It is used in conjunction with WithAdminPassword to set a username and its password. // It will create the specified user with admin power. @@ -91,5 +114,10 @@ func RunContainer(ctx context.Context, opts ...testcontainers.ContainerCustomize return nil, err } - return &OpenLDAPContainer{Container: container}, nil + return &OpenLDAPContainer{ + Container: container, + adminUsername: req.Env["LDAP_ADMIN_USERNAME"], + adminPassword: req.Env["LDAP_ADMIN_PASSWORD"], + rootDn: req.Env["LDAP_ROOT"], + }, nil } diff --git a/modules/openldap/openldap_test.go b/modules/openldap/openldap_test.go index 10acc8d191..e0540b1efe 100644 --- a/modules/openldap/openldap_test.go +++ b/modules/openldap/openldap_test.go @@ -73,7 +73,9 @@ func TestOpenLDAPWithDifferentRoot(t *testing.T) { } }) + // connectionString { connectionString, err := container.ConnectionString(ctx) + // } if err != nil { t.Fatal(err) } @@ -90,3 +92,70 @@ func TestOpenLDAPWithDifferentRoot(t *testing.T) { t.Fatal(err) } } + +func TestOpenLDAPWithLdif(t *testing.T) { + ctx := context.Background() + + container, err := RunContainer(ctx, testcontainers.WithImage("bitnami/openldap:2.6.6")) + if err != nil { + t.Fatal(err) + } + + // Clean up the container after the test is complete + t.Cleanup(func() { + if err := container.Terminate(ctx); err != nil { + t.Fatalf("failed to terminate container: %s", err) + } + }) + + // loadLdif { + ldif := ` +dn: uid=test.user,ou=users,dc=example,dc=org +changetype: add +objectclass: iNetOrgPerson +cn: Test User +sn: Test +mail: test.user@example.org +userPassword: Password1 +` + + err = container.LoadLdif(ctx, []byte(ldif)) + // } + if err != nil { + t.Fatal(err) + } + + connectionString, err := container.ConnectionString(ctx) + if err != nil { + t.Fatal(err) + } + + client, err := ldap.DialURL(connectionString) + if err != nil { + t.Fatal(err) + } + defer client.Close() + + // First bind with a read only user + err = client.Bind("cn=admin,dc=example,dc=org", "adminpassword") + if err != nil { + t.Fatal(err) + } + + result, err := client.Search(&ldap.SearchRequest{ + BaseDN: "uid=test.user,ou=users,dc=example,dc=org", + Scope: ldap.ScopeWholeSubtree, + Filter: "(objectClass=*)", + Attributes: []string{"dn"}, + }) + if err != nil { + t.Fatal(err) + } + + if len(result.Entries) != 1 { + t.Fatal("Invalid number of entries returned", result.Entries) + } + if result.Entries[0].DN != "uid=test.user,ou=users,dc=example,dc=org" { + t.Fatal("Invalid entry returned", result.Entries[0].DN) + } +}