Skip to content

Commit 52e95da

Browse files
committed
Fix #184. Be able to provide a NameIDFormat to LogoutRequest
1 parent a5423c3 commit 52e95da

5 files changed

Lines changed: 185 additions & 3 deletions

File tree

src/onelogin/saml2/auth.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ def __init__(self, request_data, old_settings=None, custom_base_path=None):
5252
self.__settings = OneLogin_Saml2_Settings(old_settings, custom_base_path)
5353
self.__attributes = []
5454
self.__nameid = None
55+
self.__nameid_format = None
5556
self.__session_index = None
5657
self.__session_expiration = None
5758
self.__authenticated = False
@@ -97,6 +98,7 @@ def process_response(self, request_id=None):
9798
if response.is_valid(self.__request_data, request_id):
9899
self.__attributes = response.get_attributes()
99100
self.__nameid = response.get_nameid()
101+
self.__nameid_format = response.get_nameid_format()
100102
self.__session_index = response.get_session_index()
101103
self.__session_expiration = response.get_session_not_on_or_after()
102104
self.__authenticated = True
@@ -214,6 +216,15 @@ def get_nameid(self):
214216
"""
215217
return self.__nameid
216218

219+
def get_nameid_format(self):
220+
"""
221+
Returns the nameID Format.
222+
223+
:returns: NameID Format
224+
:rtype: string|None
225+
"""
226+
return self.__nameid_format
227+
217228
def get_session_index(self):
218229
"""
219230
Returns the SessionIndex from the AuthnStatement.

src/onelogin/saml2/logout_request.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class OneLogin_Saml2_Logout_Request(object):
2929
3030
"""
3131

32-
def __init__(self, settings, request=None, name_id=None, session_index=None, nq=None):
32+
def __init__(self, settings, request=None, name_id=None, session_index=None, nq=None, name_id_format=None):
3333
"""
3434
Constructs the Logout Request object.
3535
@@ -47,6 +47,9 @@ def __init__(self, settings, request=None, name_id=None, session_index=None, nq=
4747
4848
:param nq: IDP Name Qualifier
4949
:type: string
50+
51+
:param name_id_format: The NameID Format that will be set in the LogoutRequest.
52+
:type: string
5053
"""
5154
self.__settings = settings
5255
self.__error = None
@@ -67,7 +70,10 @@ def __init__(self, settings, request=None, name_id=None, session_index=None, nq=
6770
cert = idp_data['x509cert']
6871

6972
if name_id is not None:
70-
nameIdFormat = sp_data['NameIDFormat']
73+
if name_id_format is not None:
74+
nameIdFormat = name_id_format
75+
else:
76+
nameIdFormat = sp_data['NameIDFormat']
7177
spNameQualifier = None
7278
else:
7379
name_id = idp_data['entityId']
@@ -78,7 +84,9 @@ def __init__(self, settings, request=None, name_id=None, session_index=None, nq=
7884
name_id,
7985
spNameQualifier,
8086
nameIdFormat,
81-
cert
87+
cert,
88+
False,
89+
nq
8290
)
8391

8492
if session_index:

src/onelogin/saml2/response.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,19 @@ def get_nameid(self):
457457
nameid_value = nameid_data['Value']
458458
return nameid_value
459459

460+
def get_nameid_format(self):
461+
"""
462+
Gets the NameID Format provided by the SAML Response from the IdP
463+
464+
:returns: NameID Format
465+
:rtype: string|None
466+
"""
467+
nameid_format = None
468+
nameid_data = self.get_nameid_data()
469+
if nameid_data and 'Format' in nameid_data.keys():
470+
nameid_format = nameid_data['Format']
471+
return nameid_format
472+
460473
def get_session_not_on_or_after(self):
461474
"""
462475
Gets the SessionNotOnOrAfter from the AuthnStatement

tests/src/OneLogin/saml2_tests/auth_test.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -857,6 +857,98 @@ def testSetStrict(self):
857857
with self.assertRaises(AssertionError):
858858
auth.set_strict('42')
859859

860+
def testIsAuthenticated(self):
861+
"""
862+
Tests the is_authenticated method of the OneLogin_Saml2_Auth
863+
"""
864+
request_data = self.get_request()
865+
del request_data['get_data']
866+
message = self.file_contents(join(self.data_path, 'responses', 'response1.xml.base64'))
867+
request_data['post_data'] = {
868+
'SAMLResponse': message
869+
}
870+
auth = OneLogin_Saml2_Auth(request_data, old_settings=self.loadSettingsJSON())
871+
auth.process_response()
872+
self.assertFalse(auth.is_authenticated())
873+
874+
message = self.file_contents(join(self.data_path, 'responses', 'valid_response.xml.base64'))
875+
request_data['post_data'] = {
876+
'SAMLResponse': message
877+
}
878+
auth = OneLogin_Saml2_Auth(request_data, old_settings=self.loadSettingsJSON())
879+
auth.process_response()
880+
self.assertTrue(auth.is_authenticated())
881+
882+
def testGetNameId(self):
883+
"""
884+
Tests the get_nameid method of the OneLogin_Saml2_Auth
885+
"""
886+
settings = self.loadSettingsJSON()
887+
request_data = self.get_request()
888+
del request_data['get_data']
889+
message = self.file_contents(join(self.data_path, 'responses', 'response1.xml.base64'))
890+
request_data['post_data'] = {
891+
'SAMLResponse': message
892+
}
893+
auth = OneLogin_Saml2_Auth(request_data, old_settings=settings)
894+
auth.process_response()
895+
self.assertFalse(auth.is_authenticated())
896+
self.assertEqual(auth.get_nameid(), None)
897+
898+
message = self.file_contents(join(self.data_path, 'responses', 'valid_response.xml.base64'))
899+
request_data['post_data'] = {
900+
'SAMLResponse': message
901+
}
902+
auth = OneLogin_Saml2_Auth(request_data, old_settings=settings)
903+
auth.process_response()
904+
self.assertTrue(auth.is_authenticated())
905+
self.assertEqual("492882615acf31c8096b627245d76ae53036c090", auth.get_nameid())
906+
907+
settings_2 = self.loadSettingsJSON('settings2.json')
908+
message = self.file_contents(join(self.data_path, 'responses', 'signed_message_encrypted_assertion2.xml.base64'))
909+
request_data['post_data'] = {
910+
'SAMLResponse': message
911+
}
912+
auth = OneLogin_Saml2_Auth(request_data, old_settings=settings_2)
913+
auth.process_response()
914+
self.assertTrue(auth.is_authenticated())
915+
self.assertEqual("25ddd7d34a7d79db69167625cda56a320adf2876", auth.get_nameid())
916+
917+
def testGetNameIdFormat(self):
918+
"""
919+
Tests the get_nameid_format method of the OneLogin_Saml2_Auth
920+
"""
921+
settings = self.loadSettingsJSON()
922+
request_data = self.get_request()
923+
del request_data['get_data']
924+
message = self.file_contents(join(self.data_path, 'responses', 'response1.xml.base64'))
925+
request_data['post_data'] = {
926+
'SAMLResponse': message
927+
}
928+
auth = OneLogin_Saml2_Auth(request_data, old_settings=settings)
929+
auth.process_response()
930+
self.assertFalse(auth.is_authenticated())
931+
self.assertEqual(auth.get_nameid_format(), None)
932+
933+
message = self.file_contents(join(self.data_path, 'responses', 'valid_response.xml.base64'))
934+
request_data['post_data'] = {
935+
'SAMLResponse': message
936+
}
937+
auth = OneLogin_Saml2_Auth(request_data, old_settings=settings)
938+
auth.process_response()
939+
self.assertTrue(auth.is_authenticated())
940+
self.assertEqual("urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", auth.get_nameid_format())
941+
942+
settings_2 = self.loadSettingsJSON('settings2.json')
943+
message = self.file_contents(join(self.data_path, 'responses', 'signed_message_encrypted_assertion2.xml.base64'))
944+
request_data['post_data'] = {
945+
'SAMLResponse': message
946+
}
947+
auth = OneLogin_Saml2_Auth(request_data, old_settings=settings_2)
948+
auth.process_response()
949+
self.assertTrue(auth.is_authenticated())
950+
self.assertEqual("urn:oasis:names:tc:SAML:2.0:nameid-format:unspecified", auth.get_nameid_format())
951+
860952
def testBuildRequestSignature(self):
861953
"""
862954
Tests the build_request_signature method of the OneLogin_Saml2_Auth

tests/src/OneLogin/saml2_tests/response_test.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,64 @@ def testReturnNameId(self):
137137
with self.assertRaisesRegexp(OneLogin_Saml2_ValidationError, 'An empty NameID value found'):
138138
response_9.get_nameid()
139139

140+
def testReturnNameIdFormat(self):
141+
"""
142+
Tests the get_nameid_format method of the OneLogin_Saml2_Response
143+
"""
144+
json_settings = self.loadSettingsJSON()
145+
146+
settings = OneLogin_Saml2_Settings(json_settings)
147+
xml = self.file_contents(join(self.data_path, 'responses', 'response1.xml.base64'))
148+
response = OneLogin_Saml2_Response(settings, xml)
149+
self.assertEqual('urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress', response.get_nameid_format())
150+
151+
xml_2 = self.file_contents(join(self.data_path, 'responses', 'response_encrypted_nameid.xml.base64'))
152+
response_2 = OneLogin_Saml2_Response(settings, xml_2)
153+
self.assertEqual('urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified', response_2.get_nameid_format())
154+
155+
xml_3 = self.file_contents(join(self.data_path, 'responses', 'valid_encrypted_assertion.xml.base64'))
156+
response_3 = OneLogin_Saml2_Response(settings, xml_3)
157+
self.assertEqual('urn:oasis:names:tc:SAML:2.0:nameid-format:transient', response_3.get_nameid_format())
158+
159+
xml_4 = self.file_contents(join(self.data_path, 'responses', 'invalids', 'no_nameid.xml.base64'))
160+
response_4 = OneLogin_Saml2_Response(settings, xml_4)
161+
with self.assertRaisesRegexp(OneLogin_Saml2_ValidationError, 'NameID not found in the assertion of the Response'):
162+
response_4.get_nameid_format()
163+
164+
json_settings['security']['wantNameId'] = True
165+
settings = OneLogin_Saml2_Settings(json_settings)
166+
167+
response_5 = OneLogin_Saml2_Response(settings, xml_4)
168+
with self.assertRaisesRegexp(OneLogin_Saml2_ValidationError, 'NameID not found in the assertion of the Response'):
169+
response_5.get_nameid_format()
170+
171+
json_settings['security']['wantNameId'] = False
172+
settings = OneLogin_Saml2_Settings(json_settings)
173+
174+
response_6 = OneLogin_Saml2_Response(settings, xml_4)
175+
nameid_6 = response_6.get_nameid_format()
176+
self.assertIsNone(nameid_6)
177+
178+
del json_settings['security']['wantNameId']
179+
settings = OneLogin_Saml2_Settings(json_settings)
180+
181+
response_7 = OneLogin_Saml2_Response(settings, xml_4)
182+
with self.assertRaisesRegexp(OneLogin_Saml2_ValidationError, 'NameID not found in the assertion of the Response'):
183+
response_7.get_nameid_format()
184+
185+
json_settings['strict'] = True
186+
settings = OneLogin_Saml2_Settings(json_settings)
187+
188+
xml_5 = self.file_contents(join(self.data_path, 'responses', 'invalids', 'wrong_spnamequalifier.xml.base64'))
189+
response_8 = OneLogin_Saml2_Response(settings, xml_5)
190+
with self.assertRaisesRegexp(OneLogin_Saml2_ValidationError, 'The SPNameQualifier value mistmatch the SP entityID value'):
191+
response_8.get_nameid_format()
192+
193+
xml_6 = self.file_contents(join(self.data_path, 'responses', 'invalids', 'empty_nameid.xml.base64'))
194+
response_9 = OneLogin_Saml2_Response(settings, xml_6)
195+
with self.assertRaisesRegexp(OneLogin_Saml2_ValidationError, 'An empty NameID value found'):
196+
response_9.get_nameid_format()
197+
140198
def testGetNameIdData(self):
141199
"""
142200
Tests the get_nameid_data method of the OneLogin_Saml2_Response

0 commit comments

Comments
 (0)