Compare commits
	
		
			3 Commits
		
	
	
		
			version/20
			...
			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 typing import Any, Optional | ||||||
| 
 | 
 | ||||||
| from django.core.cache import cache | from django.core.cache import cache | ||||||
| from django_filters.filters import AllValuesMultipleFilter | from drf_spectacular.utils import extend_schema, inline_serializer | ||||||
| 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 rest_framework.decorators import action | from rest_framework.decorators import action | ||||||
| from rest_framework.exceptions import ValidationError | from rest_framework.exceptions import ValidationError | ||||||
| from rest_framework.fields import BooleanField, DictField, ListField, SerializerMethodField | 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 rest_framework.viewsets import ModelViewSet | ||||||
| 
 | 
 | ||||||
| from authentik.admin.api.tasks import TaskSerializer | 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.sources import SourceSerializer | ||||||
| from authentik.core.api.used_by import UsedByMixin | from authentik.core.api.used_by import UsedByMixin | ||||||
| from authentik.core.api.utils import PassiveSerializer | from authentik.core.api.utils import PassiveSerializer | ||||||
| from authentik.crypto.models import CertificateKeyPair | from authentik.crypto.models import CertificateKeyPair | ||||||
| from authentik.events.monitored_tasks import TaskInfo | 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 CACHE_KEY_STATUS, SYNC_CLASSES | from authentik.sources.ldap.tasks import CACHE_KEY_STATUS, SYNC_CLASSES, ldap_sync_single | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class LDAPSourceSerializer(SourceSerializer): | class LDAPSourceSerializer(SourceSerializer): | ||||||
| @ -59,6 +55,20 @@ class LDAPSourceSerializer(SourceSerializer): | |||||||
|                 ) |                 ) | ||||||
|         return super().validate(attrs) |         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: |     class Meta: | ||||||
|         model = LDAPSource |         model = LDAPSource | ||||||
|         fields = SourceSerializer.Meta.fields + [ |         fields = SourceSerializer.Meta.fields + [ | ||||||
| @ -128,14 +138,18 @@ class LDAPSourceViewSet(UsedByMixin, ModelViewSet): | |||||||
|     ordering = ["name"] |     ordering = ["name"] | ||||||
| 
 | 
 | ||||||
|     @extend_schema( |     @extend_schema( | ||||||
|  |         request=None, | ||||||
|         responses={ |         responses={ | ||||||
|             200: LDAPSyncStatusSerializer(), |             200: LDAPSyncStatusSerializer(), | ||||||
|         } |         }, | ||||||
|     ) |     ) | ||||||
|     @action(methods=["GET"], detail=True, pagination_class=None, filter_backends=[]) |     @action(methods=["GET", "POST"], detail=True, pagination_class=None, filter_backends=[]) | ||||||
|     def sync_status(self, request: Request, slug: str) -> Response: |     def sync(self, request: Request, slug: str) -> Response: | ||||||
|         """Get source's sync status""" |         """Get source's sync status or start source sync""" | ||||||
|         source: LDAPSource = self.get_object() |         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 [] |         tasks = TaskInfo.by_name(f"ldap_sync:{source.slug}:*") or [] | ||||||
|         status = { |         status = { | ||||||
|             "tasks": tasks, |             "tasks": tasks, | ||||||
| @ -169,33 +183,3 @@ class LDAPSourceViewSet(UsedByMixin, ModelViewSet): | |||||||
|                 obj.pop("raw_dn", None) |                 obj.pop("raw_dn", None) | ||||||
|                 all_objects[class_name].append(obj) |                 all_objects[class_name].append(obj) | ||||||
|         return Response(data=all_objects) |         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 |     @property | ||||||
|     def serializer(self) -> type[Serializer]: |     def serializer(self) -> type[Serializer]: | ||||||
|         from authentik.sources.ldap.api import LDAPSourceSerializer |         from authentik.sources.ldap.api.sources import LDAPSourceSerializer | ||||||
|  |  | ||||||
|         return LDAPSourceSerializer |         return LDAPSourceSerializer | ||||||
|  |  | ||||||
| @ -253,7 +253,7 @@ class LDAPPropertyMapping(PropertyMapping): | |||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def serializer(self) -> type[Serializer]: |     def serializer(self) -> type[Serializer]: | ||||||
|         from authentik.sources.ldap.api import LDAPPropertyMappingSerializer |         from authentik.sources.ldap.api.property_mappings import LDAPPropertyMappingSerializer | ||||||
|  |  | ||||||
|         return 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.flows.planner import PLAN_CONTEXT_PENDING_USER | ||||||
| from authentik.sources.ldap.models import LDAPSource | from authentik.sources.ldap.models import LDAPSource | ||||||
| from authentik.sources.ldap.password import LDAPPasswordChanger | 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 | from authentik.stages.prompt.signals import password_validate | ||||||
|  |  | ||||||
| LOGGER = get_logger() | LOGGER = get_logger() | ||||||
|  |  | ||||||
|  |  | ||||||
| @receiver(post_save, sender=LDAPSource) | @receiver(post_save, sender=LDAPSource) | ||||||
| def sync_ldap_source_on_save(sender, instance: LDAPSource, **_): | def check_ldap_source_on_save(sender, instance: LDAPSource, **_): | ||||||
|     """Ensure that source is synced on save (if enabled)""" |     """Check LDAP source's connectivity on save (if enabled)""" | ||||||
|     if not instance.enabled: |     if not instance.enabled: | ||||||
|         return |         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) |     ldap_connectivity_check.delay(instance.pk) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | |||||||
| @ -1,5 +1,6 @@ | |||||||
| """API URLs""" | """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 = [ | api_urlpatterns = [ | ||||||
|     ("propertymappings/ldap", LDAPPropertyMappingViewSet), |     ("propertymappings/ldap", LDAPPropertyMappingViewSet), | ||||||
|  | |||||||
							
								
								
									
										39
									
								
								schema.yml
									
									
									
									
									
								
							
							
						
						
									
										39
									
								
								schema.yml
									
									
									
									
									
								
							| @ -18940,10 +18940,43 @@ paths: | |||||||
|               schema: |               schema: | ||||||
|                 $ref: '#/components/schemas/GenericError' |                 $ref: '#/components/schemas/GenericError' | ||||||
|           description: '' |           description: '' | ||||||
|   /sources/ldap/{slug}/sync_status/: |   /sources/ldap/{slug}/sync/: | ||||||
|     get: |     get: | ||||||
|       operationId: sources_ldap_sync_status_retrieve |       operationId: sources_ldap_sync_retrieve | ||||||
|       description: Get source's sync status |       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: |       parameters: | ||||||
|       - in: path |       - in: path | ||||||
|         name: slug |         name: slug | ||||||
|  | |||||||
| @ -100,7 +100,7 @@ export class LDAPSourceViewPage extends AKElement { | |||||||
|  |  | ||||||
|     load(): void { |     load(): void { | ||||||
|         new SourcesApi(DEFAULT_CONFIG) |         new SourcesApi(DEFAULT_CONFIG) | ||||||
|             .sourcesLdapSyncStatusRetrieve({ |             .sourcesLdapSyncRetrieve({ | ||||||
|                 slug: this.source.slug, |                 slug: this.source.slug, | ||||||
|             }) |             }) | ||||||
|             .then((state) => { |             .then((state) => { | ||||||
| @ -198,9 +198,8 @@ export class LDAPSourceViewPage extends AKElement { | |||||||
|                                 ?disabled=${this.syncState?.isRunning} |                                 ?disabled=${this.syncState?.isRunning} | ||||||
|                                 .apiRequest=${() => { |                                 .apiRequest=${() => { | ||||||
|                                     return new SourcesApi(DEFAULT_CONFIG) |                                     return new SourcesApi(DEFAULT_CONFIG) | ||||||
|                                         .sourcesLdapPartialUpdate({ |                                         .sourcesLdapSyncCreate({ | ||||||
|                                             slug: this.source?.slug || "", |                                             slug: this.source?.slug || "", | ||||||
|                                             patchedLDAPSourceRequest: this.source, |  | ||||||
|                                         }) |                                         }) | ||||||
|                                         .then(() => { |                                         .then(() => { | ||||||
|                                             this.dispatchEvent( |                                             this.dispatchEvent( | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	