Skip to main content

A fast and versatile LDAP editor

Docker PyPI License: MIT

This is a minimal web interface for LDAP directories.

Screenshot

Features:

  • Directory tree view
  • Entry creation / modification / deletion
  • Password management (set, change or remove a user's password, with old-password verification for self-changes)
  • LDIF import / export
  • Image support for the jpegPhoto and thumbnailPhoto attributes
  • Schema aware
  • Simple search (configurable)
  • Asynchronous LDAP backend with decent scalability
  • Available as Docker image

The app always requires authentication, even if the directory permits anonymous access. Credentials are validated via a simple bind (SASL is not supported); what a user can see and edit is governed by the directory's access rules.

Usage

Docker

For the impatient: Run it with

docker run -p 127.0.0.1:5000:5000 \
    -e LDAP_URL=ldap://your.openldap.server/ \
    dnknth/ldap-ui:latest

For the even more impatient: Start a demo with

docker compose up -d

then go to http://localhost:5000/ and log in with one of the following accounts:

UID Password Role
admin bedrock Admin (full access)
fred yabbadabbado User (read + self-password-write)

Pip

Install ldap-ui in a virtual environment:

python3 -m venv .venv
source .venv/bin/activate
pip3 install ldap-ui

After a shell rehash (if needed), the command ldap-ui becomes available:

Usage: ldap-ui [OPTIONS]

Options:
  -b, --base-dn TEXT              LDAP base DN. Required unless the BASE_DN
                                  environment variable is set.
  -h, --host TEXT                 Bind socket to this host.  [default:
                                  127.0.0.1]
  -p, --port INTEGER              Bind socket to this port. If 0, an available
                                  port will be picked.  [default: 5000]
  -l, --log-level [critical|error|warning|info|debug|trace]
                                  Log level. [default: info]
  --version                       Display the current version and exit.
  --help                          Show this message and exit.

Environment variables

LDAP access is controlled by the following optional environment variables, possibly from a .env file:

  • LDAP_URL: Connection URL in RFC 4516 format, defaults to ldap:///.
  • BASE_DN: Optional search base, e.g. dc=example,dc=org, can also be specified as part of the LDAP_URL.
  • SCHEMA_DN: Optional DN to obtain the directory schema, e.g. cn=subSchema.
  • LOGIN_ATTR: User name attribute, defaults to uid.
  • USE_TLS: Enable TLS, defaults to true for ldaps connections. Set it to a non-empty string to force STARTTLS on ldap connections.
  • BIND_AS_USER: Bind the initial LDAP connection with the login user's credentials instead of anonymously, for directories that reject anonymous binds (e.g. FreeIPA). Requires BIND_PATTERN - see Authentication methods.

If BASE_DN/SCHEMA_DN are not set, they are auto-detected from the root DSA, which must be readable anonymously:

access to dn.base="" by * read

The lookup is always anonymous - it runs before any user bind - so BIND_AS_USER mode still needs explicit BASE_DN/SCHEMA_DN on directories that deny anonymous root-DSA access.

For finer-grained control, see settings.py.

Development

Prerequisites:

ldap-ui consists of a Vue frontend and a Python backend that translates a subset of the LDAP protocol to a stateless ReST API.

pnpm build assembles the frontend in backend/ldap_ui/statics.

Review the configuration in settings.py; it is short and mostly self-explanatory (also see the notes below).

Run the backend locally:

  • make — installs dependencies, builds the frontend if needed, and starts the server.
  • make debug — starts the server in reload mode on port 5000 with DEBUG=true.

The frontend can be developed independently with hot-reload support using pnpm dev.

Notes

Authentication methods

The UI authenticates against the directory with a simple bind. The DN to bind is derived from the user name:

  1. Search anonymously by an attribute (uid by default, overridable via LOGIN_ATTR). The directory must grant anonymous read access to that attribute; to avoid that, use BIND_PATTERN (item 2).
  2. If BIND_PATTERN is set, no search is performed. BIND_PATTERN=%s requires a full DN (e.g. login cn=admin,dc=example,dc=org); BIND_PATTERN=%s,dc=example,dc=org allows cn=admin; BIND_PATTERN=cn=%s,dc=example,dc=org allows admin.

By default the backend opens its first connection anonymously. On directories that reject anonymous binds outright (e.g. FreeIPA with nsslapd-allow-anonymous-access: off) every request fails before the user's credentials are tried.

BIND_AS_USER=true combined with BIND_PATTERN derives the user's DN before connecting and binds the initial connection with the submitted credentials - no service account is needed:

LDAP_URL=ldap://freeipa.example.org:389
BASE_DN=dc=example,dc=org
SCHEMA_DN=cn=schema
BIND_PATTERN=uid=%s,cn=users,cn=accounts,dc=example,dc=org
BIND_AS_USER=true

Normal mode is the default when BIND_AS_USER is unset; /api/probe reports an error if it is set without BIND_PATTERN.

Searching

Search uses a configurable set of criteria (default: cn, gn, sn, and uid) if the query does not contain =. Wildcards are supported, e.g. f* matches all cn, gn, sn, and uid starting with f. Arbitrary attributes can also be searched with an LDAP filter, e.g. sn=F*.

Apart from the search field in the navigation bar, searches are also performed in the entry editor for any DN-valued input field.

Keyboard navigation

The editor and modal dialogs focus the first input when opening, so you can use the ⇥ key to navigate the form. Save or dismiss with the ↩ key.

The following access keys are defined:

Access Key UI Element
K Global search at page top
A Add an attribute
O Add an object class
R Reset entry modifications
S Save an entry (same as ↩)

Caveats

  • The software works with OpenLDAP using simple bind. Other directories have not been tested much, although 389 DS works to some extent.
  • SASL authentication schemes are presently not supported.
  • Passwords are transmitted as plain text. The LDAP server is expected to hash them (OpenLDAP 2.4 does). I strongly recommend exposing the app through a TLS-enabled web server.
  • HTTP Basic Authentication is performed by the app: the login dialog collects credentials and a request interceptor (src/auth.ts) attaches Authorization: Basic to every request once logged in. On startup the app probes /api/whoami; if an upstream HTTP server (or a native browser Basic challenge) already supplied the AUTHORIZATION request variable, the session is treated as authenticated and the login dialog is skipped. Otherwise the dialog credentials are used, replacing any upstream-provided header.
  • LDIF export always includes userPassword values that carry an RFC 2307 scheme prefix ({SSHA}, {SHA}, {MD5}, …), but never plaintext: values without a prefix — or explicitly marked {CLEARTEXT}/{PLAIN} — are omitted. So a directory that stores passwords in plaintext cannot leak them through an export.

Q&A

  • Q: Why are some fields not editable?
    • A: The RDN of an entry is read-only. To change it, rename the entry with a different RDN, then change the old RDN and rename back. To change passwords, click on the question mark icon on the right side. Binary fields (as per schema) are read-only. You do not want to modify them accidentally.
  • Q: Why did you write this?
    • A: PHPLdapAdmin is no longer actively maintained. I needed a replacement, and wanted to try Vue.

Acknowledgements

The Python backend uses FastAPI. The UI is built with Vue.js and Tailwind CSS. Kudos to the authors of these elegant frameworks!

Release files for ldap-ui 0.15.4

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ldap-ui 0.15.4
File Size Uploaded
ldap_ui-0.15.4.tar.gz 666.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for ldap-ui 0.15.4
File Interpreter ABI Platform
ldap_ui-0.15.4-py3-none-any.whl Python 3 none any Details

Total release size: 1.3 MB

Release files / ldap_ui-0.15.4.tar.gz

Download URL ldap_ui-0.15.4.tar.gz
Size 666.7 kB
Tags Source
SHA-256 checksum
How to use checksums
5a4907adc4faf56d7551c4ca60d0843b0a5c25c63cded2d10bb4015f0d15114a
BLAKE2b-256 checksum
How to use checksums
b2a2967eab411a1f4f6783c116564a076ab80d326282d733fd478589153e8f61
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.15 {"installer":{"name":"uv","version":"0.12.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / ldap_ui-0.15.4-py3-none-any.whl

Download URL ldap_ui-0.15.4-py3-none-any.whl
Size 662.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
22b8997329272c5aa168ebd840d0794bea18aa08efb3b46f898d40177e601c15
BLAKE2b-256 checksum
How to use checksums
94a332240c74f15feb35f22405eaff21ef2c93d54535b5074bdd9f73be7299bd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.15 {"installer":{"name":"uv","version":"0.12.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

0.15.5

2 release files

This release

0.15.4 This release

2 release files

0.13.8

2 release files

0.13.5

2 release files

0.13.4

2 release files

0.13.3

2 release files

0.13.2

2 release files

0.12.6

2 release files

0.12.5

2 release files

0.12.4

2 release files

0.12.3

2 release files

0.12.2

2 release files

0.12.1

2 release files

0.12.0

2 release files

0.11.5

2 release files

0.11.4

2 release files

0.10.3

2 release files

0.10.2

1 release file

0.10.1

1 release file

0.9.15

1 release file

0.9.14

1 release file

0.9.13

1 release file

0.9.12

1 release file

0.9.11

1 release file

0.9.10

1 release file

0.9.9

1 release file

0.9.8

1 release file

0.9.7

1 release file

0.9.6

1 release file

0.9.5

1 release file

0.9.4

1 release file

0.9.3

1 release file

0.9.2

1 release file

0.9.1

1 release file

0.9.0

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page