237 |
237 |
raise NotImplementedError
|
238 |
238 |
|
239 |
239 |
|
240 |
|
def password_policy_control_messages(ctrl):
|
|
240 |
def password_policy_control_messages(ctrl, attributes):
|
241 |
241 |
messages = []
|
242 |
242 |
|
243 |
243 |
if ctrl.error:
|
244 |
244 |
error = ppolicy.PasswordPolicyError.namedValues[ctrl.error]
|
245 |
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.'),
|
|
246 |
'passwordExpired': _('The password expired after {pwdmaxage}').format(**attributes),
|
|
247 |
'accountLocked': _('The account is locked since {pwdaccountlockedtime[0]} after {pwdmaxfailure} failures.').format(**attributes),
|
|
248 |
'changeAfterReset': _('The password was reset and must be changed.').format(**attributes),
|
|
249 |
'passwordModNotAllowed': _('It is not possible to modify the password.').format(**attributes),
|
|
250 |
'mustSupplyOldPassword': _('The old password must be supplied.').format(**attributes),
|
|
251 |
'insufficientPasswordQuality': _('The password does not meet the quality requirements.').format(**attributes),
|
|
252 |
'passwordTooShort': _('The password is too short {pwdminlength}.').format(**attributes),
|
|
253 |
'passwordTooYoung': _('It is too soon to change the password {pwdminage}.').format(**attributes),
|
|
254 |
'passwordInHistory': _('This password is among the last {pwdhistory} password that were used and cannot be used again.').format(**attributes),
|
255 |
255 |
}
|
256 |
256 |
messages.append(error2message.get(error, _('Unexpected error {error}').format(error=error)))
|
257 |
257 |
return messages
|
... | ... | |
548 |
548 |
'user_attributes': [],
|
549 |
549 |
# https://www.python-ldap.org/en/python-ldap-3.3.0/reference/ldap.html#ldap-controls
|
550 |
550 |
'use_controls': True,
|
|
551 |
'ppolicy_dn': '',
|
551 |
552 |
}
|
552 |
553 |
_REQUIRED = ('url', 'basedn')
|
553 |
554 |
_TO_ITERABLE = ('url', 'groupsu', 'groupstaff', 'groupactive')
|
... | ... | |
578 |
579 |
log.debug('got config %r', blocks)
|
579 |
580 |
return blocks
|
580 |
581 |
|
581 |
|
@staticmethod
|
582 |
|
def process_controls(request, authz_id, ctrls):
|
|
582 |
@classmethod
|
|
583 |
def process_controls(cls, request, block, conn, authz_id, ctrls):
|
|
584 |
attributes = cls.get_ppolicy_attributes(block, conn, authz_id)
|
583 |
585 |
for c in ctrls:
|
584 |
586 |
if c.controlType == ppolicy.PasswordPolicyControl.controlType:
|
585 |
|
message = ' '.join(password_policy_control_messages(c))
|
|
587 |
message = ' '.join(password_policy_control_messages(c, attributes))
|
586 |
588 |
if request is not None:
|
587 |
589 |
messages.add_message(request, messages.WARNING, message)
|
588 |
590 |
else:
|
... | ... | |
685 |
687 |
else:
|
686 |
688 |
serverctrls = []
|
687 |
689 |
results = conn.simple_bind_s(authz_id, password, serverctrls=serverctrls)
|
688 |
|
self.process_controls(request, authz_id, results[3])
|
|
690 |
self.process_controls(request, block, conn, authz_id, results[3])
|
689 |
691 |
user_login_success(authz_id)
|
690 |
692 |
if not block['connect_with_user_credentials']:
|
691 |
693 |
try:
|
... | ... | |
696 |
698 |
break
|
697 |
699 |
except ldap.INVALID_CREDENTIALS as e:
|
698 |
700 |
if block.get('use_controls') and len(e.args) > 0 and 'ctrls' in e.args[0]:
|
699 |
|
self.process_controls(request, authz_id, DecodeControlTuples(e.args[0]['ctrls']))
|
|
701 |
self.process_controls(request, block, conn, authz_id, DecodeControlTuples(e.args[0]['ctrls']))
|
700 |
702 |
user_login_failure(authz_id)
|
701 |
703 |
pass
|
702 |
704 |
else:
|
... | ... | |
1041 |
1043 |
attributes.add(at_mapping[key])
|
1042 |
1044 |
return list(set(attribute.lower() for attribute in attributes))
|
1043 |
1045 |
|
|
1046 |
@classmethod
|
|
1047 |
def get_ppolicy_attributes(cls, block, conn, dn):
|
|
1048 |
|
|
1049 |
def get_attributes(dn, attributes):
|
|
1050 |
try:
|
|
1051 |
results = conn.search_s(dn, ldap.SCOPE_BASE, u'(objectclass=*)', attributes)
|
|
1052 |
except ldap.LDAPError as e:
|
|
1053 |
log.error('unable to retrieve attributes of dn %r: %r', dn, e)
|
|
1054 |
return {}
|
|
1055 |
results = cls.normalize_ldap_results(results)
|
|
1056 |
attributes_results.update(results[0][1])
|
|
1057 |
return attributes_results
|
|
1058 |
|
|
1059 |
user_attributes = [
|
|
1060 |
'pwdaccountlockedtime',
|
|
1061 |
'pwdchangedtime',
|
|
1062 |
'pwdfailuretime',
|
|
1063 |
'pwdgraceusetime',
|
|
1064 |
'pwdhistory',
|
|
1065 |
'pwdreset',
|
|
1066 |
]
|
|
1067 |
ppolicy_attributes = [
|
|
1068 |
'pwdminage',
|
|
1069 |
'pwdmaxage',
|
|
1070 |
'pwdinhistory',
|
|
1071 |
'pwdcheckquality',
|
|
1072 |
'pwdminlength',
|
|
1073 |
'pwdexpirewarning',
|
|
1074 |
'pwdgraceauthnlimit',
|
|
1075 |
'pwdlockout',
|
|
1076 |
'pwdlockoutduration',
|
|
1077 |
'pwdmaxfailure',
|
|
1078 |
'pwdmaxrecordedfailure',
|
|
1079 |
'pwdfailurecountinterval',
|
|
1080 |
'pwdmustchange',
|
|
1081 |
'pwdallowuserchange',
|
|
1082 |
'pwdsafemodify',
|
|
1083 |
]
|
|
1084 |
attributes_results = { k: [] for k in user_attributes + ppolicy_attributes }
|
|
1085 |
|
|
1086 |
attributes_results.update(get_attributes(dn, user_attributes))
|
|
1087 |
ppolicy_dn = block.get('ppolicy_dn')
|
|
1088 |
if ppolicy_dn:
|
|
1089 |
attributes_results.update(**get_attributes(ppolicy_dn, ppolicy_attributes))
|
|
1090 |
|
|
1091 |
print(attributes_results)
|
|
1092 |
return attributes_results
|
|
1093 |
|
|
1094 |
|
1044 |
1095 |
@classmethod
|
1045 |
1096 |
def get_ldap_attributes(cls, block, conn, dn):
|
1046 |
1097 |
'''Retrieve some attributes from LDAP, add mandatory values then apply
|