sources/ldap: add more flatten to user sync, start adding tests for OpenLDAP
This commit is contained in:
parent
fadf746234
commit
9c1ade59e9
|
@ -12,7 +12,7 @@ class EnsureOp:
|
||||||
"""Ensure operation, executed as part of an ObjectManager run"""
|
"""Ensure operation, executed as part of an ObjectManager run"""
|
||||||
|
|
||||||
_obj: Type[ManagedModel]
|
_obj: Type[ManagedModel]
|
||||||
_match_fields: list[str]
|
_match_fields: tuple[str, ...]
|
||||||
_kwargs: dict
|
_kwargs: dict
|
||||||
|
|
||||||
def __init__(self, obj: Type[ManagedModel], *match_fields: str, **kwargs) -> None:
|
def __init__(self, obj: Type[ManagedModel], *match_fields: str, **kwargs) -> None:
|
||||||
|
@ -34,11 +34,11 @@ class EnsureExists(EnsureOp):
|
||||||
"defaults": self._kwargs,
|
"defaults": self._kwargs,
|
||||||
}
|
}
|
||||||
for field in self._match_fields:
|
for field in self._match_fields:
|
||||||
update_kwargs[field] = self._kwargs.get(field, None)
|
value = self._kwargs.get(field, None)
|
||||||
|
if value:
|
||||||
|
update_kwargs[field] = value
|
||||||
self._kwargs.setdefault("managed", True)
|
self._kwargs.setdefault("managed", True)
|
||||||
self._obj.objects.update_or_create(
|
self._obj.objects.update_or_create(**update_kwargs)
|
||||||
**update_kwargs
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ObjectManager:
|
class ObjectManager:
|
||||||
|
|
|
@ -11,7 +11,7 @@ class LDAPProviderManager(ObjectManager):
|
||||||
EnsureExists(
|
EnsureExists(
|
||||||
LDAPPropertyMapping,
|
LDAPPropertyMapping,
|
||||||
"object_field",
|
"object_field",
|
||||||
name="authentik default LDAP Mapping: Name",
|
name="authentik default LDAP Mapping: name",
|
||||||
object_field="name",
|
object_field="name",
|
||||||
expression="return ldap.get('name')",
|
expression="return ldap.get('name')",
|
||||||
),
|
),
|
||||||
|
@ -22,9 +22,11 @@ class LDAPProviderManager(ObjectManager):
|
||||||
object_field="email",
|
object_field="email",
|
||||||
expression="return ldap.get('mail')",
|
expression="return ldap.get('mail')",
|
||||||
),
|
),
|
||||||
|
# Active Directory-specific mappings
|
||||||
EnsureExists(
|
EnsureExists(
|
||||||
LDAPPropertyMapping,
|
LDAPPropertyMapping,
|
||||||
"object_field",
|
"object_field",
|
||||||
|
"expression",
|
||||||
name="authentik default Active Directory Mapping: sAMAccountName",
|
name="authentik default Active Directory Mapping: sAMAccountName",
|
||||||
object_field="username",
|
object_field="username",
|
||||||
expression="return ldap.get('sAMAccountName')",
|
expression="return ldap.get('sAMAccountName')",
|
||||||
|
@ -36,4 +38,13 @@ class LDAPProviderManager(ObjectManager):
|
||||||
object_field="attributes.upn",
|
object_field="attributes.upn",
|
||||||
expression="return ldap.get('userPrincipalName')",
|
expression="return ldap.get('userPrincipalName')",
|
||||||
),
|
),
|
||||||
|
# OpenLDAP specific mappings
|
||||||
|
EnsureExists(
|
||||||
|
LDAPPropertyMapping,
|
||||||
|
"object_field",
|
||||||
|
"expression",
|
||||||
|
name="authentik default OpenLDAP Mapping: uid",
|
||||||
|
object_field="username",
|
||||||
|
expression="return ldap.get('uid')",
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
"""Sync LDAP Users and groups into authentik"""
|
"""Sync LDAP Users and groups into authentik"""
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from structlog.stdlib import BoundLogger, get_logger
|
from structlog.stdlib import BoundLogger, get_logger
|
||||||
|
|
||||||
from authentik.sources.ldap.models import LDAPSource
|
from authentik.sources.ldap.models import LDAPSource
|
||||||
|
@ -33,3 +35,11 @@ class BaseLDAPSynchronizer:
|
||||||
def sync(self) -> int:
|
def sync(self) -> int:
|
||||||
"""Sync function, implemented in subclass"""
|
"""Sync function, implemented in subclass"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def _flatten(self, value: Any) -> Any:
|
||||||
|
"""Flatten `value` if its a list"""
|
||||||
|
if isinstance(value, list):
|
||||||
|
if len(value) < 1:
|
||||||
|
return None
|
||||||
|
return value[0]
|
||||||
|
return value
|
||||||
|
|
|
@ -28,8 +28,9 @@ class UserLDAPSynchronizer(BaseLDAPSynchronizer):
|
||||||
)
|
)
|
||||||
user_count = 0
|
user_count = 0
|
||||||
for user in users:
|
for user in users:
|
||||||
|
self._logger.debug(user)
|
||||||
attributes = user.get("attributes", {})
|
attributes = user.get("attributes", {})
|
||||||
user_dn = user.get("entryDN", "")
|
user_dn = self._flatten(user.get("entryDN", ""))
|
||||||
if self._source.object_uniqueness_field not in attributes:
|
if self._source.object_uniqueness_field not in attributes:
|
||||||
self._logger.warning(
|
self._logger.warning(
|
||||||
"Cannot find uniqueness Field in attributes",
|
"Cannot find uniqueness Field in attributes",
|
||||||
|
@ -37,9 +38,12 @@ class UserLDAPSynchronizer(BaseLDAPSynchronizer):
|
||||||
dn=user_dn,
|
dn=user_dn,
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
uniq = attributes[self._source.object_uniqueness_field]
|
uniq = self._flatten(attributes[self._source.object_uniqueness_field])
|
||||||
try:
|
try:
|
||||||
defaults = self._build_object_properties(user_dn, **attributes)
|
defaults = self._build_object_properties(user_dn, **attributes)
|
||||||
|
self._logger.debug("Creating user with attributes", **defaults)
|
||||||
|
if "username" not in defaults:
|
||||||
|
raise IntegrityError("Username was not set by propertymappings")
|
||||||
user, created = User.objects.update_or_create(
|
user, created = User.objects.update_or_create(
|
||||||
**{
|
**{
|
||||||
f"attributes__{LDAP_UNIQUENESS}": uniq,
|
f"attributes__{LDAP_UNIQUENESS}": uniq,
|
||||||
|
@ -58,9 +62,7 @@ class UserLDAPSynchronizer(BaseLDAPSynchronizer):
|
||||||
if created:
|
if created:
|
||||||
user.set_unusable_password()
|
user.set_unusable_password()
|
||||||
user.save()
|
user.save()
|
||||||
self._logger.debug(
|
self._logger.debug("Synced User", user=user.username, created=created)
|
||||||
"Synced User", user=attributes.get("name", ""), created=created
|
|
||||||
)
|
|
||||||
user_count += 1
|
user_count += 1
|
||||||
return user_count
|
return user_count
|
||||||
|
|
||||||
|
@ -80,19 +82,21 @@ class UserLDAPSynchronizer(BaseLDAPSynchronizer):
|
||||||
continue
|
continue
|
||||||
object_field = mapping.object_field
|
object_field = mapping.object_field
|
||||||
if object_field.startswith("attributes."):
|
if object_field.startswith("attributes."):
|
||||||
|
# Because returning a list might desired, we can't
|
||||||
|
# rely on self._flatten here. Instead, just save the result as-is
|
||||||
properties["attributes"][
|
properties["attributes"][
|
||||||
object_field.replace("attributes.", "")
|
object_field.replace("attributes.", "")
|
||||||
] = value
|
] = value
|
||||||
else:
|
else:
|
||||||
properties[object_field] = value
|
properties[object_field] = self._flatten(value)
|
||||||
except PropertyMappingExpressionException as exc:
|
except PropertyMappingExpressionException as exc:
|
||||||
self._logger.warning(
|
self._logger.warning(
|
||||||
"Mapping failed to evaluate", exc=exc, mapping=mapping
|
"Mapping failed to evaluate", exc=exc, mapping=mapping
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
if self._source.object_uniqueness_field in kwargs:
|
if self._source.object_uniqueness_field in kwargs:
|
||||||
properties["attributes"][LDAP_UNIQUENESS] = kwargs.get(
|
properties["attributes"][LDAP_UNIQUENESS] = self._flatten(
|
||||||
self._source.object_uniqueness_field
|
kwargs.get(self._source.object_uniqueness_field)
|
||||||
)
|
)
|
||||||
properties["attributes"][LDAP_DISTINGUISHED_NAME] = user_dn
|
properties["attributes"][LDAP_DISTINGUISHED_NAME] = user_dn
|
||||||
return properties
|
return properties
|
||||||
|
|
|
@ -9,88 +9,88 @@ def mock_ad_connection(password: str) -> Connection:
|
||||||
_pass = "foo" # noqa # nosec
|
_pass = "foo" # noqa # nosec
|
||||||
connection = Connection(
|
connection = Connection(
|
||||||
server,
|
server,
|
||||||
user="cn=my_user,DC=AD2012,DC=LAB",
|
user="cn=my_user,dc=goauthentik,dc=io",
|
||||||
password=_pass,
|
password=_pass,
|
||||||
client_strategy=MOCK_SYNC,
|
client_strategy=MOCK_SYNC,
|
||||||
)
|
)
|
||||||
# Entry for password checking
|
# Entry for password checking
|
||||||
connection.strategy.add_entry(
|
connection.strategy.add_entry(
|
||||||
"cn=user,ou=users,DC=AD2012,DC=LAB",
|
"cn=user,ou=users,dc=goauthentik,dc=io",
|
||||||
{
|
{
|
||||||
"name": "test-user",
|
"name": "test-user",
|
||||||
"objectSid": "unique-test-group",
|
"objectSid": "unique-test-group",
|
||||||
"objectCategory": "Person",
|
"objectClass": "person",
|
||||||
"displayName": "Erin M. Hagens",
|
"displayName": "Erin M. Hagens",
|
||||||
"sAMAccountName": "sAMAccountName",
|
"sAMAccountName": "sAMAccountName",
|
||||||
"distinguishedName": "cn=user,ou=users,DC=AD2012,DC=LAB",
|
"distinguishedName": "cn=user,ou=users,dc=goauthentik,dc=io",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
connection.strategy.add_entry(
|
connection.strategy.add_entry(
|
||||||
"cn=group1,ou=groups,DC=AD2012,DC=LAB",
|
"cn=group1,ou=groups,dc=goauthentik,dc=io",
|
||||||
{
|
{
|
||||||
"name": "test-group",
|
"name": "test-group",
|
||||||
"objectSid": "unique-test-group",
|
"objectSid": "unique-test-group",
|
||||||
"objectCategory": "Group",
|
"objectClass": "group",
|
||||||
"distinguishedName": "cn=group1,ou=groups,DC=AD2012,DC=LAB",
|
"distinguishedName": "cn=group1,ou=groups,dc=goauthentik,dc=io",
|
||||||
"member": ["cn=user0,ou=users,DC=AD2012,DC=LAB"],
|
"member": ["cn=user0,ou=users,dc=goauthentik,dc=io"],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
# Group without SID
|
# Group without SID
|
||||||
connection.strategy.add_entry(
|
connection.strategy.add_entry(
|
||||||
"cn=group2,ou=groups,DC=AD2012,DC=LAB",
|
"cn=group2,ou=groups,dc=goauthentik,dc=io",
|
||||||
{
|
{
|
||||||
"name": "test-group",
|
"name": "test-group",
|
||||||
"objectCategory": "Group",
|
"objectClass": "group",
|
||||||
"distinguishedName": "cn=group2,ou=groups,DC=AD2012,DC=LAB",
|
"distinguishedName": "cn=group2,ou=groups,dc=goauthentik,dc=io",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
connection.strategy.add_entry(
|
connection.strategy.add_entry(
|
||||||
"cn=user0,ou=users,DC=AD2012,DC=LAB",
|
"cn=user0,ou=users,dc=goauthentik,dc=io",
|
||||||
{
|
{
|
||||||
"userPassword": password,
|
"userPassword": password,
|
||||||
"sAMAccountName": "user0_sn",
|
"sAMAccountName": "user0_sn",
|
||||||
"name": "user0_sn",
|
"name": "user0_sn",
|
||||||
"revision": 0,
|
"revision": 0,
|
||||||
"objectSid": "user0",
|
"objectSid": "user0",
|
||||||
"objectCategory": "Person",
|
"objectClass": "person",
|
||||||
"distinguishedName": "cn=user0,ou=users,DC=AD2012,DC=LAB",
|
"distinguishedName": "cn=user0,ou=users,dc=goauthentik,dc=io",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
# User without SID
|
# User without SID
|
||||||
connection.strategy.add_entry(
|
connection.strategy.add_entry(
|
||||||
"cn=user1,ou=users,DC=AD2012,DC=LAB",
|
"cn=user1,ou=users,dc=goauthentik,dc=io",
|
||||||
{
|
{
|
||||||
"userPassword": "test1111",
|
"userPassword": "test1111",
|
||||||
"sAMAccountName": "user2_sn",
|
"sAMAccountName": "user2_sn",
|
||||||
"name": "user1_sn",
|
"name": "user1_sn",
|
||||||
"revision": 0,
|
"revision": 0,
|
||||||
"objectCategory": "Person",
|
"objectClass": "person",
|
||||||
"distinguishedName": "cn=user1,ou=users,DC=AD2012,DC=LAB",
|
"distinguishedName": "cn=user1,ou=users,dc=goauthentik,dc=io",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
# Duplicate users
|
# Duplicate users
|
||||||
connection.strategy.add_entry(
|
connection.strategy.add_entry(
|
||||||
"cn=user2,ou=users,DC=AD2012,DC=LAB",
|
"cn=user2,ou=users,dc=goauthentik,dc=io",
|
||||||
{
|
{
|
||||||
"userPassword": "test2222",
|
"userPassword": "test2222",
|
||||||
"sAMAccountName": "user2_sn",
|
"sAMAccountName": "user2_sn",
|
||||||
"name": "user2_sn",
|
"name": "user2_sn",
|
||||||
"revision": 0,
|
"revision": 0,
|
||||||
"objectSid": "unique-test2222",
|
"objectSid": "unique-test2222",
|
||||||
"objectCategory": "Person",
|
"objectClass": "person",
|
||||||
"distinguishedName": "cn=user2,ou=users,DC=AD2012,DC=LAB",
|
"distinguishedName": "cn=user2,ou=users,dc=goauthentik,dc=io",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
connection.strategy.add_entry(
|
connection.strategy.add_entry(
|
||||||
"cn=user3,ou=users,DC=AD2012,DC=LAB",
|
"cn=user3,ou=users,dc=goauthentik,dc=io",
|
||||||
{
|
{
|
||||||
"userPassword": "test2222",
|
"userPassword": "test2222",
|
||||||
"sAMAccountName": "user2_sn",
|
"sAMAccountName": "user2_sn",
|
||||||
"name": "user2_sn",
|
"name": "user2_sn",
|
||||||
"revision": 0,
|
"revision": 0,
|
||||||
"objectSid": "unique-test2222",
|
"objectSid": "unique-test2222",
|
||||||
"objectCategory": "Person",
|
"objectClass": "person",
|
||||||
"distinguishedName": "cn=user3,ou=users,DC=AD2012,DC=LAB",
|
"distinguishedName": "cn=user3,ou=users,dc=goauthentik,dc=io",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
connection.bind()
|
connection.bind()
|
81
authentik/sources/ldap/tests/mock_slapd.py
Normal file
81
authentik/sources/ldap/tests/mock_slapd.py
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
"""ldap testing utils"""
|
||||||
|
|
||||||
|
from ldap3 import MOCK_SYNC, OFFLINE_SLAPD_2_4, Connection, Server
|
||||||
|
|
||||||
|
|
||||||
|
def mock_slapd_connection(password: str) -> Connection:
|
||||||
|
"""Create mock AD connection"""
|
||||||
|
server = Server("my_fake_server", get_info=OFFLINE_SLAPD_2_4)
|
||||||
|
_pass = "foo" # noqa # nosec
|
||||||
|
connection = Connection(
|
||||||
|
server,
|
||||||
|
user="cn=my_user,dc=goauthentik,dc=io",
|
||||||
|
password=_pass,
|
||||||
|
client_strategy=MOCK_SYNC,
|
||||||
|
)
|
||||||
|
# Entry for password checking
|
||||||
|
connection.strategy.add_entry(
|
||||||
|
"cn=user,ou=users,dc=goauthentik,dc=io",
|
||||||
|
{
|
||||||
|
"name": "test-user",
|
||||||
|
"uid": "unique-test-group",
|
||||||
|
"objectClass": "person",
|
||||||
|
"displayName": "Erin M. Hagens",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
connection.strategy.add_entry(
|
||||||
|
"cn=group1,ou=groups,dc=goauthentik,dc=io",
|
||||||
|
{
|
||||||
|
"name": "test-group",
|
||||||
|
"uid": "unique-test-group",
|
||||||
|
"objectClass": "group",
|
||||||
|
"member": ["cn=user0,ou=users,dc=goauthentik,dc=io"],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
# Group without SID
|
||||||
|
connection.strategy.add_entry(
|
||||||
|
"cn=group2,ou=groups,dc=goauthentik,dc=io",
|
||||||
|
{
|
||||||
|
"name": "test-group",
|
||||||
|
"objectClass": "group",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
connection.strategy.add_entry(
|
||||||
|
"cn=user0,ou=users,dc=goauthentik,dc=io",
|
||||||
|
{
|
||||||
|
"userPassword": password,
|
||||||
|
"name": "user0_sn",
|
||||||
|
"uid": "user0_sn",
|
||||||
|
"objectClass": "person",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
# User without SID
|
||||||
|
connection.strategy.add_entry(
|
||||||
|
"cn=user1,ou=users,dc=goauthentik,dc=io",
|
||||||
|
{
|
||||||
|
"userPassword": "test1111",
|
||||||
|
"name": "user1_sn",
|
||||||
|
"objectClass": "person",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
# Duplicate users
|
||||||
|
connection.strategy.add_entry(
|
||||||
|
"cn=user2,ou=users,dc=goauthentik,dc=io",
|
||||||
|
{
|
||||||
|
"userPassword": "test2222",
|
||||||
|
"name": "user2_sn",
|
||||||
|
"uid": "unique-test2222",
|
||||||
|
"objectClass": "person",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
connection.strategy.add_entry(
|
||||||
|
"cn=user3,ou=users,dc=goauthentik,dc=io",
|
||||||
|
{
|
||||||
|
"userPassword": "test2222",
|
||||||
|
"name": "user2_sn",
|
||||||
|
"uid": "unique-test2222",
|
||||||
|
"objectClass": "person",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
connection.bind()
|
||||||
|
return connection
|
|
@ -1,6 +1,7 @@
|
||||||
"""LDAP Source tests"""
|
"""LDAP Source tests"""
|
||||||
from unittest.mock import Mock, PropertyMock, patch
|
from unittest.mock import Mock, PropertyMock, patch
|
||||||
|
|
||||||
|
from django.db.models import Q
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
from authentik.core.models import User
|
from authentik.core.models import User
|
||||||
|
@ -9,10 +10,10 @@ from authentik.providers.oauth2.generators import generate_client_secret
|
||||||
from authentik.sources.ldap.auth import LDAPBackend
|
from authentik.sources.ldap.auth import LDAPBackend
|
||||||
from authentik.sources.ldap.models import LDAPPropertyMapping, LDAPSource
|
from authentik.sources.ldap.models import LDAPPropertyMapping, LDAPSource
|
||||||
from authentik.sources.ldap.sync.users import UserLDAPSynchronizer
|
from authentik.sources.ldap.sync.users import UserLDAPSynchronizer
|
||||||
from authentik.sources.ldap.tests.utils import mock_ad_connection
|
from authentik.sources.ldap.tests.mock_ad import mock_ad_connection
|
||||||
|
from authentik.sources.ldap.tests.mock_slapd import mock_slapd_connection
|
||||||
|
|
||||||
LDAP_PASSWORD = generate_client_secret()
|
LDAP_PASSWORD = generate_client_secret()
|
||||||
LDAP_CONNECTION_PATCH = PropertyMock(return_value=mock_ad_connection(LDAP_PASSWORD))
|
|
||||||
|
|
||||||
|
|
||||||
class LDAPSyncTests(TestCase):
|
class LDAPSyncTests(TestCase):
|
||||||
|
@ -23,27 +24,70 @@ class LDAPSyncTests(TestCase):
|
||||||
self.source = LDAPSource.objects.create(
|
self.source = LDAPSource.objects.create(
|
||||||
name="ldap",
|
name="ldap",
|
||||||
slug="ldap",
|
slug="ldap",
|
||||||
base_dn="DC=AD2012,DC=LAB",
|
base_dn="dc=goauthentik,dc=io",
|
||||||
additional_user_dn="ou=users",
|
additional_user_dn="ou=users",
|
||||||
additional_group_dn="ou=groups",
|
additional_group_dn="ou=groups",
|
||||||
)
|
)
|
||||||
self.source.property_mappings.set(LDAPPropertyMapping.objects.all())
|
|
||||||
self.source.save()
|
|
||||||
|
|
||||||
@patch("authentik.sources.ldap.models.LDAPSource.connection", LDAP_CONNECTION_PATCH)
|
def test_auth_synced_user_ad(self):
|
||||||
def test_auth_synced_user(self):
|
|
||||||
"""Test Cached auth"""
|
"""Test Cached auth"""
|
||||||
user_sync = UserLDAPSynchronizer(self.source)
|
self.source.property_mappings.set(
|
||||||
user_sync.sync()
|
LDAPPropertyMapping.objects.filter(
|
||||||
|
Q(name__startswith="authentik default LDAP Mapping")
|
||||||
user = User.objects.get(username="user0_sn")
|
| Q(name__startswith="authentik default Active Directory Mapping")
|
||||||
auth_user_by_bind = Mock(return_value=user)
|
|
||||||
with patch(
|
|
||||||
"authentik.sources.ldap.auth.LDAPBackend.auth_user_by_bind",
|
|
||||||
auth_user_by_bind,
|
|
||||||
):
|
|
||||||
backend = LDAPBackend()
|
|
||||||
self.assertEqual(
|
|
||||||
backend.authenticate(None, username="user0_sn", password=LDAP_PASSWORD),
|
|
||||||
user,
|
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
LDAPPropertyMapping.objects.filter(
|
||||||
|
Q(name__startswith="authentik default LDAP Mapping")
|
||||||
|
| Q(name__startswith="authentik default Active Directory Mapping")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.source.save()
|
||||||
|
connection = PropertyMock(return_value=mock_ad_connection(LDAP_PASSWORD))
|
||||||
|
with patch("authentik.sources.ldap.models.LDAPSource.connection", connection):
|
||||||
|
user_sync = UserLDAPSynchronizer(self.source)
|
||||||
|
user_sync.sync()
|
||||||
|
|
||||||
|
user = User.objects.get(username="user0_sn")
|
||||||
|
auth_user_by_bind = Mock(return_value=user)
|
||||||
|
with patch(
|
||||||
|
"authentik.sources.ldap.auth.LDAPBackend.auth_user_by_bind",
|
||||||
|
auth_user_by_bind,
|
||||||
|
):
|
||||||
|
backend = LDAPBackend()
|
||||||
|
self.assertEqual(
|
||||||
|
backend.authenticate(
|
||||||
|
None, username="user0_sn", password=LDAP_PASSWORD
|
||||||
|
),
|
||||||
|
user,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_auth_synced_user_openldap(self):
|
||||||
|
"""Test Cached auth"""
|
||||||
|
self.source.object_uniqueness_field = "uid"
|
||||||
|
self.source.property_mappings.set(
|
||||||
|
LDAPPropertyMapping.objects.filter(
|
||||||
|
Q(name__startswith="authentik default LDAP Mapping")
|
||||||
|
| Q(name__startswith="authentik default OpenLDAP Mapping")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.source.save()
|
||||||
|
connection = PropertyMock(return_value=mock_slapd_connection(LDAP_PASSWORD))
|
||||||
|
with patch("authentik.sources.ldap.models.LDAPSource.connection", connection):
|
||||||
|
user_sync = UserLDAPSynchronizer(self.source)
|
||||||
|
user_sync.sync()
|
||||||
|
|
||||||
|
user = User.objects.get(username="user0_sn")
|
||||||
|
auth_user_by_bind = Mock(return_value=user)
|
||||||
|
with patch(
|
||||||
|
"authentik.sources.ldap.auth.LDAPBackend.auth_user_by_bind",
|
||||||
|
auth_user_by_bind,
|
||||||
|
):
|
||||||
|
backend = LDAPBackend()
|
||||||
|
self.assertEqual(
|
||||||
|
backend.authenticate(
|
||||||
|
None, username="user0_sn", password=LDAP_PASSWORD
|
||||||
|
),
|
||||||
|
user,
|
||||||
|
)
|
||||||
|
|
|
@ -7,7 +7,7 @@ from authentik.core.models import User
|
||||||
from authentik.providers.oauth2.generators import generate_client_secret
|
from authentik.providers.oauth2.generators import generate_client_secret
|
||||||
from authentik.sources.ldap.models import LDAPPropertyMapping, LDAPSource
|
from authentik.sources.ldap.models import LDAPPropertyMapping, LDAPSource
|
||||||
from authentik.sources.ldap.password import LDAPPasswordChanger
|
from authentik.sources.ldap.password import LDAPPasswordChanger
|
||||||
from authentik.sources.ldap.tests.utils import mock_ad_connection
|
from authentik.sources.ldap.tests.mock_ad import mock_ad_connection
|
||||||
|
|
||||||
LDAP_PASSWORD = generate_client_secret()
|
LDAP_PASSWORD = generate_client_secret()
|
||||||
LDAP_CONNECTION_PATCH = PropertyMock(return_value=mock_ad_connection(LDAP_PASSWORD))
|
LDAP_CONNECTION_PATCH = PropertyMock(return_value=mock_ad_connection(LDAP_PASSWORD))
|
||||||
|
@ -20,7 +20,7 @@ class LDAPPasswordTests(TestCase):
|
||||||
self.source = LDAPSource.objects.create(
|
self.source = LDAPSource.objects.create(
|
||||||
name="ldap",
|
name="ldap",
|
||||||
slug="ldap",
|
slug="ldap",
|
||||||
base_dn="DC=AD2012,DC=LAB",
|
base_dn="dc=goauthentik,dc=io",
|
||||||
additional_user_dn="ou=users",
|
additional_user_dn="ou=users",
|
||||||
additional_group_dn="ou=groups",
|
additional_group_dn="ou=groups",
|
||||||
)
|
)
|
||||||
|
@ -41,7 +41,7 @@ class LDAPPasswordTests(TestCase):
|
||||||
pwc = LDAPPasswordChanger(self.source)
|
pwc = LDAPPasswordChanger(self.source)
|
||||||
user = User.objects.create(
|
user = User.objects.create(
|
||||||
username="test",
|
username="test",
|
||||||
attributes={"distinguishedName": "cn=user,ou=users,DC=AD2012,DC=LAB"},
|
attributes={"distinguishedName": "cn=user,ou=users,dc=goauthentik,dc=io"},
|
||||||
)
|
)
|
||||||
self.assertFalse(pwc.ad_password_complexity("test", user)) # 1 category
|
self.assertFalse(pwc.ad_password_complexity("test", user)) # 1 category
|
||||||
self.assertFalse(pwc.ad_password_complexity("test1", user)) # 2 categories
|
self.assertFalse(pwc.ad_password_complexity("test1", user)) # 2 categories
|
||||||
|
|
|
@ -11,7 +11,7 @@ from authentik.sources.ldap.sync.groups import GroupLDAPSynchronizer
|
||||||
from authentik.sources.ldap.sync.membership import MembershipLDAPSynchronizer
|
from authentik.sources.ldap.sync.membership import MembershipLDAPSynchronizer
|
||||||
from authentik.sources.ldap.sync.users import UserLDAPSynchronizer
|
from authentik.sources.ldap.sync.users import UserLDAPSynchronizer
|
||||||
from authentik.sources.ldap.tasks import ldap_sync_all
|
from authentik.sources.ldap.tasks import ldap_sync_all
|
||||||
from authentik.sources.ldap.tests.utils import mock_ad_connection
|
from authentik.sources.ldap.tests.mock_ad import mock_ad_connection
|
||||||
|
|
||||||
LDAP_PASSWORD = generate_client_secret()
|
LDAP_PASSWORD = generate_client_secret()
|
||||||
LDAP_CONNECTION_PATCH = PropertyMock(return_value=mock_ad_connection(LDAP_PASSWORD))
|
LDAP_CONNECTION_PATCH = PropertyMock(return_value=mock_ad_connection(LDAP_PASSWORD))
|
||||||
|
@ -25,7 +25,7 @@ class LDAPSyncTests(TestCase):
|
||||||
self.source = LDAPSource.objects.create(
|
self.source = LDAPSource.objects.create(
|
||||||
name="ldap",
|
name="ldap",
|
||||||
slug="ldap",
|
slug="ldap",
|
||||||
base_dn="DC=AD2012,DC=LAB",
|
base_dn="dc=goauthentik,dc=io",
|
||||||
additional_user_dn="ou=users",
|
additional_user_dn="ou=users",
|
||||||
additional_group_dn="ou=groups",
|
additional_group_dn="ou=groups",
|
||||||
)
|
)
|
||||||
|
|
Reference in a new issue