diff --git a/authentik/core/api/sources.py b/authentik/core/api/sources.py index 3ab5f10a9..40815228c 100644 --- a/authentik/core/api/sources.py +++ b/authentik/core/api/sources.py @@ -13,7 +13,7 @@ class SourceSerializer(ModelSerializer, MetaNameSerializer): def get_object_type(self, obj): """Get object type so that we know which API Endpoint to use to get the full object""" - return obj._meta.object_name.lower().replace("provider", "") + return obj._meta.object_name.lower().replace("source", "") class Meta: diff --git a/authentik/sources/ldap/api.py b/authentik/sources/ldap/api.py index c22de70e2..c79e87652 100644 --- a/authentik/sources/ldap/api.py +++ b/authentik/sources/ldap/api.py @@ -1,5 +1,12 @@ """Source API Views""" -from rest_framework.serializers import ModelSerializer +from django.core.cache import cache +from django.db.models.base import Model +from drf_yasg2.utils import swagger_auto_schema +from rest_framework.decorators import action +from rest_framework.fields import DateTimeField +from rest_framework.request import Request +from rest_framework.response import Response +from rest_framework.serializers import ModelSerializer, Serializer from rest_framework.viewsets import ModelViewSet from authentik.core.api.sources import SourceSerializer @@ -34,6 +41,33 @@ class LDAPSourceSerializer(SourceSerializer): extra_kwargs = {"bind_password": {"write_only": True}} +class LDAPSourceSyncStatusSerializer(Serializer): + """LDAP Sync status""" + + last_sync = DateTimeField(read_only=True) + + def create(self, validated_data: dict) -> Model: + raise NotImplementedError + + def update(self, instance: Model, validated_data: dict) -> Model: + raise NotImplementedError + + +class LDAPSourceViewSet(ModelViewSet): + """LDAP Source Viewset""" + + queryset = LDAPSource.objects.all() + serializer_class = LDAPSourceSerializer + + @swagger_auto_schema(responses={200: LDAPSourceSyncStatusSerializer(many=False)}) + @action(methods=["GET"], detail=True) + # pylint: disable=invalid-name + def sync_status(self, request: Request, pk: int) -> Response: + source = self.get_object() + last_sync = cache.get(source.state_cache_prefix("last_sync"), None) + return Response(LDAPSourceSyncStatusSerializer({"last_sync": last_sync}).data) + + class LDAPPropertyMappingSerializer(ModelSerializer, MetaNameSerializer): """LDAP PropertyMapping Serializer""" @@ -49,13 +83,6 @@ class LDAPPropertyMappingSerializer(ModelSerializer, MetaNameSerializer): ] -class LDAPSourceViewSet(ModelViewSet): - """LDAP Source Viewset""" - - queryset = LDAPSource.objects.all() - serializer_class = LDAPSourceSerializer - - class LDAPPropertyMappingViewSet(ModelViewSet): """LDAP PropertyMapping Viewset""" diff --git a/swagger.yaml b/swagger.yaml index 1bfcdb6ec..1056c8348 100755 --- a/swagger.yaml +++ b/swagger.yaml @@ -4977,6 +4977,25 @@ paths: required: true type: string format: uuid + /sources/ldap/{pbm_uuid}/sync_status/: + get: + operationId: sources_ldap_sync_status + description: LDAP Source Viewset + parameters: [] + responses: + '200': + description: LDAP Sync status + schema: + $ref: '#/definitions/LDAPSourceSyncStatus' + tags: + - sources + parameters: + - name: pbm_uuid + in: path + description: A UUID string identifying this LDAP Source. + required: true + type: string + format: uuid /sources/oauth/: get: operationId: sources_oauth_list @@ -9504,6 +9523,15 @@ definitions: type: string format: uuid uniqueItems: true + LDAPSourceSyncStatus: + description: LDAP Sync status + type: object + properties: + last_sync: + title: Last sync + type: string + format: date-time + readOnly: true OAuthSource: description: OAuth Source Serializer required: