From d380194e13909b1dd0ee4c9c6aba41c1b8e88a0f Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Sun, 13 Dec 2020 19:37:47 +0100 Subject: [PATCH] */saml: test against SAML Schema --- Dockerfile | 1 + authentik/admin/tests/test_policy_binding.py | 1 + authentik/admin/tests/test_stage_bindings.py | 1 + authentik/flows/tests/test_views.py | 14 +- authentik/flows/transfer/exporter.py | 2 +- authentik/lib/tests/test_sentry.py | 8 +- authentik/providers/saml/tests/test_schema.py | 84 +++++ authentik/sources/saml/tests.py | 26 -- authentik/sources/saml/tests/__init__.py | 0 authentik/sources/saml/tests/test_metadata.py | 55 +++ xml/saml-schema-assertion-2.0.xsd | 283 +++++++++++++++ xml/saml-schema-metadata-2.0.xsd | 337 ++++++++++++++++++ xml/saml-schema-protocol-2.0.xsd | 302 ++++++++++++++++ xml/xenc-schema.xsd | 146 ++++++++ xml/xml.xsd | 287 +++++++++++++++ xml/xmldsig-core-schema.xsd | 318 +++++++++++++++++ 16 files changed, 1830 insertions(+), 35 deletions(-) create mode 100644 authentik/providers/saml/tests/test_schema.py delete mode 100644 authentik/sources/saml/tests.py create mode 100644 authentik/sources/saml/tests/__init__.py create mode 100644 authentik/sources/saml/tests/test_metadata.py create mode 100644 xml/saml-schema-assertion-2.0.xsd create mode 100644 xml/saml-schema-metadata-2.0.xsd create mode 100644 xml/saml-schema-protocol-2.0.xsd create mode 100644 xml/xenc-schema.xsd create mode 100644 xml/xml.xsd create mode 100644 xml/xmldsig-core-schema.xsd diff --git a/Dockerfile b/Dockerfile index d2d001c4..3be43603 100644 --- a/Dockerfile +++ b/Dockerfile @@ -38,6 +38,7 @@ RUN apt-get update && \ COPY ./authentik/ /authentik COPY ./pytest.ini / +COPY ./xml / COPY ./manage.py / COPY ./lifecycle/ /lifecycle diff --git a/authentik/admin/tests/test_policy_binding.py b/authentik/admin/tests/test_policy_binding.py index fc508d44..dcefeff2 100644 --- a/authentik/admin/tests/test_policy_binding.py +++ b/authentik/admin/tests/test_policy_binding.py @@ -1,5 +1,6 @@ """admin tests""" from uuid import uuid4 + from django.test import TestCase from django.test.client import RequestFactory diff --git a/authentik/admin/tests/test_stage_bindings.py b/authentik/admin/tests/test_stage_bindings.py index 0bf0b0b1..8895f99d 100644 --- a/authentik/admin/tests/test_stage_bindings.py +++ b/authentik/admin/tests/test_stage_bindings.py @@ -1,5 +1,6 @@ """admin tests""" from uuid import uuid4 + from django.test import TestCase from django.test.client import RequestFactory diff --git a/authentik/flows/tests/test_views.py b/authentik/flows/tests/test_views.py index 2de2366f..fb51d3ad 100644 --- a/authentik/flows/tests/test_views.py +++ b/authentik/flows/tests/test_views.py @@ -1,19 +1,19 @@ """flow views tests""" -from django.test.client import RequestFactory -from authentik.flows.stage import PLAN_CONTEXT_PENDING_USER_IDENTIFIER, StageView -from authentik.core.models import User from unittest.mock import MagicMock, PropertyMock, patch from django.http import HttpRequest, HttpResponse from django.shortcuts import reverse -from django.test import Client, TestCase +from django.test import TestCase +from django.test.client import RequestFactory from django.utils.encoding import force_str +from authentik.core.models import User from authentik.flows.exceptions import EmptyFlowException, FlowNonApplicableException from authentik.flows.markers import ReevaluateMarker, StageMarker from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding from authentik.flows.planner import FlowPlan, FlowPlanner -from authentik.flows.views import FlowExecutorView, NEXT_ARG_NAME, SESSION_KEY_PLAN +from authentik.flows.stage import PLAN_CONTEXT_PENDING_USER_IDENTIFIER, StageView +from authentik.flows.views import NEXT_ARG_NAME, SESSION_KEY_PLAN, FlowExecutorView from authentik.lib.config import CONFIG from authentik.policies.dummy.models import DummyPolicy from authentik.policies.http import AccessDeniedResponse @@ -451,7 +451,9 @@ class TestFlowExecutor(TestCase): ) request.user = user planner = FlowPlanner(flow) - plan = planner.plan(request, default_context={PLAN_CONTEXT_PENDING_USER_IDENTIFIER: ident}) + plan = planner.plan( + request, default_context={PLAN_CONTEXT_PENDING_USER_IDENTIFIER: ident} + ) executor = FlowExecutorView() executor.plan = plan diff --git a/authentik/flows/transfer/exporter.py b/authentik/flows/transfer/exporter.py index a7bb2432..87ecd0f6 100644 --- a/authentik/flows/transfer/exporter.py +++ b/authentik/flows/transfer/exporter.py @@ -11,7 +11,7 @@ from authentik.flows.transfer.common import ( FlowBundle, FlowBundleEntry, ) -from authentik.policies.models import Policy, PolicyBinding, PolicyBindingModel +from authentik.policies.models import Policy, PolicyBinding from authentik.stages.prompt.models import PromptStage diff --git a/authentik/lib/tests/test_sentry.py b/authentik/lib/tests/test_sentry.py index 0637c510..7ae15810 100644 --- a/authentik/lib/tests/test_sentry.py +++ b/authentik/lib/tests/test_sentry.py @@ -1,13 +1,17 @@ """test sentry integration""" from django.test import TestCase -from authentik.lib.sentry import before_send, SentryIgnoredException + +from authentik.lib.sentry import SentryIgnoredException, before_send + class TestSentry(TestCase): """test sentry integration""" def error_not_sent(self): """Test SentryIgnoredError not sent""" - self.assertIsNone(before_send(None, {"exc_info": (0, SentryIgnoredException(), 0)})) + self.assertIsNone( + before_send(None, {"exc_info": (0, SentryIgnoredException(), 0)}) + ) def error_sent(self): """Test error sent""" diff --git a/authentik/providers/saml/tests/test_schema.py b/authentik/providers/saml/tests/test_schema.py new file mode 100644 index 00000000..c4a1c074 --- /dev/null +++ b/authentik/providers/saml/tests/test_schema.py @@ -0,0 +1,84 @@ +"""Test Requests and Responses against schema""" +from base64 import b64encode + +from django.contrib.sessions.middleware import SessionMiddleware +from django.test import RequestFactory, TestCase +from guardian.utils import get_anonymous_user +from lxml import etree # nosec + +from authentik.crypto.models import CertificateKeyPair +from authentik.flows.models import Flow +from authentik.providers.saml.models import SAMLPropertyMapping, SAMLProvider +from authentik.providers.saml.processors.assertion import AssertionProcessor +from authentik.providers.saml.processors.request_parser import AuthNRequestParser +from authentik.providers.saml.tests.test_auth_n_request import dummy_get_response +from authentik.sources.saml.models import SAMLSource +from authentik.sources.saml.processors.request import RequestProcessor + + +class TestSchema(TestCase): + """Test Requests and Responses against schema""" + + def setUp(self): + cert = CertificateKeyPair.objects.first() + self.provider: SAMLProvider = SAMLProvider.objects.create( + authorization_flow=Flow.objects.get( + slug="default-provider-authorization-implicit-consent" + ), + acs_url="http://testserver/source/saml/provider/acs/", + signing_kp=cert, + verification_kp=cert, + ) + self.provider.property_mappings.set(SAMLPropertyMapping.objects.all()) + self.provider.save() + self.source = SAMLSource.objects.create( + slug="provider", + issuer="authentik", + signing_kp=cert, + ) + self.factory = RequestFactory() + + def test_request_schema(self): + """Test generated AuthNRequest against Schema""" + http_request = self.factory.get("/") + + middleware = SessionMiddleware(dummy_get_response) + middleware.process_request(http_request) + http_request.session.save() + + # First create an AuthNRequest + request_proc = RequestProcessor(self.source, http_request, "test_state") + request = request_proc.build_auth_n() + + metadata = etree.fromstring(request) # nosec + + schema = etree.XMLSchema( + etree.parse("xml/saml-schema-protocol-2.0.xsd") + ) # nosec + self.assertTrue(schema.validate(metadata)) + + def test_response_schema(self): + """Test generated AuthNRequest against Schema""" + http_request = self.factory.get("/") + http_request.user = get_anonymous_user() + + middleware = SessionMiddleware(dummy_get_response) + middleware.process_request(http_request) + http_request.session.save() + + # First create an AuthNRequest + request_proc = RequestProcessor(self.source, http_request, "test_state") + request = request_proc.build_auth_n() + + # To get an assertion we need a parsed request (parsed by provider) + parsed_request = AuthNRequestParser(self.provider).parse( + b64encode(request.encode()).decode(), "test_state" + ) + # Now create a response and convert it to string (provider) + response_proc = AssertionProcessor(self.provider, http_request, parsed_request) + response = response_proc.build_response() + + metadata = etree.fromstring(response) # nosec + + schema = etree.XMLSchema(etree.parse("xml/saml-schema-protocol-2.0.xsd")) + self.assertTrue(schema.validate(metadata)) diff --git a/authentik/sources/saml/tests.py b/authentik/sources/saml/tests.py deleted file mode 100644 index f01dc5fd..00000000 --- a/authentik/sources/saml/tests.py +++ /dev/null @@ -1,26 +0,0 @@ -"""SAML Source tests""" -from defusedxml import ElementTree -from django.test import RequestFactory, TestCase - -from authentik.crypto.models import CertificateKeyPair -from authentik.sources.saml.models import SAMLSource -from authentik.sources.saml.processors.metadata import MetadataProcessor - - -class TestMetadataProcessor(TestCase): - """Test MetadataProcessor""" - - def setUp(self): - self.source = SAMLSource.objects.create( - slug="provider", - issuer="authentik", - signing_kp=CertificateKeyPair.objects.first(), - ) - self.factory = RequestFactory() - - def test_metadata(self): - """Test Metadata generation being valid""" - request = self.factory.get("/") - xml = MetadataProcessor(self.source, request).build_entity_descriptor() - metadata = ElementTree.fromstring(xml) - self.assertEqual(metadata.attrib["entityID"], "authentik") diff --git a/authentik/sources/saml/tests/__init__.py b/authentik/sources/saml/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/authentik/sources/saml/tests/test_metadata.py b/authentik/sources/saml/tests/test_metadata.py new file mode 100644 index 00000000..105b0347 --- /dev/null +++ b/authentik/sources/saml/tests/test_metadata.py @@ -0,0 +1,55 @@ +"""SAML Source tests""" +from defusedxml import ElementTree +from django.test import RequestFactory, TestCase +from lxml import etree # nosec + +from authentik.crypto.models import CertificateKeyPair +from authentik.sources.saml.models import SAMLSource +from authentik.sources.saml.processors.metadata import MetadataProcessor + + +class TestMetadataProcessor(TestCase): + """Test MetadataProcessor""" + + def setUp(self): + self.factory = RequestFactory() + + def test_metadata_schema(self): + """Test Metadata generation being valid""" + source = SAMLSource.objects.create( + slug="provider", + issuer="authentik", + signing_kp=CertificateKeyPair.objects.first(), + ) + request = self.factory.get("/") + xml = MetadataProcessor(source, request).build_entity_descriptor() + metadata = etree.fromstring(xml) # nosec + + schema = etree.XMLSchema( + etree.parse("xml/saml-schema-metadata-2.0.xsd") + ) # nosec + self.assertTrue(schema.validate(metadata)) + + def test_metadata(self): + """Test Metadata generation being valid""" + source = SAMLSource.objects.create( + slug="provider", + issuer="authentik", + signing_kp=CertificateKeyPair.objects.first(), + ) + request = self.factory.get("/") + xml = MetadataProcessor(source, request).build_entity_descriptor() + metadata = ElementTree.fromstring(xml) + self.assertEqual(metadata.attrib["entityID"], "authentik") + + def test_metadata_without_signautre(self): + """Test Metadata generation being valid""" + source = SAMLSource.objects.create( + slug="provider", + issuer="authentik", + # signing_kp=CertificateKeyPair.objects.first(), + ) + request = self.factory.get("/") + xml = MetadataProcessor(source, request).build_entity_descriptor() + metadata = ElementTree.fromstring(xml) + self.assertEqual(metadata.attrib["entityID"], "authentik") diff --git a/xml/saml-schema-assertion-2.0.xsd b/xml/saml-schema-assertion-2.0.xsd new file mode 100644 index 00000000..478ddfa9 --- /dev/null +++ b/xml/saml-schema-assertion-2.0.xsd @@ -0,0 +1,283 @@ + + + + + + + Document identifier: saml-schema-assertion-2.0 + Location: http://docs.oasis-open.org/security/saml/v2.0/ + Revision history: + V1.0 (November, 2002): + Initial Standard Schema. + V1.1 (September, 2003): + Updates within the same V1.0 namespace. + V2.0 (March, 2005): + New assertion schema for SAML V2.0 namespace. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xml/saml-schema-metadata-2.0.xsd b/xml/saml-schema-metadata-2.0.xsd new file mode 100644 index 00000000..378dbd11 --- /dev/null +++ b/xml/saml-schema-metadata-2.0.xsd @@ -0,0 +1,337 @@ + + + + + + + + + Document identifier: saml-schema-metadata-2.0 + Location: http://docs.oasis-open.org/security/saml/v2.0/ + Revision history: + V2.0 (March, 2005): + Schema for SAML metadata, first published in SAML 2.0. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xml/saml-schema-protocol-2.0.xsd b/xml/saml-schema-protocol-2.0.xsd new file mode 100644 index 00000000..e6be1e4b --- /dev/null +++ b/xml/saml-schema-protocol-2.0.xsd @@ -0,0 +1,302 @@ + + + + + + + Document identifier: saml-schema-protocol-2.0 + Location: http://docs.oasis-open.org/security/saml/v2.0/ + Revision history: + V1.0 (November, 2002): + Initial Standard Schema. + V1.1 (September, 2003): + Updates within the same V1.0 namespace. + V2.0 (March, 2005): + New protocol schema based in a SAML V2.0 namespace. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xml/xenc-schema.xsd b/xml/xenc-schema.xsd new file mode 100644 index 00000000..85af68b5 --- /dev/null +++ b/xml/xenc-schema.xsd @@ -0,0 +1,146 @@ + + + + + + ]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xml/xml.xsd b/xml/xml.xsd new file mode 100644 index 00000000..aea7d0db --- /dev/null +++ b/xml/xml.xsd @@ -0,0 +1,287 @@ + + + + + + +
+

