Source code for onelogin.saml2.xml_utils

# -*- coding: utf-8 -*-

""" OneLogin_Saml2_XML class


Auxiliary class of SAML Python Toolkit.

"""

from os.path import join, dirname
from lxml import etree
from onelogin.saml2 import compat
from onelogin.saml2.constants import OneLogin_Saml2_Constants
from onelogin.saml2.xmlparser import tostring, fromstring


for prefix, url in OneLogin_Saml2_Constants.NSMAP.items():
    etree.register_namespace(prefix, url)


[docs]class OneLogin_Saml2_XML(object): _element_class = type(etree.Element('root')) _parse_etree = staticmethod(fromstring) _schema_class = etree.XMLSchema _text_class = compat.text_types _bytes_class = compat.bytes_type _unparse_etree = staticmethod(tostring) dump = staticmethod(etree.dump) make_root = staticmethod(etree.Element) make_child = staticmethod(etree.SubElement)
[docs] @staticmethod def to_string(xml, **kwargs): """ Serialize an element to an encoded string representation of its XML tree. :param xml: The root node :type xml: str|bytes|xml.dom.minidom.Document|etree.Element :returns: string representation of xml :rtype: string """ if isinstance(xml, OneLogin_Saml2_XML._text_class): return xml if isinstance(xml, OneLogin_Saml2_XML._element_class): OneLogin_Saml2_XML.cleanup_namespaces(xml) return OneLogin_Saml2_XML._unparse_etree(xml, **kwargs) raise ValueError("unsupported type %r" % type(xml))
[docs] @staticmethod def to_etree(xml): """ Parses an XML document or fragment from a string. :param xml: the string to parse :type xml: str|bytes|xml.dom.minidom.Document|etree.Element :returns: the root node :rtype: OneLogin_Saml2_XML._element_class """ if isinstance(xml, OneLogin_Saml2_XML._element_class): return xml if isinstance(xml, OneLogin_Saml2_XML._bytes_class): return OneLogin_Saml2_XML._parse_etree(xml, forbid_dtd=True, forbid_entities=True) if isinstance(xml, OneLogin_Saml2_XML._text_class): return OneLogin_Saml2_XML._parse_etree(compat.to_bytes(xml), forbid_dtd=True, forbid_entities=True) raise ValueError('unsupported type %r' % type(xml))
[docs] @staticmethod def validate_xml(xml, schema, debug=False): """ Validates a xml against a schema :param xml: The xml that will be validated :type xml: str|bytes|xml.dom.minidom.Document|etree.Element :param schema: The schema :type schema: string :param debug: If debug is active, the parse-errors will be showed :type debug: bool :returns: Error code or the DomDocument of the xml :rtype: xml.dom.minidom.Document """ assert isinstance(schema, compat.str_type) try: xml = OneLogin_Saml2_XML.to_etree(xml) except Exception as e: if debug: print(e) return 'unloaded_xml' schema_file = join(dirname(__file__), 'schemas', schema) with open(schema_file, 'r') as f_schema: xmlschema = OneLogin_Saml2_XML._schema_class(etree.parse(f_schema)) if not xmlschema.validate(xml): if debug: print('Errors validating the metadata: ') for error in xmlschema.error_log: print(error.message) return 'invalid_xml' return xml
[docs] @staticmethod def query(dom, query, context=None, tagid=None): """ Extracts nodes that match the query from the Element :param dom: The root of the lxml objet :type: Element :param query: Xpath Expresion :type: string :param context: Context Node :type: DOMElement :param tagid: Tag ID :type query: String :returns: The queried nodes :rtype: list """ if context is None: source = dom else: source = context if tagid is None: return source.xpath(query, namespaces=OneLogin_Saml2_Constants.NSMAP) else: return source.xpath(query, tagid=tagid, namespaces=OneLogin_Saml2_Constants.NSMAP)
[docs] @staticmethod def cleanup_namespaces(tree_or_element, top_nsmap=None, keep_ns_prefixes=None): """ Keeps the xmlns:xs namespace intact when etree.cleanup_namespaces is invoked. :param tree_or_element: An XML tree or element :type tree_or_element: etree.Element :param top_nsmap: A mapping from namespace prefixes to namespace URIs :type top_nsmap: dict :param keep_ns_prefixes: List of prefixes that should not be removed as part of the cleanup :type keep_ns_prefixes: list :returns: An XML tree or element :rtype: etree.Element """ all_prefixes_to_keep = [ OneLogin_Saml2_Constants.NS_PREFIX_XS, OneLogin_Saml2_Constants.NS_PREFIX_XSI, OneLogin_Saml2_Constants.NS_PREFIX_XSD ] if keep_ns_prefixes: all_prefixes_to_keep = list(set(all_prefixes_to_keep.extend(keep_ns_prefixes))) return etree.cleanup_namespaces(tree_or_element, keep_ns_prefixes=all_prefixes_to_keep)
[docs] @staticmethod def extract_tag_text(xml, tagname): open_tag = compat.to_bytes("<%s" % tagname) close_tag = compat.to_bytes("</%s>" % tagname) xml = OneLogin_Saml2_XML.to_string(xml) start = xml.find(open_tag) assert start != -1 end = xml.find(close_tag, start) + len(close_tag) assert end != -1 return compat.to_string(xml[start:end])
[docs] @staticmethod def element_text(node): # Double check, the LXML Parser already removes comments etree.strip_tags(node, etree.Comment) return node.text