Skip to content

Commit a092270

Browse files
committed
PR #160. Optionally raise detailed exceptions vs. returning False. Fix tests
2 parents d31952f + b8cb77b commit a092270

11 files changed

Lines changed: 405 additions & 643 deletions

File tree

src/onelogin/saml2/logout_request.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ def is_valid(self, request_data):
298298
if dom.get('NotOnOrAfter', None):
299299
na = OneLogin_Saml2_Utils.parse_SAML_to_time(dom.get('NotOnOrAfter'))
300300
if na <= OneLogin_Saml2_Utils.now():
301-
raise Exception('Timing issues (please check your clock settings)')
301+
raise Exception('Could not validate timestamp: expired. Check system clock.')
302302

303303
# Check destination
304304
if dom.get('Destination', None):
@@ -334,7 +334,7 @@ def is_valid(self, request_data):
334334
signed_query = '%s&RelayState=%s' % (signed_query, OneLogin_Saml2_Utils.get_encoded_parameter(get_data, 'RelayState', lowercase_urlencoding=lowercase_urlencoding))
335335
signed_query = '%s&SigAlg=%s' % (signed_query, OneLogin_Saml2_Utils.get_encoded_parameter(get_data, 'SigAlg', OneLogin_Saml2_Constants.RSA_SHA1, lowercase_urlencoding=lowercase_urlencoding))
336336

337-
if 'x509cert' not in idp_data or idp_data['x509cert'] is None:
337+
if 'x509cert' not in idp_data or not idp_data['x509cert']:
338338
raise Exception('In order to validate the sign on the Logout Request, the x509cert of the IdP is required')
339339
cert = idp_data['x509cert']
340340

src/onelogin/saml2/logout_response.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ def is_valid(self, request_data, request_id=None):
128128
signed_query = '%s&RelayState=%s' % (signed_query, OneLogin_Saml2_Utils.get_encoded_parameter(get_data, 'RelayState', lowercase_urlencoding=lowercase_urlencoding))
129129
signed_query = '%s&SigAlg=%s' % (signed_query, OneLogin_Saml2_Utils.get_encoded_parameter(get_data, 'SigAlg', OneLogin_Saml2_Constants.RSA_SHA1, lowercase_urlencoding=lowercase_urlencoding))
130130

131-
if 'x509cert' not in idp_data or idp_data['x509cert'] is None:
131+
if 'x509cert' not in idp_data or not idp_data['x509cert']:
132132
raise Exception('In order to validate the sign on the Logout Response, the x509cert of the IdP is required')
133133
cert = idp_data['x509cert']
134134

src/onelogin/saml2/response.py

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from xml.dom.minidom import Document
1717

1818
from onelogin.saml2.constants import OneLogin_Saml2_Constants
19-
from onelogin.saml2.utils import OneLogin_Saml2_Utils
19+
from onelogin.saml2.utils import OneLogin_Saml2_Utils, return_false_on_exception
2020

2121

2222
class OneLogin_Saml2_Response(object):
@@ -93,13 +93,21 @@ def is_valid(self, request_data, request_id=None):
9393

9494
if self.__settings.is_strict():
9595
no_valid_xml_msg = 'Invalid SAML Response. Not match the saml-schema-protocol-2.0.xsd'
96-
res = OneLogin_Saml2_Utils.validate_xml(etree.tostring(self.document), 'saml-schema-protocol-2.0.xsd', self.__settings.is_debug_active())
96+
res = OneLogin_Saml2_Utils.validate_xml(
97+
etree.tostring(self.document),
98+
'saml-schema-protocol-2.0.xsd',
99+
self.__settings.is_debug_active()
100+
)
97101
if not isinstance(res, Document):
98102
raise Exception(no_valid_xml_msg)
99103

100104
# If encrypted, check also the decrypted document
101105
if self.encrypted:
102-
res = OneLogin_Saml2_Utils.validate_xml(etree.tostring(self.decrypted_document), 'saml-schema-protocol-2.0.xsd', self.__settings.is_debug_active())
106+
res = OneLogin_Saml2_Utils.validate_xml(
107+
etree.tostring(self.decrypted_document),
108+
'saml-schema-protocol-2.0.xsd',
109+
self.__settings.is_debug_active()
110+
)
103111
if not isinstance(res, Document):
104112
raise Exception(no_valid_xml_msg)
105113

@@ -125,8 +133,7 @@ def is_valid(self, request_data, request_id=None):
125133
raise Exception('The Assertion must include a Conditions element')
126134

