providers/saml: fix invalid SAML provider metadata, add schema tests

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
Jens Langhammer 2023-02-10 12:32:18 +01:00
parent 99bb4c2cf8
commit 8de92943ab
No known key found for this signature in database
2 changed files with 37 additions and 2 deletions

View File

@ -65,7 +65,7 @@ class MetadataProcessor:
element.text = name_id_format element.text = name_id_format
yield element yield element
def get_bindings(self) -> Iterator[Element]: def get_sso_bindings(self) -> Iterator[Element]:
"""Get all Bindings supported""" """Get all Bindings supported"""
binding_url_map = { binding_url_map = {
(SAML_BINDING_REDIRECT, "SingleSignOnService"): self.http_request.build_absolute_uri( (SAML_BINDING_REDIRECT, "SingleSignOnService"): self.http_request.build_absolute_uri(
@ -80,6 +80,19 @@ class MetadataProcessor:
kwargs={"application_slug": self.provider.application.slug}, kwargs={"application_slug": self.provider.application.slug},
) )
), ),
}
for binding_svc, url in binding_url_map.items():
binding, svc = binding_svc
if self.force_binding and self.force_binding != binding:
continue
element = Element(f"{{{NS_SAML_METADATA}}}{svc}")
element.attrib["Binding"] = binding
element.attrib["Location"] = url
yield element
def get_slo_bindings(self) -> Iterator[Element]:
"""Get all Bindings supported"""
binding_url_map = {
(SAML_BINDING_REDIRECT, "SingleLogoutService"): self.http_request.build_absolute_uri( (SAML_BINDING_REDIRECT, "SingleLogoutService"): self.http_request.build_absolute_uri(
reverse( reverse(
"authentik_providers_saml:slo-redirect", "authentik_providers_saml:slo-redirect",
@ -163,10 +176,13 @@ class MetadataProcessor:
if signing_descriptor is not None: if signing_descriptor is not None:
idp_sso_descriptor.append(signing_descriptor) idp_sso_descriptor.append(signing_descriptor)
for binding in self.get_slo_bindings():
idp_sso_descriptor.append(binding)
for name_id_format in self.get_name_id_formats(): for name_id_format in self.get_name_id_formats():
idp_sso_descriptor.append(name_id_format) idp_sso_descriptor.append(name_id_format)
for binding in self.get_bindings(): for binding in self.get_sso_bindings():
idp_sso_descriptor.append(binding) idp_sso_descriptor.append(binding)
if self.provider.signing_kp: if self.provider.signing_kp:

View File

@ -4,10 +4,12 @@ from pathlib import Path
import xmlsec import xmlsec
from defusedxml.lxml import fromstring from defusedxml.lxml import fromstring
from django.test import RequestFactory, TestCase from django.test import RequestFactory, TestCase
from lxml import etree # nosec
from authentik.core.models import Application from authentik.core.models import Application
from authentik.core.tests.utils import create_test_cert, create_test_flow from authentik.core.tests.utils import create_test_cert, create_test_flow
from authentik.lib.generators import generate_id from authentik.lib.generators import generate_id
from authentik.lib.xml import lxml_from_string
from authentik.providers.saml.models import SAMLBindings, SAMLPropertyMapping, SAMLProvider from authentik.providers.saml.models import SAMLBindings, SAMLPropertyMapping, SAMLProvider
from authentik.providers.saml.processors.metadata import MetadataProcessor from authentik.providers.saml.processors.metadata import MetadataProcessor
from authentik.providers.saml.processors.metadata_parser import ServiceProviderMetadataParser from authentik.providers.saml.processors.metadata_parser import ServiceProviderMetadataParser
@ -43,6 +45,23 @@ class TestServiceProviderMetadataParser(TestCase):
metadata_b = MetadataProcessor(provider, request).build_entity_descriptor() metadata_b = MetadataProcessor(provider, request).build_entity_descriptor()
self.assertEqual(metadata_a, metadata_b) self.assertEqual(metadata_a, metadata_b)
def test_schema(self):
"""Test that metadata generation is consistent"""
provider = SAMLProvider.objects.create(
name=generate_id(),
authorization_flow=self.flow,
)
Application.objects.create(
name=generate_id(),
slug=generate_id(),
provider=provider,
)
request = self.factory.get("/")
metadata = lxml_from_string(MetadataProcessor(provider, request).build_entity_descriptor())
schema = etree.XMLSchema(etree.parse("xml/saml-schema-metadata-2.0.xsd")) # nosec
self.assertTrue(schema.validate(metadata))
def test_simple(self): def test_simple(self):
"""Test simple metadata without Signing""" """Test simple metadata without Signing"""
metadata = ServiceProviderMetadataParser().parse(load_fixture("fixtures/simple.xml")) metadata = ServiceProviderMetadataParser().parse(load_fixture("fixtures/simple.xml"))