This repository has been archived on 2024-05-31. You can view files and clone it, but cannot push or open issues or pull requests.
authentik/passbook/providers/saml/xml_render.py

98 lines
3 KiB
Python
Raw Normal View History

2018-11-16 08:10:35 +00:00
"""Functions for creating XML output."""
2019-10-07 14:33:48 +00:00
from __future__ import annotations
2019-10-07 14:50:13 +00:00
2019-10-07 14:33:48 +00:00
from typing import TYPE_CHECKING
2018-11-16 08:10:35 +00:00
2019-10-01 08:24:10 +00:00
from structlog import get_logger
2018-11-16 08:10:35 +00:00
2018-11-16 09:08:15 +00:00
from passbook.lib.utils.template import render_to_string
2019-12-31 11:51:16 +00:00
from passbook.providers.saml.xml_signing import get_signature_xml, sign_with_signxml
2019-10-07 14:33:48 +00:00
if TYPE_CHECKING:
from passbook.providers.saml.models import SAMLProvider
2018-11-16 08:10:35 +00:00
LOGGER = get_logger()
2018-11-16 08:10:35 +00:00
def _get_attribute_statement(params):
"""Inserts AttributeStatement, if we have any attributes.
Modifies the params dict.
PRE-REQ: params['SUBJECT'] has already been created (usually by a call to
_get_subject()."""
2019-12-31 11:51:16 +00:00
attributes = params.get("ATTRIBUTES", [])
2018-11-16 08:10:35 +00:00
if not attributes:
2019-12-31 11:51:16 +00:00
params["ATTRIBUTE_STATEMENT"] = ""
2018-11-16 08:10:35 +00:00
return
# Build complete AttributeStatement.
2019-12-31 11:51:16 +00:00
params["ATTRIBUTE_STATEMENT"] = render_to_string(
"saml/xml/attributes.xml", {"attributes": attributes}
)
2018-11-16 08:10:35 +00:00
def _get_in_response_to(params):
"""Insert InResponseTo if we have a RequestID.
Modifies the params dict."""
# NOTE: I don't like this. We're mixing templating logic here, but the
# current design requires this; maybe refactor using better templates, or
# just bite the bullet and use elementtree to produce the XML; see comments
# in xml_templates about Canonical XML.
2019-12-31 11:51:16 +00:00
request_id = params.get("REQUEST_ID", None)
2018-11-16 08:10:35 +00:00
if request_id:
2019-12-31 11:51:16 +00:00
params["IN_RESPONSE_TO"] = 'InResponseTo="%s" ' % request_id
2018-11-16 08:10:35 +00:00
else:
2019-12-31 11:51:16 +00:00
params["IN_RESPONSE_TO"] = ""
2018-11-16 08:10:35 +00:00
def _get_subject(params):
"""Insert Subject. Modifies the params dict."""
2019-12-31 11:51:16 +00:00
params["SUBJECT_STATEMENT"] = render_to_string("saml/xml/subject.xml", params)
2018-11-16 08:10:35 +00:00
def get_assertion_xml(template, parameters, signed=False):
"""Get XML for Assertion"""
# Reset signature.
params = {}
params.update(parameters)
2019-12-31 11:51:16 +00:00
params["ASSERTION_SIGNATURE"] = ""
2018-11-16 08:10:35 +00:00
_get_in_response_to(params)
_get_subject(params) # must come before _get_attribute_statement()
_get_attribute_statement(params)
unsigned = render_to_string(template, params)
# LOGGER.debug('Unsigned: %s', unsigned)
if not signed:
return unsigned
# Sign it.
signature_xml = get_signature_xml()
2019-12-31 11:51:16 +00:00
params["ASSERTION_SIGNATURE"] = signature_xml
2018-11-16 08:10:35 +00:00
return render_to_string(template, params)
2019-12-31 11:51:16 +00:00
def get_response_xml(parameters, saml_provider: SAMLProvider, assertion_id=""):
2018-11-16 08:10:35 +00:00
"""Returns XML for response, with signatures, if signed is True."""
# Reset signatures.
params = {}
params.update(parameters)
2019-12-31 11:51:16 +00:00
params["RESPONSE_SIGNATURE"] = ""
2018-11-16 08:10:35 +00:00
_get_in_response_to(params)
2019-12-31 11:51:16 +00:00
raw_response = render_to_string("saml/xml/response.xml", params)
2018-11-16 08:10:35 +00:00
# LOGGER.debug('Unsigned: %s', unsigned)
if not saml_provider.signing:
return raw_response
2018-11-16 08:10:35 +00:00
signature_xml = get_signature_xml()
2019-12-31 11:51:16 +00:00
params["RESPONSE_SIGNATURE"] = signature_xml
# LOGGER.debug("Raw response: %s", raw_response)
signed = sign_with_signxml(
2019-12-31 11:51:16 +00:00
saml_provider.signing_key,
raw_response,
saml_provider.signing_cert,
reference_uri=assertion_id,
)
return signed