0001-ldap-redirect-password-change-if-it-is-about-to-expi.patch
src/authentic2/backends/ldap_backend.py | ||
---|---|---|
56 | 56 | |
57 | 57 |
from authentic2.compat_lasso import lasso |
58 | 58 | |
59 |
from authentic2 import crypto, app_settings |
|
59 |
from authentic2 import crypto, app_settings, constants
|
|
60 | 60 |
from authentic2.models import UserExternalId |
61 | 61 |
from authentic2.middleware import StoreRequestMiddleware |
62 | 62 |
from authentic2.user_login_failure import user_login_failure, user_login_success |
... | ... | |
585 | 585 |
message = ' '.join(password_policy_control_messages(c)) |
586 | 586 |
if request is not None: |
587 | 587 |
messages.add_message(request, messages.WARNING, message) |
588 |
if c.graceAuthNsRemaining or c.timeBeforeExpiration: |
|
589 |
request.session[constants.PASSWORD_EXPIRATION_SESSION_KEY] = True |
|
588 | 590 |
else: |
589 | 591 |
message = str(vars(c)) |
590 | 592 |
log.info('ldap: bind error with authz_id "%s" -> "%s"', authz_id, message) |
src/authentic2/constants.py | ||
---|---|---|
19 | 19 |
CANCEL_FIELD_NAME = 'cancel' |
20 | 20 |
AUTHENTICATION_EVENTS_SESSION_KEY = 'authentication-events' |
21 | 21 |
SWITCH_USER_SESSION_KEY = '_switch_user' |
22 |
PASSWORD_EXPIRATION_SESSION_KEY = '_password_expiration' |
|
22 | 23 |
LAST_LOGIN_SESSION_KEY = '_last_login' |
23 | 24 |
SERVICE_FIELD_NAME = 'service' |
24 | 25 |
NEXT_URL_SIGNATURE = 'next-signature' |
src/authentic2/middleware.py | ||
---|---|---|
28 | 28 |
from django.utils.six.moves.urllib import parse as urlparse |
29 | 29 |
from django import http |
30 | 30 | |
31 |
from . import app_settings, utils, plugins |
|
31 |
from . import app_settings, utils, plugins, constants
|
|
32 | 32 |
from .utils.service import get_service_from_request, get_service_from_session |
33 | 33 | |
34 | 34 | |
... | ... | |
167 | 167 |
return utils.redirect(request, 'continue', resolve=True, params={'next': url}) |
168 | 168 | |
169 | 169 | |
170 |
class ChangePasswordAboutToExpireMiddleware(MiddlewareMixin): |
|
171 |
'''If the password is about to expire, redirect the user to the password |
|
172 |
modification page. |
|
173 |
''' |
|
174 |
def process_response(self, request, response): |
|
175 |
if request.session.get(constants.PASSWORD_EXPIRATION_SESSION_KEY) is None: |
|
176 |
return response |
|
177 |
del request.session[constants.PASSWORD_EXPIRATION_SESSION_KEY] |
|
178 |
return utils.redirect(request, 'password_change', resolve=True) |
|
179 | ||
180 | ||
170 | 181 |
class ServiceAccessControlMiddleware(MiddlewareMixin): |
171 | 182 |
def process_exception(self, request, exception): |
172 | 183 |
if not isinstance(exception, (utils.ServiceAccessDenied,)): |
src/authentic2/settings.py | ||
---|---|---|
92 | 92 |
'authentic2.middleware.StoreRequestMiddleware', |
93 | 93 |
'authentic2.middleware.RequestIdMiddleware', |
94 | 94 |
'authentic2.middleware.ServiceAccessControlMiddleware', |
95 |
'authentic2.middleware.ChangePasswordAboutToExpireMiddleware', |
|
95 | 96 |
'authentic2.middleware.CookieTestMiddleware', |
96 | 97 |
'django.middleware.common.CommonMiddleware', |
97 | 98 |
'django.middleware.http.ConditionalGetMiddleware', |
tests/test_ldap.py | ||
---|---|---|
742 | 742 |
assert force_bytes('Étienne Michu') in response.body |
743 | 743 | |
744 | 744 | |
745 |
def test_reset_password_ldap_user(slapd, settings, app, db): |
|
746 |
settings.LDAP_AUTH_SETTINGS = [{ |
|
747 |
'url': [slapd.ldap_url], |
|
748 |
'binddn': force_text(slapd.root_bind_dn), |
|
749 |
'bindpw': force_text(slapd.root_bind_password), |
|
750 |
'basedn': u'o=ôrga', |
|
751 |
'use_tls': False, |
|
752 |
}] |
|
745 |
def reset_password_ldap_user(settings, app): |
|
753 | 746 |
assert User.objects.count() == 0 |
754 | 747 |
# first login |
755 | 748 |
response = app.get('/login/') |
... | ... | |
781 | 774 |
response.form['new_password1'] = new_password |
782 | 775 |
response.form['new_password2'] = new_password |
783 | 776 |
response = response.form.submit(status=302).maybe_follow() |
777 |
# logout |
|
778 |
response = response.click('Logout') |
|
779 |
if response.status_code == 200: # Django 1.7, same_origin is bugged; testserver != localhost:80 |
|
780 |
response = response.form.submit().maybe_follow() |
|
781 |
else: |
|
782 |
response = response.maybe_follow() |
|
783 |
return new_password |
|
784 | ||
785 | ||
786 |
def test_reset_password_ldap_user(slapd, settings, app, db): |
|
787 |
settings.LDAP_AUTH_SETTINGS = [{ |
|
788 |
'url': [slapd.ldap_url], |
|
789 |
'binddn': force_text(slapd.root_bind_dn), |
|
790 |
'bindpw': force_text(slapd.root_bind_password), |
|
791 |
'basedn': u'o=ôrga', |
|
792 |
'use_tls': False, |
|
793 |
}] |
|
794 |
new_password = reset_password_ldap_user(settings, app) |
|
784 | 795 |
# verify password has changed |
785 | 796 |
slapd.get_connection().bind_s(DN, new_password) |
786 | 797 |
with pytest.raises(ldap.INVALID_CREDENTIALS): |
... | ... | |
1130 | 1141 |
assert 'password will expire' in caplog.text |
1131 | 1142 | |
1132 | 1143 | |
1144 |
def test_login_ppolicy_pwdExpireWarning(slapd_ppolicy, settings, app, db, caplog): |
|
1145 |
settings.LDAP_AUTH_SETTINGS = [{ |
|
1146 |
'url': [slapd_ppolicy.ldap_url], |
|
1147 |
'binddn': force_text(slapd_ppolicy.root_bind_dn), |
|
1148 |
'bindpw': force_text(slapd_ppolicy.root_bind_password), |
|
1149 |
'basedn': u'o=ôrga', |
|
1150 |
'use_tls': False, |
|
1151 |
}] |
|
1152 | ||
1153 |
pwdMaxAge = 3600 |
|
1154 |
slapd_ppolicy.add_ldif(''' |
|
1155 |
dn: cn=default,ou=ppolicies,o=ôrga |
|
1156 |
cn: default |
|
1157 |
objectclass: top |
|
1158 |
objectclass: device |
|
1159 |
objectclass: pwdPolicy |
|
1160 |
objectclass: pwdPolicyChecker |
|
1161 |
pwdAttribute: userPassword |
|
1162 |
pwdMinAge: 0 |
|
1163 |
pwdMaxAge: {pwdMaxAge} |
|
1164 |
pwdInHistory: 1 |
|
1165 |
pwdCheckQuality: 0 |
|
1166 |
pwdMinLength: 0 |
|
1167 |
pwdExpireWarning: {pwdMaxAge} |
|
1168 |
pwdGraceAuthnLimit: 0 |
|
1169 |
pwdLockout: TRUE |
|
1170 |
pwdLockoutDuration: 0 |
|
1171 |
pwdMaxFailure: 0 |
|
1172 |
pwdMaxRecordedFailure: 0 |
|
1173 |
pwdFailureCountInterval: 0 |
|
1174 |
pwdMustChange: FALSE |
|
1175 |
pwdAllowUserChange: TRUE |
|
1176 |
pwdSafeModify: FALSE |
|
1177 |
'''.format(pwdMaxAge=pwdMaxAge)) |
|
1178 | ||
1179 |
password = reset_password_ldap_user(settings, app) |
|
1180 | ||
1181 |
time.sleep(2) |
|
1182 | ||
1183 |
response = app.get('/login/') |
|
1184 |
response.form['username'] = USERNAME |
|
1185 |
response.form['password'] = password |
|
1186 |
response = response.form.submit('login-password-submit') |
|
1187 |
assert response['Location'].endswith('/password/change/') |
|
1188 | ||
1189 | ||
1133 | 1190 |
def test_authenticate_ppolicy_pwdAllowUserChange(slapd_ppolicy, settings, db, caplog): |
1134 | 1191 |
settings.LDAP_AUTH_SETTINGS = [{ |
1135 | 1192 |
'url': [slapd_ppolicy.ldap_url], |
1136 |
- |