Skip to content

Commit bd86f1e

Browse files
committed
Adjusted acs endpoint to extract NameQualifier and SPNameQualifier from SAMLResponse. Adjusted single logout service to provide NameQualifier and SPNameQualifier to logout method. Add getNameIdNameQualifier to Auth and SamlResponse. Extend logout method from Auth and LogoutRequest constructor to support SPNameQualifier parameter. Align LogoutRequest constructor with SAML specs
1 parent b6741fd commit bd86f1e

File tree

11 files changed

+271
-36
lines changed

11 files changed

+271
-36
lines changed

README.md

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -822,17 +822,17 @@ target_url = 'https://example.com'
822822
auth.logout(return_to=target_url)
823823
```
824824

825-
Also there are 4 optional parameters that can be set:
825+
Also there are another 5 optional parameters that can be set:
826826

827827
* ``name_id``. That will be used to build the LogoutRequest. If not ``name_id`` parameter is set and the auth object processed a
828828
SAML Response with a NameId, then this NameId will be used.
829829
* ``session_index``. SessionIndex that identifies the session of the user.
830830
* ``nq``. IDP Name Qualifier
831831
* ``name_id_format``. The NameID Format that will be set in the LogoutRequest
832+
* ``spnq``: The ``NameID SP NameQualifier`` will be set in the ``LogoutRequest``.
832833

833834
If no name_id is provided, the LogoutRequest will contain a NameID with the entity Format.
834835
If name_id is provided and no name_id_format is provided, the NameIDFormat of the settings will be used.
835-
If nq is provided, the SPNameQualifier will be also attached to the NameId.
836836

837837
If a match on the LogoutResponse ID and the LogoutRequest ID to be sent is required, that LogoutRequest ID must to be extracted and stored for future validation, we can get that ID by:
838838

@@ -858,7 +858,12 @@ elif 'sso2' in request.args: # Another SSO init action
858858
return_to = '%sattrs/' % request.host_url # but set a custom RelayState URL
859859
return redirect(auth.login(return_to))
860860
elif 'slo' in request.args: # SLO action. Will sent a Logout Request to IdP
861-
return redirect(auth.logout())
861+
nameid = request.session['samlNameId']
862+
nameid_format = request.session['samlNameIdFormat']
863+
nameid_nq = request.session['samlNameIdNameQualifier']
864+
nameid_spnq = request.session['samlNameIdSPNameQualifier']
865+
session_index = request.session['samlSessionIndex']
866+
return redirect(auth.logout(None, nameid, session_index, nameid_nq, nameid_format, nameid_spnq))
862867
elif 'acs' in request.args: # Assertion Consumer Service
863868
auth.process_response() # Process the Response of the IdP
864869
errors = auth.get_errors() # This method receives an array with the errors
@@ -867,6 +872,11 @@ elif 'acs' in request.args: # Assertion Consumer Service
867872
msg = "Not authenticated" # data retrieved or not (user authenticated)
868873
else:
869874
request.session['samlUserdata'] = auth.get_attributes() # Retrieves user data
875+
request.session['samlNameId'] = auth.get_nameid()
876+
request.session['samlNameIdFormat'] = auth.get_nameid_format()
877+
request.session['samlNameIdNameQualifier'] = auth.get_nameid_nq()
878+
request.session['samlNameIdSPNameQualifier'] = auth.get_nameid_spnq()
879+
request.session['samlSessionIndex'] = auth.get_session_index()
870880
self_url = OneLogin_Saml2_Utils.get_self_url(req)
871881
if 'RelayState' in request.form and self_url != request.form['RelayState']:
872882
return redirect(auth.redirect_to(request.form['RelayState'])) # Redirect if there is a relayState

demo-django/demo/settings.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@
2222
# SECURITY WARNING: don't run with debug turned on in production!
2323
DEBUG = True
2424

25-
ALLOWED_HOSTS = []
26-
25+
ALLOWED_HOSTS = ['pitbulk.no-ip.org']
2726

2827
# Application definition
2928

demo-django/demo/views.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,19 @@ def index(request):
5050
return_to = OneLogin_Saml2_Utils.get_self_url(req) + reverse('attrs')
5151
return HttpResponseRedirect(auth.login(return_to))
5252
elif 'slo' in req['get_data']:
53-
name_id = None
54-
session_index = None
53+
name_id = session_index = name_id_format = name_id_nq = name_id_spnq = None
5554
if 'samlNameId' in request.session:
5655
name_id = request.session['samlNameId']
5756
if 'samlSessionIndex' in request.session:
5857
session_index = request.session['samlSessionIndex']
58+
if 'samlNameIdFormat' in request.session:
59+
name_id_format = request.session['samlNameIdFormat']
60+
if 'samlNameIdNameQualifier' in request.session:
61+
name_id_nq = request.session['samlNameIdNameQualifier']
62+
if 'samlNameIdSPNameQualifier' in request.session:
63+
name_id_spnq = request.session['samlNameIdSPNameQualifier']
5964

60-
return HttpResponseRedirect(auth.logout(name_id=name_id, session_index=session_index))
65+
return HttpResponseRedirect(auth.logout(name_id=name_id, session_index=session_index, nq=name_id_nq, name_id_format=name_id_format, spnq=name_id_spnq))
6166

6267
# If LogoutRequest ID need to be stored in order to later validate it, do instead
6368
# slo_built_url = auth.logout(name_id=name_id, session_index=session_index)
@@ -77,6 +82,9 @@ def index(request):
7782
del request.session['AuthNRequestID']
7883
request.session['samlUserdata'] = auth.get_attributes()
7984
request.session['samlNameId'] = auth.get_nameid()
85+
request.session['samlNameIdFormat'] = auth.get_nameid_format()
86+
request.session['samlNameIdNameQualifier'] = auth.get_nameid_nq()
87+
request.session['samlNameIdSPNameQualifier'] = auth.get_nameid_spnq()
8088
request.session['samlSessionIndex'] = auth.get_session_index()
8189
if 'RelayState' in req['post_data'] and OneLogin_Saml2_Utils.get_self_url(req) != req['post_data']['RelayState']:
8290
return HttpResponseRedirect(auth.redirect_to(req['post_data']['RelayState']))

demo-django/saml/settings.json

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,29 @@
22
"strict": true,
33
"debug": true,
44
"sp": {
5-
"entityId": "https://<sp_domain>/metadata/",
5+
"entityId": "http://pitbulk.no-ip.org:8000/metadata/",
66
"assertionConsumerService": {
7-
"url": "https://<sp_domain>/?acs",
7+
"url": "http://pitbulk.no-ip.org:8000/?acs",
88
"binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
99
},
1010
"singleLogoutService": {
11-
"url": "https://<sp_domain>/?sls",
11+
"url": "http://pitbulk.no-ip.org:8000/?sls",
1212
"binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
1313
},
1414
"NameIDFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified",
15-
"x509cert": "",
16-
"privateKey": ""
15+
"x509cert": "MIICbDCCAdWgAwIBAgIBADANBgkqhkiG9w0BAQ0FADBTMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lbG9naW4gSW5jMRgwFgYDVQQDDA9pZHAuZXhhbXBsZS5jb20wHhcNMTQwOTIzMTIyNDA4WhcNNDIwMjA4MTIyNDA4WjBTMQswCQYDVQQGEwJ1czETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMT25lbG9naW4gSW5jMRgwFgYDVQQDDA9pZHAuZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOWA+YHU7cvPOrBOfxCscsYTJB+kH3MaA9BFrSHFS+KcR6cw7oPSktIJxUgvDpQbtfNcOkE/tuOPBDoech7AXfvH6d7Bw7xtW8PPJ2mB5Hn/HGW2roYhxmfh3tR5SdwN6i4ERVF8eLkvwCHsNQyK2Ref0DAJvpBNZMHCpS24916/AgMBAAGjUDBOMB0GA1UdDgQWBBQ77/qVeiigfhYDITplCNtJKZTM8DAfBgNVHSMEGDAWgBQ77/qVeiigfhYDITplCNtJKZTM8DAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4GBAJO2j/1uO80E5C2PM6Fk9mzerrbkxl7AZ/mvlbOn+sNZE+VZ1AntYuG8ekbJpJtG1YfRfc7EA9mEtqvv4dhv7zBy4nK49OR+KpIBjItWB5kYvrqMLKBa32sMbgqqUqeF1ENXKjpvLSuPdfGJZA3dNa/+Dyb8GGqWe707zLyc5F8m",
16+
"privateKey": "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAOWA+YHU7cvPOrBOfxCscsYTJB+kH3MaA9BFrSHFS+KcR6cw7oPSktIJxUgvDpQbtfNcOkE/tuOPBDoech7AXfvH6d7Bw7xtW8PPJ2mB5Hn/HGW2roYhxmfh3tR5SdwN6i4ERVF8eLkvwCHsNQyK2Ref0DAJvpBNZMHCpS24916/AgMBAAECgYEA0wDXZPS9hKqMTNh+nnfONioXBjhA6fQ7GVtWKDxa3ofMoPyt7ejGL/Hnvcv13Vn02UAsFx1bKrCstDqVtYwrWrnmywXyH+o9paJnTmd+cRIjWU8mRvCrxzH5I/Bcvbp1qZoASuqZEaGwNjM6JpW2o3QTmHGMALcLUPfEvhApssECQQDy2e65E86HcFhi/Ta8TQ0odDCNbiWA0bI1Iu8B7z+NAy1D1+WnCd7w2u9U6CF/k2nFHCsvxEoeANM0z7h5T/XvAkEA8e4JqKmDrfdiakQT7nf9svU2jXZtxSbPiIRMafNikDvzZ1vJCZkvdmaWYL70GlDZIwc9ad67rHZ/n/fqX1d0MQJAbRpRsJ5gY+KqItbFt3UaWzlP8sowWR5cZJjsLb9RmsV5mYguKYw6t5R0f33GRu1wUFimYlBaR/5w5MIJi57LywJATO1a5uWX+G5MPewNxmsjIY91XEAHIYR4wzkGLz5z3dciS4BVCZdLD0QJlxPA/MkuckPwFET9uhYn+M7VGKHvUQJBANSDwsY+BdCGpi/WRV37HUfwLl07damaFbW3h08PQx8G8SuF7DpN+FPBcI6VhzrIWNRBxWprkgeGioKNfFWzSaM="
1717
},
1818
"idp": {
19-
"entityId": "https://app.onelogin.com/saml/metadata/<onelogin_connector_id>",
19+
"entityId": "https://app.onelogin.com/saml/metadata/3dbd155e-be64-4a4d-8fab-e44788bce74f",
2020
"singleSignOnService": {
21-
"url": "https://app.onelogin.com/trust/saml2/http-post/sso/<onelogin_connector_id>",
21+
"url": "https://sgarcia-us-preprod.onelogin.com/trust/saml2/http-redirect/sso/850162",
2222
"binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
2323
},
2424
"singleLogoutService": {
25-
"url": "https://app.onelogin.com/trust/saml2/http-redirect/slo/<onelogin_connector_id>",
25+
"url": "https://sgarcia-us-preprod.onelogin.com/trust/saml2/http-redirect/slo/850162",
2626
"binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
2727
},
28-
"x509cert": "<onelogin_connector_cert>"
28+
"x509cert": "MIIEZTCCA02gAwIBAgIUPyy/A3bZAZ4m28PzEUUoT7RJhxIwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UEBhMCVVMxKzApBgNVBAoMIk9uZUxvZ2luIFRlc3QgKHNnYXJjaWEtdXMtcHJlcHJvZCkxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwWT25lTG9naW4gQWNjb3VudCA4OTE0NjAeFw0xNjA4MDQyMjI5MzdaFw0yMTA4MDUyMjI5MzdaMHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDN6iqQGcLOCglNO42I2rkzE05UXSiMXT6c8ALThMMiaDw6qqzo3sd/tKK+NcNKWLIIC8TozWVyh5ykUiVZps+08xil7VsTU7E+wKu3kvmOsvw2wlRwtnoKZJwYhnr+RkBa+h1r3ZYUgXm1ZPeHMKj1g18KaWz9+MxYL6BhKqrOzfW/P2xxVRcFH7/pq+ZsDdgNzD2GD+apzY4MZyZj/N6BpBWJ0GlFsmtBegpbX3LBitJuFkk5L4/U/jjF1AJa3boBdCUVfATqO5G03H4XS1GySjBIRQXmlUF52rLjg6xCgWJ30/+t1X+IHLJeixiQ0vxyh6C4/usCEt94cgD1r8ADAgMBAAGjgfIwge8wDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUPW0DcH0G3IwynWgi74co4wZ6n7gwga8GA1UdIwSBpzCBpIAUPW0DcH0G3IwynWgi74co4wZ6n7ihdqR0MHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDaCFD8svwN22QGeJtvD8xFFKE+0SYcSMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQUFAAOCAQEAQhB4q9jrycwbHrDSoYR1X4LFFzvJ9Us75wQquRHXpdyS9D6HUBXMGI6ahPicXCQrfLgN8vzMIiqZqfySXXv/8/dxe/X4UsWLYKYJHDJmxXD5EmWTa65chjkeP1oJAc8f3CKCpcP2lOBTthbnk2fEVAeLHR4xNdQO0VvGXWO9BliYPpkYqUIBvlm+Fg9mF7AM/Uagq2503XXIE1Lq//HON68P10vNMwLSKOtYLsoTiCnuIKGJqG37MsZVjQ1ZPRcO+LSLkq0i91gFxrOrVCrgztX4JQi5XkvEsYZGIXXjwHqxTVyt3adZWQO0LPxPqRiUqUzyhDhLo/xXNrHCu4VbMw=="
2929
}
30-
}
30+
}

demo-flask/index.py

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,32 +47,56 @@ def index():
4747

4848
if 'sso' in request.args:
4949
return redirect(auth.login())
50+
# If AuthNRequest ID need to be stored in order to later validate it, do instead
51+
# sso_built_url = auth.login()
52+
# request.session['AuthNRequestID'] = auth.get_last_request_id()
53+
# return redirect(sso_built_url)
5054
elif 'sso2' in request.args:
5155
return_to = '%sattrs/' % request.host_url
5256
return redirect(auth.login(return_to))
5357
elif 'slo' in request.args:
54-
name_id = None
55-
session_index = None
58+
name_id = session_index = name_id_format = name_id_nq = name_id_spnq = None
5659
if 'samlNameId' in session:
5760
name_id = session['samlNameId']
5861
if 'samlSessionIndex' in session:
5962
session_index = session['samlSessionIndex']
60-
61-
return redirect(auth.logout(name_id=name_id, session_index=session_index))
63+
if 'samlNameIdFormat' in session:
64+
name_id_format = session['samlNameIdFormat']
65+
if 'samlNameIdNameQualifier' in session:
66+
name_id_nq = session['samlNameIdNameQualifier']
67+
if 'samlNameIdSPNameQualifier' in session:
68+
name_id_spnq = session['samlNameIdSPNameQualifier']
69+
70+
return redirect(auth.logout(name_id=name_id, session_index=session_index, nq=name_id_nq, name_id_format=name_id_format, spnq=name_id_spnq))
71+
# If LogoutRequest ID need to be stored in order to later validate it, do instead
72+
# slo_built_url = auth.logout(name_id=name_id, session_index=session_index)
73+
# session['LogoutRequestID'] = auth.get_last_request_id()
74+
#return redirect(slo_built_url)
6275
elif 'acs' in request.args:
63-
auth.process_response()
76+
request_id = None
77+
if 'AuthNRequestID' in session:
78+
request_id = session['AuthNRequestID']
79+
80+
auth.process_response(request_id=request_id)
6481
errors = auth.get_errors()
6582
not_auth_warn = not auth.is_authenticated()
6683
if len(errors) == 0:
84+
if 'AuthNRequestID' in session:
85+
del session['AuthNRequestID']
6786
session['samlUserdata'] = auth.get_attributes()
68-
session['samlNameId'] = auth.get_nameid()
87+
session['samlNameIdFormat'] = auth.get_nameid_format()
88+
session['samlNameIdNameQualifier'] = auth.get_nameid_nq()
89+
session['samlNameIdSPNameQualifier'] = auth.get_nameid_spnq()
6990
session['samlSessionIndex'] = auth.get_session_index()
7091
self_url = OneLogin_Saml2_Utils.get_self_url(req)
7192
if 'RelayState' in request.form and self_url != request.form['RelayState']:
7293
return redirect(auth.redirect_to(request.form['RelayState']))
7394
elif 'sls' in request.args:
95+
request_id = None
96+
if 'LogoutRequestID' in session:
97+
request_id = session['LogoutRequestID']
7498
dscb = lambda: session.clear()
75-
url = auth.process_slo(delete_session_cb=dscb)
99+
url = auth.process_slo(request_id=request_id, delete_session_cb=dscb)
76100
errors = auth.get_errors()
77101
if len(errors) == 0:
78102
if url is not None:

src/onelogin/saml2/auth.py

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ def __init__(self, request_data, old_settings=None, custom_base_path=None):
5353
self.__attributes = []
5454
self.__nameid = None
5555
self.__nameid_format = None
56+
self.__nameid_nq = None
57+
self.__nameid_spnq = None
5658
self.__session_index = None
5759
self.__session_expiration = None
5860
self.__authenticated = False
@@ -104,6 +106,8 @@ def process_response(self, request_id=None):
104106
self.__attributes = response.get_attributes()
105107
self.__nameid = response.get_nameid()
106108
self.__nameid_format = response.get_nameid_format()
109+
self.__nameid_nq = response.get_nameid_nq()
110+
self.__nameid_spnq = response.get_nameid_spnq()
107111
self.__session_index = response.get_session_index()
108112
self.__session_expiration = response.get_session_not_on_or_after()
109113
self.__last_message_id = response.get_id()
@@ -245,6 +249,24 @@ def get_nameid_format(self):
245249
"""
246250
return self.__nameid_format
247251

252+
def get_nameid_nq(self):
253+
"""
254+
Returns the nameID NameQualifier of the Assertion.
255+
256+
:returns: NameID NameQualifier
257+
:rtype: string|None
258+
"""
259+
return self.__nameid_nq
260+
261+
def get_nameid_spnq(self):
262+
"""
263+
Returns the nameID SP NameQualifier of the Assertion.
264+
265+
:returns: NameID SP NameQualifier
266+
:rtype: string|None
267+
"""
268+
return self.__nameid_spnq
269+
248270
def get_session_index(self):
249271
"""
250272
Returns the SessionIndex from the AuthnStatement.
@@ -362,7 +384,7 @@ def login(self, return_to=None, force_authn=False, is_passive=False, set_nameid_
362384
parameters['Signature'] = self.build_request_signature(saml_request, parameters['RelayState'], security['signatureAlgorithm'])
363385
return self.redirect_to(self.get_sso_url(), parameters)
364386

365-
def logout(self, return_to=None, name_id=None, session_index=None, nq=None, name_id_format=None):
387+
def logout(self, return_to=None, name_id=None, session_index=None, nq=None, name_id_format=None, spnq=None):
366388
"""
367389
Initiates the SLO process.
368390
@@ -381,6 +403,9 @@ def logout(self, return_to=None, name_id=None, session_index=None, nq=None, name
381403
:param name_id_format: The NameID Format that will be set in the LogoutRequest.
382404
:type: string
383405
406+
:param spnq: SP Name Qualifier
407+
:type: string
408+
384409
:returns: Redirection url
385410
"""
386411
slo_url = self.get_slo_url()
@@ -400,7 +425,8 @@ def logout(self, return_to=None, name_id=None, session_index=None, nq=None, name
400425
name_id=name_id,
401426
session_index=session_index,
402427
nq=nq,
403-
name_id_format=name_id_format
428+
name_id_format=name_id_format,
429+
spnq=spnq
404430
)
405431
self.__last_request = logout_request.get_xml()
406432
self.__last_request_id = logout_request.id

src/onelogin/saml2/logout_request.py

Lines changed: 16 additions & 8 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, name_id_format=None):
32+
def __init__(self, settings, request=None, name_id=None, session_index=None, nq=None, name_id_format=None, spnq=None):
3333
"""
3434
Constructs the Logout Request object.
3535
@@ -50,6 +50,10 @@ def __init__(self, settings, request=None, name_id=None, session_index=None, nq=
5050
5151
:param name_id_format: The NameID Format that will be set in the LogoutRequest.
5252
:type: string
53+
54+
:param spnq: SP Name Qualifier
55+
:type: string
56+
5357
"""
5458
self.__settings = settings
5559
self.__error = None
@@ -79,19 +83,23 @@ def __init__(self, settings, request=None, name_id=None, session_index=None, nq=
7983
if not name_id_format and sp_data['NameIDFormat'] != OneLogin_Saml2_Constants.NAMEID_UNSPECIFIED:
8084
name_id_format = sp_data['NameIDFormat']
8185
else:
86+
name_id = idp_data['entityId']
8287
name_id_format = OneLogin_Saml2_Constants.NAMEID_ENTITY
8388

84-
spNameQualifier = None
85-
if name_id_format == OneLogin_Saml2_Constants.NAMEID_ENTITY:
86-
name_id = idp_data['entityId']
89+
# From saml-core-2.0-os 8.3.6, when the entity Format is used:
90+
# "The NameQualifier, SPNameQualifier, and SPProvidedID attributes
91+
# MUST be omitted.
92+
if name_id_format and name_id_format == OneLogin_Saml2_Constants.NAMEID_ENTITY:
8793
nq = None
88-
elif nq is not None:
89-
# We only gonna include SPNameQualifier if NameQualifier is provided
90-
spNameQualifier = sp_data['entityId']
94+
spnq = None
95+
96+
# NameID Format UNSPECIFIED omitted
97+
if name_id_format and name_id_format == OneLogin_Saml2_Constants.NAMEID_UNSPECIFIED:
98+
name_id_format = None
9199

92100
name_id_obj = OneLogin_Saml2_Utils.generate_name_id(
93101
name_id,
94-
spNameQualifier,
102+
spnq,
95103
name_id_format,
96104
cert,
97105
False,

src/onelogin/saml2/response.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,32 @@ def get_nameid_format(self):
512512
nameid_format = nameid_data['Format']
513513
return nameid_format
514514

515+
def get_nameid_nq(self):
516+
"""
517+
Gets the NameID NameQualifier provided by the SAML Response from the IdP
518+
519+
:returns: NameID NameQualifier
520+
:rtype: string|None
521+
"""
522+
nameid_nq = None
523+
nameid_data = self.get_nameid_data()
524+
if nameid_data and 'NameQualifier' in nameid_data.keys():
525+
nameid_nq = nameid_data['NameQualifier']
526+
return nameid_nq
527+
528+
def get_nameid_spnq(self):
529+
"""
530+
Gets the NameID SP NameQualifier provided by the SAML response from the IdP.
531+
532+
:returns: NameID SP NameQualifier
533+
:rtype: string|None
534+
"""
535+
nameid_spnq = None
536+
nameid_data = self.get_nameid_data()
537+
if nameid_data and 'SPNameQualifier' in nameid_data.keys():
538+
nameid_spnq = nameid_data['SPNameQualifier']
539+
return nameid_spnq
540+
515541
def get_session_not_on_or_after(self):
516542
"""
517543
Gets the SessionNotOnOrAfter from the AuthnStatement

0 commit comments

Comments
 (0)