Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
assert a._get_saml_doc_etree(fake_data) is None
@patch('samlauthenticator.samlauthenticator.b64decode')
def test_not_valid_xml(self, mock_b64decode):
a = SAMLAuthenticator()
fake_data = {a.login_post_field: 'this string isn\'t important'}
mock_b64decode.return_value = 'bad xml string'
assert a._get_saml_doc_etree(fake_data) is None
class TestValidSamlResponse(unittest.TestCase):
response_etree = etree.fromstring(test_constants.sample_response_xml)
metadata_etree = etree.fromstring(test_constants.sample_metadata_xml)
verified_signed_xml = XMLVerifier().verify(response_etree, x509_cert=test_constants.x509_cert).signed_xml
@patch('samlauthenticator.samlauthenticator.datetime')
def test_valid_saml_auth(self, mock_datetime):
mock_datetime.now.return_value = datetime(2019, 4, 9, 21, 35, 0, tzinfo=timezone.utc)
mock_datetime.strptime = datetime.strptime
a = SAMLAuthenticator()
signed_xml = a._verify_saml_signature(self.metadata_etree, self.response_etree)
assert etree.tostring(signed_xml) == etree.tostring(self.verified_signed_xml)
response_is_valid, signed_xml = a._test_valid_saml_response(self.metadata_etree, self.response_etree)
assert response_is_valid
# Check the signed xml is the subset of the xml that is returned by signxml
# Export the SignedObject as an XML string.
buff = StringIO.StringIO()
signed_object.export(buff, 1, pretty_print=True)
signed_object_xml = buff.getvalue()
except Exception, err:
raise OpenADRInterfaceException('Error exporting the SignedObject: {}'.format(err), None)
if self.security_level == 'high':
try:
signature_lxml, signed_object_lxml = self.calculate_signature(signed_object_xml)
except Exception, err:
raise OpenADRInterfaceException('Error signing the SignedObject: {}'.format(err), None)
payload_lxml = self.payload_element(signature_lxml, signed_object_lxml)
try:
# Verify that the payload, with signature, is well-formed and can be validated.
signxml.XMLVerifier().verify(payload_lxml, ca_pem_file=VTN_CA_CERT_FILENAME)
except Exception, err:
raise OpenADRInterfaceException('Error verifying the SignedObject: {}'.format(err), None)
else:
signed_object_lxml = etree_.fromstring(signed_object_xml)
payload_lxml = self.payload_element(None, signed_object_lxml)
if self.log_xml:
_log.debug('VEN PAYLOAD:')
_log.debug('\n{}'.format(etree_.tostring(payload_lxml, pretty_print=True)))
# Post payload XML to the VTN as an HTTP request. Return the VTN's response, if any.
endpoint = self.vtn_address + (self.oadr_current_service or POLL)
try:
payload_xml = etree_.tostring(payload_lxml)
# OADR rule 53: If simple HTTP mode is used, send the following headers: Host, Content-Length, Content-Type.
# The EPRI VTN server responds with a 400 "bad request" if a "Host" header is sent.
def parse_signed(self, xml_tree: XmlNode, certificate: X509) -> XmlNode:
"""
Replaces all parameters with only the signed parameters. You should
provide an x509 certificate obtained out-of-band, usually via the
SAML metadata. Otherwise the signed data will be verified with only
the certificate provided in the request. This is INSECURE and
more-or-less only useful for testing.
"""
return XMLVerifier().verify(xml_tree, x509_cert=certificate).signed_xml
def _verify_saml_signature(self, saml_metadata, decoded_saml_doc):
xpath_with_namespaces = self._make_xpath_builder()
find_cert = xpath_with_namespaces('//ds:KeyInfo/ds:X509Data/ds:X509Certificate/text()')
cert_value = None
try:
cert_value = find_cert(saml_metadata)[0]
except Exception as e:
self.log.warning('Could not get cert value from saml metadata')
self._log_exception_error(e)
return None
signed_xml = None
try:
signed_xml = XMLVerifier().verify(decoded_saml_doc, x509_cert=cert_value).signed_xml
except Exception as e:
self.log.warning('Failed to verify signature on SAML Response')
self._log_exception_error(e)
return signed_xml
def parse_signed(self, x509_cert: str = None):
"""
Replaces all parameters with only the signed parameters. You should
provide an x509 certificate obtained out-of-band, usually via the
SAML metadata. Otherwise the signed data will be verified with only
the certificate provided in the request. This is INSECURE and
more-or-less only useful for testing.
:param x509_cert:
:return:
"""
self._assert_xml_tree()
self.xml_tree = XMLVerifier().verify(self.xml_tree, x509_cert=x509_cert).signed_xml
self._signed_data = True
def assinar(self, xml, retorna_string=False):
# busca tag que tem id(reference_uri), logo nao importa se tem namespace
reference = xml.find(".//*[@Id]").attrib['Id']
# retira acentos
xml_str = remover_acentos(etree.tostring(xml, encoding="unicode", pretty_print=False))
xml = etree.fromstring(xml_str)
signer = XMLSigner(
method=signxml.methods.enveloped, signature_algorithm="rsa-sha1",
digest_algorithm='sha1',
c14n_algorithm='http://www.w3.org/TR/2001/REC-xml-c14n-20010315')
ns = {None: signer.namespaces['ds']}
signer.namespaces = ns
ref_uri = ('#%s' % reference) if reference else None
signed_root = signer.sign(
xml, key=self.key, cert=self.cert, reference_uri=ref_uri)
ns = {'ns': NAMESPACE_SIG}
# coloca o certificado na tag X509Data/X509Certificate
tagX509Data = signed_root.find('.//ns:X509Data', namespaces=ns)
etree.SubElement(tagX509Data, 'X509Certificate').text = self.cert
if retorna_string:
@staticmethod
def calculate_signature(signed_object_xml):
"""
Calculate a digital signature for the SignedObject to be sent to the VTN.
@param signed_object_xml: (xml string) A SignedObject.
@return: (lxml) A Signature and a SignedObject.
"""
signed_object_lxml = etree_.fromstring(signed_object_xml)
signed_object_lxml.set('Id', 'signedObject')
# Use XMLSigner to create a Signature.
# Use "detached method": the signature lives alonside the signed object in the XML element tree.
# Use c14n "exclusive canonicalization": the signature is independent of namespace inclusion/exclusion.
signer = signxml.XMLSigner(method=signxml.methods.detached,
c14n_algorithm='http://www.w3.org/2001/10/xml-exc-c14n#')
signature_lxml = signer.sign(signed_object_lxml,
key=open(KEY_FILENAME, 'rb').read(),
cert=open(CERT_FILENAME, 'rb').read(),
key_name='123')
# This generated Signature lacks the ReplayProtect property described in OpenADR profile spec section 10.6.3.
return signature_lxml, signed_object_lxml
from signxml import XMLSigner, XMLVerifier
signer = XMLSigner()
verifier = XMLVerifier()
def sign(tree, key, cert, **kwargs):
return signer.sign(tree, key=key, cert=cert, **kwargs)
def verify(tree, **kwargs):
return verifier.verify(tree, **kwargs).signed_xml is not None
def assinar(self, xml, retorna_string=False):
# busca tag que tem id(reference_uri), logo nao importa se tem namespace
reference = xml.find(".//*[@Id]").attrib['Id']
# retira acentos
xml_str = remover_acentos(etree.tostring(xml, encoding="unicode", pretty_print=False))
xml = etree.fromstring(xml_str)
signer = XMLSigner(
method=signxml.methods.enveloped, signature_algorithm="rsa-sha1",
digest_algorithm='sha1',
c14n_algorithm='http://www.w3.org/TR/2001/REC-xml-c14n-20010315')
ns = {None: signer.namespaces['ds']}
signer.namespaces = ns
ref_uri = ('#%s' % reference) if reference else None
signed_root = signer.sign(
xml, key=self.key, cert=self.cert, reference_uri=ref_uri)
ns = {'ns': NAMESPACE_SIG}
# coloca o certificado na tag X509Data/X509Certificate
tagX509Data = signed_root.find('.//ns:X509Data', namespaces=ns)
etree.SubElement(tagX509Data, 'X509Certificate').text = self.cert
if retorna_string:
return etree.tostring(signed_root, encoding="unicode", pretty_print=False)