Compare commits
	
		
			3 Commits
		
	
	
		
			build-scri
			...
			sources/ld
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 3835734ed4 | |||
| e0355b13cd | |||
| d092093e94 | 
							
								
								
									
										40
									
								
								authentik/sources/ldap/api/property_mappings.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								authentik/sources/ldap/api/property_mappings.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,40 @@ | ||||
| """Source 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"] | ||||
| @ -2,10 +2,7 @@ | ||||
| from typing import Any, Optional | ||||
| 
 | ||||
| from django.core.cache import cache | ||||
| 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 BooleanField, DictField, ListField, SerializerMethodField | ||||
| @ -15,14 +12,13 @@ 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.core.api.utils import PassiveSerializer | ||||
| 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.tasks import CACHE_KEY_STATUS, SYNC_CLASSES | ||||
| from authentik.sources.ldap.models import LDAPSource | ||||
| from authentik.sources.ldap.tasks import CACHE_KEY_STATUS, SYNC_CLASSES, ldap_sync_single | ||||
| 
 | ||||
| 
 | ||||
| class LDAPSourceSerializer(SourceSerializer): | ||||
| @ -59,6 +55,20 @@ class LDAPSourceSerializer(SourceSerializer): | ||||
|                 ) | ||||
|         return super().validate(attrs) | ||||
| 
 | ||||
|     def create(self, validated_data) -> LDAPSource: | ||||
|         # Create both creates the actual model and assigns m2m fields | ||||
|         instance: LDAPSource = super().create(validated_data) | ||||
|         if not instance.enabled: | ||||
|             return instance | ||||
|         # Don't sync sources when they don't have any property mappings. This will only happen if: | ||||
|         # - the user forgets to set them or | ||||
|         # - the source is newly created, this is the first save event | ||||
|         #   and the mappings are created with an m2m event | ||||
|         if not instance.property_mappings.exists() or not instance.property_mappings_group.exists(): | ||||
|             return instance | ||||
|         ldap_sync_single.delay(instance.pk) | ||||
|         return instance | ||||
| 
 | ||||
|     class Meta: | ||||
|         model = LDAPSource | ||||
|         fields = SourceSerializer.Meta.fields + [ | ||||
| @ -128,14 +138,18 @@ class LDAPSourceViewSet(UsedByMixin, ModelViewSet): | ||||
|     ordering = ["name"] | ||||
| 
 | ||||
|     @extend_schema( | ||||
|         request=None, | ||||
|         responses={ | ||||
|             200: LDAPSyncStatusSerializer(), | ||||
|         } | ||||
|         }, | ||||
|     ) | ||||
|     @action(methods=["GET"], detail=True, pagination_class=None, filter_backends=[]) | ||||
|     def sync_status(self, request: Request, slug: str) -> Response: | ||||
|         """Get source's sync status""" | ||||
|         source: LDAPSource = self.get_object() | ||||
|     @action(methods=["GET", "POST"], detail=True, pagination_class=None, filter_backends=[]) | ||||
|     def sync(self, request: Request, slug: str) -> Response: | ||||
|         """Get source's sync status or start source sync""" | ||||
|         source = self.get_object() | ||||
|         if request.method == "POST": | ||||
|             # We're not waiting for the sync to finish here as it could take multiple hours | ||||
|             ldap_sync_single.delay(source.pk) | ||||
|         tasks = TaskInfo.by_name(f"ldap_sync:{source.slug}:*") or [] | ||||
|         status = { | ||||
|             "tasks": tasks, | ||||
| @ -169,33 +183,3 @@ class LDAPSourceViewSet(UsedByMixin, ModelViewSet): | ||||
|                 obj.pop("raw_dn", None) | ||||
|                 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"] | ||||
| @ -115,7 +115,7 @@ class LDAPSource(Source): | ||||
|  | ||||
|     @property | ||||
|     def serializer(self) -> type[Serializer]: | ||||
|         from authentik.sources.ldap.api import LDAPSourceSerializer | ||||
|         from authentik.sources.ldap.api.sources import LDAPSourceSerializer | ||||
|  | ||||
|         return LDAPSourceSerializer | ||||
|  | ||||
| @ -253,7 +253,7 @@ class LDAPPropertyMapping(PropertyMapping): | ||||
|  | ||||
|     @property | ||||
|     def serializer(self) -> type[Serializer]: | ||||
|         from authentik.sources.ldap.api import LDAPPropertyMappingSerializer | ||||
|         from authentik.sources.ldap.api.property_mappings import LDAPPropertyMappingSerializer | ||||
|  | ||||
|         return LDAPPropertyMappingSerializer | ||||
|  | ||||
|  | ||||
| @ -14,24 +14,17 @@ from authentik.events.models import Event, EventAction | ||||
| from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER | ||||
| from authentik.sources.ldap.models import LDAPSource | ||||
| from authentik.sources.ldap.password import LDAPPasswordChanger | ||||
| from authentik.sources.ldap.tasks import ldap_connectivity_check, ldap_sync_single | ||||
| from authentik.sources.ldap.tasks import ldap_connectivity_check | ||||
| from authentik.stages.prompt.signals import password_validate | ||||
|  | ||||
| LOGGER = get_logger() | ||||
|  | ||||
|  | ||||
| @receiver(post_save, sender=LDAPSource) | ||||
| def sync_ldap_source_on_save(sender, instance: LDAPSource, **_): | ||||
|     """Ensure that source is synced on save (if enabled)""" | ||||
| def check_ldap_source_on_save(sender, instance: LDAPSource, **_): | ||||
|     """Check LDAP source's connectivity on save (if enabled)""" | ||||
|     if not instance.enabled: | ||||
|         return | ||||
|     # Don't sync sources when they don't have any property mappings. This will only happen if: | ||||
|     # - the user forgets to set them or | ||||
|     # - the source is newly created, this is the first save event | ||||
|     #   and the mappings are created with an m2m event | ||||
|     if not instance.property_mappings.exists() or not instance.property_mappings_group.exists(): | ||||
|         return | ||||
|     ldap_sync_single.delay(instance.pk) | ||||
|     ldap_connectivity_check.delay(instance.pk) | ||||
|  | ||||
|  | ||||
|  | ||||
| @ -1,5 +1,6 @@ | ||||
| """API URLs""" | ||||
| from authentik.sources.ldap.api import LDAPPropertyMappingViewSet, LDAPSourceViewSet | ||||
| from authentik.sources.ldap.api.property_mappings import LDAPPropertyMappingViewSet | ||||
| from authentik.sources.ldap.api.sources import LDAPSourceViewSet | ||||
|  | ||||
| api_urlpatterns = [ | ||||
|     ("propertymappings/ldap", LDAPPropertyMappingViewSet), | ||||
|  | ||||
							
								
								
									
										39
									
								
								schema.yml
									
									
									
									
									
								
							
							
						
						
									
										39
									
								
								schema.yml
									
									
									
									
									
								
							| @ -18940,10 +18940,43 @@ paths: | ||||
|               schema: | ||||
|                 $ref: '#/components/schemas/GenericError' | ||||
|           description: '' | ||||
|   /sources/ldap/{slug}/sync_status/: | ||||
|   /sources/ldap/{slug}/sync/: | ||||
|     get: | ||||
|       operationId: sources_ldap_sync_status_retrieve | ||||
|       description: Get source's sync status | ||||
|       operationId: sources_ldap_sync_retrieve | ||||
|       description: Get source's sync status or start source sync | ||||
|       parameters: | ||||
|       - in: path | ||||
|         name: slug | ||||
|         schema: | ||||
|           type: string | ||||
|           description: Internal source name, used in URLs. | ||||
|         required: true | ||||
|       tags: | ||||
|       - sources | ||||
|       security: | ||||
|       - authentik: [] | ||||
|       responses: | ||||
|         '200': | ||||
|           content: | ||||
|             application/json: | ||||
|               schema: | ||||
|                 $ref: '#/components/schemas/LDAPSyncStatus' | ||||
|           description: '' | ||||
|         '400': | ||||
|           content: | ||||
|             application/json: | ||||
|               schema: | ||||
|                 $ref: '#/components/schemas/ValidationError' | ||||
|           description: '' | ||||
|         '403': | ||||
|           content: | ||||
|             application/json: | ||||
|               schema: | ||||
|                 $ref: '#/components/schemas/GenericError' | ||||
|           description: '' | ||||
|     post: | ||||
|       operationId: sources_ldap_sync_create | ||||
|       description: Get source's sync status or start source sync | ||||
|       parameters: | ||||
|       - in: path | ||||
|         name: slug | ||||
|  | ||||
| @ -100,7 +100,7 @@ export class LDAPSourceViewPage extends AKElement { | ||||
|  | ||||
|     load(): void { | ||||
|         new SourcesApi(DEFAULT_CONFIG) | ||||
|             .sourcesLdapSyncStatusRetrieve({ | ||||
|             .sourcesLdapSyncRetrieve({ | ||||
|                 slug: this.source.slug, | ||||
|             }) | ||||
|             .then((state) => { | ||||
| @ -198,9 +198,8 @@ export class LDAPSourceViewPage extends AKElement { | ||||
|                                 ?disabled=${this.syncState?.isRunning} | ||||
|                                 .apiRequest=${() => { | ||||
|                                     return new SourcesApi(DEFAULT_CONFIG) | ||||
|                                         .sourcesLdapPartialUpdate({ | ||||
|                                         .sourcesLdapSyncCreate({ | ||||
|                                             slug: this.source?.slug || "", | ||||
|                                             patchedLDAPSourceRequest: this.source, | ||||
|                                         }) | ||||
|                                         .then(() => { | ||||
|                                             this.dispatchEvent( | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	