Skip to content

Commit f11d48f

Browse files
author
Tessa Bloomer
committed
Resolved issue 226 with testing
1 parent 4380bf1 commit f11d48f

File tree

2 files changed

+69
-3
lines changed

2 files changed

+69
-3
lines changed

src/onelogin/saml2/response.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"""
1111

1212
from copy import deepcopy
13+
from urllib.parse import urlsplit, urlunsplit
1314
from onelogin.saml2.constants import OneLogin_Saml2_Constants
1415
from onelogin.saml2.utils import OneLogin_Saml2_Utils, OneLogin_Saml2_Error, OneLogin_Saml2_ValidationError, return_false_on_exception
1516
from onelogin.saml2.xml_utils import OneLogin_Saml2_XML
@@ -191,7 +192,7 @@ def is_valid(self, request_data, request_id=None, raise_exceptions=False):
191192
# Checks destination
192193
destination = self.document.get('Destination', None)
193194
if destination:
194-
if not destination.startswith(current_url):
195+
if not self.__standardize_url(destination).startswith(self.__standardize_url(current_url)):
195196
# TODO: Review if following lines are required, since we can control the
196197
# request_data
197198
# current_url_routed = OneLogin_Saml2_Utils.get_self_routed_url_no_query(request_data)
@@ -865,6 +866,15 @@ def __decrypt_assertion(self, xml):
865866
decrypted = OneLogin_Saml2_Utils.decrypt_element(encrypted_data, key, debug=debug, inplace=True)
866867
xml.replace(encrypted_assertion_nodes[0], decrypted)
867868
return xml
869+
870+
def __standardize_url(self, url):
871+
try:
872+
parsed = list(urlsplit(url))
873+
parsed[1] = parsed[1].lower()
874+
standardized_url = urlunsplit(parsed)
875+
return standardized_url
876+
except Exception:
877+
return url
868878

869879
def get_error(self):
870880
"""

tests/src/OneLogin/saml2_tests/response_test.py

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
from onelogin.saml2.settings import OneLogin_Saml2_Settings
1919
from onelogin.saml2.utils import OneLogin_Saml2_Utils
2020

21-
2221
class OneLogin_Saml2_Response_Test(unittest.TestCase):
2322
data_path = join(dirname(dirname(dirname(dirname(__file__)))), 'data')
2423
settings_path = join(dirname(dirname(dirname(dirname(__file__)))), 'settings')
@@ -50,6 +49,24 @@ def get_request_data(self):
5049
'script_name': 'index.html'
5150
}
5251

52+
def get_request_data_domain_capitalized(self):
53+
return {
54+
'http_host': 'StuFF.Com',
55+
'script_name': 'endpoints/endpoints/acs.php'
56+
}
57+
58+
def get_request_data_path_capitalized(self):
59+
return {
60+
'http_host': 'stuff.com',
61+
'script_name': 'Endpoints/endPoints/acs.php'
62+
}
63+
64+
def get_request_data_both_capitalized(self):
65+
return {
66+
'http_host': 'StuFF.Com',
67+
'script_name': 'Endpoints/endPoints/aCs.php'
68+
}
69+
5370
def testConstruct(self):
5471
"""
5572
Tests the OneLogin_Saml2_Response Constructor.
@@ -977,7 +994,7 @@ def testIsInValidDuplicatedAttrs(self):
977994
response = OneLogin_Saml2_Response(settings, xml)
978995
with self.assertRaisesRegex(Exception, 'Found an Attribute element with duplicated Name'):
979996
response.get_attributes()
980-
997+
981998
def testIsInValidDestination(self):
982999
"""
9831000
Tests the is_valid method of the OneLogin_Saml2_Response class
@@ -1014,6 +1031,45 @@ def testIsInValidDestination(self):
10141031
self.assertFalse(response_5.is_valid(self.get_request_data()))
10151032
self.assertIn('A valid SubjectConfirmation was not found on this Response', response_5.get_error())
10161033

1034+
settings.set_strict(True)
1035+
response_2 = OneLogin_Saml2_Response(settings, message)
1036+
self.assertFalse(response_2.is_valid(self.get_request_data()))
1037+
self.assertIn('The response was received at', response_2.get_error())
1038+
1039+
def testIsInValidCapitalizationOfDestinationElements(self):
1040+
"""
1041+
Tests the is_valid method of the OneLogin_Saml2_Response class
1042+
Case Invalid Response due to differences in capitalization of path
1043+
"""
1044+
1045+
settings = OneLogin_Saml2_Settings(self.loadSettingsJSON())
1046+
message = self.file_contents(join(self.data_path, 'responses', 'unsigned_response.xml.base64'))
1047+
1048+
#Test path capitalized
1049+
settings.set_strict(True)
1050+
response = OneLogin_Saml2_Response(settings, message)
1051+
self.assertFalse(response.is_valid(self.get_request_data_path_capitalized()))
1052+
self.assertIn('The response was received at', response.get_error())
1053+
1054+
#Test both domain and path capitalized
1055+
response_2 = OneLogin_Saml2_Response(settings, message)
1056+
self.assertFalse(response_2.is_valid(self.get_request_data_both_capitalized()))
1057+
self.assertIn('The response was received at', response_2.get_error())
1058+
1059+
def testIsValidCapitalizationOfDestinationHost(self):
1060+
"""
1061+
Tests the is_valid method of the OneLogin_Saml2_Response class
1062+
Case Valid Response, even if host is differently capitalized (per RFC)
1063+
"""
1064+
settings = OneLogin_Saml2_Settings(self.loadSettingsJSON())
1065+
message = self.file_contents(join(self.data_path, 'responses', 'unsigned_response.xml.base64'))
1066+
1067+
#Test path capitalized
1068+
settings.set_strict(True)
1069+
response = OneLogin_Saml2_Response(settings, message)
1070+
self.assertFalse(response.is_valid(self.get_request_data_domain_capitalized()))
1071+
self.assertNotIn('The response was received at', response.get_error())
1072+
10171073
def testIsInValidAudience(self):
10181074
"""
10191075
Tests the is_valid method of the OneLogin_Saml2_Response class

0 commit comments

Comments
 (0)