2018-11-16 08:10:35 +00:00
|
|
|
"""Functions for creating XML output."""
|
|
|
|
|
2019-10-01 09:24:10 +01: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
|
2018-12-14 14:30:11 +00:00
|
|
|
from passbook.saml_idp.xml_signing import get_signature_xml, sign_with_signxml
|
2018-11-16 08:10:35 +00:00
|
|
|
|
2019-10-04 09:08:53 +01: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()."""
|
|
|
|
attributes = params.get('ATTRIBUTES', [])
|
|
|
|
if not attributes:
|
|
|
|
params['ATTRIBUTE_STATEMENT'] = ''
|
|
|
|
return
|
|
|
|
# Build complete AttributeStatement.
|
|
|
|
params['ATTRIBUTE_STATEMENT'] = render_to_string('saml/xml/attributes.xml', {
|
|
|
|
'attributes': attributes})
|
|
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
request_id = params.get('REQUEST_ID', None)
|
|
|
|
if request_id:
|
|
|
|
params['IN_RESPONSE_TO'] = 'InResponseTo="%s" ' % request_id
|
|
|
|
else:
|
|
|
|
params['IN_RESPONSE_TO'] = ''
|
|
|
|
|
|
|
|
|
|
|
|
def _get_subject(params):
|
|
|
|
"""Insert Subject. Modifies the params dict."""
|
|
|
|
params['SUBJECT_STATEMENT'] = render_to_string('saml/xml/subject.xml', params)
|
|
|
|
|
|
|
|
|
|
|
|
def get_assertion_xml(template, parameters, signed=False):
|
|
|
|
"""Get XML for Assertion"""
|
|
|
|
# Reset signature.
|
|
|
|
params = {}
|
|
|
|
params.update(parameters)
|
|
|
|
params['ASSERTION_SIGNATURE'] = ''
|
|
|
|
|
|
|
|
_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()
|
|
|
|
params['ASSERTION_SIGNATURE'] = signature_xml
|
|
|
|
return render_to_string(template, params)
|
|
|
|
|
|
|
|
|
2018-12-14 14:44:46 +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)
|
|
|
|
params['RESPONSE_SIGNATURE'] = ''
|
|
|
|
_get_in_response_to(params)
|
|
|
|
|
2018-12-14 14:30:11 +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)
|
2018-12-14 14:30:11 +00:00
|
|
|
if not saml_provider.signing:
|
|
|
|
return raw_response
|
2018-11-16 08:10:35 +00:00
|
|
|
|
2018-12-14 14:30:11 +00:00
|
|
|
signature_xml = get_signature_xml()
|
|
|
|
params['RESPONSE_SIGNATURE'] = signature_xml
|
|
|
|
# LOGGER.debug("Raw response: %s", raw_response)
|
|
|
|
|
|
|
|
signed = sign_with_signxml(
|
2018-12-26 16:21:20 +00:00
|
|
|
saml_provider.signing_key, raw_response, saml_provider.signing_cert,
|
2018-12-26 20:56:08 +00:00
|
|
|
reference_uri=assertion_id)
|
2018-12-14 14:30:11 +00:00
|
|
|
return signed
|