Skip to content

Commit

Permalink
providers/saml: fix invalid SAML Response when assertion and response…
Browse files Browse the repository at this point in the history
… are signed (cherry-pick #12611) (#12613)

providers/saml: fix invalid SAML Response when assertion and response are signed (#12611)

* providers/saml: fix invalid SAML Response when assertion and response are signed



* validate against schema too



---------

Signed-off-by: Jens Langhammer <[email protected]>
Co-authored-by: Jens L. <[email protected]>
  • Loading branch information
gcp-cherry-pick-bot[bot] and BeryJu authored Jan 9, 2025
1 parent 1cf0f57 commit cbe429f
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 1 deletion.
14 changes: 13 additions & 1 deletion authentik/providers/saml/processors/assertion.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ def get_assertion(self) -> Element:
assertion.attrib["IssueInstant"] = self._issue_instant
assertion.append(self.get_issuer())

if self.provider.signing_kp:
if self.provider.signing_kp and self.provider.sign_assertion:
sign_algorithm_transform = SIGN_ALGORITHM_TRANSFORM_MAP.get(
self.provider.signature_algorithm, xmlsec.constants.TransformRsaSha1
)
Expand Down Expand Up @@ -295,6 +295,18 @@ def get_response(self) -> Element:

response.append(self.get_issuer())

if self.provider.signing_kp and self.provider.sign_response:
sign_algorithm_transform = SIGN_ALGORITHM_TRANSFORM_MAP.get(
self.provider.signature_algorithm, xmlsec.constants.TransformRsaSha1
)
signature = xmlsec.template.create(
response,
xmlsec.constants.TransformExclC14N,
sign_algorithm_transform,
ns=xmlsec.constants.DSigNs,
)
response.append(signature)

status = SubElement(response, f"{{{NS_SAML_PROTOCOL}}}Status")
status_code = SubElement(status, f"{{{NS_SAML_PROTOCOL}}}StatusCode")
status_code.attrib["Value"] = "urn:oasis:names:tc:SAML:2.0:status:Success"
Expand Down
17 changes: 17 additions & 0 deletions authentik/providers/saml/tests/test_auth_n_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,25 @@

from base64 import b64encode

from defusedxml.lxml import fromstring
from django.http.request import QueryDict
from django.test import TestCase
from lxml import etree # nosec

from authentik.blueprints.tests import apply_blueprint
from authentik.core.tests.utils import create_test_admin_user, create_test_cert, create_test_flow
from authentik.crypto.models import CertificateKeyPair
from authentik.events.models import Event, EventAction
from authentik.lib.generators import generate_id
from authentik.lib.tests.utils import get_request
from authentik.lib.xml import lxml_from_string
from authentik.providers.saml.models import SAMLPropertyMapping, SAMLProvider
from authentik.providers.saml.processors.assertion import AssertionProcessor
from authentik.providers.saml.processors.authn_request_parser import AuthNRequestParser
from authentik.sources.saml.exceptions import MismatchedRequestID
from authentik.sources.saml.models import SAMLSource
from authentik.sources.saml.processors.constants import (
NS_MAP,
SAML_BINDING_REDIRECT,
SAML_NAME_ID_FORMAT_EMAIL,
SAML_NAME_ID_FORMAT_UNSPECIFIED,
Expand Down Expand Up @@ -185,6 +189,19 @@ def test_request_signed_both(self):
self.assertEqual(response.count(response_proc._assertion_id), 2)
self.assertEqual(response.count(response_proc._response_id), 2)

schema = etree.XMLSchema(
etree.parse("schemas/saml-schema-protocol-2.0.xsd", parser=etree.XMLParser()) # nosec
)
self.assertTrue(schema.validate(lxml_from_string(response)))

response_xml = fromstring(response)
self.assertEqual(
len(response_xml.xpath("//saml:Assertion/ds:Signature", namespaces=NS_MAP)), 1
)
self.assertEqual(
len(response_xml.xpath("//samlp:Response/ds:Signature", namespaces=NS_MAP)), 1
)

# Now parse the response (source)
http_request.POST = QueryDict(mutable=True)
http_request.POST["SAMLResponse"] = b64encode(response.encode()).decode()
Expand Down

0 comments on commit cbe429f

Please sign in to comment.