299 |
299 |
raise NotImplementedError
|
300 |
300 |
|
301 |
301 |
|
302 |
|
def password_policy_control_messages(ctrl):
|
|
302 |
def password_policy_control_messages(ctrl, attributes):
|
303 |
303 |
messages = []
|
304 |
304 |
|
305 |
305 |
if ctrl.error:
|
306 |
306 |
error = ppolicy.PasswordPolicyError.namedValues[ctrl.error]
|
307 |
307 |
error2message = {
|
308 |
|
'passwordExpired': _('The password expired'),
|
309 |
|
'accountLocked': _('The account is locked.'),
|
|
308 |
'passwordExpired': _('The password expired after {pwdmaxage}').format(**attributes),
|
|
309 |
'accountLocked': _(
|
|
310 |
'The account is locked since {pwdaccountlockedtime[0]} after {pwdmaxfailure} failures.'
|
|
311 |
).format(**attributes),
|
310 |
312 |
'changeAfterReset': _('The password was reset and must be changed.'),
|
311 |
313 |
'passwordModNotAllowed': _('It is not possible to modify the password.'),
|
312 |
314 |
'mustSupplyOldPassword': _('The old password must be supplied.'),
|
313 |
315 |
'insufficientPasswordQuality': _('The password does not meet the quality requirements.'),
|
314 |
|
'passwordTooShort': _('The password is too short.'),
|
315 |
|
'passwordTooYoung': _('It is too soon to change the password.'),
|
316 |
|
'passwordInHistory': _('This password was recently used and cannot be used again.'),
|
|
316 |
'passwordTooShort': _('The password is too short {pwdminlength}.').format(**attributes),
|
|
317 |
'passwordTooYoung': _('It is too soon to change the password {pwdminage}.').format(**attributes),
|
|
318 |
'passwordInHistory': _(
|
|
319 |
'This password is among the last {pwdhistory} password that were used and cannot be used again.'
|
|
320 |
).format(**attributes),
|
317 |
321 |
}
|
318 |
322 |
messages.append(error2message.get(error, _('Unexpected error {error}').format(error=error)))
|
319 |
323 |
return messages
|
... | ... | |
621 |
625 |
'user_attributes': [],
|
622 |
626 |
# https://www.python-ldap.org/en/python-ldap-3.3.0/reference/ldap.html#ldap-controls
|
623 |
627 |
'use_controls': True,
|
|
628 |
'ppolicy_dn': '',
|
624 |
629 |
}
|
625 |
630 |
_REQUIRED = ('url', 'basedn')
|
626 |
631 |
_TO_ITERABLE = ('url', 'groupsu', 'groupstaff', 'groupactive')
|
... | ... | |
659 |
664 |
log.debug('got config %r', blocks)
|
660 |
665 |
return blocks
|
661 |
666 |
|
662 |
|
@staticmethod
|
663 |
|
def process_controls(request, authz_id, ctrls):
|
|
667 |
@classmethod
|
|
668 |
def process_controls(cls, request, block, conn, authz_id, ctrls):
|
|
669 |
attributes = cls.get_ppolicy_attributes(block, conn, authz_id)
|
664 |
670 |
for c in ctrls:
|
665 |
671 |
if c.controlType == ppolicy.PasswordPolicyControl.controlType:
|
666 |
|
message = ' '.join(password_policy_control_messages(c))
|
|
672 |
message = ' '.join(password_policy_control_messages(c, attributes))
|
667 |
673 |
if request is not None:
|
668 |
674 |
messages.add_message(request, messages.WARNING, message)
|
669 |
675 |
if c.graceAuthNsRemaining or c.timeBeforeExpiration:
|
... | ... | |
791 |
797 |
else:
|
792 |
798 |
serverctrls = []
|
793 |
799 |
results = conn.simple_bind_s(authz_id, password, serverctrls=serverctrls)
|
794 |
|
self.process_controls(request, authz_id, results[3])
|
|
800 |
self.process_controls(request, block, conn, authz_id, results[3])
|
795 |
801 |
user_login_success(authz_id)
|
796 |
802 |
if not block['connect_with_user_credentials']:
|
797 |
803 |
try:
|
... | ... | |
803 |
809 |
except ldap.INVALID_CREDENTIALS as e:
|
804 |
810 |
if block.get('use_controls') and len(e.args) > 0 and 'ctrls' in e.args[0]:
|
805 |
811 |
self.process_controls(
|
806 |
|
request, authz_id, DecodeControlTuples(e.args[0]['ctrls'])
|
|
812 |
request, block, conn, authz_id, DecodeControlTuples(e.args[0]['ctrls'])
|
807 |
813 |
)
|
808 |
814 |
attributes = self.get_ldap_attributes(block, conn, authz_id)
|
809 |
815 |
user = self.lookup_existing_user(authz_id, block, attributes)
|
... | ... | |
1167 |
1173 |
attributes.add(at_mapping[key])
|
1168 |
1174 |
return list(set(attribute.lower() for attribute in attributes))
|
1169 |
1175 |
|
|
1176 |
@classmethod
|
|
1177 |
def get_ppolicy_attributes(cls, block, conn, dn):
|
|
1178 |
def get_attributes(dn, attributes):
|
|
1179 |
try:
|
|
1180 |
results = conn.search_s(dn, ldap.SCOPE_BASE, u'(objectclass=*)', attributes)
|
|
1181 |
except ldap.LDAPError as e:
|
|
1182 |
log.error('unable to retrieve attributes of dn %r: %r', dn, e)
|
|
1183 |
return {}
|
|
1184 |
results = cls.normalize_ldap_results(results)
|
|
1185 |
attributes_results.update(results[0][1])
|
|
1186 |
return attributes_results
|
|
1187 |
|
|
1188 |
user_attributes = [
|
|
1189 |
'pwdaccountlockedtime',
|
|
1190 |
'pwdchangedtime',
|
|
1191 |
'pwdfailuretime',
|
|
1192 |
'pwdgraceusetime',
|
|
1193 |
'pwdhistory',
|
|
1194 |
'pwdreset',
|
|
1195 |
]
|
|
1196 |
ppolicy_attributes = [
|
|
1197 |
'pwdminage',
|
|
1198 |
'pwdmaxage',
|
|
1199 |
'pwdinhistory',
|
|
1200 |
'pwdcheckquality',
|
|
1201 |
'pwdminlength',
|
|
1202 |
'pwdexpirewarning',
|
|
1203 |
'pwdgraceauthnlimit',
|
|
1204 |
'pwdlockout',
|
|
1205 |
'pwdlockoutduration',
|
|
1206 |
'pwdmaxfailure',
|
|
1207 |
'pwdmaxrecordedfailure',
|
|
1208 |
'pwdfailurecountinterval',
|
|
1209 |
'pwdmustchange',
|
|
1210 |
'pwdallowuserchange',
|
|
1211 |
'pwdsafemodify',
|
|
1212 |
]
|
|
1213 |
attributes_results = {k: [] for k in user_attributes + ppolicy_attributes}
|
|
1214 |
|
|
1215 |
attributes_results.update(get_attributes(dn, user_attributes))
|
|
1216 |
ppolicy_dn = block.get('ppolicy_dn')
|
|
1217 |
if ppolicy_dn:
|
|
1218 |
attributes_results.update(**get_attributes(ppolicy_dn, ppolicy_attributes))
|
|
1219 |
|
|
1220 |
return attributes_results
|
|
1221 |
|
1170 |
1222 |
@classmethod
|
1171 |
1223 |
def get_ldap_attributes(cls, block, conn, dn):
|
1172 |
1224 |
"""Retrieve some attributes from LDAP, add mandatory values then apply
|