Project

General

Profile

0001-ldap-move-messages-from-hard-coded-to-settings-51294.patch

Loïc Dachary, 04 March 2021 04:59 PM

Download (7.42 KB)

View differences:

Subject: [PATCH] ldap: move messages from hard coded to settings (#51294)

Fixes: #51294

Licenses: MIT
 src/authentic2/backends/ldap_backend.py | 59 ++++++++++++++-----------
 tests/test_ldap.py                      |  6 +--
 2 files changed, 35 insertions(+), 30 deletions(-)
src/authentic2/backends/ldap_backend.py
47 47
from django.contrib import messages
48 48
from django.contrib.auth import get_user_model
49 49
from django.contrib.auth.models import Group
50
from django.template import loader
50 51
from django.utils.encoding import force_bytes, force_text
51 52
from django.utils import six
52 53
from django.utils.six.moves.urllib import parse as urlparse
53
from django.utils.translation import ugettext as _, ngettext
54
from django.utils.translation import ugettext as _, gettext
54 55

  
55 56
from authentic2.a2_rbac.models import Role
56 57

  
......
237 238
    raise NotImplementedError
238 239

  
239 240

  
240
def password_policy_control_messages(ctrl):
241
    messages = []
241
def password_policy_control_messages(ctrl, control_messages):
242
    results = []
242 243

  
243 244
    if ctrl.error:
244 245
        error = ppolicy.PasswordPolicyError.namedValues[ctrl.error]
245
        error2message = {
246
            'passwordExpired': _('The password expired'),
247
            'accountLocked': _('The account is locked.'),
248
            'changeAfterReset': _('The password was reset and must be changed.'),
249
            'passwordModNotAllowed': _('It is not possible to modify the password.'),
250
            'mustSupplyOldPassword': _('The old password must be supplied.'),
251
            'insufficientPasswordQuality': _('The password does not meet the quality requirements.'),
252
            'passwordTooShort': _('The password is too short.'),
253
            'passwordTooYoung': _('It is too soon to change the password.'),
254
            'passwordInHistory': _('This password was recently used and cannot be used again.'),
255
        }
256
        messages.append(error2message.get(error, _('Unexpected error {error}').format(error=error)))
257
        return messages
246
        if error not in control_messages:
247
            results.append(_('Unexpected error {error}').format(error=error))
248
        else:
249
            results.append(control_messages[error])
250
        return results
258 251

  
259 252
    if ctrl.timeBeforeExpiration:
260 253
        expiration_date = time.asctime(time.localtime(time.time() + ctrl.timeBeforeExpiration))
261
        messages.append(_('The password will expire at {expiration_date}.').format(
262
            expiration_date=expiration_date))
254
        results.append(control_messages['expiration_date'].format(expiration_date=expiration_date))
263 255
    if ctrl.graceAuthNsRemaining:
264
        messages.append(ngettext(
265
            'This password expired: this is the last time it can be used.',
266
            'This password expired and can only be used {graceAuthNsRemaining} times, including this one.',
267
            ctrl.graceAuthNsRemaining).format(graceAuthNsRemaining=ctrl.graceAuthNsRemaining))
268
    return messages
256
        results.append(control_messages['graceAuthNsRemaining'].format(graceAuthNsRemaining=ctrl.graceAuthNsRemaining))
257
    return results
269 258

  
270 259
class LDAPUser(User):
271 260
    SESSION_LDAP_DATA_KEY = 'ldap-data'
......
548 537
        'user_attributes': [],
549 538
        # https://www.python-ldap.org/en/python-ldap-3.3.0/reference/ldap.html#ldap-controls
550 539
        'use_controls': True,
540
        'ppolicy_messages': {
541
            'passwordExpired': gettext('The password expired'),
542
            'accountLocked': gettext('The account is locked.'),
543
            'changeAfterReset': gettext('The password was reset and must be changed.'),
544
            'passwordModNotAllowed': gettext('It is not possible to modify the password.'),
545
            'mustSupplyOldPassword': gettext('The old password must be supplied.'),
546
            'insufficientPasswordQuality': gettext('The password does not meet the quality requirements.'),
547
            'passwordTooShort': gettext('The password is too short.'),
548
            'passwordTooYoung': gettext('It is too soon to change the password.'),
549
            'passwordInHistory': gettext('This password was recently used and cannot be used again.'),
550
            'expiration_date': gettext('The password will expire at {expiration_date}.'),
551
            'graceAuthNsRemaining': gettext(
552
                'This password expired and can only be used {graceAuthNsRemaining} time(s), including this one.'
553
            ),
554
        },
551 555
    }
552 556
    _REQUIRED = ('url', 'basedn')
553 557
    _TO_ITERABLE = ('url', 'groupsu', 'groupstaff', 'groupactive')
......
579 583
        return blocks
580 584

  
581 585
    @staticmethod
582
    def process_controls(request, authz_id, ctrls):
586
    def process_controls(request, authz_id, ctrls, block):
583 587
        for c in ctrls:
584 588
            if c.controlType == ppolicy.PasswordPolicyControl.controlType:
585
                message = ' '.join(password_policy_control_messages(c))
589
                message = ' '.join(password_policy_control_messages(c, block['ppolicy_messages']))
586 590
                if request is not None:
587 591
                    messages.add_message(request, messages.WARNING, message)
588 592
                    if c.graceAuthNsRemaining or c.timeBeforeExpiration:
......
708 712
                            else:
709 713
                                serverctrls = []
710 714
                            results = conn.simple_bind_s(authz_id, password, serverctrls=serverctrls)
711
                            self.process_controls(request, authz_id, results[3])
715
                            self.process_controls(request, authz_id, results[3], block)
712 716
                            user_login_success(authz_id)
713 717
                            if not block['connect_with_user_credentials']:
714 718
                                try:
......
719 723
                            break
720 724
                        except ldap.INVALID_CREDENTIALS as e:
721 725
                            if block.get('use_controls') and len(e.args) > 0 and 'ctrls' in e.args[0]:
722
                                self.process_controls(request, authz_id, DecodeControlTuples(e.args[0]['ctrls']))
726
                                self.process_controls(request, authz_id,
727
                                                      DecodeControlTuples(e.args[0]['ctrls']), block)
723 728
                            user_login_failure(authz_id)
724 729
                            pass
725 730
                    else:
tests/test_ldap.py
1089 1089

  
1090 1090
    assert 'used 2 time' not in caplog.text
1091 1091
    assert authenticate(username=USERNAME, password=password) is not None
1092
    assert 'used 2 times' in caplog.text
1092
    assert 'used 2 time' in caplog.text
1093 1093

  
1094
    assert 'last time' not in caplog.text
1094
    assert 'used 1 time' not in caplog.text
1095 1095
    assert authenticate(username=USERNAME, password=password) is not None
1096
    assert 'last time' in caplog.text
1096
    assert 'used 1 time' in caplog.text
1097 1097

  
1098 1098

  
1099 1099
def test_authenticate_ppolicy_pwdExpireWarning(slapd_ppolicy, settings, db, caplog):
1100
-