feat: add directory adapter

LDAPS directory adapter with certificate verification, allow-listed reads and
writes, escaped filters, password verification and change, password policy
and account state, plus an in-memory fake. Adds the LDAP_* configuration,
python-ldap with its image libraries, the CA file mount and an opt-in live
test for the test OU.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N3t4WL7bd9bdHHNjEsxzqC
This commit is contained in:
2026-10-17 18:28:50 +02:00
co-authored by Claude Sonnet 5.5
parent 5f72ea8db6
commit 96d3e77161
18 changed files with 1324 additions and 3 deletions
+35
View File
@@ -70,3 +70,38 @@ def test_process_exits_nonzero_naming_both_variables(tmp_path):
assert "SECRET_KEY" in result.stderr
assert "LOG_LEVEL" in result.stderr
assert "Traceback" not in result.stderr
@pytest.mark.parametrize(
"name",
[
"LDAP_SERVER",
"LDAP_USER_OU_DN",
"LDAP_BIND_DN",
"LDAP_BIND_PASSWORD",
"LDAP_CA_FILE",
"LDAP_PUBLISHER_GROUP_DN",
"LDAP_ADMIN_GROUP_DN",
],
)
def test_each_directory_variable_is_required(valid_env, name):
del valid_env[name]
with pytest.raises(ConfigError, match=name):
load_config(valid_env)
def test_directory_settings_are_loaded_and_the_password_is_not_printed(valid_env):
directory = load_config(valid_env).directory
assert directory.server == "dc.example.test"
assert directory.user_ou_dn == "OU=Users,DC=example,DC=test"
assert "service-password" not in repr(directory)
def test_ldap_server_must_not_be_a_url(valid_env):
with pytest.raises(ConfigError, match="LDAP_SERVER"):
load_config({**valid_env, "LDAP_SERVER": "ldap://dc.example.test"})
def test_a_missing_ca_file_is_reported(valid_env, tmp_path):
with pytest.raises(ConfigError, match="LDAP_CA_FILE: must be an existing file"):
load_config({**valid_env, "LDAP_CA_FILE": str(tmp_path / "missing.pem")})