Skip to content

Commit 31d814f

Browse files
committed
fix: default hash method for Werkzeug < 3.0.0
Follow up after dpgaspar#2306. Later configuration was added in dpgaspar#2332 that added default configuration for hash method and also there scrypt was set as default, however Werkzeug < 3.0.0 does not support it. This PR retrieves the default method properly, depending on installed Werkzeug version.
1 parent e2f7fde commit 31d814f

File tree

5 files changed

+28
-15
lines changed

5 files changed

+28
-15
lines changed

bin/hash_db_password.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from werkzeug.security import generate_password_hash
55

66
from flask_appbuilder.security.sqla.models import User
7-
7+
from flask_appbuilder.security.utils import get_default_hash_method
88

99
try:
1010
from app import app, db
@@ -36,11 +36,13 @@
3636
log.error("Config db key {}".format(app.config['SQLALCHEMY_DATABASE_URI']))
3737
exit()
3838

39+
3940
for user in users:
4041
log.info("Hashing password for {0}".format(user.username))
42+
4143
user.password = generate_password_hash(
4244
password=user.password,
43-
method=app.config.get('FAB_PASSWORD_HASH_METHOD', 'scrypt'),
45+
method=get_default_hash_method(app),
4446
salt_length=app.config.get('FAB_PASSWORD_HASH_SALT_LENGTH', 16),
4547
)
4648
try:
@@ -49,5 +51,5 @@
4951
except Exception as e:
5052
db.session.rollback()
5153
log.error("Error updating password for {0}: {1}".format(user.full_name, str(e)))
52-
54+
5355

flask_appbuilder/security/manager.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
RegisterUserOAuthView,
2323
RegisterUserOIDView,
2424
)
25+
from .utils import get_default_hash_method
2526
from .views import (
2627
AuthDBView,
2728
AuthLDAPView,
@@ -63,7 +64,6 @@
6364

6465
log = logging.getLogger(__name__)
6566

66-
6767
class AbstractSecurityManager(BaseManager):
6868
"""
6969
Abstract SecurityManager class, declares all methods used by the
@@ -238,13 +238,16 @@ def __init__(self, appbuilder):
238238

239239
# Werkzeug prior to 3.0.0 does not support scrypt
240240
parsed_werkzeug_version = Version(importlib.metadata.version("werkzeug"))
241+
241242
if parsed_werkzeug_version < Version("3.0.0"):
243+
app.config.setdefault("FAB_PASSWORD_HASH_METHOD", "pbkdf2:sha256")
242244
app.config.setdefault(
243245
"AUTH_DB_FAKE_PASSWORD_HASH_CHECK",
244246
"pbkdf2:sha256:150000$Z3t6fmj2$22da622d94a1f8118"
245247
"c0976a03d2f18f680bfff877c9a965db9eedc51bc0be87c",
246248
)
247249
else:
250+
app.config.setdefault("FAB_PASSWORD_HASH_METHOD", "pbkdf2:sha256")
248251
app.config.setdefault(
249252
"AUTH_DB_FAKE_PASSWORD_HASH_CHECK",
250253
"scrypt:32768:8:1$wiDa0ruWlIPhp9LM$6e40"
@@ -952,9 +955,7 @@ def reset_password(self, userid, password):
952955
user = self.get_user_by_id(userid)
953956
user.password = generate_password_hash(
954957
password=password,
955-
method=self.appbuilder.get_app.config.get(
956-
"FAB_PASSWORD_HASH_METHOD", "scrypt"
957-
),
958+
method=get_default_hash_method(),
958959
salt_length=self.appbuilder.get_app.config.get(
959960
"FAB_PASSWORD_HASH_SALT_LENGTH", 16
960961
),

flask_appbuilder/security/sqla/apis/user/api.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,13 @@
1111
UserPutSchema,
1212
)
1313
from flask_appbuilder.security.sqla.models import Group, Role, User
14+
from flask_appbuilder.security.utils import get_default_hash_method
1415
from marshmallow import ValidationError
1516
from sqlalchemy.exc import IntegrityError
1617
from werkzeug.security import generate_password_hash
1718

19+
# Werkzeug prior to 3.0.0 does not support scrypt
20+
parsed_werkzeug_version = Version(importlib.metadata.version("werkzeug"))
1821

1922
class UserApi(ModelRestApi):
2023
resource_name = "security/users"
@@ -74,9 +77,7 @@ def pre_update(self, item, data):
7477
if "password" in data and data["password"]:
7578
item.password = generate_password_hash(
7679
password=data["password"],
77-
method=self.appbuilder.get_app.config.get(
78-
"FAB_PASSWORD_HASH_METHOD", "scrypt"
79-
),
80+
method=get_default_hash_method(self.appbuilder.get_app),
8081
salt_length=self.appbuilder.get_app.config.get(
8182
"FAB_PASSWORD_HASH_SALT_LENGTH", 16
8283
),

flask_appbuilder/security/utils.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,21 @@
1+
import importlib
12
from random import SystemRandom
23
import string
34

5+
from packaging.version import Version
46
LETTERS_AND_DIGITS = string.ascii_letters + string.digits
57

68

79
def generate_random_string(length=30):
810
rand = SystemRandom()
911
return "".join(rand.choice(LETTERS_AND_DIGITS) for _ in range(length))
12+
13+
def get_default_hash_method(app):
14+
"""
15+
Returns the default password hash method based on the Werkzeug version.
16+
"""
17+
parsed_werkzeug_version = Version(importlib.metadata.version("werkzeug"))
18+
if parsed_werkzeug_version < Version("3.0.0"):
19+
return app.config.get('FAB_PASSWORD_HASH_METHOD', 'pbkdf2:sha256')
20+
else:
21+
return app.config.get('FAB_PASSWORD_HASH_METHOD', 'scrypt')

flask_appbuilder/security/views.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
roles_or_groups_required,
2323
UserInfoEdit,
2424
)
25-
from flask_appbuilder.security.utils import generate_random_string
25+
from flask_appbuilder.security.utils import generate_random_string, get_default_hash_method
2626
from flask_appbuilder.utils.base import get_safe_redirect, lazy_formatter_gettext
2727
from flask_appbuilder.validators import PasswordComplexityValidator
2828
from flask_appbuilder.views import expose, ModelView, SimpleFormView
@@ -38,7 +38,6 @@
3838

3939
log = logging.getLogger(__name__)
4040

41-
4241
class PermissionModelView(ModelView):
4342
route_base = "/permissions"
4443
base_permissions = ["can_list"]
@@ -448,9 +447,7 @@ def pre_update(self, item: Any) -> None:
448447
def pre_add(self, item: Any) -> None:
449448
item.password = generate_password_hash(
450449
password=item.password,
451-
method=self.appbuilder.get_app.config.get(
452-
"FAB_PASSWORD_HASH_METHOD", "scrypt"
453-
),
450+
method=get_default_hash_method(self.appbuilder.get_app),
454451
salt_length=self.appbuilder.get_app.config.get(
455452
"FAB_PASSWORD_HASH_SALT_LENGTH", 16
456453
),

0 commit comments

Comments
 (0)