About the XML namespace

+ +
+

+ This schema document describes the XML namespace, in a form + suitable for import by other schema documents. +

+

+ See + http://www.w3.org/XML/1998/namespace.html and + + http://www.w3.org/TR/REC-xml for information + about this namespace. +

+

+ Note that local names in this namespace are intended to be + defined only by the World Wide Web Consortium or its subgroups. + The names currently defined in this namespace are listed below. + They should not be used with conflicting semantics by any Working + Group, specification, or document instance. +

+

+ See further below in this document for more information about how to refer to this schema document from your own + XSD schema documents and about the + namespace-versioning policy governing this schema document. +

+
+
+
+
+ + + + +
+ +

lang (as an attribute name)

+

+ denotes an attribute whose value + is a language code for the natural language of the content of + any element; its value is inherited. This name is reserved + by virtue of its definition in the XML specification.

+ +
+
+

Notes

+

+ Attempting to install the relevant ISO 2- and 3-letter + codes as the enumerated possible values is probably never + going to be a realistic possibility. +

+

+ See BCP 47 at + http://www.rfc-editor.org/rfc/bcp/bcp47.txt + and the IANA language subtag registry at + + http://www.iana.org/assignments/language-subtag-registry + for further information. +

+

+ The union allows for the 'un-declaration' of xml:lang with + the empty string. +

