Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add OpenLDAP module #737

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
31 changes: 31 additions & 0 deletions docs/modules/openLDAP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# OpenLDAP Module

[OpenLDAP](https://www.openldap.org/) is an open-source implementation of the LDAP protocol, enabling hierarchical storage and management of directory information.

## Install

```bash
npm install @testcontainers/openldap --save-dev
```

## Examples

<!--codeinclude-->
[Start container:](../../packages/modules/openldap/src/openldap-container.test.ts) inside_block:startContainer
<!--/codeinclude-->

<!--codeinclude-->
[Connect openldap client to container:](../../packages/modules/openldap/src/openldap-container.test.ts) inside_block:simpleConnect
<!--/codeinclude-->

<!--codeinclude-->
[Start container with password authentication:](../../packages/modules/openldap/src/openldap-container.test.ts) inside_block:startWithCredentials
<!--/codeinclude-->

<!--codeinclude-->
[Define volume for persistent/predefined data:](../../packages/modules/openldap/src/openldap-container.test.ts) inside_block:persistentData
<!--/codeinclude-->

<!--codeinclude-->
[Execute a command inside the container:](../../packages/modules/openldap/src/openldap-container.test.ts) inside_block:executeCommand
<!--/codeinclude-->
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ nav:
- Neo4J: modules/neo4j.md
- PostgreSQL: modules/postgresql.md
- Qdrant: modules/qdrant.md
- OpenLDAP: modules/openLDAP.md
- Redis: modules/redis.md
- Selenium: modules/selenium.md
- Weaviate: modules/weaviate.md
Expand Down
66 changes: 66 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions packages/modules/openldap/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { Config } from "jest";
import * as path from "path";

const config: Config = {
preset: "ts-jest",
moduleNameMapper: {
"^testcontainers$": path.resolve(__dirname, "../../testcontainers/src"),
},
};

export default config;
37 changes: 37 additions & 0 deletions packages/modules/openldap/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"name": "@testcontainers/openldap",
"version": "10.7.2",
"license": "MIT",
"keywords": [
"openldap",
"testing",
"docker",
"testcontainers"
],
"description": "openldap module for Testcontainers",
"homepage": "https://github.com/testcontainers/testcontainers-node#readme",
"repository": {
"type": "git",
"url": "https://github.com/testcontainers/testcontainers-node"
},
"bugs": {
"url": "https://github.com/testcontainers/testcontainers-node/issues"
},
"main": "build/index.js",
"files": [
"build"
],
"publishConfig": {
"access": "public"
},
"scripts": {
"prepack": "shx cp ../../../README.md . && shx cp ../../../LICENSE .",
"build": "tsc --project tsconfig.build.json"
},
"devDependencies": {
"ldapts": "^7.0.10"
},
"dependencies": {
"testcontainers": "^10.7.2"
}
}
5 changes: 5 additions & 0 deletions packages/modules/openldap/src/import.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -e
# shellcheck disable=SC2046
ldapadd -x $([[ -n "$1" ]] && echo "-D $1") $([[ -n "$2" ]] && echo "-w $2") -f /tmp/import.ldif
echo "Imported"
1 change: 1 addition & 0 deletions packages/modules/openldap/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { OpenldapContainer, StartedOpenldapContainer } from "./openldap-container";
27 changes: 27 additions & 0 deletions packages/modules/openldap/src/initData.ldif
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# LDIF file to create two users

# Entry for User 1
dn: uid=testuser1,ou=users,dc=example,dc=com
changetype: add
objectClass: inetOrgPerson
objectClass: posixAccount
cn: User 1
uid: user1
sn: Lastname1
userPassword: Password123
uidNumber: 1001
gidNumber: 1001
homeDirectory: /home/user1

# Entry for User 2
dn: uid=testuser2,ou=users,dc=example,dc=com
changetype: add
objectClass: inetOrgPerson
objectClass: posixAccount
cn: User 2
uid: user2
sn: Lastname2
userPassword: Password456
uidNumber: 1002
gidNumber: 1002
homeDirectory: /home/user2
123 changes: 123 additions & 0 deletions packages/modules/openldap/src/openldap-container.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { OpenldapContainer, StartedOpenldapContainer } from "./openldap-container";
import * as os from "os";
import * as path from "path";
import * as fs from "fs";
import { Client } from "ldapts";

describe("OpenLdapContainer", () => {
jest.setTimeout(240_000);

// startContainer {
it("should connect and execute set-get", async () => {
const container = await new OpenldapContainer().withRootDn("dc=example,dc=org").start();

const client = await connectTo(container);

const newUserName = "foo";
const dn = `cn=${newUserName}, ${container.getRootDn()}`;
await client.add(dn, {
cn: newUserName,
uidNumber: "1000",
gidNumber: "1000",
homeDirectory: `/home/${newUserName}`,
uid: newUserName,
sn: "LastName",
objectclass: ["inetOrgPerson", "posixAccount"],
});
const user = await client.search(dn);
expect(user.searchEntries[0].object).not.toBeNull();

await client.unbind();
await container.stop();
});
// }

it("should connect with password and execute set-get", async () => {
const container = await new OpenldapContainer().withPassword("test").start();

const client = await connectTo(container);

//await client.set("key", "val");
//expect(await client.get("key")).toBe("val");

await client.unbind();
await container.stop();
});

// persistentData {
it("should reconnect with volume and persistence data", async () => {
const sourcePath = fs.mkdtempSync(path.join(os.tmpdir(), "ldap-"));
const container = await new OpenldapContainer().withPassword("test").withPersistence(sourcePath).start();
let client = await connectTo(container);

//await client.set("key", "val");
await client.unbind();
await container.restart();
client = await connectTo(container);
//expect(await client.get("key")).toBe("val");

await client.unbind();
await container.stop();
try {
fs.rmSync(sourcePath, { force: true, recursive: true });
} catch (e) {
//Ignore clean up, when have no access on fs.
console.log(e);
}
});
// }

// initial data import {
it("should load initial data and can read it", async () => {
const container = await new OpenldapContainer()
.withPassword("test")
.withInitialLdif(path.join(__dirname, "initData.ldif"))
.start();
const client = await connectTo(container);
const user1 = await client.search("uid=testuser1,ou=users,dc=example,dc=com");
expect(user1.searchEntries.length).toBe(1);

client.unbind();
await container.stop();
});
// }

// startWithCredentials {
it("should start with credentials and login", async () => {
const username = "cn=admin,dc=example,dc=org";
const password = "testPassword";

// Test authentication
const container = await new OpenldapContainer().withUsername(username).withPassword(password).start();
expect(container.getConnectionUrl()).toEqual(`ldap://${container.getHost()}:${container.getPort()}`);
expect(container.getUsername()).toEqual(username);
expect(container.getPassword()).toEqual(password);
const client = await connectTo(container);

await client.unbind();
await container.stop();
});
// }

// executeCommand {
it("should execute container cmd and return the result", async () => {
const container = await new OpenldapContainer().start();

const queryResult = await container.executeCliCmd("info", ["clients"]);
expect(queryResult).toEqual(expect.stringContaining("connected_clients:1"));

await container.stop();
});
// }

// simpleConnect {
async function connectTo(container: StartedOpenldapContainer) {
const client = new Client({
url: container.getConnectionUrl(),
});
await client.bind(`cn=${container.getUsername()},${container.getRootDn()}`, container.getPassword());
expect(client.isConnected).toBeTruthy();
return client;
}
// }
});