Skip to content

Commit 3fa4cd7

Browse files
committed
Related to #34. Implemented the AuthNRequestContext in a different way
1 parent 0705917 commit 3fa4cd7

File tree

5 files changed

+56
-14
lines changed

5 files changed

+56
-14
lines changed

README.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -212,8 +212,6 @@ This is the settings.json file:
212212
// represent the requested subject.
213213
// Take a look on src/onelogin/saml2/constants.py to see the NameIdFormat that are supported.
214214
"NameIDFormat": "urn:oasis:names:tc:SAML:2.0:nameid-format:unspecified",
215-
// Specifies the AuthnContextClassRef that will be sent in the login request
216-
'AuthnContextClassRef' => 'urn:oasis:names:tc:SAML:2.0:ac:classes:Password',
217215
// Usually x509cert and privateKey of the SP are provided by files placed at
218216
// the certs folder. But we can also provide them with the following parameters
219217
'x509cert' => '',
@@ -300,7 +298,13 @@ In addition to the required settings data (idp, sp), there is extra information
300298

301299
// Indicates a requirement for the NameID received by
302300
// this SP to be encrypted.
303-
"wantNameIdEncrypted": false
301+
"wantNameIdEncrypted": false,
302+
303+
// Authentication context.
304+
// Set to false and no AuthContext will be sent in the AuthNRequest,
305+
// Set true or don't present thi parameter and you will get an AuthContext 'exact' 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport'
306+
// Set an array with the possible auth context values: array ('urn:oasis:names:tc:SAML:2.0:ac:classes:Password', 'urn:oasis:names:tc:SAML:2.0:ac:classes:X509'),
307+
'requestedAuthnContext' => true,
304308
},
305309

306310
// Contact information template, it is recommended to suply a

src/onelogin/saml2/authn_request.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,17 @@ def __init__(self, settings):
5858
if 'displayname' in organization_data[lang] and organization_data[lang]['displayname'] is not None:
5959
provider_name_str = 'ProviderName="%s"' % organization_data[lang]['displayname']
6060

61-
auth_context_class_ref = sp_data['AuthnContextClassRef']
61+
requested_authn_context_str = ''
62+
if 'requestedAuthnContext' in security.keys() and security['requestedAuthnContext'] != False:
63+
if security['requestedAuthnContext'] == True:
64+
requested_authn_context_str = """ <samlp:RequestedAuthnContext Comparison="exact">
65+
<saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef>
66+
</samlp:RequestedAuthnContext>"""
67+
else:
68+
requested_authn_context_str = ' <samlp:RequestedAuthnContext Comparison="exact">'
69+
for authn_context in security['requestedAuthnContext']:
70+
requested_authn_context_str += '<saml:AuthnContextClassRef>%s</saml:AuthnContextClassRef>' % authn_context
71+
requested_authn_context_str += ' </samlp:RequestedAuthnContext>'
6272

6373
request = """<samlp:AuthnRequest
6474
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
@@ -74,9 +84,7 @@ def __init__(self, settings):
7484
<samlp:NameIDPolicy
7585
Format="%(name_id_policy)s"
7686
AllowCreate="true" />
77-
<samlp:RequestedAuthnContext Comparison="exact">
78-
<saml:AuthnContextClassRef>%(auth_context_class_ref)s</saml:AuthnContextClassRef>
79-
</samlp:RequestedAuthnContext>
87+
%(requested_authn_context_str)s
8088
</samlp:AuthnRequest>""" % \
8189
{
8290
'id': uid,
@@ -86,7 +94,7 @@ def __init__(self, settings):
8694
'assertion_url': sp_data['assertionConsumerService']['url'],
8795
'entity_id': sp_data['entityId'],
8896
'name_id_policy': name_id_policy_format,
89-
'auth_context_class_ref': auth_context_class_ref,
97+
'requested_authn_context_str': requested_authn_context_str,
9098
}
9199

92100
self.__authn_request = request

src/onelogin/saml2/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ class OneLogin_Saml2_Constants(object):
5757
# Auth Context Class
5858
AC_UNSPECIFIED = 'urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified'
5959
AC_PASSWORD = 'urn:oasis:names:tc:SAML:2.0:ac:classes:Password'
60+
AC_PASSWORD_PROTECTED = 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport'
6061
AC_X509 = 'urn:oasis:names:tc:SAML:2.0:ac:classes:X509'
6162
AC_SMARTCARD = 'urn:oasis:names:tc:SAML:2.0:ac:classes:Smartcard'
6263
AC_KERBEROS = 'urn:oasis:names:tc:SAML:2.0:ac:classes:Kerberos'