+
+
+
+ + + + + + + + + +
+ + + + +
+ +

space (as an attribute name)

+

+ denotes an attribute whose + value is a keyword indicating what whitespace processing + discipline is intended for the content of the element; its + value is inherited. This name is reserved by virtue of its + definition in the XML specification.

+ +
+
+
+ + + + + + +
+ + + +
+ +

base (as an attribute name)

+

+ denotes an attribute whose value + provides a URI to be used as the base for interpreting any + relative URIs in the scope of the element on which it + appears; its value is inherited. This name is reserved + by virtue of its definition in the XML Base specification.

+ +

+ See http://www.w3.org/TR/xmlbase/ + for information about this attribute. +

+
+
+
+
+ + + + +
+ +

id (as an attribute name)

+

+ denotes an attribute whose value + should be interpreted as if declared to be of type ID. + This name is reserved by virtue of its definition in the + xml:id specification.

+ +

+ See http://www.w3.org/TR/xml-id/ + for information about this attribute. +

+
+
+
+
+ + + + + + + + + + +
+ +

Father (in any context at all)

+ +
+

+ denotes Jon Bosak, the chair of + the original XML Working Group. This name is reserved by + the following decision of the W3C XML Plenary and + XML Coordination groups: +

+
+

+ In appreciation for his vision, leadership and + dedication the W3C XML Plenary on this 10th day of + February, 2000, reserves for Jon Bosak in perpetuity + the XML name "xml:Father". +

