diff --git a/authentik/sources/scim/api.py b/authentik/sources/scim/api.py index 94edc6213..4a867fd84 100644 --- a/authentik/sources/scim/api.py +++ b/authentik/sources/scim/api.py @@ -1,7 +1,10 @@ """SCIMSource API Views""" +from django.urls import reverse_lazy +from rest_framework.fields import SerializerMethodField from rest_framework.viewsets import ModelViewSet from authentik.core.api.sources import SourceSerializer +from authentik.core.api.tokens import TokenSerializer from authentik.core.api.used_by import UsedByMixin from authentik.core.models import USER_ATTRIBUTE_SA, Token, TokenIntents, User from authentik.sources.scim.models import SCIMSource @@ -10,6 +13,19 @@ from authentik.sources.scim.models import SCIMSource class SCIMSourceSerializer(SourceSerializer): """SCIMSource Serializer""" + root_url = SerializerMethodField() + token_obj = TokenSerializer(source="token", required=False, read_only=True) + + def get_root_url(self, instance: SCIMSource) -> str: + """Get Root URL""" + relative_url = reverse_lazy( + "authentik_sources_scim:v2-root", + kwargs={"source_slug": instance.slug}, + ) + if "request" not in self.context: + return relative_url + return self.context["request"].build_absolute_uri(relative_url) + def create(self, validated_data): instance: SCIMSource = super().create(validated_data) identifier = f"ak-source-scim-{instance.pk}" @@ -32,7 +48,7 @@ class SCIMSourceSerializer(SourceSerializer): class Meta: model = SCIMSource - fields = SourceSerializer.Meta.fields + ["token"] + fields = SourceSerializer.Meta.fields + ["token", "root_url", "token_obj"] class SCIMSourceViewSet(UsedByMixin, ModelViewSet): diff --git a/authentik/sources/scim/urls.py b/authentik/sources/scim/urls.py index 0ca409ee6..380503d64 100644 --- a/authentik/sources/scim/urls.py +++ b/authentik/sources/scim/urls.py @@ -2,6 +2,7 @@ from django.urls import path from authentik.sources.scim.views.v2 import ( + base, groups, resource_types, schemas, @@ -10,6 +11,11 @@ from authentik.sources.scim.views.v2 import ( ) urlpatterns = [ + path( + "/v2", + base.SCIMRootView.as_view(), + name="v2-root", + ), path( "/v2/Users", users.UsersView.as_view(), diff --git a/authentik/sources/scim/views/v2/base.py b/authentik/sources/scim/views/v2/base.py index e9527f08a..c35d9fa56 100644 --- a/authentik/sources/scim/views/v2/base.py +++ b/authentik/sources/scim/views/v2/base.py @@ -2,6 +2,8 @@ from rest_framework.parsers import JSONParser from rest_framework.permissions import IsAuthenticated from rest_framework.renderers import JSONRenderer +from rest_framework.request import Request +from rest_framework.response import Response from rest_framework.views import APIView from authentik.sources.scim.views.v2.auth import SCIMTokenAuth @@ -28,3 +30,10 @@ class SCIMView(APIView): permission_classes = [IsAuthenticated] parser_classes = [SCIMParser] renderer_classes = [SCIMRenderer] + + +class SCIMRootView(SCIMView): + """Root SCIM View""" + + def dispatch(self, request: Request, *args, **kwargs) -> Response: + return Response({"message": "Use this base-URL with an SCIM-compatible system."})