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
79 lines
2.8 KiB
Python
79 lines
2.8 KiB
Python
"""Opt-in check against the real test OU. Skipped unless PORTAL_LIVE_LDAP=1.
|
|
|
|
Set LIVE_LDAP_SERVER, LIVE_LDAP_USER_OU_DN, LIVE_LDAP_BIND_DN, LIVE_LDAP_BIND_PASSWORD,
|
|
LIVE_LDAP_CA_FILE, LIVE_LDAP_PUBLISHER_GROUP_DN, LIVE_LDAP_ADMIN_GROUP_DN and
|
|
LIVE_LDAP_TEST_PASSWORD (the current password of ptest-user1). Needs python-ldap (Linux).
|
|
Only ever point this at the test OU. It restores what it writes and never changes a password.
|
|
"""
|
|
|
|
import os
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from portal.adapters.directory import Forbidden, LdapDirectory
|
|
from portal.config.env import DirectoryConfig
|
|
|
|
pytestmark = pytest.mark.skipif(
|
|
os.environ.get("PORTAL_LIVE_LDAP") != "1", reason="set PORTAL_LIVE_LDAP=1 to run"
|
|
)
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def live():
|
|
pytest.importorskip("ldap")
|
|
env = os.environ
|
|
config = DirectoryConfig(
|
|
server=env["LIVE_LDAP_SERVER"],
|
|
user_ou_dn=env["LIVE_LDAP_USER_OU_DN"],
|
|
bind_dn=env["LIVE_LDAP_BIND_DN"],
|
|
bind_password=env["LIVE_LDAP_BIND_PASSWORD"],
|
|
ca_file=Path(env["LIVE_LDAP_CA_FILE"]),
|
|
publisher_group_dn=env["LIVE_LDAP_PUBLISHER_GROUP_DN"],
|
|
admin_group_dn=env["LIVE_LDAP_ADMIN_GROUP_DN"],
|
|
)
|
|
return LdapDirectory(config)
|
|
|
|
|
|
def test_reads_state_and_roles(live):
|
|
user = live.find_user("ptest-user1")
|
|
assert user is not None and user.enabled and not user.is_publisher
|
|
assert live.find_user("ptest-publisher").is_publisher
|
|
assert live.find_user("ptest-admin").is_admin
|
|
assert live.find_user("ptest-disabled").enabled is False
|
|
assert live.find_user("ptest-expired").must_change_password is True
|
|
assert live.find_user("does-not-exist") is None
|
|
|
|
|
|
def test_users_outside_the_ou_are_not_found(live):
|
|
assert live.find_user("Administrator") is None
|
|
|
|
|
|
def test_allowed_attributes_are_written_and_restored(live):
|
|
before = live.find_user("ptest-user1")
|
|
try:
|
|
live.update_attributes("ptest-user1", {"mobile": "+49 000 0000", "displayName": "Test X"})
|
|
after = live.find_user("ptest-user1")
|
|
assert (after.mobile, after.display_name) == ("+49 000 0000", "Test X")
|
|
finally:
|
|
live.update_attributes(
|
|
"ptest-user1", {"mobile": before.mobile, "displayName": before.display_name}
|
|
)
|
|
assert live.find_user("ptest-user1").mobile == before.mobile
|
|
|
|
|
|
def test_other_attributes_are_refused(live):
|
|
with pytest.raises(Forbidden):
|
|
live.update_attributes("ptest-user1", {"description": "x"})
|
|
|
|
|
|
def test_password_policy_is_returned(live):
|
|
policy = live.password_policy("ptest-user1")
|
|
assert policy.min_length >= 0
|
|
|
|
|
|
def test_verify_password(live):
|
|
assert live.verify_password("ptest-user1", os.environ["LIVE_LDAP_TEST_PASSWORD"]) is True
|
|
assert live.verify_password("ptest-user1", "definitely-not-the-password") is False
|
|
assert live.verify_password("ptest-user1", "") is False
|