+
+
+
+
+
+ + + +
+

About this schema document

+ +
+

+ This schema defines attributes and an attribute group suitable + for use by schemas wishing to allow xml:base, + xml:lang, xml:space or + xml:id attributes on elements they define. +

+

+ To enable this, such a schema must import this schema for + the XML namespace, e.g. as follows: +

+
+          <schema . . .>
+           . . .
+           <import namespace="http://www.w3.org/XML/1998/namespace"
+                      schemaLocation="http://www.w3.org/2001/xml.xsd"/>
+     
+

+ or +

+
+           <import namespace="http://www.w3.org/XML/1998/namespace"
+                      schemaLocation="http://www.w3.org/2009/01/xml.xsd"/>
+     
+

+ Subsequently, qualified reference to any of the attributes or the + group defined below will have the desired effect, e.g. +

+
+          <type . . .>
+           . . .
+           <attributeGroup ref="xml:specialAttrs"/>
+     
+

+ will define a type which will schema-validate an instance element + with any of those attributes. +

+
+
+
+
+ + + +
+

Versioning policy for this schema document

+
+

+ In keeping with the XML Schema WG's standard versioning + policy, this schema document will persist at + + http://www.w3.org/2009/01/xml.xsd. +

+

+ At the date of issue it can also be found at + + http://www.w3.org/2001/xml.xsd. +

+

+ The schema document at that URI may however change in the future, + in order to remain compatible with the latest version of XML + Schema itself, or with the XML namespace itself. In other words, + if the XML Schema or XML namespaces change, the version of this + document at + http://www.w3.org/2001/xml.xsd + + will change accordingly; the version at + + http://www.w3.org/2009/01/xml.xsd + + will not change. +

+

+ Previous dated (and unchanging) versions of this schema + document are at: +

+ +
+
+
+
+ +
+ diff --git a/xml/xmldsig-core-schema.xsd b/xml/xmldsig-core-schema.xsd new file mode 100644 index 00000000..df126b30 --- /dev/null +++ b/xml/xmldsig-core-schema.xsd @@ -0,0 +1,318 @@ + + + + + + ]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +