UDM REST API Client
Client library and CLI to interact with the Univention Directory Manager (UDM) REST API.
Features
- Asynchronous and synchronous Python functions
- Command line interface
- Automatic handling of HTTP(S) sessions
- Type annotations
- Python 3.11+
Usage
Synchronous Python code
from univention.admin.rest.client import UDM
udm = UDM.http("https://10.20.30.40/univention/udm/", "Administrator", "s3cr3t")
module = udm.get("users/user")
# 1. create a user
obj = module.new()
obj.properties["username"] = "foo"
obj.properties["password"] = "univention"
obj.properties["lastname"] = "foo"
obj.save()
# 2. search for users
for result in module.search("uid=*"):
obj = result.open()
print(obj)
print(obj.properties)
print(obj.objects.groups)
# 3. get by dn
ldap_base = udm.get_ldap_base()
obj = module.get(f"uid=foo,cn=users,{ldap_base}")
# 4. get referenced objects e.g. groups
pg = obj.objects["primaryGroup"][0].open()
print(pg.dn, pg.properties)
print(obj.objects["groups"])
# 5. modify
obj.properties["description"] = "foo"
obj.save()
# 6. move to the ldap base
obj.move(ldap_base)
# 7. remove
obj.delete()
Asynchronous Python code
import asyncio
from univention.admin.rest.async_client import UDM
async def main():
async with UDM.http("http://10.20.30.40./univention/udm/", "Administrator", "s3cr3t") as udm:
module = await udm.get("users/user")
# 1. create a user
obj = await module.new()
obj.properties["username"] = "foo"
obj.properties["password"] = "univention"
obj.properties["lastname"] = "foo"
await obj.save()
# 2. search for users
async for result in module.search("uid=*"):
obj = await result.open()
print(obj)
print(obj.properties)
print(obj.objects.groups)
# 3. get by dn
ldap_base = await udm.get_ldap_base()
obj = await module.get(f"uid=foo,cn=users,{ldap_base}")
# 4. get referenced objects e.g. groups
pg = await obj.objects["primaryGroup"][0].open()
print(pg.dn, pg.properties)
print(obj.objects["groups"])
# 5. modify
obj.properties["description"] = "foo"
await obj.save()
# 6. move to the ldap base
await obj.move(ldap_base)
# 7. remove
await obj.delete()
asyncio.run(main())
Custom Request ID Generation
By default, the UDM client generates unique request IDs using uuid.uuid4().hex for each HTTP request. You can provide a custom request ID generator function to control how request IDs are created:
from univention.admin.rest.client import UDM
import time
# Custom request ID generator using timestamp and counter
counter = 0
def custom_request_id():
global counter
counter += 1
return f"req-{int(time.time())}-{counter}"
# Use with synchronous client
udm = UDM.http(
"https://10.20.30.40/univention/udm/",
"Administrator",
"s3cr3t",
request_id_generator=custom_request_id
)
# Use with bearer token authentication
udm = UDM.bearer(
"https://10.20.30.40/univention/udm/",
"your-bearer-token",
request_id_generator=custom_request_id
)
For asynchronous clients:
from univention.admin.rest.async_client import UDM
async def main():
async with UDM.http(
"http://10.20.30.40./univention/udm/",
"Administrator",
"s3cr3t",
request_id_generator=lambda: f"async-{uuid.uuid4().hex[:8]}"
) as udm:
# Your async code here
pass
The request_id_generator function should return a string that will be used as the X-Request-Id header value. If the function returns None, no request ID will be added to that particular request.
Command line interface
PASS_FILE="$HOME/pw-$(date +%Y%m%d%H%M%S)"
echo "s3cr3t" > "$PASS_FILE"
udm \
--username Administrator \
--bindpwdfile "$PASS_FILE" \
--uri http://10.20.30.40/univention/udm/ \
users/user list \
--filter uid=Administrator
rm -f "$PASS_FILE"
Instead of using udm, the CLI can also be called using python3 -m univention.admin.rest.client.
Error codes
Error codes and other details of the UDM REST API can be found in its documentation.
Installation
The dependencies for the CLI, synchronous and asynchronous clients differ. They are available separately as "extra" dependencies, to reduce your projects total dependencies. Without installing the "extra" requirements, only the common dependencies of the three interfaces will be installed, and none will be usable.
async: installsaiohttpsync: installsrequestscli: installspython-ldapandrequests
To install the library including the dependencies for the synchronous client via pip from PyPI run:
pip install udm-rest-api-client[sync]
To install the library including the dependencies for the asynchronous client via pip from PyPI run:
pip install udm-rest-api-client[async]
To install the CLI (incl. the required library and its dependencies):
pip install udm-rest-api-client[cli]
Multiple extras can be specified at the same time:
pip install udm-rest-api-client[async,cli,dev,sync]
CLI and pipx
If you wish to use the udm command line interface from anywhere in your system,
without having to manually handle virtual environments,
install it using pipx:
pipx install udm-rest-api-client[cli]
which udm
$HOME/.local/bin/udm
Testing
The tests need a UDM REST API.
The CI pipeline deploys a minimal Nubus into a Kubernetes namespace
with the nubus-k8s CI component
and runs the tests against it.
To run them locally against any Nubus deployment:
pip install -e '.[async,sync,dev,cli]'
export UDM_URL=https://nubus.example.com/univention/udm/
export UDM_USERNAME=Administrator
export UDM_PASSWORD=s3cr3t
pytest
The tests create and delete users named foo_*.
Do not run them against a production system.
Building and publishing
There is a Makefile that offers all required commands. A help text is available, when started without arguments:
$ make
clean remove all build and Python artifacts
clean-build remove build artifacts
clean-pyc remove Python file artifacts
build builds source and wheel package
publish package and upload a release to pypi
publish-test package and upload a release to the pypi test site
install-hatch install 'hatch' using 'pipx'
The build system uses hatch.
To install it, run make install-hatch.
Then execute make publish-test to build and publish the client to https://test.pypi.org/project/udm-rest-api-client/ or make publish to publish to https://pypi.org/project/udm-rest-api-client/ .
Use prek to run pre-commit linters.
License
The UDM REST API Client is distributed under the terms of the GNU Affero General Public License v3.0 only (AGPLv3) license.
Changelog
udm-rest-api-client (0.2.0) unstable; urgency=low
- Add methods for paginated search: Module.search_page() and Module.search_paginated()
- Add Object.restore() to restore objects from the Recycle Bin
- Add HTTPError.errors registry, the TooManyRequests (429) exception and Session.has_relation()
- Return Self from UDM.http() and UDM.bearer()
- Handle empty 204 responses in the async client
- Increase the maximum number of response headers for aiohttp 3.13.4
- Speed up resolving of relations
- Deprecate the CLI option --bindpwd in favor of --bindpwdfile
- Add docstrings to the client classes and exceptions
- Update copyright to 2026, remove the vacancies link
- Run the tests against a Nubus deployed into Kubernetes with the nubus-k8s CI component instead of Docker Compose
- Switch pre-commit linting to prek
- Build Debian packages for Debian 13 and Ubuntu 26.04, no longer for Debian 11 and Ubuntu 20.04 and 22.04
... see here for earlier changes.
Metadata
Release files for udm-rest-api-client 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| udm_rest_api_client-0.2.0.tar.gz | 53.6 kB | Without provenance | Details for udm_rest_api_client-0.2.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| udm_rest_api_client-0.2.0-py3-none-any.whl | Python 3 | none | any | Details for udm_rest_api_client-0.2.0-py3-none-any.whl |
Total release size: 93.3 kB
Release files / udm_rest_api_client-0.2.0.tar.gz
| Download URL | udm_rest_api_client-0.2.0.tar.gz |
|---|---|
| Size | 53.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
59674f170ea31677c54bb66c1aa1723850cd8833ea894e8d8234dc715aaf6b52
|
|
BLAKE2b-256 checksum How to use checksums |
8dffdc219bdf2556215dc30e022fcafd05187da23a100e6ffb0d38d8a62282ab
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
Hatch/1.18.2 {"ci":true,"cpu":"x86_64","distro":{"id":"trixie","libc":{"lib":"glibc","version":"2.41"},"name":"Debian GNU/Linux","version":"13"},"implementation":{"name":"CPython","version":"3.11.18"},"installer":{"name":"hatch","version":"1.18.2"},"openssl_version":"OpenSSL 3.5.7 9 Jun 2026","python":"3.11.18","system":{"name":"Linux","release":"6.12.100+deb13-cloud-amd64"}} HTTPX2/2.16.0
|
Release files / udm_rest_api_client-0.2.0-py3-none-any.whl
| Download URL | udm_rest_api_client-0.2.0-py3-none-any.whl |
|---|---|
| Size | 39.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
106a35a78f7740e516b0c6e4ca9c9c9e5015cededd713e4c9da178f90f380dd1
|
|
BLAKE2b-256 checksum How to use checksums |
236a328405a950ed04d6e3c8518cc11c4bac382ad416d74250199ad2100cc601
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
Hatch/1.18.2 {"ci":true,"cpu":"x86_64","distro":{"id":"trixie","libc":{"lib":"glibc","version":"2.41"},"name":"Debian GNU/Linux","version":"13"},"implementation":{"name":"CPython","version":"3.11.18"},"installer":{"name":"hatch","version":"1.18.2"},"openssl_version":"OpenSSL 3.5.7 9 Jun 2026","python":"3.11.18","system":{"name":"Linux","release":"6.12.100+deb13-cloud-amd64"}} HTTPX2/2.16.0
|
Security for udm-rest-api-client 0.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.