providers/saml: fix invalid SAML provider metadata, add schema tests
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
parent
99bb4c2cf8
commit
8de92943ab
|
@ -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:
|
||||||
|
|
|
@ -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"))
|
||||||
|
|
Reference in New Issue