127135
# Validates Assertion timestamps
128-
if not self.validate_timestamps():
129-
raise Exception('Timing issues (please check your clock settings)')
136+
self.validate_timestamps(raise_exceptions=True)
130137

131138
# Checks that an AuthnStatement element exists and is unique
132139
if not self.check_one_authnstatement():
@@ -217,12 +224,14 @@ def is_valid(self, request_data, request_id=None):
217224
fingerprintalg = idp_data.get('certFingerprintAlgorithm', None)
218225

219226
# If find a Signature on the Response, validates it checking the original response
220-
if has_signed_response and not OneLogin_Saml2_Utils.validate_sign(self.document, cert, fingerprint, fingerprintalg, xpath=OneLogin_Saml2_Utils.RESPONSE_SIGNATURE_XPATH):
221-
raise Exception('Signature validation failed. SAML Response rejected')
227+
if has_signed_response:
228+
# Raise exception if response signature is invalid
229+
OneLogin_Saml2_Utils.validate_sign(self.document, cert, fingerprint, fingerprintalg, xpath=OneLogin_Saml2_Utils.RESPONSE_SIGNATURE_XPATH, raise_exceptions=True)
222230

223231
document_check_assertion = self.decrypted_document if self.encrypted else self.document
224-
if has_signed_assertion and not OneLogin_Saml2_Utils.validate_sign(document_check_assertion, cert, fingerprint, fingerprintalg, xpath=OneLogin_Saml2_Utils.ASSERTION_SIGNATURE_XPATH):
225-
raise Exception('Signature validation failed. SAML Response rejected')
232+
if has_signed_assertion:
233+
# Raise exception if assertion signature is invalid
234+
OneLogin_Saml2_Utils.validate_sign(document_check_assertion, cert, fingerprint, fingerprintalg, xpath=OneLogin_Saml2_Utils.ASSERTION_SIGNATURE_XPATH, raise_exceptions=True)
226235

227236
return True
228237
except Exception as err:
@@ -485,13 +494,20 @@ def process_signed_elements(self):
485494
signed_elements.append(signed_element)
486495

487496
if signed_elements:
488-
if not self.validate_signed_elements(signed_elements):
497+
if not self.validate_signed_elements(signed_elements, raise_exceptions=True):
489498
raise Exception('Found an unexpected Signature Element. SAML Response rejected')
490499
return signed_elements
491500

501+
@return_false_on_exception
492502
def validate_signed_elements(self, signed_elements):
493503
"""
494504
Verifies that the document has the expected signed nodes.
505+
506+
:param signed_elements: The signed elements to be checked
507+
:type signed_elements: list
508+
509+
:param raise_exceptions: Whether to return false on failure or raise an exception
510+
:type raise_exceptions: Boolean
495511
"""
496512
if len(signed_elements) > 2:
497513
return False
@@ -518,10 +534,14 @@ def validate_signed_elements(self, signed_elements):
518534

519535
return True
520536

537+
@return_false_on_exception
521538
def validate_timestamps(self):
522539
"""
523540
Verifies that the document is valid according to Conditions Element
524541
542+
:param raise_exceptions: Whether to return false on failure or raise an exception
543+
:type raise_exceptions: Boolean
544+
525545
:returns: True if the condition is valid, False otherwise
526546
:rtype: bool
527547
"""
@@ -531,9 +551,9 @@ def validate_timestamps(self):
531551
nb_attr = conditions_node.get('NotBefore')
532552
nooa_attr = conditions_node.get('NotOnOrAfter')
533553
if nb_attr and OneLogin_Saml2_Utils.parse_SAML_to_time(nb_attr) > OneLogin_Saml2_Utils.now() + OneLogin_Saml2_Constants.ALLOWED_CLOCK_DRIFT:
534-
return False
554+
raise Exception('Could not validate timestamp: not yet valid. Check system clock.')
535555
if nooa_attr and OneLogin_Saml2_Utils.parse_SAML_to_time(nooa_attr) + OneLogin_Saml2_Constants.ALLOWED_CLOCK_DRIFT <= OneLogin_Saml2_Utils.now():
536-
return False
556+
raise Exception('Could not validate timestamp: expired. Check system clock.')
537557
return True
538558

539559
def __query_assertion(self, xpath_expr):
@@ -588,8 +608,10 @@ def __decrypt_assertion(self, dom):
588608
Decrypts the Assertion
589609
590610
:raises: Exception if no private key available
611+
591612
:param dom: Encrypted Assertion
592613
:type dom: Element
614+
593615
:returns: Decrypted Assertion
594616
:rtype: Element
595617
"""

0 commit comments

Comments
 (0)