Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 26 additions & 3 deletions authentik/providers/saml/api/providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from rest_framework.fields import CharField, FileField, SerializerMethodField
from rest_framework.parsers import MultiPartParser
from rest_framework.permissions import AllowAny
from rest_framework.renderers import BaseRenderer, JSONRenderer
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.serializers import PrimaryKeyRelatedField, ValidationError
Expand All @@ -38,6 +39,16 @@
LOGGER = get_logger()


class RawXMLDataRenderer(BaseRenderer):
"""Renderer to allow application/xml as value for 'Accept' in the metadata endpoint."""

media_type = "application/xml"
format = "xml"

def render(self, data, accepted_media_type=None, renderer_context=None):
return data


class SAMLProviderSerializer(ProviderSerializer):
"""SAMLProvider Serializer"""

Expand Down Expand Up @@ -238,9 +249,21 @@ class SAMLProviderViewSet(UsedByMixin, ModelViewSet):
],
description="Optionally force the metadata to only include one binding.",
),
# Explicitly excluded, because otherwise spectacular automatically
# add it when using multiple renderer_classes
OpenApiParameter(
name="format",
exclude=True,
required=False,
),
],
)
@action(methods=["GET"], detail=True, permission_classes=[AllowAny])
@action(
methods=["GET"],
detail=True,
permission_classes=[AllowAny],
renderer_classes=[JSONRenderer, RawXMLDataRenderer],
)
def metadata(self, request: Request, pk: int) -> Response:
"""Return metadata as XML string"""
# We don't use self.get_object() on purpose as this view is un-authenticated
Expand All @@ -258,9 +281,9 @@ def metadata(self, request: Request, pk: int) -> Response:
f'attachment; filename="{provider.name}_authentik_meta.xml"'
)
return response
return Response({"metadata": metadata})
return Response({"metadata": metadata}, content_type="application/json")
except Provider.application.RelatedObjectDoesNotExist:
return Response({"metadata": ""})
return Response({"metadata": ""}, content_type="application/json")

@permission_required(
None,
Expand Down
21 changes: 21 additions & 0 deletions authentik/providers/saml/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,22 @@ def test_metadata_download(self):
)
self.assertEqual(200, response.status_code)
self.assertIn("Content-Disposition", response)
# Test download with Accept: application/xml
response = self.client.get(
reverse("authentik_api:samlprovider-metadata", kwargs={"pk": provider.pk})
+ "?download",
HTTP_ACCEPT="application/xml",
)
self.assertEqual(200, response.status_code)
self.assertIn("Content-Disposition", response)

response = self.client.get(
reverse("authentik_api:samlprovider-metadata", kwargs={"pk": provider.pk})
+ "?download",
HTTP_ACCEPT="application/xml;charset=UTF-8",
)
self.assertEqual(200, response.status_code)
self.assertIn("Content-Disposition", response)

def test_metadata_invalid(self):
"""Test metadata export (invalid)"""
Expand All @@ -121,6 +137,11 @@ def test_metadata_invalid(self):
reverse("authentik_api:samlprovider-metadata", kwargs={"pk": "abc"}),
)
self.assertEqual(404, response.status_code)
response = self.client.get(
reverse("authentik_api:samlprovider-metadata", kwargs={"pk": provider.pk}),
HTTP_ACCEPT="application/invalid-mime-type",
)
self.assertEqual(406, response.status_code)

def test_import_success(self):
"""Test metadata import (success case)"""
Expand Down
3 changes: 3 additions & 0 deletions schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22090,6 +22090,9 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/SAMLMetadata'
application/xml:
schema:
$ref: '#/components/schemas/SAMLMetadata'
description: ''
'404':
description: Provider has no application assigned
Expand Down