Commit b9097101 authored by Jérome Perrin's avatar Jérome Perrin

xhtml_style/authentication_policy: merge "logged_in" implementation

This simplify code and also bring the functionnality of c484f8aa
(erp5_xhtml_style Base_cancel and logged_in: do not allow redirection outside
ERP5 site., 2016-02-12) for the cases where authentication_policy is
installed.

This also fixes a problem with translations of "Your password will expire at
 {date}", which was using different messages for every possible date.

Tests needed to be updated because we now redirect with properly URL encoded
parameters.
parent 40cfe242
Pipeline #13111 failed with stage
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ZopePageTemplate" module="Products.PageTemplates.ZopePageTemplate"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/html</string> </value>
</item>
<item>
<key> <string>expand</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>logged_in</string> </value>
</item>
<item>
<key> <string>output_encoding</string> </key>
<value> <string>iso-8859-15</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <unicode></unicode> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<tal:block xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:metal="http://xml.zope.org/namespaces/metal"
xmlns:i18n="http://xml.zope.org/namespaces/i18n">
<tal:block tal:condition="here/portal_skins/updateSkinCookie | nothing"
tal:define="dummy here/setupCurrentSkin;" />
<tal:block tal:define="response request/RESPONSE;
mtool here/portal_membership;
isAnon mtool/isAnonymousUser|nothing;">
<tal:block tal:condition="isAnon">
<tal:block tal:define="dummy python: response.expireCookie('__ac', path='/');
is_user_account_blocked python: request.get('is_user_account_blocked', False);
is_user_account_password_expired python: request.get('is_user_account_password_expired', False);">
<!-- Login and/or password is incorrect. -->
<tal:block tal:condition="python: not is_user_account_blocked and not is_user_account_password_expired">
<tal:block tal:define="url python: '%s/login_form?portal_status_message=%s' % (here.absolute_url(), here.Base_translateString('Login and/or password is incorrect.'));
url python: request.get('came_from') and '%s&amp;came_from=%s' % (url, request['came_from']) or url;
dummy python: response.redirect(url);"/>
</tal:block>
<!-- Login is blocked. -->
<tal:block tal:condition="is_user_account_blocked">
<tal:block tal:define="url python: '%s/login_form?portal_status_message=%s' % (here.absolute_url(), here.Base_translateString('Account is blocked.'));
url python: request.get('came_from') and '%s&amp;came_from=%s' % (url, request['came_from']) or url;
dummy python: response.redirect(url);"/>
</tal:block>
<!-- Password is expired permanently. -->
<tal:block tal:condition="is_user_account_password_expired">
<tal:block tal:define="message python: {False: 'Password is expired.',
True: 'Password is expired. You will soon receive an email with details about how you can recover it.'}.get(here.getPortalObject().portal_preferences.isPreferredSystemRecoverExpiredPassword());
url python: '%s/login_form?portal_status_message=%s' % (here.absolute_url(), here.Base_translateString(message));
url python: request.get('came_from') and '%s&amp;came_from=%s' % (url, request['came_from']) or url;
dummy python: response.redirect(url);"/>
</tal:block>
</tal:block>
</tal:block>
<tal:block tal:condition="not: isAnon"
tal:define="is_user_account_password_expired_expire_date python:request.get('is_user_account_password_expired_expire_date', 0);">
<!-- Password will expire soon just warn user ? -->
<tal:block tal:condition="is_user_account_password_expired_expire_date">
<tal:block tal:define="came_from python: request.get('came_from') or here.absolute_url();
dummy python: response.redirect('%s/ERP5Site_viewNewPersonCredentialUpdateDialog?portal_status_message=%s&amp;cancel_url=%s' %(came_from, here.Base_translateString('Your password will expire at %s. You are advised to change it as soon as possible.' %context.Base_FormatDate(is_user_account_password_expired_expire_date, hour_minute=1)), came_from));" />
</tal:block>
<tal:block tal:condition="not: is_user_account_password_expired_expire_date">
<tal:block tal:define="came_from python: request.get('came_from') or here.absolute_url();
dummy python: response.redirect(came_from);" />
</tal:block>
</tal:block>
</tal:block>
</tal:block>
\ No newline at end of file
......@@ -31,6 +31,7 @@
from functools import partial
import unittest
import urllib
import urlparse
from StringIO import StringIO
import time
import httplib
......@@ -711,7 +712,10 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
basic='test-05:used_ALREADY_1234',
)
response = publish()
self.assertTrue(response.getHeader("Location").endswith("login_form?portal_status_message=Account is blocked."))
redirect_url = urlparse.urlparse(response.getHeader("Location"))
self.assertEqual(redirect_url.path, '{}/login_form'.format(portal.absolute_url_path()))
redirect_url_params = urlparse.parse_qsl(redirect_url.query)
self.assertEqual(redirect_url_params, [('portal_status_message', 'Account is blocked.')] )
# test expire password message, first unblock it
login.Login_unblockLogin()
......@@ -719,7 +723,10 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
self.tic()
self._clearCache()
response = publish()
self.assertTrue(response.getHeader("Location").endswith("login_form?portal_status_message=Password is expired."))
redirect_url = urlparse.urlparse(response.getHeader("Location"))
self.assertEqual(redirect_url.path, '{}/login_form'.format(portal.absolute_url_path()))
redirect_url_params = urlparse.parse_qsl(redirect_url.query)
self.assertEqual(redirect_url_params, [('portal_status_message', 'Password is expired.')] )
self.assertTrue(login.isPasswordExpired())
# test we're redirected to update password due to soon expire
......@@ -728,9 +735,11 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
self.tic()
self._clearCache()
response = publish()
self.assertTrue('Your password will expire' in response.getHeader("Location"))
self.assertTrue('You are advised to change it as soon as possible' in response.getHeader("Location"))
redirect_url = urlparse.urlparse(response.getHeader("Location"))
self.assertEqual(redirect_url.path, '{}/ERP5Site_viewNewPersonCredentialUpdateDialog'.format(portal.absolute_url_path()))
redirect_url_params = urlparse.parse_qs(redirect_url.query)
self.assertIn('Your password will expire', redirect_url_params['portal_status_message'][0])
self.assertIn('You are advised to change it as soon as possible', redirect_url_params['portal_status_message'][0])
# test proper login
preference.setPreferredPasswordLifetimeExpireWarningDuration(12)
......@@ -742,6 +751,18 @@ class TestAuthenticationPolicy(ERP5TypeTestCase):
)
self.assertTrue('Welcome to ERP5' in response.getBody())
# test external redirection prevention
response = self.publish(
portal.absolute_url_path() + '/logged_in',
basic='test-05:used_ALREADY_1234',
stdin=StringIO(urllib.urlencode({'came_from': 'https://www.erp5.com'})),
request_method='POST',
)
redirect_url = urlparse.urlparse(response.getHeader("Location"))
self.assertEqual(redirect_url.path, portal.absolute_url_path())
redirect_url_params = urlparse.parse_qsl(redirect_url.query)
self.assertEqual(redirect_url_params, [('portal_status_message', 'Redirection to an external site prevented.')] )
def test_ExpireOldAuthenticationEventList(self):
"""
Check that expiring old Authentication Event list works.
......
portal = context.getPortalObject()
translateString = portal.Base_translateString
if portal.portal_skins.updateSkinCookie():
portal.setupCurrentSkin()
url = REQUEST.get("came_from")
if portal.portal_membership.isAnonymousUser():
RESPONSE.expireCookie("__ac", path="/")
keep_item_dict = {
'portal_status_message': context.Base_translateString("Login and/or password is incorrect."),
'portal_status_message': translateString("Login and/or password is incorrect."),
}
if url:
keep_item_dict['came_from'] = url
# handle authentication policy requests parameters
if REQUEST.get('is_user_account_blocked'):
keep_item_dict['portal_status_message'] = translateString('Account is blocked.')
if REQUEST.get('is_user_account_password_expired'):
keep_item_dict['portal_status_message'] = translateString('Password is expired.')
if portal.portal_preferences.isPreferredSystemRecoverExpiredPassword():
keep_item_dict['portal_status_message'] = translateString(
'Password is expired. You will soon receive an email with details about how you can recover it.')
context.Base_redirect(
form_id='login_form',
keep_items=keep_item_dict,
......@@ -16,11 +25,25 @@ if portal.portal_membership.isAnonymousUser():
return
if not url:
url = context.absolute_url()
if REQUEST.get('is_user_account_password_expired_expire_date'):
return context.Base_redirect(
'ERP5Site_viewNewPersonCredentialUpdateDialog',
keep_items={
'cancel_url': url,
'portal_status_message': translateString(
'Your password will expire at {date}. '
'You are advised to change it as soon as possible.',
mapping={'date':
portal.Base_FormatDate(
REQUEST.get('is_user_account_password_expired_expire_date'),
hour_minute=1)})})
topmost_url_document = context.Base_getURLTopmostDocumentValue()
if not topmost_url_document.isURLAncestorOf(url):
return topmost_url_document.Base_redirect(
keep_items={
'portal_status_message': context.Base_translateString('Redirection to an external site prevented.'),
'portal_status_message': translateString('Redirection to an external site prevented.'),
}
)
return RESPONSE.redirect(url)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment