diff --git a/authentik/sources/ldap/api/__init__.py b/authentik/sources/ldap/api/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/authentik/sources/ldap/api/property_mappings.py b/authentik/sources/ldap/api/property_mappings.py new file mode 100644 index 000000000..c8527979e --- /dev/null +++ b/authentik/sources/ldap/api/property_mappings.py @@ -0,0 +1,40 @@ +"""Property mapping API Views""" +from django_filters.filters import AllValuesMultipleFilter +from django_filters.filterset import FilterSet +from drf_spectacular.types import OpenApiTypes +from drf_spectacular.utils import extend_schema_field +from rest_framework.viewsets import ModelViewSet + +from authentik.core.api.propertymappings import PropertyMappingSerializer +from authentik.core.api.used_by import UsedByMixin +from authentik.sources.ldap.models import LDAPPropertyMapping + + +class LDAPPropertyMappingSerializer(PropertyMappingSerializer): + """LDAP PropertyMapping Serializer""" + + class Meta: + model = LDAPPropertyMapping + fields = PropertyMappingSerializer.Meta.fields + [ + "object_field", + ] + + +class LDAPPropertyMappingFilter(FilterSet): + """Filter for LDAPPropertyMapping""" + + managed = extend_schema_field(OpenApiTypes.STR)(AllValuesMultipleFilter(field_name="managed")) + + class Meta: + model = LDAPPropertyMapping + fields = "__all__" + + +class LDAPPropertyMappingViewSet(UsedByMixin, ModelViewSet): + """LDAP PropertyMapping Viewset""" + + queryset = LDAPPropertyMapping.objects.all() + serializer_class = LDAPPropertyMappingSerializer + filterset_class = LDAPPropertyMappingFilter + search_fields = ["name"] + ordering = ["name"] diff --git a/authentik/sources/ldap/api/source_connections.py b/authentik/sources/ldap/api/source_connections.py new file mode 100644 index 000000000..d0b7e88f1 --- /dev/null +++ b/authentik/sources/ldap/api/source_connections.py @@ -0,0 +1,32 @@ +"""LDAP Source Serializer""" +from django_filters.rest_framework import DjangoFilterBackend +from rest_framework.filters import OrderingFilter, SearchFilter +from rest_framework.viewsets import ModelViewSet + +from authentik.api.authorization import OwnerFilter, OwnerSuperuserPermissions +from authentik.core.api.sources import UserSourceConnectionSerializer +from authentik.core.api.used_by import UsedByMixin +from authentik.sources.ldap.models import LDAPUserSourceConnection + + +class LDAPUserSourceConnectionSerializer(UserSourceConnectionSerializer): + """LDAP Source Serializer""" + + class Meta: + model = LDAPUserSourceConnection + fields = ["pk", "user", "source", "unique_identifier"] + extra_kwargs = { + "access_token": {"write_only": True}, + } + + +class LDAPUserSourceConnectionViewSet(UsedByMixin, ModelViewSet): + """Source Viewset""" + + queryset = LDAPUserSourceConnection.objects.all() + serializer_class = LDAPUserSourceConnectionSerializer + filterset_fields = ["source__slug"] + search_fields = ["source__slug"] + permission_classes = [OwnerSuperuserPermissions] + filter_backends = [OwnerFilter, DjangoFilterBackend, OrderingFilter, SearchFilter] + ordering = ["source__slug"] diff --git a/authentik/sources/ldap/api.py b/authentik/sources/ldap/api/sources.py similarity index 80% rename from authentik/sources/ldap/api.py rename to authentik/sources/ldap/api/sources.py index 1914c66da..b0097b2f7 100644 --- a/authentik/sources/ldap/api.py +++ b/authentik/sources/ldap/api/sources.py @@ -1,10 +1,7 @@ """Source API Views""" from typing import Any -from django_filters.filters import AllValuesMultipleFilter -from django_filters.filterset import FilterSet -from drf_spectacular.types import OpenApiTypes -from drf_spectacular.utils import extend_schema, extend_schema_field, inline_serializer +from drf_spectacular.utils import extend_schema, inline_serializer from rest_framework.decorators import action from rest_framework.exceptions import ValidationError from rest_framework.fields import DictField, ListField @@ -14,12 +11,11 @@ from rest_framework.response import Response from rest_framework.viewsets import ModelViewSet from authentik.admin.api.tasks import TaskSerializer -from authentik.core.api.propertymappings import PropertyMappingSerializer from authentik.core.api.sources import SourceSerializer from authentik.core.api.used_by import UsedByMixin from authentik.crypto.models import CertificateKeyPair from authentik.events.monitored_tasks import TaskInfo -from authentik.sources.ldap.models import LDAPPropertyMapping, LDAPSource +from authentik.sources.ldap.models import LDAPSource from authentik.sources.ldap.tasks import SYNC_CLASSES @@ -155,32 +151,3 @@ class LDAPSourceViewSet(UsedByMixin, ModelViewSet): all_objects[class_name].append(obj) return Response(data=all_objects) - -class LDAPPropertyMappingSerializer(PropertyMappingSerializer): - """LDAP PropertyMapping Serializer""" - - class Meta: - model = LDAPPropertyMapping - fields = PropertyMappingSerializer.Meta.fields + [ - "object_field", - ] - - -class LDAPPropertyMappingFilter(FilterSet): - """Filter for LDAPPropertyMapping""" - - managed = extend_schema_field(OpenApiTypes.STR)(AllValuesMultipleFilter(field_name="managed")) - - class Meta: - model = LDAPPropertyMapping - fields = "__all__" - - -class LDAPPropertyMappingViewSet(UsedByMixin, ModelViewSet): - """LDAP PropertyMapping Viewset""" - - queryset = LDAPPropertyMapping.objects.all() - serializer_class = LDAPPropertyMappingSerializer - filterset_class = LDAPPropertyMappingFilter - search_fields = ["name"] - ordering = ["name"] diff --git a/authentik/sources/ldap/models.py b/authentik/sources/ldap/models.py index ac7f32aca..9cf0facfd 100644 --- a/authentik/sources/ldap/models.py +++ b/authentik/sources/ldap/models.py @@ -10,7 +10,7 @@ from ldap3 import ALL, NONE, RANDOM, Connection, Server, ServerPool, Tls from ldap3.core.exceptions import LDAPInsufficientAccessRightsResult, LDAPSchemaError from rest_framework.serializers import Serializer -from authentik.core.models import Group, PropertyMapping, Source +from authentik.core.models import Group, PropertyMapping, Source, UserSourceConnection from authentik.crypto.models import CertificateKeyPair from authentik.lib.config import CONFIG from authentik.lib.models import DomainlessURLValidator @@ -212,3 +212,21 @@ class LDAPPropertyMapping(PropertyMapping): class Meta: verbose_name = _("LDAP Property Mapping") verbose_name_plural = _("LDAP Property Mappings") + + +class LDAPUserSourceConnection(UserSourceConnection): + """Connection between an authentik user and an LDAP source.""" + + unique_identifier = models.TextField(unique=True) + + @property + def serializer(self) -> Serializer: + from authentik.sources.ldap.api.source_connections import ( + LDAPUserSourceConnectionSerializer, + ) + + return LDAPUserSourceConnectionSerializer + + class Meta: + verbose_name = _("LDAP User Source Connection") + verbose_name_plural = _("LDAP User Source Connections") diff --git a/authentik/sources/ldap/urls.py b/authentik/sources/ldap/urls.py index 30390f332..8e18bf11f 100644 --- a/authentik/sources/ldap/urls.py +++ b/authentik/sources/ldap/urls.py @@ -1,7 +1,10 @@ """API URLs""" -from authentik.sources.ldap.api import LDAPPropertyMappingViewSet, LDAPSourceViewSet +from authentik.sources.ldap.api.sources import LDAPSourceViewSet +from authentik.sources.ldap.api.property_mappings import LDAPPropertyMappingViewSet +from authentik.sources.ldap.api.source_connections import LDAPUserSourceConnectionViewSet api_urlpatterns = [ ("propertymappings/ldap", LDAPPropertyMappingViewSet), + ("sources/user_connections/ldap", LDAPUserSourceConnectionViewSet), ("sources/ldap", LDAPSourceViewSet), ]