From 89bc72958e26e786ceae2b2b16a1b74dd52e693a Mon Sep 17 00:00:00 2001 From: Ozzie Isaacs Date: Thu, 16 Feb 2023 16:23:06 +0100 Subject: [PATCH] new random password generation algorithm to ensure compliance with password rules bugfix opds login limit --- cps/admin.py | 16 +++++++++------- cps/helper.py | 29 +++++++++++++++++++++++++++-- cps/usermanagement.py | 3 +++ cps/web.py | 2 +- 4 files changed, 40 insertions(+), 10 deletions(-) diff --git a/cps/admin.py b/cps/admin.py index 1f7937ba..0cf087ef 100644 --- a/cps/admin.py +++ b/cps/admin.py @@ -1961,12 +1961,6 @@ def _handle_edit_user(to_save, content, languages, translations, kobo_support): log.warning("No admin user remaining, can't remove admin role from {}".format(content.name)) flash(_("No admin user remaining, can't remove admin role"), category="error") return redirect(url_for('admin.admin')) - anonymous = content.is_anonymous - content.role = constants.selected_roles(to_save) - if anonymous: - content.role |= constants.ROLE_ANONYMOUS - else: - content.role &= ~constants.ROLE_ANONYMOUS val = [int(k[5:]) for k in to_save if k.startswith('show_')] sidebar, __ = get_sidebar_config() @@ -1994,6 +1988,15 @@ def _handle_edit_user(to_save, content, languages, translations, kobo_support): if to_save.get("locale"): content.locale = to_save["locale"] try: + anonymous = content.is_anonymous + content.role = constants.selected_roles(to_save) + if anonymous: + content.role |= constants.ROLE_ANONYMOUS + else: + content.role &= ~constants.ROLE_ANONYMOUS + if to_save.get("password", ""): + content.password = generate_password_hash(helper.valid_password(to_save.get["password"])) + new_email = valid_email(to_save.get("email", content.email)) if not new_email: raise Exception(_("Email can't be empty and has to be a valid Email")) @@ -2006,7 +2009,6 @@ def _handle_edit_user(to_save, content, languages, translations, kobo_support): content.name = check_username(to_save["name"]) if to_save.get("kindle_mail") != content.kindle_mail: content.kindle_mail = valid_email(to_save["kindle_mail"]) if to_save["kindle_mail"] else "" - content.password = generate_password_hash(helper.valid_password(to_save.get("password", ""))) except Exception as ex: log.error(ex) flash(str(ex), category="error") diff --git a/cps/helper.py b/cps/helper.py index 428cbda2..85d71122 100644 --- a/cps/helper.py +++ b/cps/helper.py @@ -18,6 +18,7 @@ # along with this program. If not, see . import os +import random import io import mimetypes import re @@ -621,11 +622,35 @@ def reset_password(user_id): ub.session.rollback() return 0, None - def generate_random_password(min_length): + min_length = max(8, min_length) - 4 + random_source = "abcdefghijklmnopqrstuvwxyz01234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%&*()?" + # select 1 lowercase + s = "abcdefghijklmnopqrstuvwxyz" + password = [s[c % len(s)] for c in os.urandom(1)] + # select 1 uppercase + s = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + password.extend([s[c % len(s)] for c in os.urandom(1)]) + # select 1 digit + s = "01234567890" + password.extend([s[c % len(s)] for c in os.urandom(1)]) + # select 1 special symbol + s = "!@#$%&*()?" + password.extend([s[c % len(s)] for c in os.urandom(1)]) + + # generate other characters + password.extend([random_source[c % len(random_source)] for c in os.urandom(min_length)]) + + # password_list = list(password) + # shuffle all characters + random.SystemRandom().shuffle(password) + return ''.join(password) + + +'''def generate_random_password(min_length): s = "abcdefghijklmnopqrstuvwxyz01234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%&*()?" passlen = min_length - return "".join(s[c % len(s)] for c in os.urandom(passlen)) + return "".join(s[c % len(s)] for c in os.urandom(passlen))''' def uniq(inpt): diff --git a/cps/usermanagement.py b/cps/usermanagement.py index 4753cd95..d8f64012 100644 --- a/cps/usermanagement.py +++ b/cps/usermanagement.py @@ -52,6 +52,7 @@ def requires_basic_auth_if_no_ano(f): login_result, error = services.ldap.bind_user(auth.username, auth.password) if login_result: user = _fetch_user_by_name(auth.username) + [limiter.limiter.storage.clear(k.key) for k in limiter.current_limits] login_user(user) return f(*args, **kwargs) elif login_result is not None: @@ -68,6 +69,7 @@ def _load_user_from_auth_header(username, password): limiter.check() user = _fetch_user_by_name(username) if bool(user and check_password_hash(str(user.password), password)) and user.name != "Guest": + [limiter.limiter.storage.clear(k.key) for k in limiter.current_limits] login_user(user) return user else: @@ -102,6 +104,7 @@ def load_user_from_reverse_proxy_header(req): if rp_header_username: user = _fetch_user_by_name(rp_header_username) if user: + [limiter.limiter.storage.clear(k.key) for k in limiter.current_limits] login_user(user) return user return None diff --git a/cps/web.py b/cps/web.py index ab3f6388..45e62ec5 100644 --- a/cps/web.py +++ b/cps/web.py @@ -1256,7 +1256,7 @@ def register_post(): content.name = nickname content.email = email password = generate_random_password(config.config_password_min_length) - content.password = generate_password_hash(valid_password(password)) + content.password = generate_password_hash(password) content.role = config.config_default_role content.locale = config.config_default_locale content.sidebar_view = config.config_default_show