94 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			94 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| """Functions for creating XML output."""
 | |
| from __future__ import annotations
 | |
| from typing import TYPE_CHECKING
 | |
| 
 | |
| from structlog import get_logger
 | |
| 
 | |
| from passbook.lib.utils.template import render_to_string
 | |
| from passbook.providers.saml.xml_signing import (get_signature_xml,
 | |
|                                                  sign_with_signxml)
 | |
| 
 | |
| if TYPE_CHECKING:
 | |
|     from passbook.providers.saml.models import SAMLProvider
 | |
| 
 | |
| LOGGER = get_logger()
 | |
| 
 | |
| 
 | |
| 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)
 | |
| 
 | |
| 
 | |
| def get_response_xml(parameters, saml_provider: SAMLProvider, assertion_id=''):
 | |
|     """Returns XML for response, with signatures, if signed is True."""
 | |
|     # Reset signatures.
 | |
|     params = {}
 | |
|     params.update(parameters)
 | |
|     params['RESPONSE_SIGNATURE'] = ''
 | |
|     _get_in_response_to(params)
 | |
| 
 | |
|     raw_response = render_to_string('saml/xml/response.xml', params)
 | |
| 
 | |
|     # LOGGER.debug('Unsigned: %s', unsigned)
 | |
|     if not saml_provider.signing:
 | |
|         return raw_response
 | |
| 
 | |
|     signature_xml = get_signature_xml()
 | |
|     params['RESPONSE_SIGNATURE'] = signature_xml
 | |
|     # LOGGER.debug("Raw response: %s", raw_response)
 | |
| 
 | |
|     signed = sign_with_signxml(
 | |
|         saml_provider.signing_key, raw_response, saml_provider.signing_cert,
 | |
|         reference_uri=assertion_id)
 | |
|     return signed
 | 
