Skip to content

Commit 8282a18

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 8282a18

File tree

8 files changed

+41
-22
lines changed

8 files changed

+41
-22
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/_compat.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
# -*- coding: utf-8 -*-
22
"""
3-
Some py2/py3 compatibility support based on a stripped down
4-
version of six so we don't have to depend on a specific version
5-
of it.
3+
Some py2/py3 compatibility support based on a stripped down
4+
version of six so we don't have to depend on a specific version
5+
of it.
66
7-
:copyright: (c) 2013 by Armin Ronacher.
8-
:license: BSD, see LICENSE for more details.
7+
:copyright: (c) 2013 by Armin Ronacher.
8+
:license: BSD, see LICENSE for more details.
99
"""
1010
import sys
1111

flask_appbuilder/api/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ def wraps(self: "BaseApi", *args: Any, **kwargs: Any) -> Response:
119119

120120

121121
def rison(
122-
schema: Optional[Dict[str, Any]] = None
122+
schema: Optional[Dict[str, Any]] = None,
123123
) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
124124
"""
125125
Use this decorator to parse URI *Rison* arguments to

flask_appbuilder/console.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
"""
2-
Console utility to help manage F.A.B's apps
2+
Console utility to help manage F.A.B's apps
33
4-
use:
4+
use:
55
6-
$ fabmanager --help
6+
$ fabmanager --help
77
"""
8+
89
from io import BytesIO
910
import os
1011
import shutil

flask_appbuilder/security/manager.py

Lines changed: 5 additions & 3 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,
@@ -238,13 +239,16 @@ def __init__(self, appbuilder):
238239

239240
# Werkzeug prior to 3.0.0 does not support scrypt
240241
parsed_werkzeug_version = Version(importlib.metadata.version("werkzeug"))
242+
241243
if parsed_werkzeug_version < Version("3.0.0"):
244+
app.config.setdefault("FAB_PASSWORD_HASH_METHOD", "pbkdf2:sha256")
242245
app.config.setdefault(
243246
"AUTH_DB_FAKE_PASSWORD_HASH_CHECK",
244247
"pbkdf2:sha256:150000$Z3t6fmj2$22da622d94a1f8118"
245248
"c0976a03d2f18f680bfff877c9a965db9eedc51bc0be87c",
246249
)
247250
else:
251+
app.config.setdefault("FAB_PASSWORD_HASH_METHOD", "pbkdf2:sha256")
248252
app.config.setdefault(
249253
"AUTH_DB_FAKE_PASSWORD_HASH_CHECK",
250254
"scrypt:32768:8:1$wiDa0ruWlIPhp9LM$6e40"
@@ -952,9 +956,7 @@ def reset_password(self, userid, password):
952956
user = self.get_user_by_id(userid)
953957
user.password = generate_password_hash(
954958
password=password,
955-
method=self.appbuilder.get_app.config.get(
956-
"FAB_PASSWORD_HASH_METHOD", "scrypt"
957-
),
959+
method=get_default_hash_method(),
958960
salt_length=self.appbuilder.get_app.config.get(
959961
"FAB_PASSWORD_HASH_SALT_LENGTH", 16
960962
),

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
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
@@ -74,9 +75,7 @@ def pre_update(self, item, data):
7475
if "password" in data and data["password"]:
7576
item.password = generate_password_hash(
7677
password=data["password"],
77-
method=self.appbuilder.get_app.config.get(
78-
"FAB_PASSWORD_HASH_METHOD", "scrypt"
79-
),
78+
method=get_default_hash_method(self.appbuilder.get_app),
8079
salt_length=self.appbuilder.get_app.config.get(
8180
"FAB_PASSWORD_HASH_SALT_LENGTH", 16
8281
),

flask_appbuilder/security/utils.py

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

5+
from packaging.version import Version
6+
47
LETTERS_AND_DIGITS = string.ascii_letters + string.digits
58

69

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

flask_appbuilder/security/views.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@
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 (
26+
generate_random_string,
27+
get_default_hash_method,
28+
)
2629
from flask_appbuilder.utils.base import get_safe_redirect, lazy_formatter_gettext
2730
from flask_appbuilder.validators import PasswordComplexityValidator
2831
from flask_appbuilder.views import expose, ModelView, SimpleFormView
@@ -448,9 +451,7 @@ def pre_update(self, item: Any) -> None:
448451
def pre_add(self, item: Any) -> None:
449452
item.password = generate_password_hash(
450453
password=item.password,
451-
method=self.appbuilder.get_app.config.get(
452-
"FAB_PASSWORD_HASH_METHOD", "scrypt"
453-
),
454+
method=get_default_hash_method(self.appbuilder.get_app),
454455
salt_length=self.appbuilder.get_app.config.get(
455456
"FAB_PASSWORD_HASH_SALT_LENGTH", 16
456457
),

0 commit comments

Comments
 (0)