Skip to content

Commit f7e59c3

Browse files
committed
Allow AuthnRequest with no NameIDPolicy
1 parent 43ff778 commit f7e59c3

6 files changed

Lines changed: 89 additions & 19 deletions

File tree

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -498,10 +498,11 @@ We can set a 'return_to' url parameter to the login function and that will be co
498498
target_url = 'https://example.com'
499499
auth.login(return_to=target_url)
500500
```
501-
The login method can recieve 2 more optional parameters:
501+
The login method can recieve 3 more optional parameters:
502502

503-
* force_authn When true the AuthNReuqest will set the ForceAuthn='true'
504-
* is_passive When true the AuthNReuqest will set the Ispassive='true'
503+
* force_authn When true the AuthNReuqest will set the ForceAuthn='true'
504+
* is_passive When true the AuthNReuqest will set the Ispassive='true'
505+
* set_nameid_policy When true the AuthNReuqest will set a nameIdPolicy element.
505506

506507
#### The SP Endpoints ####
507508

src/onelogin/saml2/auth.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -261,22 +261,26 @@ def get_attribute(self, name):
261261
assert isinstance(name, compat.str_type)
262262
return self.__attributes.get(name)
263263

264-
def login(self, return_to=None, force_authn=False, is_passive=False):
264+
def login(self, return_to=None, force_authn=False, is_passive=False, set_nameid_policy=True):
265265
"""
266266
Initiates the SSO process.
267267
268268
:param return_to: Optional argument. The target URL the user should be redirected to after login.
269269
:type return_to: string
270270
271271
:param force_authn: Optional argument. When true the AuthNReuqest will set the ForceAuthn='true'.
272-
:type force_authn: string
272+
:type force_authn: bool
273273
274274
:param is_passive: Optional argument. When true the AuthNReuqest will set the Ispassive='true'.
275-
:type is_passive: string
275+
:type is_passive: bool
276+
277+
:param set_nameid_policy: Optional argument. When true the AuthNReuqest will set a nameIdPolicy element.
278+
:type set_nameid_policy: bool
276279
277280
:returns: Redirection url
281+
:rtype: string
278282
"""
279-
authn_request = OneLogin_Saml2_Authn_Request(self.__settings, force_authn, is_passive)
283+
authn_request = OneLogin_Saml2_Authn_Request(self.__settings, force_authn, is_passive, set_nameid_policy)
280284

281285
saml_request = authn_request.get_request()
282286
parameters = {'SAMLRequest': saml_request}

src/onelogin/saml2/authn_request.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class OneLogin_Saml2_Authn_Request(object):
2222
2323
"""
2424

25-
def __init__(self, settings, force_authn=False, is_passive=False):
25+
def __init__(self, settings, force_authn=False, is_passive=False, set_nameid_policy=True):
2626
"""
2727
Constructs the AuthnRequest object.
2828
@@ -34,6 +34,9 @@ def __init__(self, settings, force_authn=False, is_passive=False):
3434
3535
:param is_passive: Optional argument. When true the AuthNReuqest will set the Ispassive='true'.
3636
:type is_passive: bool
37+
38+
:param set_nameid_policy: Optional argument. When true the AuthNReuqest will set a nameIdPolicy element.
39+
:type set_nameid_policy: bool
3740
"""
3841
self.__settings = settings
3942

@@ -47,10 +50,6 @@ def __init__(self, settings, force_authn=False, is_passive=False):
4750

4851
destination = idp_data['singleSignOnService']['url']
4952

50-
name_id_policy_format = sp_data['NameIDFormat']
51-
if security['wantNameIdEncrypted']:
52-
name_id_policy_format = OneLogin_Saml2_Constants.NAMEID_ENCRYPTED
53-
5453
provider_name_str = ''
5554
organization_data = settings.get_organization()
5655
if isinstance(organization_data, dict) and organization_data:
@@ -72,6 +71,17 @@ def __init__(self, settings, force_authn=False, is_passive=False):
7271
if is_passive is True:
7372
is_passive_str = 'IsPassive="true"'
7473

74+
nameid_policy_str = ''
75+
if set_nameid_policy:
76+
name_id_policy_format = sp_data['NameIDFormat']
77+
if security['wantNameIdEncrypted']:
78+
name_id_policy_format = OneLogin_Saml2_Constants.NAMEID_ENCRYPTED
79+
80+
nameid_policy_str = """
81+
<samlp:NameIDPolicy
82+
Format="%s"
83+
AllowCreate="true" />""" % name_id_policy_format
84+
7585
requested_authn_context_str = ''
7686
if security['requestedAuthnContext'] is not False:
7787
authn_comparison = 'exact'
@@ -102,7 +112,7 @@ def __init__(self, settings, force_authn=False, is_passive=False):
102112
'destination': destination,
103113
'assertion_url': sp_data['assertionConsumerService']['url'],
104114
'entity_id': sp_data['entityId'],
105-
'name_id_policy': name_id_policy_format,
115+
'nameid_policy_str': nameid_policy_str,
106116
'requested_authn_context_str': requested_authn_context_str,
107117
'attr_consuming_service_str': attr_consuming_service_str,
108118
}

src/onelogin/saml2/xml_templates.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,8 @@ class OneLogin_Saml2_Templates(object):
3232
Destination="%(destination)s"
3333
ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
3434
AssertionConsumerServiceURL="%(assertion_url)s"%(attr_consuming_service_str)s>
35-
<saml:Issuer>%(entity_id)s</saml:Issuer>
36-
37-
<samlp:NameIDPolicy
38-
Format="%(name_id_policy)s"
39-
AllowCreate="true" />
40-
%(requested_authn_context_str)s
35+
<saml:Issuer>%(entity_id)s</saml:Issuer>%(nameid_policy_str)s
36+
%(requested_authn_context_str)s
4137
</samlp:AuthnRequest>"""
4238

4339
LOGOUT_REQUEST = """\

tests/src/OneLogin/saml2_tests/auth_test.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,40 @@ def testLoginIsPassive(self):
665665
request_3 = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(parsed_query_3['SAMLRequest'][0]))
666666
self.assertIn('IsPassive="true"', request_3)
667667

668+
def testLoginSetNameIDPolicy(self):
669+
"""
670+
Tests the login method of the OneLogin_Saml2_Auth class
671+
Case Logout with no parameters. A AuthN Request is built with and without NameIDPolicy
672+
"""
673+
settings_info = self.loadSettingsJSON()
674+
return_to = u'http://example.com/returnto'
675+
settings_info['idp']['singleSignOnService']['url']
676+
677+
auth = OneLogin_Saml2_Auth(self.get_request(), old_settings=settings_info)
678+
target_url = auth.login(return_to)
679+
parsed_query = parse_qs(urlparse(target_url)[4])
680+
sso_url = settings_info['idp']['singleSignOnService']['url']
681+
self.assertIn(sso_url, target_url)
682+
self.assertIn('SAMLRequest', parsed_query)
683+
request = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(parsed_query['SAMLRequest'][0]))
684+
self.assertIn('<samlp:NameIDPolicy', request)
685+
686+
auth_2 = OneLogin_Saml2_Auth(self.get_request(), old_settings=settings_info)
687+
target_url_2 = auth_2.login(return_to, False, False, True)
688+
parsed_query_2 = parse_qs(urlparse(target_url_2)[4])
689+
self.assertIn(sso_url, target_url_2)
690+
self.assertIn('SAMLRequest', parsed_query_2)
691+
request_2 = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(parsed_query_2['SAMLRequest'][0]))
692+
self.assertIn('<samlp:NameIDPolicy', request_2)
693+
694+
auth_3 = OneLogin_Saml2_Auth(self.get_request(), old_settings=settings_info)
695+
target_url_3 = auth_3.login(return_to, False, False, False)
696+
parsed_query_3 = parse_qs(urlparse(target_url_3)[4])
697+
self.assertIn(sso_url, target_url_3)
698+
self.assertIn('SAMLRequest', parsed_query_3)
699+
request_3 = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(parsed_query_3['SAMLRequest'][0]))
700+
self.assertNotIn('<samlp:NameIDPolicy', request_3)
701+
668702
def testLogout(self):
669703
"""
670704
Tests the logout method of the OneLogin_Saml2_Auth class

tests/src/OneLogin/saml2_tests/authn_request_test.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,31 @@ def testCreateRequestIsPassive(self):
195195
self.assertRegexpMatches(inflated_3, '^<samlp:AuthnRequest')
196196
self.assertIn('IsPassive="true"', inflated_3)
197197

198+
def testCreateRequestSetNameIDPolicy(self):
199+
"""
200+
Tests the OneLogin_Saml2_Authn_Request Constructor.
201+
The creation of a deflated SAML Request with and without NameIDPolicy
202+
"""
203+
saml_settings = self.loadSettingsJSON()
204+
settings = OneLogin_Saml2_Settings(saml_settings)
205+
authn_request = OneLogin_Saml2_Authn_Request(settings)
206+
authn_request_encoded = authn_request.get_request()
207+
inflated = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(authn_request_encoded))
208+
self.assertRegexpMatches(inflated, '^<samlp:AuthnRequest')
209+
self.assertIn('<samlp:NameIDPolicy', inflated)
210+
211+
authn_request_2 = OneLogin_Saml2_Authn_Request(settings, False, False, True)
212+
authn_request_encoded_2 = authn_request_2.get_request()
213+
inflated_2 = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(authn_request_encoded_2))
214+
self.assertRegexpMatches(inflated_2, '^<samlp:AuthnRequest')
215+
self.assertIn('<samlp:NameIDPolicy', inflated_2)
216+
217+
authn_request_3 = OneLogin_Saml2_Authn_Request(settings, False, False, False)
218+
authn_request_encoded_3 = authn_request_3.get_request()
219+
inflated_3 = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(authn_request_encoded_3))
220+
self.assertRegexpMatches(inflated_3, '^<samlp:AuthnRequest')
221+
self.assertNotIn('<samlp:NameIDPolicy', inflated_3)
222+
198223
def testCreateDeflatedSAMLRequestURLParameter(self):
199224
"""
200225
Tests the OneLogin_Saml2_Authn_Request Constructor.

0 commit comments

Comments
 (0)