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