From f7733559de7c8cd8a5bd683b2c53b523b6fb639b Mon Sep 17 00:00:00 2001 From: Chris Wagner Date: Sun, 7 Jan 2024 15:38:43 -0800 Subject: [PATCH] Updates for python 3.12 - add CI support - utcnow is deprecated - create our own so DB is still naive UTC timestamps - remove use of dateutil (it was just used for tests) - many places didn't use DATETIME_FACTORY - now they do - update translations - many copyright dates - remove JSON compatibility code for Flask pre 2.x --- .github/workflows/tests.yml | 12 +- .pre-commit-config.yaml | 2 +- CHANGES.rst | 1 + LICENSE | 2 +- README.rst | 2 +- docs/conf.py | 4 +- docs/configuration.rst | 5 +- examples/unified_signin/server/api.py | 6 +- flask_security/__init__.py | 7 +- flask_security/core.py | 6 +- flask_security/datastore.py | 30 +- flask_security/json.py | 40 +-- flask_security/models/fsqla.py | 9 +- flask_security/password_util.py | 2 +- .../af_ZA/LC_MESSAGES/flask_security.po | 284 +++++++++-------- .../ca_ES/LC_MESSAGES/flask_security.po | 275 ++++++++-------- .../da_DK/LC_MESSAGES/flask_security.po | 275 ++++++++-------- .../de_DE/LC_MESSAGES/flask_security.po | 294 +++++++++--------- .../es_ES/LC_MESSAGES/flask_security.po | 292 ++++++++--------- .../eu_ES/LC_MESSAGES/flask_security.po | 281 +++++++++-------- .../translations/flask_security.pot | 267 ++++++++-------- .../fr_FR/LC_MESSAGES/flask_security.po | 282 +++++++++-------- .../hu_HU/LC_MESSAGES/flask_security.po | 285 +++++++++-------- .../hy_AM/LC_MESSAGES/flask_security.po | 289 ++++++++--------- .../is_IS/LC_MESSAGES/flask_security.po | 275 ++++++++-------- .../it_IT/LC_MESSAGES/flask_security.po | 285 +++++++++-------- .../ja_JP/LC_MESSAGES/flask_security.po | 275 ++++++++-------- .../nl_NL/LC_MESSAGES/flask_security.po | 287 +++++++++-------- .../pl_PL/LC_MESSAGES/flask_security.po | 287 +++++++++-------- .../pt_BR/LC_MESSAGES/flask_security.po | 275 ++++++++-------- .../pt_PT/LC_MESSAGES/flask_security.po | 275 ++++++++-------- .../ru_RU/LC_MESSAGES/flask_security.po | 287 +++++++++-------- .../tr_TR/LC_MESSAGES/flask_security.po | 275 ++++++++-------- .../zh_Hans_CN/LC_MESSAGES/flask_security.po | 277 +++++++++-------- flask_security/utils.py | 35 ++- flask_security/webauthn.py | 7 +- pyproject.toml | 4 +- pytest.ini | 2 + requirements/tests.txt | 5 +- tests/conftest.py | 7 +- tests/test_datastore.py | 16 +- tests/test_webauthn.py | 7 +- tests/view_scaffold.py | 17 +- tox.ini | 6 +- 44 files changed, 3028 insertions(+), 2828 deletions(-) Index: Flask-Security-Too-5.3.3/LICENSE =================================================================== --- Flask-Security-Too-5.3.3.orig/LICENSE +++ Flask-Security-Too-5.3.3/LICENSE @@ -1,7 +1,7 @@ MIT License Copyright (C) 2012-2021 by Matthew Wright -Copyright (C) 2019-2021 by Chris Wagner +Copyright (C) 2019-2024 by Chris Wagner Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in Index: Flask-Security-Too-5.3.3/docs/configuration.rst =================================================================== --- Flask-Security-Too-5.3.3.orig/docs/configuration.rst +++ Flask-Security-Too-5.3.3/docs/configuration.rst @@ -578,9 +578,10 @@ Core - rarely need changing .. py:data:: SECURITY_DATETIME_FACTORY - Specifies the default datetime factory. + Specifies the default datetime factory. The default is naive-UTC which + corresponds to many DB's DateTime type. - Default:``datetime.datetime.utcnow``. + Default:``flask_security.naive_utcnow``. .. py:data:: SECURITY_CONFIRM_SALT Index: Flask-Security-Too-5.3.3/examples/unified_signin/server/api.py =================================================================== --- Flask-Security-Too-5.3.3.orig/examples/unified_signin/server/api.py +++ Flask-Security-Too-5.3.3/examples/unified_signin/server/api.py @@ -1,10 +1,10 @@ """ -Copyright 2020 by J. Christopher Wagner (jwag). All rights reserved. +Copyright 2020-2024 by J. Christopher Wagner (jwag). All rights reserved. :license: MIT, see LICENSE for more details. """ -from datetime import datetime +from datetime import datetime, timezone from flask import Blueprint, abort, current_app, jsonify from flask_security import auth_required @@ -17,7 +17,7 @@ api = Blueprint("api", __name__) @api.route("/health", methods=["GET"]) @auth_required("session") def health(): - return jsonify(secret="lush oranges", date=datetime.utcnow()) + return jsonify(secret="lush oranges", date=datetime.now(timezone.utc)) @api.route("/popmail", methods=["GET"]) Index: Flask-Security-Too-5.3.3/flask_security/__init__.py =================================================================== --- Flask-Security-Too-5.3.3.orig/flask_security/__init__.py +++ Flask-Security-Too-5.3.3/flask_security/__init__.py @@ -2,11 +2,11 @@ flask_security ~~~~~~~~~~~~~~ - Flask-Security is a Flask extension that aims to add quick and simple - security via Flask-Login, Flask-Principal, Flask-WTF, and passlib. + Flask-Security is a Flask extension that aims to add comprehensive security + to Flask applications. :copyright: (c) 2012-2019 by Matt Wright. - :copyright: (c) 2019-2023 by J. Christopher Wagner. + :copyright: (c) 2019-2024 by J. Christopher Wagner. :license: MIT, see LICENSE for more details. """ @@ -111,6 +111,7 @@ from .utils import ( login_user, logout_user, lookup_identity, + naive_utcnow, password_breached_validator, password_complexity_validator, password_length_validator, Index: Flask-Security-Too-5.3.3/flask_security/core.py =================================================================== --- Flask-Security-Too-5.3.3.orig/flask_security/core.py +++ Flask-Security-Too-5.3.3/flask_security/core.py @@ -7,7 +7,7 @@ :copyright: (c) 2012 by Matt Wright. :copyright: (c) 2017 by CERN. :copyright: (c) 2017 by ETH Zurich, Swiss Data Science Center. - :copyright: (c) 2019-2023 by J. Christopher Wagner (jwag). + :copyright: (c) 2019-2024 by J. Christopher Wagner (jwag). :license: MIT, see LICENSE for more details. """ @@ -95,6 +95,7 @@ from .utils import ( get_identity_attributes, get_message, get_request_attr, + naive_utcnow, localize_callback, set_request_attr, uia_email_mapper, @@ -283,7 +284,7 @@ _default_config: t.Dict[str, t.Any] = { "API_ENABLED_METHODS": ["session", "token"], "HASHING_SCHEMES": ["sha256_crypt", "hex_md5"], "DEPRECATED_HASHING_SCHEMES": ["hex_md5"], - "DATETIME_FACTORY": datetime.utcnow, + "DATETIME_FACTORY": naive_utcnow, "TOTP_SECRETS": None, "TOTP_ISSUER": None, "SMS_SERVICE": "Dummy", @@ -1255,6 +1256,7 @@ class Security: self.register_blueprint: bool self.two_factor_plugins: TfPlugin self.oauthglue: t.Optional[OAuthGlue] = None + self.datetime_factory: t.Callable[[], datetime] self.login_manager: "flask_login.LoginManager" self._mail_util: MailUtil Index: Flask-Security-Too-5.3.3/flask_security/datastore.py =================================================================== --- Flask-Security-Too-5.3.3.orig/flask_security/datastore.py +++ Flask-Security-Too-5.3.3/flask_security/datastore.py @@ -5,10 +5,10 @@ This module contains an user datastore classes. :copyright: (c) 2012 by Matt Wright. - :copyright: (c) 2019-2023 by J. Christopher Wagner (jwag). + :copyright: (c) 2019-2024 by J. Christopher Wagner (jwag). :license: MIT, see LICENSE for more details. """ -import datetime +from datetime import datetime import json import typing as t import uuid @@ -817,6 +817,8 @@ class SQLAlchemyUserDatastore(SQLAlchemy extensions: t.Optional[str] = None, **kwargs: t.Any, ) -> None: + from .proxies import _security + if not hasattr(self, "webauthn_model") or not self.webauthn_model: raise NotImplementedError @@ -830,7 +832,7 @@ class SQLAlchemyUserDatastore(SQLAlchemy backup_state=backup_state, transports=transports, extensions=extensions, - lastuse_datetime=datetime.datetime.utcnow(), + lastuse_datetime=_security.datetime_factory(), **kwargs, ) user.webauthn.append(webauthn) @@ -944,6 +946,8 @@ class MongoEngineUserDatastore(MongoEngi extensions: t.Optional[str] = None, **kwargs: t.Any, ) -> None: + from .proxies import _security + if not hasattr(self, "webauthn_model") or not self.webauthn_model: raise NotImplementedError webauthn = self.webauthn_model( @@ -957,7 +961,7 @@ class MongoEngineUserDatastore(MongoEngi backup_state=backup_state, transports=transports, extensions=extensions, - lastuse_datetime=datetime.datetime.utcnow(), + lastuse_datetime=_security.datetime_factory(), **kwargs, ) user.webauthn.append(webauthn) @@ -1079,6 +1083,8 @@ class PeeweeUserDatastore(PeeweeDatastor extensions: t.Optional[str] = None, **kwargs: t.Any, ) -> None: + from .proxies import _security + if not hasattr(self, "webauthn_model") or not self.webauthn_model: raise NotImplementedError webauthn = self.webauthn_model( @@ -1092,7 +1098,7 @@ class PeeweeUserDatastore(PeeweeDatastor backup_state=backup_state, transports=transports, extensions=extensions, - lastuse_datetime=datetime.datetime.utcnow(), + lastuse_datetime=_security.datetime_factory(), **kwargs, ) self.put(webauthn) # type: ignore @@ -1164,9 +1170,9 @@ if t.TYPE_CHECKING: # pragma: no cover fs_uniquifier: str fs_token_uniquifier: str fs_webauthn_user_handle: str - confirmed_at: t.Optional[datetime.datetime] - last_login_at: datetime.datetime - current_login_at: datetime.datetime + confirmed_at: t.Optional[datetime] + last_login_at: datetime + current_login_at: datetime last_login_ip: t.Optional[str] current_login_ip: t.Optional[str] login_count: int @@ -1176,8 +1182,8 @@ if t.TYPE_CHECKING: # pragma: no cover mf_recovery_codes: t.Optional[t.List[str]] us_phone_number: t.Optional[str] us_totp_secrets: t.Optional[t.Union[str, bytes]] - create_datetime: datetime.datetime - update_datetime: datetime.datetime + create_datetime: datetime + update_datetime: datetime roles: t.List["Role"] webauthn: t.List["WebAuthn"] @@ -1189,7 +1195,7 @@ if t.TYPE_CHECKING: # pragma: no cover name: str description: t.Optional[str] permissions: t.Optional[t.List[str]] - update_datetime: datetime.datetime + update_datetime: datetime def __init__(self, **kwargs): ... @@ -1204,7 +1210,7 @@ if t.TYPE_CHECKING: # pragma: no cover backup_state: bool device_type: str extensions: t.Optional[str] - lastuse_datetime: datetime.datetime + lastuse_datetime: datetime user_id: int usage: str Index: Flask-Security-Too-5.3.3/flask_security/json.py =================================================================== --- Flask-Security-Too-5.3.3.orig/flask_security/json.py +++ Flask-Security-Too-5.3.3/flask_security/json.py @@ -1,32 +1,13 @@ """ Flask-Security JSON extensions. - :copyright: (c) 2022-2023 by J. Christopher Wagner (jwag). + :copyright: (c) 2022-2024 by J. Christopher Wagner (jwag). :license: MIT, see LICENSE for more details. Pieces of this code liberally copied from flask-mongoengine. """ -def _use_encoder(superclass): # pragma: no cover - """Flask < 2.2""" - - class FsJsonEncoder(superclass): - """Flask-Security JSON encoder. - Extends Flask's JSONencoder to handle lazy-text. - """ - - def default(self, obj): - from .babel import is_lazy_string - - if is_lazy_string(obj): - return str(obj) - else: - return super().default(obj) - - return FsJsonEncoder - - def _use_provider(superclass): """Flask 2.2 onwards - customize JSONProvider""" @@ -44,19 +25,6 @@ def _use_provider(superclass): def setup_json(app, bp=None): # Called at init_app time. - if hasattr(app, "json_provider_class"): - # Flask >= 2.2 - app.json_provider_class = _use_provider(app.json_provider_class) - app.json = app.json_provider_class(app) - # a bit if a hack - if a package (e.g. flask-mongoengine) hasn't - # converted yet - they might use json_encoder. This ONLY applies - # to this specific version of Flask that happens to use _json_encoder to - # signal if any app/extension has registered an old style encoder. - # (app.json_encoder is always set) - # (If they do, then Flask 2.2.x won't use json_provider at all) - # Of course if they do this AFTER we're initialized all bets are off. - if getattr(app, "_json_encoder", None): - app.json_encoder = _use_encoder(app.json_encoder) - - elif bp: # pragma: no cover - bp.json_encoder = _use_encoder(app.json_encoder) + # Flask >= 2.2 + app.json_provider_class = _use_provider(app.json_provider_class) + app.json = app.json_provider_class(app) Index: Flask-Security-Too-5.3.3/flask_security/models/fsqla.py =================================================================== --- Flask-Security-Too-5.3.3.orig/flask_security/models/fsqla.py +++ Flask-Security-Too-5.3.3/flask_security/models/fsqla.py @@ -1,5 +1,5 @@ """ -Copyright 2019-2021 by J. Christopher Wagner (jwag). All rights reserved. +Copyright 2019-2024 by J. Christopher Wagner (jwag). All rights reserved. :license: MIT, see LICENSE for more details. @@ -11,7 +11,6 @@ BE AWARE: Once any version of this is sh a new version needs to be created. """ -import datetime from typing import cast from sqlalchemy import ( Boolean, @@ -25,7 +24,7 @@ from sqlalchemy.ext.declarative import d from sqlalchemy.ext.mutable import MutableList from sqlalchemy.sql import func -from flask_security import AsaList, RoleMixin, UserMixin +from flask_security import AsaList, RoleMixin, UserMixin, naive_utcnow class FsModels: @@ -77,7 +76,7 @@ class FsRoleMixin(RoleMixin): type_=DateTime, nullable=False, server_default=func.now(), - onupdate=datetime.datetime.utcnow, + onupdate=naive_utcnow, ) @@ -126,5 +125,5 @@ class FsUserMixin(UserMixin): type_=DateTime, nullable=False, server_default=func.now(), - onupdate=datetime.datetime.utcnow, + onupdate=naive_utcnow, ) Index: Flask-Security-Too-5.3.3/flask_security/password_util.py =================================================================== --- Flask-Security-Too-5.3.3.orig/flask_security/password_util.py +++ Flask-Security-Too-5.3.3/flask_security/password_util.py @@ -4,7 +4,7 @@ Utility class providing methods for validating and normalizing passwords. - :copyright: (c) 2020-2021 by J. Christopher Wagner (jwag). + :copyright: (c) 2020-2024 by J. Christopher Wagner (jwag). :license: MIT, see LICENSE for more details. """ Index: Flask-Security-Too-5.3.3/flask_security/utils.py =================================================================== --- Flask-Security-Too-5.3.3.orig/flask_security/utils.py +++ Flask-Security-Too-5.3.3/flask_security/utils.py @@ -5,12 +5,12 @@ Flask-Security utils module :copyright: (c) 2012-2019 by Matt Wright. - :copyright: (c) 2019-2023 by J. Christopher Wagner (jwag). + :copyright: (c) 2019-2024 by J. Christopher Wagner (jwag). :license: MIT, see LICENSE for more details. """ import abc import base64 -import datetime +from datetime import datetime, timedelta, timezone from functools import partial import hashlib import hmac @@ -104,6 +104,25 @@ else: return response +# From a miguel grinberg blog around dealing with 3.12. +# Our default SQLAlchemy Datetime is naive. +# Note that most code should call _security.datetime_factory() +def aware_utcnow(): + return datetime.now(timezone.utc) + + +def aware_utcfromtimestamp(timestamp): + return datetime.fromtimestamp(timestamp, timezone.utc) + + +def naive_utcnow(): + return aware_utcnow().replace(tzinfo=None) + + +def naive_utcfromtimestamp(timestamp): + return aware_utcfromtimestamp(timestamp).replace(tzinfo=None) + + def find_csrf_field_name(): """ We need to clear it on logout (since that isn't being done by Flask-WTF). @@ -207,8 +226,8 @@ def logout_user() -> None: def check_and_update_authn_fresh( - within: datetime.timedelta, - grace: datetime.timedelta, + within: timedelta, + grace: timedelta, method: t.Optional[str] = None, ) -> bool: """Check if user authenticated within specified time and update grace period. @@ -255,7 +274,7 @@ def check_and_update_authn_fresh( # No session, you can't play. return False - now = datetime.datetime.utcnow() + now = naive_utcnow() new_exp = now + grace grace_ts = int(new_exp.timestamp()) @@ -271,7 +290,7 @@ def check_and_update_authn_fresh( session["fs_gexp"] = grace_ts return False - authn_time = datetime.datetime.utcfromtimestamp(session["fs_paa"]) + authn_time = naive_utcfromtimestamp(session["fs_paa"]) # allow for some time drift where it's possible authn_time is in the future # but let's be cautious and not allow arbitrary future times delta = now - authn_time @@ -673,7 +692,7 @@ def get_within_delta(key, app=None): """ txt = config_value(key, app=app) values = txt.split() - return datetime.timedelta(**{values[1]: int(values[0])}) + return timedelta(**{values[1]: int(values[0])}) def send_mail(subject, recipient, template, **context): @@ -758,7 +777,7 @@ def get_token_status(token, serializer, def check_and_get_token_status( - token: str, serializer_name: str, within: datetime.timedelta + token: str, serializer_name: str, within: timedelta ) -> t.Tuple[bool, bool, t.Any]: """Get the status of a token and return data. Index: Flask-Security-Too-5.3.3/flask_security/webauthn.py =================================================================== --- Flask-Security-Too-5.3.3.orig/flask_security/webauthn.py +++ Flask-Security-Too-5.3.3/flask_security/webauthn.py @@ -4,7 +4,7 @@ Flask-Security WebAuthn module - :copyright: (c) 2021-2023 by J. Christopher Wagner (jwag). + :copyright: (c) 2021-2024 by J. Christopher Wagner (jwag). :license: MIT, see LICENSE for more details. This implements support for webauthn/FIDO2 Level 2 using the py_webauthn package. @@ -36,7 +36,6 @@ """ -import datetime import json import time import typing as t @@ -677,7 +676,7 @@ def webauthn_signin_response(token: str) after_this_request(view_commit) assert form.cred assert form.user - form.cred.lastuse_datetime = datetime.datetime.utcnow() + form.cred.lastuse_datetime = _security.datetime_factory() form.cred.sign_count = form.authentication_verification.new_sign_count form.cred.backup_state = getattr( form.authentication_verification, "credential_backed_up", False @@ -833,7 +832,7 @@ def webauthn_verify_response(token: str) # update last use and sign count after_this_request(view_commit) assert form.cred - form.cred.lastuse_datetime = datetime.datetime.utcnow() + form.cred.lastuse_datetime = _security.datetime_factory() form.cred.sign_count = form.authentication_verification.new_sign_count _datastore.put(form.cred) Index: Flask-Security-Too-5.3.3/pyproject.toml =================================================================== --- Flask-Security-Too-5.3.3.orig/pyproject.toml +++ Flask-Security-Too-5.3.3/pyproject.toml @@ -31,6 +31,7 @@ classifiers=[ "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Development Status :: 5 - Production/Stable", @@ -70,7 +71,6 @@ low = [ "babel==2.12.1", "bcrypt==4.0.1", "bleach==6.0.0", - "python-dateutil==2.8.2", "jinja2==3.1.2", "itsdangerous==2.1.2", "markupsafe==2.1.2", @@ -82,6 +82,8 @@ low = [ "qrcode==7.4.2", # authlib requires requests "requests", + # passlib required setuptools + "setuptools", "sqlalchemy==2.0.12", "sqlalchemy-utils==0.41.1", "webauthn==1.11.0", Index: Flask-Security-Too-5.3.3/pytest.ini =================================================================== --- Flask-Security-Too-5.3.3.orig/pytest.ini +++ Flask-Security-Too-5.3.3/pytest.ini @@ -20,6 +20,8 @@ filterwarnings = ignore::DeprecationWarning:mongoengine: ignore::DeprecationWarning:flask_login:0 ignore::DeprecationWarning:pydantic_core:0 + # next for py 3.12 + ignore::DeprecationWarning:pkg_resources:0 ignore::UserWarning:cbor2:0 ignore:.*passwordless feature.*:DeprecationWarning:flask_security:0 ignore::DeprecationWarning:passlib:0 Index: Flask-Security-Too-5.3.3/requirements/tests.txt =================================================================== --- Flask-Security-Too-5.3.3.orig/requirements/tests.txt +++ Flask-Security-Too-5.3.3/requirements/tests.txt @@ -3,7 +3,7 @@ Babel Flask-Login@git+https://github.com/maxcountryman/flask-login@main Flask-Mailman Flask-Principal -peewee +peewee; python_version < '3.12' Flask-SQLAlchemy argon2_cffi authlib @@ -13,7 +13,6 @@ check-manifest coverage cryptography djlint -python-dateutil mongoengine mongomock msgcheck @@ -27,6 +26,8 @@ pytest qrcode # authlib requires requests requests +# passlib requires setuptools which is deprecated +setuptools sqlalchemy sqlalchemy-utils webauthn Index: Flask-Security-Too-5.3.3/tests/conftest.py =================================================================== --- Flask-Security-Too-5.3.3.orig/tests/conftest.py +++ Flask-Security-Too-5.3.3/tests/conftest.py @@ -5,7 +5,7 @@ Test fixtures and what not :copyright: (c) 2017 by CERN. - :copyright: (c) 2019-2022 by J. Christopher Wagner (jwag). + :copyright: (c) 2019-2024 by J. Christopher Wagner (jwag). :license: MIT, see LICENSE for more details. """ @@ -37,6 +37,7 @@ from flask_security import ( auth_token_required, http_auth_required, get_request_attr, + naive_utcnow, login_required, roles_accepted, roles_required, @@ -563,7 +564,7 @@ def sqlalchemy_session_setup(request, ap DateTime, nullable=False, server_default=func.now(), - onupdate=datetime.utcnow, + onupdate=naive_utcnow, ) class User(Base, UserMixin): @@ -597,7 +598,7 @@ def sqlalchemy_session_setup(request, ap DateTime, nullable=False, server_default=func.now(), - onupdate=datetime.utcnow, + onupdate=naive_utcnow, ) @declared_attr Index: Flask-Security-Too-5.3.3/tests/test_datastore.py =================================================================== --- Flask-Security-Too-5.3.3.orig/tests/test_datastore.py +++ Flask-Security-Too-5.3.3/tests/test_datastore.py @@ -5,15 +5,21 @@ Datastore tests :copyright: (c) 2012 by Matt Wright. - :copyright: (c) 2019-2022 by J. Christopher Wagner (jwag). + :copyright: (c) 2019-2024 by J. Christopher Wagner (jwag). :license: MIT, see LICENSE for more details. """ -import datetime from pytest import raises, skip, importorskip from tests.test_utils import init_app_with_options, get_num_queries, is_sqlalchemy -from flask_security import RoleMixin, Security, UserMixin, LoginForm, RegisterForm +from flask_security import ( + RoleMixin, + Security, + UserMixin, + LoginForm, + RegisterForm, + naive_utcnow, +) from flask_security.datastore import Datastore, UserDatastore @@ -294,7 +300,7 @@ def test_modify_permissions(app, datasto assert perms == t1.get_permissions() if hasattr(t1, "update_datetime"): orig_update_time = t1.update_datetime - assert t1.update_datetime <= datetime.datetime.utcnow() + assert t1.update_datetime <= naive_utcnow() ds.add_permissions_to_role(t1, "execute") ds.commit() @@ -414,7 +420,7 @@ def test_uuid(app, request, tmpdir, real username = Column(String(255), unique=True, nullable=True) password = Column(String(255)) active = Column(Boolean()) - created_at = Column(DateTime, default=datetime.datetime.utcnow) + created_at = Column(DateTime, default=naive_utcnow()) confirmed_at = Column(DateTime()) roles = relationship( "Role", secondary="roles_users", backref=backref("users", lazy="dynamic") Index: Flask-Security-Too-5.3.3/tests/test_webauthn.py =================================================================== --- Flask-Security-Too-5.3.3.orig/tests/test_webauthn.py +++ Flask-Security-Too-5.3.3/tests/test_webauthn.py @@ -4,7 +4,7 @@ WebAuthn tests - :copyright: (c) 2021-2023 by J. Christopher Wagner (jwag). + :copyright: (c) 2021-2024 by J. Christopher Wagner (jwag). :license: MIT, see LICENSE for more details. """ @@ -12,7 +12,6 @@ from base64 import urlsafe_b64encode import copy import datetime -from dateutil import parser import json import re import typing as t @@ -383,7 +382,7 @@ def test_basic_json(app, clients, get_me response = clients.get("/wan-register", headers=headers) active_creds = response.json["response"]["registered_credentials"] assert active_creds[0]["name"] == "testr1" - assert parser.parse(active_creds[0]["lastuse"]) == fake_dt + assert datetime.datetime.fromisoformat(active_creds[0]["lastuse"]) == fake_dt # sign in - simple case use identity so we get back allowCredentials logout(clients) @@ -409,7 +408,7 @@ def test_basic_json(app, clients, get_me # fetch credentials and verify lastuse was updated response = clients.get("/wan-register", headers=headers) active_creds = response.json["response"]["registered_credentials"] - assert parser.parse(active_creds[0]["lastuse"]) != fake_dt + assert datetime.datetime.fromisoformat(active_creds[0]["lastuse"]) != fake_dt assert active_creds[0]["transports"] == ["usb"] assert active_creds[0]["usage"] == "first" Index: Flask-Security-Too-5.3.3/tests/view_scaffold.py =================================================================== --- Flask-Security-Too-5.3.3.orig/tests/view_scaffold.py +++ Flask-Security-Too-5.3.3/tests/view_scaffold.py @@ -1,4 +1,4 @@ -# :copyright: (c) 2019-2023 by J. Christopher Wagner (jwag). +# :copyright: (c) 2019-2024 by J. Christopher Wagner (jwag). # :license: MIT, see LICENSE for more details. """ @@ -21,7 +21,7 @@ data and a mail sender that flashes what """ -import datetime +from datetime import timedelta import os import typing as t @@ -46,7 +46,12 @@ from flask_security.signals import ( user_not_registered, user_registered, ) -from flask_security.utils import hash_password, uia_email_mapper, uia_phone_mapper +from flask_security.utils import ( + hash_password, + naive_utcnow, + uia_email_mapper, + uia_phone_mapper, +) def _find_bool(v): @@ -101,8 +106,8 @@ def create_app(): app.config["SECURITY_TOTP_SECRETS"] = { "1": "TjQ9Qa31VOrfEzuPy4VHQWPCTmRzCnFzMKLxXYiZu9B" } - app.config["SECURITY_FRESHNESS"] = datetime.timedelta(minutes=1) - app.config["SECURITY_FRESHNESS_GRACE_PERIOD"] = datetime.timedelta(minutes=2) + app.config["SECURITY_FRESHNESS"] = timedelta(minutes=1) + app.config["SECURITY_FRESHNESS_GRACE_PERIOD"] = timedelta(minutes=2) app.config["SECURITY_USERNAME_ENABLE"] = True app.config["SECURITY_USERNAME_REQUIRED"] = True app.config["SECURITY_PASSWORD_REQUIRED"] = True @@ -286,7 +291,7 @@ def add_user(ds, email, password, roles) roles = [ds.find_or_create_role(rn) for rn in roles] ds.commit() user = ds.create_user( - email=email, password=pw, active=True, confirmed_at=datetime.datetime.utcnow() + email=email, password=pw, active=True, confirmed_at=naive_utcnow() ) ds.commit() for role in roles: