Remediate the 2026-09-03 feature-test findings
This commit is contained in:
@@ -187,9 +187,28 @@ def update_user(userid: int):
|
||||
user.failedlogins = 0
|
||||
changes['unlocked'] = {'old': True, 'new': False}
|
||||
|
||||
# Password change
|
||||
# Password change.
|
||||
#
|
||||
# An ADMIN may set one here - that is how a temporary password is issued,
|
||||
# and mustchangepassword forces the holder to replace it at next login.
|
||||
# Somebody changing their OWN password may not: this route asks for no
|
||||
# current password and enforces no length, so it was a way around
|
||||
# /api/auth/change-password, which requires both. The two rules have to
|
||||
# agree or the weaker one is the real one.
|
||||
if 'password' in data and data['password']:
|
||||
if not current_user.hasrole('admin'):
|
||||
return error_response(
|
||||
ErrorCodes.FORBIDDEN,
|
||||
'Change your own password at /api/auth/change-password, which '
|
||||
'requires your current one.',
|
||||
http_code=403)
|
||||
if len(data['password']) < 8:
|
||||
return error_response(
|
||||
ErrorCodes.VALIDATION_ERROR,
|
||||
'Password must be at least 8 characters')
|
||||
user.passwordhash = generate_password_hash(data['password'])
|
||||
# Issued by somebody else, so it is temporary by definition.
|
||||
user.mustchangepassword = True
|
||||
changes['password'] = {'old': '***', 'new': '***'}
|
||||
|
||||
if changes:
|
||||
|
||||
Reference in New Issue
Block a user