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
|
... | ... | |
226 |
227 |
|
227 |
228 |
|
228 |
229 |
def map_text(d):
|
229 |
|
if d is None:
|
|
230 |
if d is None or callable(d):
|
230 |
231 |
return d
|
231 |
232 |
elif isinstance(d, six.string_types):
|
232 |
233 |
return force_text(d)
|
... | ... | |
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 |
|
timeBeforeExpiration = time.asctime(time.localtime(time.time() + ctrl.timeBeforeExpiration))
|
261 |
|
messages.append(_('The password will expire at {timeBeforeExpiration}.').format(
|
262 |
|
timeBeforeExpiration=timeBeforeExpiration))
|
|
253 |
expiration_date = time.asctime(time.localtime(time.time() + ctrl.timeBeforeExpiration))
|
|
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'](ctrl.graceAuthNsRemaining).format(
|
|
257 |
graceAuthNsRemaining=ctrl.graceAuthNsRemaining))
|
|
258 |
return results
|
269 |
259 |
|
270 |
260 |
class LDAPUser(User):
|
271 |
261 |
SESSION_LDAP_DATA_KEY = 'ldap-data'
|
... | ... | |
548 |
538 |
'user_attributes': [],
|
549 |
539 |
# https://www.python-ldap.org/en/python-ldap-3.3.0/reference/ldap.html#ldap-controls
|
550 |
540 |
'use_controls': True,
|
|
541 |
'ppolicy_messages': {
|
|
542 |
'passwordExpired': lambda *args: _('The password expired'),
|
|
543 |
'accountLocked': lambda *args: _('The account is locked.'),
|
|
544 |
'changeAfterReset': lambda *args: _('The password was reset and must be changed.'),
|
|
545 |
'passwordModNotAllowed': lambda *args: _('It is not possible to modify the password.'),
|
|
546 |
'mustSupplyOldPassword': lambda *args: _('The old password must be supplied.'),
|
|
547 |
'insufficientPasswordQuality': lambda *args: _('The password does not meet the quality requirements.'),
|
|
548 |
'passwordTooShort': lambda *args: _('The password is too short.'),
|
|
549 |
'passwordTooYoung': lambda *args: _('It is too soon to change the password.'),
|
|
550 |
'passwordInHistory': lambda *args: _('This password was recently used and cannot be used again.'),
|
|
551 |
'expiration_date': lambda *args: _('The password will expire at {expiration_date}.'),
|
|
552 |
'graceAuthNsRemaining': lambda *args: ngettext(
|
|
553 |
'This password expired: this is the last time it can be used.',
|
|
554 |
'This password expired and can only be used {graceAuthNsRemaining} times, including this one.',
|
|
555 |
*args),
|
|
556 |
},
|
551 |
557 |
}
|
552 |
558 |
_REQUIRED = ('url', 'basedn')
|
553 |
559 |
_TO_ITERABLE = ('url', 'groupsu', 'groupstaff', 'groupactive')
|
... | ... | |
579 |
585 |
return blocks
|
580 |
586 |
|
581 |
587 |
@staticmethod
|
582 |
|
def process_controls(request, authz_id, ctrls):
|
|
588 |
def process_controls(request, authz_id, ctrls, block):
|
583 |
589 |
for c in ctrls:
|
584 |
590 |
if c.controlType == ppolicy.PasswordPolicyControl.controlType:
|
585 |
|
message = ' '.join(password_policy_control_messages(c))
|
|
591 |
message = ' '.join(password_policy_control_messages(c, block['ppolicy_messages']))
|
586 |
592 |
if request is not None:
|
587 |
593 |
messages.add_message(request, messages.WARNING, message)
|
588 |
594 |
else:
|
... | ... | |
706 |
712 |
else:
|
707 |
713 |
serverctrls = []
|
708 |
714 |
results = conn.simple_bind_s(authz_id, password, serverctrls=serverctrls)
|
709 |
|
self.process_controls(request, authz_id, results[3])
|
|
715 |
self.process_controls(request, authz_id, results[3], block)
|
710 |
716 |
user_login_success(authz_id)
|
711 |
717 |
if not block['connect_with_user_credentials']:
|
712 |
718 |
try:
|
... | ... | |
717 |
723 |
break
|
718 |
724 |
except ldap.INVALID_CREDENTIALS as e:
|
719 |
725 |
if block.get('use_controls') and len(e.args) > 0 and 'ctrls' in e.args[0]:
|
720 |
|
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)
|
721 |
728 |
user_login_failure(authz_id)
|
722 |
729 |
pass
|
723 |
730 |
else:
|
724 |
|
-
|