Skip to content

Commit 2b0c59f

Browse files
committed
New release 2.1.0. Add ForceAuh and IsPassive support
1 parent dbf03dc commit 2b0c59f

File tree

7 files changed

+173
-9
lines changed

7 files changed

+173
-9
lines changed

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,13 @@ You can install it executing:
9696
If you want to know how a project can handle python packages review this [guide](https://packaging.python.org/en/latest/tutorial.html) and review this [sampleproject](https://github.com/pypa/sampleproject)
9797

9898

99+
Security warning
100+
----------------
101+
102+
In production, the **strict** parameter MUST be set as **"true"**. Otherwise
103+
your environment is not secure and will be exposed to attacks.
104+
105+
99106
Getting started
100107
---------------
101108

@@ -447,6 +454,10 @@ We can set a 'return_to' url parameter to the login function and that will be co
447454
target_url = 'https://example.com'
448455
auth.login(return_to=target_url)
449456
```
457+
The login method can recieve 2 more optional parameters:
458+
459+
* force_authn When true the AuthNReuqest will set the ForceAuthn='true'
460+
* is_passive When true the AuthNReuqest will set the Ispassive='true'
450461

451462
#### The SP Endpoints ####
452463

docs/saml2/saml2.html

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -232,13 +232,18 @@ <h1>OneLogin saml2 Module<a class="headerlink" href="#onelogin-saml2-package" ti
232232

233233
<dl class="method">
234234
<dt id="saml2.auth.OneLogin_Saml2_Auth.login">
235-
<tt class="descname">login</tt><big>(</big><em>return_to=None</em><big>)</big><a class="reference internal" href="_modules/saml2/auth.html#OneLogin_Saml2_Auth.login"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#saml2.auth.OneLogin_Saml2_Auth.login" title="Permalink to this definition"></a></dt>
235+
<tt class="descname">login</tt><big>(</big><em>return_to=None</em>, <em>force_authn=False</em>, <em>is_passive=False</em><big>)</big><a class="reference internal" href="_modules/saml2/auth.html#OneLogin_Saml2_Auth.login"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#saml2.auth.OneLogin_Saml2_Auth.login" title="Permalink to this definition"></a></dt>
236236
<dd><p>Initiates the SSO process.</p>
237237
<table class="docutils field-list" frame="void" rules="none">
238238
<col class="field-name" />
239239
<col class="field-body" />
240240
<tbody valign="top">
241-
<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><strong>return_to</strong> (<em>string</em>) &#8211; Optional argument. The target URL the user should be redirected to after login.</td>
241+
<tr class="field-odd field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first simple">
242+
<li><strong>return_to</strong> (<em>string</em>) &#8211; Optional argument. The target URL the user should be redirected to after login.</li>
243+
<li><strong>force_authn</strong> (<em>bool</em>) &#8211; Optional argument. When true the AuthNReuqest will set the ForceAuthn='true'.</li>
244+
<li><strong>is_passive</strong> (<em>bool</em>) &#8211; Optional argument. When true the AuthNReuqest will set the Ispassive='true'.</li>
245+
</ul>
246+
</td>
242247
</tr>
243248
<tr class="field-even field"><th class="field-name">Returns:</th><td class="field-body">Redirection url</td>
244249
</tr>
@@ -346,7 +351,7 @@ <h1>OneLogin saml2 Module<a class="headerlink" href="#onelogin-saml2-package" ti
346351
<span id="authn-request-module"></span><h2><tt class="xref py py-mod docutils literal"><span class="pre">authn_request</span></tt> Class<a class="headerlink" href="#module-saml2.authn_request" title="Permalink to this headline"></a></h2>
347352
<dl class="class">
348353
<dt id="saml2.authn_request.OneLogin_Saml2_Authn_Request">
349-
<em class="property">class </em><tt class="descclassname">onelogin.saml2.authn_request.</tt><tt class="descname">OneLogin_Saml2_Authn_Request</tt><big>(</big><em>settings</em><big>)</big><a class="reference internal" href="_modules/saml2/authn_request.html#OneLogin_Saml2_Authn_Request"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#saml2.authn_request.OneLogin_Saml2_Authn_Request" title="Permalink to this definition"></a></dt>
354+
<em class="property">class </em><tt class="descclassname">onelogin.saml2.authn_request.</tt><tt class="descname">OneLogin_Saml2_Authn_Request</tt><big>(</big><em>settings</em>, <em>force_authn=False</em>, <em>is_passive=False</em><big>)</big><a class="reference internal" href="_modules/saml2/authn_request.html#OneLogin_Saml2_Authn_Request"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#saml2.authn_request.OneLogin_Saml2_Authn_Request" title="Permalink to this definition"></a></dt>
350355
<dd><dl class="method">
351356
<dt id="saml2.authn_request.OneLogin_Saml2_Authn_Request.get_request">
352357
<tt class="descname">get_request</tt><big>(</big><big>)</big><a class="reference internal" href="_modules/saml2/authn_request.html#OneLogin_Saml2_Authn_Request.get_request"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#saml2.authn_request.OneLogin_Saml2_Authn_Request.get_request" title="Permalink to this definition"></a></dt>

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
setup(
1111
name='python-saml',
12-
version='2.0.2',
12+
version='2.1.0',
1313
description='Onelogin Python Toolkit. Add SAML support to your Python software using this library',
1414
classifiers=[
1515
'Development Status :: 4 - Beta',

src/onelogin/saml2/auth.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -247,16 +247,22 @@ def get_attribute(self, name):
247247
value = self.__attributes[name]
248248
return value
249249

250-
def login(self, return_to=None):
250+
def login(self, return_to=None, force_authn=False, is_passive=False):
251251
"""
252252
Initiates the SSO process.
253253
254254
:param return_to: Optional argument. The target URL the user should be redirected to after login.
255255
:type return_to: string
256256
257+
:param force_authn: Optional argument. When true the AuthNReuqest will set the ForceAuthn='true'.
258+
:type force_authn: string
259+
260+
:param is_passive: Optional argument. When true the AuthNReuqest will set the Ispassive='true'.
261+
:type is_passive: string
262+
257263
:returns: Redirection url
258264
"""
259-
authn_request = OneLogin_Saml2_Authn_Request(self.__settings)
265+
authn_request = OneLogin_Saml2_Authn_Request(self.__settings, force_authn, is_passive)
260266

261267
saml_request = authn_request.get_request()
262268
parameters = {'SAMLRequest': saml_request}

src/onelogin/saml2/authn_request.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,18 @@ class OneLogin_Saml2_Authn_Request(object):
2424
2525
"""
2626

27-
def __init__(self, settings):
27+
def __init__(self, settings, force_authn=False, is_passive=False):
2828
"""
2929
Constructs the AuthnRequest object.
3030
31-
Arguments are:
32-
* (OneLogin_Saml2_Settings) settings. Setting data
31+
:param settings: OSetting data
32+
:type return_to: OneLogin_Saml2_Settings
33+
34+
:param force_authn: Optional argument. When true the AuthNReuqest will set the ForceAuthn='true'.
35+
:type force_authn: bool
36+
37+
:param is_passive: Optional argument. When true the AuthNReuqest will set the Ispassive='true'.
38+
:type is_passive: bool
3339
"""
3440
self.__settings = settings
3541

@@ -58,6 +64,14 @@ def __init__(self, settings):
5864
if 'displayname' in organization_data[lang] and organization_data[lang]['displayname'] is not None:
5965
provider_name_str = 'ProviderName="%s"' % organization_data[lang]['displayname']
6066

67+
force_authn_str = ''
68+
if force_authn is True:
69+
force_authn_str = 'ForceAuthn="true"'
70+
71+
is_passive_str = ''
72+
if is_passive is True:
73+
is_passive_str = 'IsPassive="true"'
74+
6175
requested_authn_context_str = ''
6276
if 'requestedAuthnContext' in security.keys() and security['requestedAuthnContext'] is not False:
6377
if security['requestedAuthnContext'] is True:
@@ -76,6 +90,8 @@ def __init__(self, settings):
7690
ID="%(id)s"
7791
Version="2.0"
7892
%(provider_name)s
93+
%(force_authn_str)s
94+
%(is_passive_str)s
7995
IssueInstant="%(issue_instant)s"
8096
Destination="%(destination)s"
8197
ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
@@ -89,6 +105,8 @@ def __init__(self, settings):
89105
{
90106
'id': uid,
91107
'provider_name': provider_name_str,
108+
'force_authn_str': force_authn_str,
109+
'is_passive_str': is_passive_str,
92110
'issue_instant': issue_instant,
93111
'destination': destination,
94112
'assertion_url': sp_data['assertionConsumerService']['url'],

tests/src/OneLogin/saml2_tests/auth_test.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,74 @@ def testLoginSigned(self):
582582
self.assertIn(return_to, parsed_query['RelayState'])
583583
self.assertIn(OneLogin_Saml2_Constants.RSA_SHA1, parsed_query['SigAlg'])
584584

585+
def testLoginForceAuthN(self):
586+
"""
587+
Tests the login method of the OneLogin_Saml2_Auth class
588+
Case Logout with no parameters. A AuthN Request is built with ForceAuthn and redirect executed
589+
"""
590+
settings_info = self.loadSettingsJSON()
591+
return_to = u'http://example.com/returnto'
592+
sso_url = settings_info['idp']['singleSignOnService']['url']
593+
594+
auth = OneLogin_Saml2_Auth(self.get_request(), old_settings=settings_info)
595+
target_url = auth.login(return_to)
596+
parsed_query = parse_qs(urlparse(target_url)[4])
597+
sso_url = settings_info['idp']['singleSignOnService']['url']
598+
self.assertIn(sso_url, target_url)
599+
self.assertIn('SAMLRequest', parsed_query)
600+
request = OneLogin_Saml2_Utils.decode_base64_and_inflate(parsed_query['SAMLRequest'][0])
601+
self.assertNotIn('ForceAuthn="true"', request)
602+
603+
auth_2 = OneLogin_Saml2_Auth(self.get_request(), old_settings=settings_info)
604+
target_url_2 = auth_2.login(return_to, False, False)
605+
parsed_query_2 = parse_qs(urlparse(target_url_2)[4])
606+
self.assertIn(sso_url, target_url_2)
607+
self.assertIn('SAMLRequest', parsed_query_2)
608+
request_2 = OneLogin_Saml2_Utils.decode_base64_and_inflate(parsed_query_2['SAMLRequest'][0])
609+
self.assertNotIn('ForceAuthn="true"', request_2)
610+
611+
auth_3 = OneLogin_Saml2_Auth(self.get_request(), old_settings=settings_info)
612+
target_url_3 = auth_3.login(return_to, True, False)
613+
parsed_query_3 = parse_qs(urlparse(target_url_3)[4])
614+
self.assertIn(sso_url, target_url_3)
615+
self.assertIn('SAMLRequest', parsed_query_3)
616+
request_3 = OneLogin_Saml2_Utils.decode_base64_and_inflate(parsed_query_3['SAMLRequest'][0])
617+
self.assertIn('ForceAuthn="true"', request_3)
618+
619+
def testLoginIsPassive(self):
620+
"""
621+
Tests the login method of the OneLogin_Saml2_Auth class
622+
Case Logout with no parameters. A AuthN Request is built with IsPassive and redirect executed
623+
"""
624+
settings_info = self.loadSettingsJSON()
625+
return_to = u'http://example.com/returnto'
626+
sso_url = settings_info['idp']['singleSignOnService']['url']
627+
628+
auth = OneLogin_Saml2_Auth(self.get_request(), old_settings=settings_info)
629+
target_url = auth.login(return_to)
630+
parsed_query = parse_qs(urlparse(target_url)[4])
631+
sso_url = settings_info['idp']['singleSignOnService']['url']
632+
self.assertIn(sso_url, target_url)
633+
self.assertIn('SAMLRequest', parsed_query)
634+
request = OneLogin_Saml2_Utils.decode_base64_and_inflate(parsed_query['SAMLRequest'][0])
635+
self.assertNotIn('IsPassive="true"', request)
636+
637+
auth_2 = OneLogin_Saml2_Auth(self.get_request(), old_settings=settings_info)
638+
target_url_2 = auth_2.login(return_to, False, False)
639+
parsed_query_2 = parse_qs(urlparse(target_url_2)[4])
640+
self.assertIn(sso_url, target_url_2)
641+
self.assertIn('SAMLRequest', parsed_query_2)
642+
request_2 = OneLogin_Saml2_Utils.decode_base64_and_inflate(parsed_query_2['SAMLRequest'][0])
643+
self.assertNotIn('IsPassive="true"', request_2)
644+
645+
auth_3 = OneLogin_Saml2_Auth(self.get_request(), old_settings=settings_info)
646+
target_url_3 = auth_3.login(return_to, False, True)
647+
parsed_query_3 = parse_qs(urlparse(target_url_3)[4])
648+
self.assertIn(sso_url, target_url_3)
649+
self.assertIn('SAMLRequest', parsed_query_3)
650+
request_3 = OneLogin_Saml2_Utils.decode_base64_and_inflate(parsed_query_3['SAMLRequest'][0])
651+
self.assertIn('IsPassive="true"', request_3)
652+
585653
def testLogout(self):
586654
"""
587655
Tests the logout method of the OneLogin_Saml2_Auth class

tests/src/OneLogin/saml2_tests/authn_request_test.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,62 @@ def testCreateRequestAuthContext(self):
117117
self.assertIn(OneLogin_Saml2_Constants.AC_PASSWORD_PROTECTED, inflated)
118118
self.assertIn(OneLogin_Saml2_Constants.AC_X509, inflated)
119119

120+
def testCreateRequestForceAuthN(self):
121+
"""
122+
Tests the OneLogin_Saml2_Authn_Request Constructor.
123+
The creation of a deflated SAML Request with ForceAuthn="true"
124+
"""
125+
saml_settings = self.loadSettingsJSON()
126+
settings = OneLogin_Saml2_Settings(saml_settings)
127+
authn_request = OneLogin_Saml2_Authn_Request(settings)
128+
authn_request_encoded = authn_request.get_request()
129+
decoded = b64decode(authn_request_encoded)
130+
inflated = decompress(decoded, -15)
131+
self.assertRegexpMatches(inflated, '^<samlp:AuthnRequest')
132+
self.assertNotIn('ForceAuthn="true"', inflated)
133+
134+
authn_request_2 = OneLogin_Saml2_Authn_Request(settings, False, False)
135+
authn_request_encoded_2 = authn_request_2.get_request()
136+
decoded_2 = b64decode(authn_request_encoded_2)
137+
inflated_2 = decompress(decoded_2, -15)
138+
self.assertRegexpMatches(inflated_2, '^<samlp:AuthnRequest')
139+
self.assertNotIn('ForceAuthn="true"', inflated_2)
140+
141+
authn_request_3 = OneLogin_Saml2_Authn_Request(settings, True, False)
142+
authn_request_encoded_3 = authn_request_3.get_request()
143+
decoded_3 = b64decode(authn_request_encoded_3)
144+
inflated_3 = decompress(decoded_3, -15)
145+
self.assertRegexpMatches(inflated_3, '^<samlp:AuthnRequest')
146+
self.assertIn('ForceAuthn="true"', inflated_3)
147+
148+
def testCreateRequestIsPassive(self):
149+
"""
150+
Tests the OneLogin_Saml2_Authn_Request Constructor.
151+
The creation of a deflated SAML Request with IsPassive="true"
152+
"""
153+
saml_settings = self.loadSettingsJSON()
154+
settings = OneLogin_Saml2_Settings(saml_settings)
155+
authn_request = OneLogin_Saml2_Authn_Request(settings)
156+
authn_request_encoded = authn_request.get_request()
157+
decoded = b64decode(authn_request_encoded)
158+
inflated = decompress(decoded, -15)
159+
self.assertRegexpMatches(inflated, '^<samlp:AuthnRequest')
160+
self.assertNotIn('IsPassive="true"', inflated)
161+
162+
authn_request_2 = OneLogin_Saml2_Authn_Request(settings, False, False)
163+
authn_request_encoded_2 = authn_request_2.get_request()
164+
decoded_2 = b64decode(authn_request_encoded_2)
165+
inflated_2 = decompress(decoded_2, -15)
166+
self.assertRegexpMatches(inflated_2, '^<samlp:AuthnRequest')
167+
self.assertNotIn('IsPassive="true"', inflated_2)
168+
169+
authn_request_3 = OneLogin_Saml2_Authn_Request(settings, False, True)
170+
authn_request_encoded_3 = authn_request_3.get_request()
171+
decoded_3 = b64decode(authn_request_encoded_3)
172+
inflated_3 = decompress(decoded_3, -15)
173+
self.assertRegexpMatches(inflated_3, '^<samlp:AuthnRequest')
174+
self.assertIn('IsPassive="true"', inflated_3)
175+
120176
def testCreateDeflatedSAMLRequestURLParameter(self):
121177
"""
122178
Tests the OneLogin_Saml2_Authn_Request Constructor.

0 commit comments

Comments
 (0)