Implement correct password verification of Umlaunts, kyrillic, greek.. charactersets, CJK-Characters, and special characters (#2964)
This commit is contained in:
commit
14b578dd3a
|
@ -125,13 +125,6 @@ def create_app():
|
|||
|
||||
ub.password_change(cli_param.user_credentials)
|
||||
|
||||
if not limiter:
|
||||
log.info('*** "flask-limiter" is needed for calibre-web to run. '
|
||||
'Please install it using pip: "pip install flask-limiter" ***')
|
||||
print('*** "flask-limiter" is needed for calibre-web to run. '
|
||||
'Please install it using pip: "pip install flask-limiter" ***')
|
||||
web_server.stop(True)
|
||||
sys.exit(8)
|
||||
if sys.version_info < (3, 0):
|
||||
log.info(
|
||||
'*** Python2 is EOL since end of 2019, this version of Calibre-Web is no longer supporting Python2, '
|
||||
|
@ -141,13 +134,6 @@ def create_app():
|
|||
'please update your installation to Python3 ***')
|
||||
web_server.stop(True)
|
||||
sys.exit(5)
|
||||
if not wtf_present:
|
||||
log.info('*** "flask-WTF" is needed for calibre-web to run. '
|
||||
'Please install it using pip: "pip install flask-WTF" ***')
|
||||
print('*** "flask-WTF" is needed for calibre-web to run. '
|
||||
'Please install it using pip: "pip install flask-WTF" ***')
|
||||
web_server.stop(True)
|
||||
sys.exit(7)
|
||||
|
||||
lm.login_view = 'web.login'
|
||||
lm.anonymous_user = ub.Anonymous
|
||||
|
@ -158,13 +144,21 @@ def create_app():
|
|||
calibre_db.init_db()
|
||||
|
||||
updater_thread.init_updater(config, web_server)
|
||||
# Perform dry run of updater and exit afterwards
|
||||
# Perform dry run of updater and exit afterward
|
||||
if cli_param.dry_run:
|
||||
updater_thread.dry_run()
|
||||
sys.exit(0)
|
||||
updater_thread.start()
|
||||
|
||||
for res in dependency_check() + dependency_check(True):
|
||||
requirements = dependency_check()
|
||||
for res in requirements:
|
||||
if res['found'] == "not installed":
|
||||
message = ('Cannot import {name} module, it is needed to run calibre-web, '
|
||||
'please install it using "pip install {name}"').format(name=res["name"])
|
||||
log.info(message)
|
||||
print("*** " + message + " ***")
|
||||
web_server.stop(True)
|
||||
sys.exit(8)
|
||||
for res in requirements + dependency_check(True):
|
||||
log.info('*** "{}" version does not meet the requirements. '
|
||||
'Should: {}, Found: {}, please consider installing required version ***'
|
||||
.format(res['name'],
|
||||
|
|
|
@ -1834,6 +1834,7 @@ def _configuration_update_helper():
|
|||
_config_checkbox(to_save, "config_password_number")
|
||||
_config_checkbox(to_save, "config_password_lower")
|
||||
_config_checkbox(to_save, "config_password_upper")
|
||||
_config_checkbox(to_save, "config_password_character")
|
||||
_config_checkbox(to_save, "config_password_special")
|
||||
if 0 < int(to_save.get("config_password_min_length", "0")) < 41:
|
||||
_config_int(to_save, "config_password_min_length")
|
||||
|
|
|
@ -165,6 +165,7 @@ class _Settings(_Base):
|
|||
config_password_number = Column(Boolean, default=True)
|
||||
config_password_lower = Column(Boolean, default=True)
|
||||
config_password_upper = Column(Boolean, default=True)
|
||||
config_password_character = Column(Boolean, default=True)
|
||||
config_password_special = Column(Boolean, default=True)
|
||||
config_session = Column(Integer, default=1)
|
||||
config_ratelimiter = Column(Boolean, default=True)
|
||||
|
|
|
@ -22,6 +22,7 @@ import random
|
|||
import io
|
||||
import mimetypes
|
||||
import re
|
||||
import regex
|
||||
import shutil
|
||||
import socket
|
||||
from datetime import datetime, timedelta
|
||||
|
@ -54,7 +55,8 @@ from . import calibre_db, cli_param
|
|||
from .tasks.convert import TaskConvert
|
||||
from . import logger, config, db, ub, fs
|
||||
from . import gdriveutils as gd
|
||||
from .constants import STATIC_DIR as _STATIC_DIR, CACHE_TYPE_THUMBNAILS, THUMBNAIL_TYPE_COVER, THUMBNAIL_TYPE_SERIES, SUPPORTED_CALIBRE_BINARIES
|
||||
from .constants import (STATIC_DIR as _STATIC_DIR, CACHE_TYPE_THUMBNAILS, THUMBNAIL_TYPE_COVER, THUMBNAIL_TYPE_SERIES,
|
||||
SUPPORTED_CALIBRE_BINARIES)
|
||||
from .subproc_wrapper import process_wait
|
||||
from .services.worker import WorkerThread
|
||||
from .tasks.mail import TaskEmail
|
||||
|
@ -694,14 +696,16 @@ def valid_password(check_password):
|
|||
if config.config_password_min_length > 0:
|
||||
verify += r"^(?=.{" + str(config.config_password_min_length) + ",}$)"
|
||||
if config.config_password_number:
|
||||
verify += r"(?=.*?\d)"
|
||||
verify += "(?=.*?\d)"
|
||||
if config.config_password_lower:
|
||||
verify += r"(?=.*?[a-z])"
|
||||
verify += "(?=.*?[\p{Ll}])"
|
||||
if config.config_password_upper:
|
||||
verify += r"(?=.*?[A-Z])"
|
||||
verify += "(?=.*?[\p{Lu}])"
|
||||
if config.config_password_character:
|
||||
verify += "(?=.*?[\p{Letter}])"
|
||||
if config.config_password_special:
|
||||
verify += r"(?=.*?[^A-Za-z\s0-9])"
|
||||
match = re.match(verify, check_password)
|
||||
verify += "(?=.*?[^\p{Letter}\s0-9])"
|
||||
match = regex.match(verify, check_password)
|
||||
if not match:
|
||||
raise Exception(_("Password doesn't comply with password validation rules"))
|
||||
return check_password
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
"wordSequences": "Das Passwort enthält Buchstabensequenzen",
|
||||
"wordLowercase": "Bitte mindestens einen Kleinbuchstaben verwenden",
|
||||
"wordUppercase": "Bitte mindestens einen Großbuchstaben verwenden",
|
||||
"word": "Bitte mindestens einen Buchstaben verwenden",
|
||||
"wordOneNumber": "Bitte mindestens eine Ziffern verwenden",
|
||||
"wordOneSpecialChar": "Bitte mindestens ein Sonderzeichen verwenden",
|
||||
"errorList": "Fehler:",
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
"wordRepetitions": "Too many repetitions",
|
||||
"wordSequences": "Your password contains sequences",
|
||||
"wordLowercase": "Use at least one lowercase character",
|
||||
"word": "Use at least one character",
|
||||
"wordUppercase": "Use at least one uppercase character",
|
||||
"wordOneNumber": "Use at least one number",
|
||||
"wordOneSpecialChar": "Use at least one special character",
|
||||
|
|
|
@ -144,13 +144,13 @@ try {
|
|||
|
||||
validation.wordTwoCharacterClasses = function(options, word, score) {
|
||||
var specialCharRE = new RegExp(
|
||||
'(.' + options.rules.specialCharClass + ')'
|
||||
'(.' + options.rules.specialCharClass + ')', 'u'
|
||||
);
|
||||
|
||||
if (
|
||||
word.match(/([a-z].*[A-Z])|([A-Z].*[a-z])/) ||
|
||||
(word.match(/([a-zA-Z])/) && word.match(/([0-9])/)) ||
|
||||
(word.match(specialCharRE) && word.match(/[a-zA-Z0-9_]/))
|
||||
word.match(/(\p{Ll}.*\p{Lu})|(\p{Lu}.*\p{Ll})/u) ||
|
||||
(word.match(/(\p{Letter})/u) && word.match(/([0-9])/)) ||
|
||||
(word.match(specialCharRE) && word.match(/[\p{Letter}0-9_]/u))
|
||||
) {
|
||||
return score;
|
||||
}
|
||||
|
@ -202,11 +202,15 @@ try {
|
|||
};
|
||||
|
||||
validation.wordLowercase = function(options, word, score) {
|
||||
return word.match(/[a-z]/) && score;
|
||||
return word.match(/\p{Ll}/u) && score;
|
||||
};
|
||||
|
||||
validation.wordUppercase = function(options, word, score) {
|
||||
return word.match(/[A-Z]/) && score;
|
||||
return word.match(/\p{Lu}/u) && score;
|
||||
};
|
||||
|
||||
validation.word = function(options, word, score) {
|
||||
return word.match(/\p{Letter}/u) && score;
|
||||
};
|
||||
|
||||
validation.wordOneNumber = function(options, word, score) {
|
||||
|
@ -218,7 +222,7 @@ try {
|
|||
};
|
||||
|
||||
validation.wordOneSpecialChar = function(options, word, score) {
|
||||
var specialCharRE = new RegExp(options.rules.specialCharClass);
|
||||
var specialCharRE = new RegExp(options.rules.specialCharClass, 'u');
|
||||
return word.match(specialCharRE) && score;
|
||||
};
|
||||
|
||||
|
@ -228,27 +232,27 @@ try {
|
|||
options.rules.specialCharClass +
|
||||
'.*' +
|
||||
options.rules.specialCharClass +
|
||||
')'
|
||||
')', 'u'
|
||||
);
|
||||
|
||||
return word.match(twoSpecialCharRE) && score;
|
||||
};
|
||||
|
||||
validation.wordUpperLowerCombo = function(options, word, score) {
|
||||
return word.match(/([a-z].*[A-Z])|([A-Z].*[a-z])/) && score;
|
||||
return word.match(/(\p{Ll}.*\p{Lu})|(\p{Lu}.*\p{Ll})/u) && score;
|
||||
};
|
||||
|
||||
validation.wordLetterNumberCombo = function(options, word, score) {
|
||||
return word.match(/([a-zA-Z])/) && word.match(/([0-9])/) && score;
|
||||
return word.match(/([\p{Letter}])/u) && word.match(/([0-9])/) && score;
|
||||
};
|
||||
|
||||
validation.wordLetterNumberCharCombo = function(options, word, score) {
|
||||
var letterNumberCharComboRE = new RegExp(
|
||||
'([a-zA-Z0-9].*' +
|
||||
'([\p{Letter}0-9].*' +
|
||||
options.rules.specialCharClass +
|
||||
')|(' +
|
||||
options.rules.specialCharClass +
|
||||
'.*[a-zA-Z0-9])'
|
||||
'.*[\p{Letter}0-9])', 'u'
|
||||
);
|
||||
|
||||
return word.match(letterNumberCharComboRE) && score;
|
||||
|
@ -341,6 +345,7 @@ defaultOptions.rules.scores = {
|
|||
wordTwoCharacterClasses: 2,
|
||||
wordRepetitions: -25,
|
||||
wordLowercase: 1,
|
||||
word: 1,
|
||||
wordUppercase: 3,
|
||||
wordOneNumber: 3,
|
||||
wordThreeNumbers: 5,
|
||||
|
@ -361,6 +366,7 @@ defaultOptions.rules.activated = {
|
|||
wordTwoCharacterClasses: true,
|
||||
wordRepetitions: true,
|
||||
wordLowercase: true,
|
||||
word: true,
|
||||
wordUppercase: true,
|
||||
wordOneNumber: true,
|
||||
wordThreeNumbers: true,
|
||||
|
@ -372,7 +378,7 @@ defaultOptions.rules.activated = {
|
|||
wordIsACommonPassword: true
|
||||
};
|
||||
defaultOptions.rules.raisePower = 1.4;
|
||||
defaultOptions.rules.specialCharClass = "(?=.*?[^A-Za-z\s0-9])"; //'[!,@,#,$,%,^,&,*,?,_,~]';
|
||||
defaultOptions.rules.specialCharClass = "(?=.*?[^\\p{Letter}\\s0-9])"; //'[!,@,#,$,%,^,&,*,?,_,~]';
|
||||
// List taken from https://github.com/danielmiessler/SecLists (MIT License)
|
||||
defaultOptions.rules.commonPasswords = [
|
||||
'123456',
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -38,22 +38,20 @@ $(document).ready(function() {
|
|||
showVerdicts: false,
|
||||
}
|
||||
options.rules= {
|
||||
specialCharClass: "(?=.*?[^A-Za-z\\s0-9])",
|
||||
specialCharClass: "(?=.*?[^\\p{Letter}\\s0-9])",
|
||||
activated: {
|
||||
wordNotEmail: false,
|
||||
wordMinLength: $('#password').data("min"),
|
||||
// wordMaxLength: false,
|
||||
// wordInvalidChar: true,
|
||||
wordSimilarToUsername: false,
|
||||
wordSequences: false,
|
||||
wordTwoCharacterClasses: false,
|
||||
wordRepetitions: false,
|
||||
wordLowercase: $('#password').data("lower") === "True" ? true : false,
|
||||
wordUppercase: $('#password').data("upper") === "True" ? true : false,
|
||||
word: $('#password').data("word") === "True" ? true : false,
|
||||
wordOneNumber: $('#password').data("number") === "True" ? true : false,
|
||||
wordThreeNumbers: false,
|
||||
wordOneSpecialChar: $('#password').data("special") === "True" ? true : false,
|
||||
// wordTwoSpecialChar: true,
|
||||
wordUpperLowerCombo: false,
|
||||
wordLetterNumberCombo: false,
|
||||
wordLetterNumberCharCombo: false
|
||||
|
|
|
@ -410,6 +410,10 @@
|
|||
<input type="checkbox" id="config_password_upper" name="config_password_upper" {% if config.config_password_upper %}checked{% endif %}>
|
||||
<label for="config_password_upper">{{_('Enforce uppercase characters')}}</label>
|
||||
</div>
|
||||
<div class="form-group" style="margin-left:10px;">
|
||||
<input type="checkbox" id="config_password_character" name="config_password_character" {% if config.config_password_character %}checked{% endif %}>
|
||||
<label for="config_password_lower">{{_('Enforce characters (needed For Chinese/Japanese/Korean Characters)')}}</label>
|
||||
</div>
|
||||
<div class="form-group" style="margin-left:10px;">
|
||||
<input type="checkbox" id="config_password_special" name="config_password_special" {% if config.config_password_special %}checked{% endif %}>
|
||||
<label for="config_password_special">{{_('Enforce special characters')}}</label>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
{% endif %}
|
||||
<div class="form-group">
|
||||
<label for="password">{{_('Password')}}</label>
|
||||
<input type="password" class="form-control" name="password" id="password" data-lang="{{ current_user.locale }}" data-verify="{{ config.config_password_policy }}" {% if config.config_password_policy %} data-min={{ config.config_password_min_length }} data-special={{ config.config_password_special }} data-upper={{ config.config_password_upper }} data-lower={{ config.config_password_lower }} data-number={{ config.config_password_number }}{% endif %} value="" autocomplete="off">
|
||||
<input type="password" class="form-control" name="password" id="password" data-lang="{{ current_user.locale }}" data-verify="{{ config.config_password_policy }}" {% if config.config_password_policy %} data-min={{ config.config_password_min_length }} data-word={{ config.config_password_character }} data-special={{ config.config_password_special }} data-upper={{ config.config_password_upper }} data-lower={{ config.config_password_lower }} data-number={{ config.config_password_number }}{% endif %} value="" autocomplete="off">
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="form-group">
|
||||
|
@ -177,7 +177,7 @@
|
|||
<script src="{{ url_for('static', filename='js/libs/bootstrap-table/bootstrap-editable.min.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/libs/pwstrength/i18next.min.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/libs/pwstrength/i18nextHttpBackend.min.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/libs/pwstrength/pwstrength-bootstrap.min.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/libs/pwstrength/pwstrength-bootstrap.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/password.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/table.js') }}"></script>
|
||||
{% endblock %}
|
||||
|
|
|
@ -18,3 +18,4 @@ flask-wtf>=0.14.2,<1.3.0
|
|||
chardet>=3.0.0,<4.1.0
|
||||
advocate>=1.0.0,<1.1.0
|
||||
Flask-Limiter>=2.3.0,<3.6.0
|
||||
regex>=2022.3.2,<2024.2.25
|
||||
|
|
Loading…
Reference in New Issue
Block a user