src/onelogin/saml2/settings.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -266,10 +266,6 @@ def __add_default_values(self):
266266
if 'nameIdEncrypted' not in self.__security:
267267
self.__security['nameIdEncrypted'] = False
268268

269-
# AuthnContextClassRef
270-
if 'AuthnContextClassRef' not in self.__sp:
271-
self.__sp['AuthnContextClassRef'] = OneLogin_Saml2_Constants.AC_PASSWORD
272-
273269
# Sign provided
274270
if 'authnRequestsSigned' not in self.__security.keys():
275271
self.__security['authnRequestsSigned'] = False
@@ -302,6 +298,9 @@ def __add_default_values(self):
302298
if 'privateKey' not in self.__sp:
303299
self.__sp['privateKey'] = ''
304300

301+
if 'requestedAuthnContext' not in self.__security.keys():
302+
self.__security['requestedAuthnContext'] = True
303+
305304
def check_settings(self, settings):
306305
"""
307306
Checks the settings info.

tests/src/OneLogin/saml2_tests/authn_request_test.py

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,14 +77,44 @@ def testCreateRequestAuthContext(self):
7777
self.assertIn(OneLogin_Saml2_Constants.AC_PASSWORD, inflated)
7878
self.assertNotIn(OneLogin_Saml2_Constants.AC_X509, inflated)
7979

80-
saml_settings['sp']['AuthnContextClassRef'] = OneLogin_Saml2_Constants.AC_X509
80+
saml_settings['security']['requestedAuthnContext'] = True
8181
settings = OneLogin_Saml2_Settings(saml_settings)
8282
authn_request = OneLogin_Saml2_Authn_Request(settings)
8383
authn_request_encoded = authn_request.get_request()
8484
decoded = b64decode(authn_request_encoded)
8585
inflated = decompress(decoded, -15)
8686
self.assertRegexpMatches(inflated, '^<samlp:AuthnRequest')
87-
self.assertNotIn(OneLogin_Saml2_Constants.AC_PASSWORD, inflated)
87+
self.assertIn(OneLogin_Saml2_Constants.AC_PASSWORD_PROTECTED, inflated)
88+
self.assertNotIn(OneLogin_Saml2_Constants.AC_X509, inflated)
89+
90+
del saml_settings['security']['requestedAuthnContext']
91+
settings = OneLogin_Saml2_Settings(saml_settings)
92+
authn_request = OneLogin_Saml2_Authn_Request(settings)
93+
authn_request_encoded = authn_request.get_request()
94+
decoded = b64decode(authn_request_encoded)
95+
inflated = decompress(decoded, -15)
96+
self.assertRegexpMatches(inflated, '^<samlp:AuthnRequest')
97+
self.assertIn(OneLogin_Saml2_Constants.AC_PASSWORD_PROTECTED, inflated)
98+
self.assertNotIn(OneLogin_Saml2_Constants.AC_X509, inflated)
99+
100+
saml_settings['security']['requestedAuthnContext'] = False
101+
settings = OneLogin_Saml2_Settings(saml_settings)
102+
authn_request = OneLogin_Saml2_Authn_Request(settings)
103+
authn_request_encoded = authn_request.get_request()
104+
decoded = b64decode(authn_request_encoded)
105+
inflated = decompress(decoded, -15)
106+
self.assertRegexpMatches(inflated, '^<samlp:AuthnRequest')
107+
self.assertNotIn(OneLogin_Saml2_Constants.AC_PASSWORD_PROTECTED, inflated)
108+
self.assertNotIn(OneLogin_Saml2_Constants.AC_X509, inflated)
109+
110+
saml_settings['security']['requestedAuthnContext'] = (OneLogin_Saml2_Constants.AC_PASSWORD_PROTECTED, OneLogin_Saml2_Constants.AC_X509)
111+
settings = OneLogin_Saml2_Settings(saml_settings)
112+
authn_request = OneLogin_Saml2_Authn_Request(settings)
113+
authn_request_encoded = authn_request.get_request()
114+
decoded = b64decode(authn_request_encoded)
115+
inflated = decompress(decoded, -15)
116+
self.assertRegexpMatches(inflated, '^<samlp:AuthnRequest')
117+
self.assertIn(OneLogin_Saml2_Constants.AC_PASSWORD_PROTECTED, inflated)
88118
self.assertIn(OneLogin_Saml2_Constants.AC_X509, inflated)
89119

90120
def testCreateDeflatedSAMLRequestURLParameter(self):

0 commit comments

Comments
 (0)